diff --git a/.github/ISSUE_TEMPLATE/feaure_request.yml b/.github/ISSUE_TEMPLATE/feaure_request.yml index 479a5c2b1..34d0f3eb4 100644 --- a/.github/ISSUE_TEMPLATE/feaure_request.yml +++ b/.github/ISSUE_TEMPLATE/feaure_request.yml @@ -1,7 +1,7 @@ name: Feature Request description: Request a new feature from @VisActor/vtable title: '[Feature] ' -labels: [new-feature] +labels: [feature] body: - type: markdown attributes: diff --git a/common/changes/@visactor/vtable/1167-feature-selected-highlight_2024-06-05-03-27.json b/common/changes/@visactor/vtable/1167-feature-selected-highlight_2024-06-05-03-27.json deleted file mode 100644 index 63839fd1b..000000000 --- a/common/changes/@visactor/vtable/1167-feature-selected-highlight_2024-06-05-03-27.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "refactor: memory release logic optimization #1856\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/changes/@visactor/vtable/feat-inputEditorConfig_2024-06-05-03-03.json b/common/changes/@visactor/vtable/feat-inputEditorConfig_2024-06-05-03-03.json deleted file mode 100644 index 97c5b1dc8..000000000 --- a/common/changes/@visactor/vtable/feat-inputEditorConfig_2024-06-05-03-03.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "changes": [ - { - "comment": "feat: add textArea editor\n\n", - "type": "none", - "packageName": "@visactor/vtable" - } - ], - "packageName": "@visactor/vtable", - "email": "892739385@qq.com" -} \ No newline at end of file diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index 2b9f38182..45c56d49c 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.2.0","mainProject":"@visactor/vtable","nextBump":"minor"}] +[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.3.2","mainProject":"@visactor/vtable","nextBump":"patch"}] diff --git a/docs/assets/api/en/icon/base-icon.md b/docs/assets/api/en/icon/base-icon.md index f6539fe15..1ad1ea8f3 100644 --- a/docs/assets/api/en/icon/base-icon.md +++ b/docs/assets/api/en/icon/base-icon.md @@ -20,10 +20,6 @@ IconPosition enumeration type. * */ export enum IconPosition { - /**The icon in front of the text line content follows the text positioning and wraps with the text */ - inlineFront = 'inlineFront', - /**The icon after the text line content, positioned with the text, and wrapped with the text. For example, the sort chart is placed in the first line of the text content */ - inlineEnd = 'inlineEnd', /**Button on the left side of the cell and affected by padding */ left = 'left', /**The button on the right side of the cell is affected by padding, such as the pin chart */ @@ -35,7 +31,12 @@ export enum IconPosition { /**The icon on the right side of the cell content block follows the text positioning and does not wrap with the text */ contentRight = 'contentRight', /**Free positioning in the cell */ - absolute = 'absolute' + absolute = 'absolute', + + /**The icon in front of the text line content follows the text positioning and wraps with the text */ + inlineFront = 'inlineFront', + /**The icon after the text line content, positioned with the text, and wrapped with the text. For example, the sort chart is placed in the first line of the text content */ + inlineEnd = 'inlineEnd', } ``` diff --git a/docs/assets/api/en/methods.md b/docs/assets/api/en/methods.md index 6f62321fb..9aeb62807 100644 --- a/docs/assets/api/en/methods.md +++ b/docs/assets/api/en/methods.md @@ -1157,3 +1157,11 @@ set row height. /**set row height */ setRowHeight: (row: number, height: number) ``` + +## cellIsInVisualView(Function) + +Determines whether the cell is in the visible area of the cell. If the cell is completely in the visible area, it returns true. If part or all of the cell is outside the visible area, it returns false. + +``` + cellIsInVisualView(col: number, row: number) +``` diff --git a/docs/assets/api/zh/icon/base-icon.md b/docs/assets/api/zh/icon/base-icon.md index ceaf5d159..b1b6515e7 100644 --- a/docs/assets/api/zh/icon/base-icon.md +++ b/docs/assets/api/zh/icon/base-icon.md @@ -20,10 +20,6 @@ IconPosition 枚举类型。 * */ export enum IconPosition { - /**文本行内容前面的图标,跟随文本定位,随文本折行 */ - inlineFront = 'inlineFront', - /**文本行内容后面的图标,跟随文本定位,随文本折行。如sort图表 放在文本内容的第一行 */ - inlineEnd = 'inlineEnd', /**单元格左侧按钮 且受padding影响 */ left = 'left', /**单元格右侧按钮 受padding影响 如pin图表 */ @@ -35,7 +31,12 @@ export enum IconPosition { /**在单元格内容块的右侧的图标,跟随文本定位,不随文本折行 */ contentRight = 'contentRight', /**在单元格中自由定位 */ - absolute = 'absolute' + absolute = 'absolute', + + /**文本行内容前面的图标,跟随文本定位,随文本折行 */ + inlineFront = 'inlineFront', + /**文本行内容后面的图标,跟随文本定位,随文本折行。如sort图表 放在文本内容的第一行 */ + inlineEnd = 'inlineEnd', } ``` @@ -50,11 +51,12 @@ icon 的名称,会作为内部缓存的 key。 ${prefix} funcType (IconFuncTypeEnum) -重置VTable内部的icon时需要指定 icon 的功能类型。 +重置 VTable 内部的 icon 时需要指定 icon 的功能类型。 -特别是具有切换状态的功能性的图标请务必配置上funcType,例如排序功能 funcType 配置 sort,name 配置 sort_normal 或 sort_downward,或 sort_upward。这样才能准确替换到内部相应的icon图标。 +特别是具有切换状态的功能性的图标请务必配置上 funcType,例如排序功能 funcType 配置 sort,name 配置 sort_normal 或 sort_downward,或 sort_upward。这样才能准确替换到内部相应的 icon 图标。 + +IconFuncTypeEnum 枚举类型定义: -IconFuncTypeEnum枚举类型定义: ``` enum IconFuncTypeEnum { pin = 'pin', @@ -100,7 +102,8 @@ ${prefix} tooltip (Object) #${prefix} placement (Placement) 气泡框位置,可选值为 top、left、right 或 bottom。 -Placement枚举类型定义: +Placement 枚举类型定义: + ``` enum Placement { top = 'top', @@ -129,4 +132,4 @@ Placement枚举类型定义: 气泡框是否显示箭头。 ${prefix} interactive (boolean) -是否可交互,默认为 true。目前已知不可交互按钮为下拉菜单状态。 \ No newline at end of file +是否可交互,默认为 true。目前已知不可交互按钮为下拉菜单状态。 diff --git a/docs/assets/api/zh/methods.md b/docs/assets/api/zh/methods.md index 4babb23b7..1109e4fe4 100644 --- a/docs/assets/api/zh/methods.md +++ b/docs/assets/api/zh/methods.md @@ -1155,3 +1155,11 @@ interface ISortedMapItem { /**设置行高 */ setRowHeight: (row: number, height: number) ``` + +## cellIsInVisualView(Function) + +判断单元格是否在单元格可见区域,如果单元格完全都在可见区域才会返回 true,如果有部分或者完全都在可见区域外就返回 false + +``` + cellIsInVisualView(col: number, row: number) +``` diff --git a/docs/assets/changelog/en/release.md b/docs/assets/changelog/en/release.md index 3af01cbd4..0b0b0299b 100644 --- a/docs/assets/changelog/en/release.md +++ b/docs/assets/changelog/en/release.md @@ -1,3 +1,75 @@ +# v1.3.2 + +2024-06-17 + + +**🆕 New feature** + +- **@visactor/vtable**: add blankAreaClickDeselect and outsideClickDeselect config + +**🐛 Bug fix** + +- **@visactor/vtable**: cellIsInVisualView api error [#1864](https://github.com/VisActor/VTable/issues/1864) +- **@visactor/vtable**: if set style autoWrapText, this config not wort when resize column width [#1892](https://github.com/VisActor/VTable/issues/1892) + +**🔨 Refactor** + +- **@visactor/vtable**: tooltip support scroll [#1887](https://github.com/VisActor/VTable/issues/1887) +- **@visactor/vtable**: when not records pivot table can show corner header [#1895](https://github.com/VisActor/VTable/issues/1895) +- **@visactor/vtable**: when rowTree children not set value can supplement indicators [#1924](https://github.com/VisActor/VTable/issues/1924) + + + +[more detail about v1.3.2](https://github.com/VisActor/VTable/releases/tag/v1.3.2) + +# v1.3.1 + +2024-06-14 + + +**🐛 Bug fix** + +- **@visactor/vtable**: fix frozenColCount large than colCount error [#1872](https://github.com/VisActor/VTable/issues/1872) +- **@visactor/vtable**: fix merge cell size update [#1869](https://github.com/VisActor/VTable/issues/1869) +- **@visactor/vtable**: optimize row height update when useOneRowHeightFillAll + +**📖 Site / documentation update** + +- **@visactor/vtable**: update changlog of rush + + + +[more detail about v1.3.1](https://github.com/VisActor/VTable/releases/tag/v1.3.1) + +# v1.3.0 + +2024-06-12 + + +**🆕 New feature** + +- **@visactor/vtable**: add ignoreIcon&formatExportOutput config in vtable-export [#1813](https://github.com/VisActor/VTable/issues/1813) +- **@visactor/vtable**: add textArea editor +- **@visactor/vtable**: add strokeColor style [#1847](https://github.com/VisActor/VTable/issues/1847) +- **@visactor/vtable**: add dx&dy in title component [#1874](https://github.com/VisActor/VTable/issues/1874) +- **@visactor/vtable**: add shrinkSparklineFirst config [#1862](https://github.com/VisActor/VTable/issues/1862) +- **@visactor/vtable**: tooltip disappear delay time [#1848](https://github.com/VisActor/VTable/issues/1848) +- **@visactor/vtable**: add sort config for pivotTable [#1865](https://github.com/VisActor/VTable/issues/1865) + +**🐛 Bug fix** + +- **@visactor/vtable**: icon inlineEnd inlineFront x position compute error [#1882](https://github.com/VisActor/VTable/issues/1882) +- **@visactor/vtable**: drill down icon can not be click [#1899](https://github.com/VisActor/VTable/issues/1899) +- **@visactor/vtable**: fix frozenColCount large than colCount error [#1872](https://github.com/VisActor/VTable/issues/1872) +- **@visactor/vtable**: fix ellipsis error in _disableColumnAndRowSizeRound mode [#1884](https://github.com/VisActor/VTable/issues/1884) + +**🔨 Refactor** + +- **@visactor/vtable**: memory release logic optimization [#1856](https://github.com/VisActor/VTable/issues/1856) +- **@visactor/vtable**: arrow key with shift ctrl key to select cells [#1873](https://github.com/VisActor/VTable/issues/1873) + +[more detail about v1.3.0](https://github.com/VisActor/VTable/releases/tag/v1.3.0) + # v1.2.0 2024-06-06 diff --git a/docs/assets/changelog/zh/release.md b/docs/assets/changelog/zh/release.md index b74719bb5..c70e7fb73 100644 --- a/docs/assets/changelog/zh/release.md +++ b/docs/assets/changelog/zh/release.md @@ -1,32 +1,98 @@ -# v1.2.0 +# v1.3.2 -2024-06-06 +2024-06-17 **🆕 新增功能** -- **@visactor/vtable**: support select highlightMode effect [#1167](https://github.com/VisActor/VTable/issues/1167) -- **@visactor/vtable**: add isAggregation api [#1803](https://github.com/VisActor/VTable/issues/1803) -- **@visactor/vtable**: optimize large column performance [#1840](https://github.com/VisActor/VTable/issues/1840) [#1824](https://github.com/VisActor/VTable/issues/1824) -- **@visactor/vtable**: add merge cell custom graphic attribute sync [#1718](https://github.com/VisActor/VTable/issues/1718) +- **@visactor/vtable**: add blankAreaClickDeselect and outsideClickDeselect config **🐛 功能修复** -- **@visactor/vtable**: when has no records should not has aggregation row [#1804](https://github.com/VisActor/VTable/issues/1804) -- **@visactor/vtable**: updateColumns set editor error [#1828](https://github.com/VisActor/VTable/issues/1828) -- **@visactor/vtable**: fix maxCharactersNumber effect [#1830](https://github.com/VisActor/VTable/issues/1830) +- **@visactor/vtable**: cellIsInVisualView api error [#1864](https://github.com/VisActor/VTable/issues/1864) +- **@visactor/vtable**: if set style autoWrapText, this config not wort when resize column width [#1892](https://github.com/VisActor/VTable/issues/1892) **🔨 功能重构** -- **@visactor/vtable**: update pixelRatio when resize [#1823](https://github.com/VisActor/VTable/issues/1823) -- **@visactor/vtable**: selectAllOnCtrlA option +- **@visactor/vtable**: tooltip support scroll [#1887](https://github.com/VisActor/VTable/issues/1887) +- **@visactor/vtable**: when not records pivot table can show corner header [#1895](https://github.com/VisActor/VTable/issues/1895) +- **@visactor/vtable**: when rowTree children not set value can supplement indicators [#1924](https://github.com/VisActor/VTable/issues/1924) + -**🔧 项目配置** + +[更多详情请查看 v1.3.2](https://github.com/VisActor/VTable/releases/tag/v1.3.2) + +# v1.3.1 + +2024-06-14 + + +**🐛 功能修复** + +- **@visactor/vtable**: fix frozenColCount large than colCount error [#1872](https://github.com/VisActor/VTable/issues/1872) +- **@visactor/vtable**: fix merge cell size update [#1869](https://github.com/VisActor/VTable/issues/1869) +- **@visactor/vtable**: optimize row height update when useOneRowHeightFillAll + +**📖 文档更新** -- **@visactor/vtable**: update vrender version +- **@visactor/vtable**: update changlog of rush +[更多详情请查看 v1.3.1](https://github.com/VisActor/VTable/releases/tag/v1.3.1) + +# v1.3.0 + +2024-06-12 + + +**🆕 新增功能** + +- **@visactor/vtable**: vtable-export增加ignoreIcon&formatExportOutput配置 [#1813](https://github.com/VisActor/VTable/issues/1813) +- **@visactor/vtable**: 增加 textArea editor +- **@visactor/vtable**: 增加 strokeColor 样式 [#1847](https://github.com/VisActor/VTable/issues/1847) +- **@visactor/vtable**: title component增加 dx&dy 配置 [#1874](https://github.com/VisActor/VTable/issues/1874) +- **@visactor/vtable**: 增加 shrinkSparklineFirst 配置 [#1862](https://github.com/VisActor/VTable/issues/1862) +- **@visactor/vtable**: 增加 tooltip 消失延迟时间 [#1848](https://github.com/VisActor/VTable/issues/1848) +- **@visactor/vtable**: 增加透视表排序配置 [#1865](https://github.com/VisActor/VTable/issues/1865) + +**🐛 功能修复** + +- **@visactor/vtable**: 修复部分图标位置计算问题 [#1882](https://github.com/VisActor/VTable/issues/1882) +- **@visactor/vtable**: 修复下钻按钮点击问题 [#1899](https://github.com/VisActor/VTable/issues/1899) +- **@visactor/vtable**: 修复frozenColCount超过列数时的显示问题 [#1872](https://github.com/VisActor/VTable/issues/1872) +- **@visactor/vtable**: 修复_disableColumnAndRowSizeRound模式下文字省略问题 [#1884](https://github.com/VisActor/VTable/issues/1884) + +**🔨 功能重构** + +- **@visactor/vtable**: 优化内存释放逻辑 [#1856](https://github.com/VisActor/VTable/issues/1856) +- **@visactor/vtable**: 支持方向键 + shift ctrl选中多个单元格 [#1873](https://github.com/VisActor/VTable/issues/1873) + +[更多详情请查看 v1.3.0](https://github.com/VisActor/VTable/releases/tag/v1.3.0) + +# v1.2.0 + +2024-06-06 + + +**🆕 新增功能** + +- **@visactor/vtable**: 增加select highlightMode效果 [#1167](https://github.com/VisActor/VTable/issues/1167) +- **@visactor/vtable**: 补充isAggregation api [#1803](https://github.com/VisActor/VTable/issues/1803) +- **@visactor/vtable**: 优化大量列时的性能问题 [#1840](https://github.com/VisActor/VTable/issues/1840) [#1824](https://github.com/VisActor/VTable/issues/1824) +- **@visactor/vtable**: 补充合并单元格自定义图元更新 [#1718](https://github.com/VisActor/VTable/issues/1718) + +**🐛 功能修复** + +- **@visactor/vtable**: 修复无数据时汇总行展示 [#1804](https://github.com/VisActor/VTable/issues/1804) +- **@visactor/vtable**: 修复updateColumns时设置editor问题 [#1828](https://github.com/VisActor/VTable/issues/1828) +- **@visactor/vtable**: 修复maxCharactersNumber效果 [#1830](https://github.com/VisActor/VTable/issues/1830) + +**🔨 功能重构** + +- **@visactor/vtable**: resize时更新pixelRatio [#1823](https://github.com/VisActor/VTable/issues/1823) +- **@visactor/vtable**: 增加selectAllOnCtrlA配置 + [更多详情请查看 v1.2.0](https://github.com/VisActor/VTable/releases/tag/v1.2.0) # v1.1.2 @@ -35,7 +101,7 @@ **🔧 项目配置** - + - **@visactor/vtable**: update vrender version [更多详情请查看 v1.1.2](https://github.com/VisActor/VTable/releases/tag/v1.1.2) @@ -46,12 +112,12 @@ **🐛 功能修复** - -- **@visactor/vtable**: when set emptyTip interaction not work well with has records [#1818](https://github.com/VisActor/VTable/issues/1818) -- **@visactor/vtable**: fix table frame corner radius display problem [#1783](https://github.com/VisActor/VTable/issues/1783) + +- **@visactor/vtable**: when set emptyTip interaction not work well with has records [#1818](https://github.com/VisActor/VTable/issues/1818) +- **@visactor/vtable**: fix table frame corner radius display problem [#1783](https://github.com/VisActor/VTable/issues/1783) **🔨 功能重构** - + - **@visactor/vtable**: dimension value same with indicator key cell value error [#1817](https://github.com/VisActor/VTable/issues/1817) [更多详情请查看 v1.1.1](https://github.com/VisActor/VTable/releases/tag/v1.1.1) diff --git a/docs/assets/demo/en/basic-functionality/pre-sort.md b/docs/assets/demo/en/basic-functionality/pre-sort.md index 3d98f1243..3789b2805 100644 --- a/docs/assets/demo/en/basic-functionality/pre-sort.md +++ b/docs/assets/demo/en/basic-functionality/pre-sort.md @@ -4,7 +4,7 @@ group: Basic Features title: Pre Sort cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pre-sort.png order: 3-2 -link: '../guide/basic_function/sort' +link: '../guide/basic_function/sort/list_sort' --- # Pre Sort @@ -36,54 +36,54 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/test-demo-data field: 'id', title: 'ID', width: 80, - sort: true, + sort: true }, { field: 'email1', title: 'email(pre-sorted)', width: 250, - sort: true, + sort: true }, { field: 'hobbies', title: 'hobbies(unsorted)', width: 200, - sort: true, + sort: true }, { field: 'birthday', title: 'birthday', - width: 120, + width: 120 }, { field: 'sex', title: 'sex', - width: 100, + width: 100 }, { field: 'tel', title: 'telephone', - width: 150, + width: 150 }, { field: 'work', title: 'job', - width: 200, + width: 200 }, { field: 'city', title: 'city', - width: 150, - }, + width: 150 + } ]; const option = { records: data.data, columns }; - const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID),option); + const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window['tableInstance'] = tableInstance; - - data.sort.forEach((item) => { + + data.sort.forEach(item => { tableInstance.setSortedIndexMap(item.key, item.value); }); }); diff --git a/docs/assets/demo/en/basic-functionality/sort.md b/docs/assets/demo/en/basic-functionality/sort.md index 4496e8e3a..4e392507c 100644 --- a/docs/assets/demo/en/basic-functionality/sort.md +++ b/docs/assets/demo/en/basic-functionality/sort.md @@ -4,7 +4,7 @@ group: Basic Features title: Sort cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/sort.gif order: 3-2 -link: '../guide/basic_function/sort' +link: '../guide/basic_function/sort/list_sort' option: ListTable-columns-text#sort --- @@ -14,101 +14,99 @@ In this example, the columns \["Order ID", "Customer ID", "Quantity", "Sales", " ## Key Configurations -* `columns[x].sort` Set to true to sort by default rules, or set the function form to specify the sorting rules - sort: (v1: any, v2: any, order: 'desc'|'asc'|'normal') => { - if (order === 'desc') { - return v1 === v2 ? 0 : v1 > v2 ? -1 : 1; - } - return v1 === v2 ? 0 : v1 > v2 ? 1 : -1; - } +- `columns[x].sort` Set to true to sort by default rules, or set the function form to specify the sorting rules + sort: (v1: any, v2: any, order: 'desc'|'asc'|'normal') => { + if (order === 'desc') { + return v1 === v2 ? 0 : v1 > v2 ? -1 : 1; + } + return v1 === v2 ? 0 : v1 > v2 ? 1 : -1; + } ## Code demo ```javascript livedemo template=vtable - -let tableInstance; - fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') - .then((res) => res.json()) - .then((data) => { - -const columns =[ - { - "field": "Order ID", - "title": "Order ID", - "width": "auto", - "sort":true - }, - { - "field": "Customer ID", - "title": "Customer ID", - "width": "auto", - "sort":true - }, - { - "field": "Product Name", - "title": "Product Name", - "width": "auto" - }, - { - "field": "Category", - "title": "Category", - "width": "auto" - }, - { - "field": "Sub-Category", - "title": "Sub-Category", - "width": "auto" - }, - { - "field": "Region", - "title": "Region", - "width": "auto" - }, - { - "field": "City", - "title": "City", - "width": "auto" - }, - { - "field": "Order Date", - "title": "Order Date", - "width": "auto" - }, - { - "field": "Quantity", - "title": "Quantity", - "width": "auto", - "sort":true - }, - { - "field": "Sales", - "title": "Sales", - "width": "auto", - "sort":(v1, v2, order) => { - if (order === 'desc') { - return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? -1 : 1; +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') + .then(res => res.json()) + .then(data => { + const columns = [ + { + field: 'Order ID', + title: 'Order ID', + width: 'auto', + sort: true + }, + { + field: 'Customer ID', + title: 'Customer ID', + width: 'auto', + sort: true + }, + { + field: 'Product Name', + title: 'Product Name', + width: 'auto' + }, + { + field: 'Category', + title: 'Category', + width: 'auto' + }, + { + field: 'Sub-Category', + title: 'Sub-Category', + width: 'auto' + }, + { + field: 'Region', + title: 'Region', + width: 'auto' + }, + { + field: 'City', + title: 'City', + width: 'auto' + }, + { + field: 'Order Date', + title: 'Order Date', + width: 'auto' + }, + { + field: 'Quantity', + title: 'Quantity', + width: 'auto', + sort: true + }, + { + field: 'Sales', + title: 'Sales', + width: 'auto', + sort: (v1, v2, order) => { + if (order === 'desc') { + return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? -1 : 1; + } + return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? 1 : -1; } - return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? 1 : -1; }, - }, - { - "field": "Profit", - "title": "Profit", - "width": "auto", - "sort":true - } -]; + { + field: 'Profit', + title: 'Profit', + width: 'auto', + sort: true + } + ]; -const option = { - records:data, - columns, - sortState:{ - field:"Sales", - order:'asc' - }, - widthMode:'standard' -}; -tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); -window['tableInstance'] = tableInstance; - }) + const option = { + records: data, + columns, + sortState: { + field: 'Sales', + order: 'asc' + }, + widthMode: 'standard' + }; + tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); ``` diff --git a/docs/assets/demo/en/cell-type/list-table-chart.md b/docs/assets/demo/en/cell-type/list-table-chart.md index 15c514a11..9343de817 100644 --- a/docs/assets/demo/en/cell-type/list-table-chart.md +++ b/docs/assets/demo/en/cell-type/list-table-chart.md @@ -21,233 +21,233 @@ Combine vchart chart library and render it into tables to enrich visual display ## Code Demo ```javascript livedemo template=vtable - VTable.register.chartModule('vchart', VChart); - const records = [ - { - projectName: 'Project No.1', - startTime: '2023/5/1', - endTime: '2023/5/10', - estimateDays: 10, - goal: 0.6, - progress: [ - { - value: 0.5, - label: '50%', - goal: 0.6 - } - ], - master:'Julin' +VTable.register.chartModule('vchart', VChart); +const records = [ + { + projectName: 'Project No.1', + startTime: '2023/5/1', + endTime: '2023/5/10', + estimateDays: 10, + goal: 0.6, + progress: [ + { + value: 0.5, + label: '50%', + goal: 0.6 + } + ], + master: 'Julin' + }, + { + projectName: 'Project No.2', + startTime: '2023/5/1', + endTime: '2023/5/5', + estimateDays: 5, + goal: 0.5, + progress: [ + { + value: 0.5, + label: '50%', + goal: 0.5 + } + ], + master: 'Jack' + }, + { + projectName: 'Project No.3', + startTime: '2023/5/7', + endTime: '2023/5/8', + estimateDays: 3, + goal: 0.2, + progress: [ + { + value: 0.3, + label: '30%', + goal: 0.2 + } + ], + master: 'Mary' + }, + { + projectName: 'Project No.4', + startTime: '2023/5/11', + endTime: '2023/5/12', + estimateDays: 2, + goal: 0.8, + progress: [ + { + value: 0.9, + label: '90%', + goal: 0.8 + } + ], + master: 'Porry' + }, + { + projectName: 'Project No.5', + startTime: '2023/5/0', + endTime: '2023/5/10', + estimateDays: 2, + goal: 1, + progress: [ + { + value: 0.8, + label: '80%', + goal: 1 + } + ], + master: 'Sheery' + } +]; +const columns = [ + { + field: 'projectName', + title: 'Project Name', + width: 'auto', + style: { + color: '#ff689d', + fontWeight: 'bold' + } + }, + { + field: 'progress', + title: 'Schedule', + width: 300, + cellType: 'chart', + chartModule: 'vchart', + style: { + padding: 1 }, - { - projectName: 'Project No.2', - startTime: '2023/5/1', - endTime: '2023/5/5', - estimateDays: 5, - goal: 0.5, - progress: [ - { - value: 0.5, - label: '50%', - goal: 0.5 + chartSpec: { + type: 'linearProgress', + progress: { + style: { + fill: '#32a645', + lineCap: '' } - ], - master:'Jack' - }, - { - projectName: 'Project No.3', - startTime: '2023/5/7', - endTime: '2023/5/8', - estimateDays: 3, - goal: 0.2, - progress: [ + }, + data: { + id: 'id0' + }, + direction: 'horizontal', + xField: 'value', + yField: 'label', + seriesField: 'type', + cornerRadius: 20, + bandWidth: 12, + padding: 10, + axes: [ { - value: 0.3, - label: '30%', - goal: 0.2 - } - ], - master:'Mary' - }, - { - projectName: 'Project No.4', - startTime: '2023/5/11', - endTime: '2023/5/12', - estimateDays: 2, - goal: 0.8, - progress: [ + orient: 'right', + type: 'band', + domainLine: { visible: false }, + tick: { visible: false }, + label: { + formatMethod: val => val, + style: { + fontSize: 14, + fontWeight: 'bold', + fill: '#32a645' + } + }, + maxWidth: '60%' // 配置坐标轴的最大空间 + }, { - value: 0.9, - label: '90%', - goal: 0.8 + orient: 'bottom', + label: { visible: true, inside: true }, + type: 'linear', + visible: false, + grid: { + visible: false + } } ], - master:'Porry' - }, - { - projectName: 'Project No.5', - startTime: '2023/5/0', - endTime: '2023/5/10', - estimateDays: 2, - goal: 1, - progress: [ + extensionMark: [ { - value: 0.8, - label: '80%', - goal: 1 - } - ], - master:'Sheery' - } - ]; - const columns = [ - { - field: 'projectName', - title: 'Project Name', - width: 'auto', - style: { - color: '#ff689d', - fontWeight: 'bold' - } - }, - { - field: 'progress', - title: 'Schedule', - width: 300, - cellType: 'chart', - chartModule: 'vchart', - style: { - padding: 1 - }, - chartSpec: { - type: 'linearProgress', - progress: { + type: 'rule', + dataId: 'id0', + visible: true, style: { - fill: '#32a645', - lineCap: '' + x: (datum, ctx, elements, dataView) => { + debugger; + return ctx.valueToX([datum.goal]); + }, + y: (datum, ctx, elements, dataView) => { + return ctx.valueToY([datum.label]) - 5; + }, + x1: (datum, ctx, elements, dataView) => { + return ctx.valueToX([datum.goal]); + }, + y1: (datum, ctx, elements, dataView) => { + return ctx.valueToY([datum.label]) + 5; + }, + stroke: 'red', + lineWidth: 2 } }, - data: { - id: 'id0' - }, - direction: 'horizontal', - xField: 'value', - yField: 'label', - seriesField: 'type', - height: 150, - cornerRadius: 20, - bandWidth: 12, - axes: [ - { - orient: 'right', - type: 'band', - domainLine: { visible: false }, - tick: { visible: false }, - label: { - formatMethod: val => val, - style: { - fontSize: 14, - fontWeight: 'bold', - fill: '#32a645' - } + { + type: 'symbol', + dataId: 'id0', + visible: true, + style: { + symbolType: 'triangleDown', + x: (datum, ctx, elements, dataView) => { + return ctx.valueToX([datum.goal]); }, - maxWidth: '60%' // 配置坐标轴的最大空间 - }, - { - orient: 'bottom', - label: { visible: true, inside: true }, - type: 'linear', - visible: false, - grid: { - visible: false - } - } - ], - extensionMark: [ - { - type: 'rule', - dataId: 'id0', - visible: true, - style: { - x: (datum, ctx, elements, dataView) => { - debugger; - return ctx.valueToX([datum.goal]); - }, - y: (datum, ctx, elements, dataView) => { - return ctx.valueToY([datum.label]) - 5; - }, - x1: (datum, ctx, elements, dataView) => { - return ctx.valueToX([datum.goal]); - }, - y1: (datum, ctx, elements, dataView) => { - return ctx.valueToY([datum.label]) + 5; - }, - stroke: 'red', - lineWidth: 2 - } - }, - { - type: 'symbol', - dataId: 'id0', - visible: true, - style: { - symbolType: 'triangleDown', - x: (datum, ctx, elements, dataView) => { - return ctx.valueToX([datum.goal]); - }, - y: (datum, ctx, elements, dataView) => { - return ctx.valueToY([datum.label]) - 10; - }, - size: 15, - scaleY: 0.5, - fill: 'red' - } + y: (datum, ctx, elements, dataView) => { + return ctx.valueToY([datum.label]) - 10; + }, + size: 15, + scaleY: 0.5, + fill: 'red' } - ] - } - }, - { - field: 'goal', - title: 'Target', - width: 'auto', - fieldFormat(rec) { - return rec.goal * 100 + '%'; - }, - style: { - color: 'red', - fontWeight: 'bold' - } - }, - { - field: 'startTime', - title: 'Start Time', - width: 'auto' - }, - { - field: 'endTime', - title: 'End Time', - width: 'auto' + } + ] + } + }, + { + field: 'goal', + title: 'Target', + width: 'auto', + fieldFormat(rec) { + return rec.goal * 100 + '%'; }, - { - field: 'master', - title: 'Master', - width: 'auto', - style: { - color: 'purple', - fontWeight: 'bold' - } + style: { + color: 'red', + fontWeight: 'bold' + } + }, + { + field: 'startTime', + title: 'Start Time', + width: 'auto' + }, + { + field: 'endTime', + title: 'End Time', + width: 'auto' + }, + { + field: 'master', + title: 'Master', + width: 'auto', + style: { + color: 'purple', + fontWeight: 'bold' } - ]; + } +]; - const option = { - records, - columns, - widthMode: 'standard', - hover: { - highlightMode: 'cross' - }, - defaultRowHeight: 60, - autoFillWidth:true, - }; -const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID),option); +const option = { + records, + columns, + widthMode: 'standard', + hover: { + highlightMode: 'cross' + }, + defaultRowHeight: 60, + autoFillWidth: true +}; +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window['tableInstance'] = tableInstance; ``` diff --git a/docs/assets/demo/en/component/tooltip.md b/docs/assets/demo/en/component/tooltip.md index 1b1ad3592..a2f5c0133 100644 --- a/docs/assets/demo/en/component/tooltip.md +++ b/docs/assets/demo/en/component/tooltip.md @@ -9,109 +9,153 @@ option: ListTable#tooltip.isShowOverflowTextTooltip # Tooltip -In this example, tooltip.isShowOverflowTextTooltip is configured to be true, and it will be prompted when the text that cannot be omitted is hover. -At the same time through monitoring`mouseenter_cell`Event, when the mouse moves into the cell that meets the prompt condition \[first column order number], the interface showTooltip is called to display the prompt information. +This example shows tooltips for four scenarios. + +1. Set `tooltip.isShowOverflowTextTooltip` to `true` to enable overflow text prompts. When hovering over the text that is too long to be displayed, the text will be displayed. In this example, the text in the cells of the `Product Name` column is omitted, and you can hover over the cell to see the prompt information. + +2. The description information of the table header is displayed by configuring `description`. + +3. This example also shows how to actively display the tooltip through the interface. By listening to the `mouseenter_cell` event, when the mouse moves into the cell of the first column of order numbers, the interface `showTooltip` is called to display the prompt information. + +4. Customize the prompt information of the icon, configure `headerIcon` in the `orderId` column to `order`, and configure `tooltip` in the configuration of the icon `order` to display the prompt information. + +The prompt information supports hovering to select and copy. When there is too much content, the maximum width and height can be configured for scrolling interaction. ## Key Configurations -\-`tooltip.isShowOverflowTextTooltip` Enable the prompt for long omitted text +-`tooltip.isShowOverflowTextTooltip` Enable the prompt for long omitted text -\-`showTooltip` Show the calling interface of tooltip +-`showTooltip` Show the calling interface of tooltip ## Code demo -```javascript livedemo template=vtable - -let tableInstance; - fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') - .then((res) => res.json()) - .then((data) => { +## 代码演示 -const columns =[ - { - "field": "Order ID", - "title": "Order ID", - "width": "auto" - }, - { - "field": "Customer ID", - "title": "Customer ID", - "width": "auto" - }, - { - "field": "Product Name", - "title": "Product Name", - "width": "200" - }, - { - "field": "Category", - "title": "Category", - "width": "auto" - }, - { - "field": "Sub-Category", - "title": "Sub-Category", - "width": "auto" - }, - { - "field": "Region", - "title": "Region", - "width": "auto" - }, - { - "field": "City", - "title": "City", - "width": "auto" - }, - { - "field": "Order Date", - "title": "Order Date", - "width": "auto" - }, - { - "field": "Quantity", - "title": "Quantity", - "width": "auto" - }, - { - "field": "Sales", - "title": "Sales", - "width": "auto" - }, - { - "field": "Profit", - "title": "Profit", - "width": "auto" - } -]; +```javascript livedemo template=vtable +VTable.register.icon('order', { + type: 'svg', + svg: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/order.svg', + width: 22, + height: 22, + name: 'order', + positionType: VTable.TYPES.IconPosition.right, + marginRight: 0, + hover: { + width: 22, + height: 22, + bgColor: 'rgba(101, 117, 168, 0.1)' + }, + tooltip: { + // 气泡框,按钮的的解释信息 + title: + 'Order ID is the unique identifier for each order.\n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique', + style: { bgColor: 'black', arrowMark: true, color: 'white', maxHeight: 100, maxWidth: 200 }, + disappearDelay: 100 + }, + cursor: 'pointer' +}); +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') + .then(res => res.json()) + .then(data => { + const columns = [ + { + field: 'Order ID', + title: 'Order ID', + width: 'auto', + headerIcon: 'order', + description: 'Order ID is the unique identifier for each order.\n It is a unique identifier for each order.' + }, + { + field: 'Customer ID', + title: 'Customer ID', + width: 'auto', + description: + 'Customer ID is the unique identifier for each customer.\n It is a unique identifier for each customer.' + }, + { + field: 'Product Name', + title: 'Product Name', + width: '200', + description: 'Product Name is the name of the product.' + }, + { + field: 'Category', + title: 'Category', + width: 'auto', + description: 'Category is the category of the product.' + }, + { + field: 'Sub-Category', + title: 'Sub-Category', + width: 'auto', + description: 'Sub-Category is the sub-category of the product.' + }, + { + field: 'Region', + title: 'Region', + width: 'auto', + description: 'Region is the region of the order produced.' + }, + { + field: 'City', + title: 'City', + width: 'auto', + description: 'City is the city of the order produced.' + }, + { + field: 'Order Date', + title: 'Order Date', + width: 'auto', + description: 'Order Date is the date of the order produced.' + }, + { + field: 'Quantity', + title: 'Quantity', + width: 'auto', + description: 'Quantity is the quantity of the order.' + }, + { + field: 'Sales', + title: 'Sales', + width: 'auto', + description: 'Sales is the sales of the order.' + }, + { + field: 'Profit', + title: 'Profit', + width: 'auto', + description: 'Profit is the profit of the order.' + } + ]; - const option = { - records:data, - columns, - widthMode:'standard', - tooltip:{ - isShowOverflowTextTooltip: true, - } - }; - tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); - window['tableInstance'] = tableInstance; - tableInstance.on('mouseenter_cell', (args) => { - const { col, row, targetIcon } = args; - if(col===0&&row>=1){ - const rect = tableInstance.getVisibleCellRangeRelativeRect({ col, row }); - tableInstance.showTooltip(col, row, { - content: 'Order ID:'+tableInstance.getCellValue(col,row), - referencePosition: { rect, placement: VTable.TYPES.Placement.right }, //TODO - className: 'defineTooltip', - style: { - bgColor: 'black', - color: 'white', - font: 'normal bold normal 14px/1 STKaiti', - arrowMark: true, - }, - }); - } + const option = { + records: data, + columns, + widthMode: 'standard', + tooltip: { + isShowOverflowTextTooltip: true + } + }; + tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + tableInstance.on('mouseenter_cell', args => { + const { col, row, targetIcon } = args; + if (col === 0 && row >= 1) { + const rect = tableInstance.getVisibleCellRangeRelativeRect({ col, row }); + tableInstance.showTooltip(col, row, { + content: 'Order ID:' + tableInstance.getCellValue(col, row), + referencePosition: { rect, placement: VTable.TYPES.Placement.right }, //TODO + className: 'defineTooltip', + disappearDelay: 100, + style: { + bgColor: 'black', + color: 'white', + font: 'normal bold normal 14px/1 STKaiti', + arrowMark: true + } + }); + } }); -}) + }); ``` - diff --git a/docs/assets/demo/en/data-analysis/pivot-analysis-sort-dimension.md b/docs/assets/demo/en/data-analysis/pivot-analysis-sort-dimension.md index 127d1f82c..652047a15 100644 --- a/docs/assets/demo/en/data-analysis/pivot-analysis-sort-dimension.md +++ b/docs/assets/demo/en/data-analysis/pivot-analysis-sort-dimension.md @@ -9,7 +9,7 @@ option: PivotTable#dataConfig.sortRules # Sort dimension values of pivot analysis table -The pivot table is sorted according to the dimension value of a certain dimension. SortRules can be configured in dataConfig. Multiple sorting rules can be configured. The one configured first has a higher priority. +The pivot table is sorted according to the dimension value of a certain dimension. SortRules can be configured in dataConfig. Multiple sorting rules can be configured. The one configured first has a higher priority. In this example, the rows dimension 'Sub-Category' is configured with sort: true, which will display a sort icon in the header cell that displays the dimension name. Click the icon to sort by dimension value. ## Key Configurations @@ -40,6 +40,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American { dimensionKey: 'Sub-Category', title: 'Sub-Catogery', + sort: true, headerStyle: { textStick: true }, @@ -133,8 +134,8 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American sortBy: ['Office Supplies', 'Technology', 'Furniture'] }, { - sortField: 'Sub-Category', - sortBy: ['Chairs', 'Tables', 'Labels', 'Art', 'Paper', 'Appliances'] + sortField: 'Segment', + sortBy: ['Home Office', 'Consumer', 'Corporate'] } ] }, diff --git a/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md b/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md index 39e33ab1d..d2fcffd52 100644 --- a/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md +++ b/docs/assets/demo/en/data-analysis/pivot-analysis-sort-indicator.md @@ -9,7 +9,7 @@ option: PivotTable#dataConfig.sortRules # Pivot analysis table is sorted by indicator value -The pivot table is sorted according to the dimension value of a certain dimension. SortRules can be configured in dataConfig. Multiple sorting rules can be configured. The one configured first has a higher priority. +The pivot table is sorted according to the dimension value of a certain dimension. SortRules can be configured in dataConfig. Multiple sorting rules can be configured. The one configured first has a higher priority. In this example, the indicators indicator is configured with sort:true, which will display a sort icon in the header cell that displays the indicator name. Click the icon to sort by indicator value. ## Key Configurations @@ -75,7 +75,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American indicatorKey: 'Quantity', title: 'Quantity', width: 'auto', - showSort: false, + sort: true, headerStyle: { fontWeight: 'normal' }, @@ -100,7 +100,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American indicatorKey: 'Sales', title: 'Sales', width: 'auto', - showSort: false, + sort: true, headerStyle: { fontWeight: 'normal' }, diff --git a/docs/assets/demo/en/export/table-export-format.md b/docs/assets/demo/en/export/table-export-format.md new file mode 100644 index 000000000..ecfd11b36 --- /dev/null +++ b/docs/assets/demo/en/export/table-export-format.md @@ -0,0 +1,240 @@ +--- +category: examples +group: export +title: Table export (custom export) +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/checkbox-demo.png +order: 4-6 +link: '../../guide/export/excel' +# option: ListTable +--- + +# Table export(custom export) + +By default, when exporting, the text or image inside the exported cell will be output to Excel. If you need to customize the export content, you can set `formatExportOutput` to a function, and the return value of the function is the exported string. If the return value is `undefined`, the default export logic will be processed. + +## Code demo + +```javascript livedemo template=vtable +// You need to introduce the plug-in package when using it `@visactor/vtable-export` +// import { +// downloadCsv, +// exportVTableToCsv, +// downloadExcel, +// exportVTableToExcel, +// } from "@visactor/vtable-export"; +// When umd is introduced, the export tool will be mounted to VTable.export + +const data = [ + { + 类别: '办公用品', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '信封', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色信封', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色信封', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '器具', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '订书机', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '计算器', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] + }, + { + 类别: '技术', + 销售额: '229.696', + 数量: '20', + 利润: '90.704', + children: [ + { + 类别: '设备', // 对应原子类别 + 销售额: '225.44', + 数量: '5', + 利润: '462.56' + }, + { + 类别: '配件', // 对应原子类别 + 销售额: '375.92', + 数量: '8', + 利润: '550.2' + }, + { + 类别: '复印机', // 对应原子类别 + 销售额: '425.44', + 数量: '7', + 利润: '342.56' + }, + { + 类别: '电话', // 对应原子类别 + 销售额: '175.92', + 数量: '6', + 利润: '750.2' + } + ] + }, + { + 类别: '家具', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '桌子', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色桌子', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色桌子', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '椅子', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '老板椅', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '沙发椅', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] + }, + { + 类别: '生活家电(懒加载)', + 销售额: '229.696', + 数量: '20', + 利润: '90.704' + } +]; +const option = { + container: document.getElementById(CONTAINER_ID), + columns: [ + { + field: '类别', + tree: true, + title: '类别', + width: 'auto', + sort: true + }, + { + field: '销售额', + title: '销售额', + width: 'auto', + sort: true + // tree: true, + }, + { + field: '利润', + title: '利润', + width: 'auto', + sort: true + } + ], + showPin: true, //显示VTable内置冻结列图标 + widthMode: 'standard', + allowFrozenColCount: 2, + records: data, + + hierarchyIndent: 20, + hierarchyExpandLevel: 2, + hierarchyTextStartAlignment: true, + sortState: { + field: '销售额', + order: 'asc' + }, + theme: VTable.themes.BRIGHT, + defaultRowHeight: 32 +}; +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window.tableInstance = tableInstance; + +bindExport(); + +function bindExport() { + let exportContainer = document.getElementById('export-buttom'); + if (exportContainer) { + exportContainer.parentElement.removeChild(exportContainer); + } + + exportContainer = document.createElement('div'); + exportContainer.id = 'export-buttom'; + exportContainer.style.position = 'absolute'; + exportContainer.style.bottom = '0'; + exportContainer.style.right = '0'; + + window['tableInstance'].getContainer().appendChild(exportContainer); + + const exportCsvButton = document.createElement('button'); + exportCsvButton.innerHTML = 'CSV-export'; + const exportExcelButton = document.createElement('button'); + exportExcelButton.innerHTML = 'Excel-export'; + exportContainer.appendChild(exportCsvButton); + exportContainer.appendChild(exportExcelButton); + + exportCsvButton.addEventListener('click', () => { + if (window.tableInstance) { + downloadCsv(exportVTableToCsv(window.tableInstance), 'export'); + } + }); + + exportExcelButton.addEventListener('click', async () => { + if (window.tableInstance) { + downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + } + }); +} +``` diff --git a/docs/assets/demo/en/export/table-export-ignore-icon.md b/docs/assets/demo/en/export/table-export-ignore-icon.md new file mode 100644 index 000000000..3e0c1a4ed --- /dev/null +++ b/docs/assets/demo/en/export/table-export-ignore-icon.md @@ -0,0 +1,108 @@ +--- +category: examples +group: export +title: 表格导出(忽略图标) +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/export-tree.png +order: 4-6 +link: '../../guide/export/excel' +# option: ListTable +--- + +# 表格导出(忽略图标) + +By default, when the cell has an icon, the icon and text will be treated as an image when exporting. If you do not need to export the icon, you can set `ignoreIcon` to true, and only the text will be output. + +## Code demo + +```javascript livedemo template=vtable +// You need to introduce the plug-in package when using it `@visactor/vtable-export` +// import { +// downloadCsv, +// exportVTableToCsv, +// downloadExcel, +// exportVTableToExcel, +// } from "@visactor/vtable-export"; +// When umd is introduced, the export tool will be mounted to VTable.export + +const records = [ + { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, + { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, + { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, + { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, + { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } +]; + +const columns = [ + { + field: 'isCheck', + title: '', + width: 60, + headerType: 'checkbox', + cellType: 'checkbox' + }, + { + field: 'productName', + title: 'productName', + width: 120 + }, + { + field: 'price', + title: 'checkbox', + width: 120, + cellType: 'checkbox', + disable: true, + checked: true + }, + { + field: 'check', + title: 'checkbox', + width: 120, + cellType: 'checkbox' + // disable: true + } +]; +const option = { + records, + columns +}; +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window.tableInstance = tableInstance; + +bindExport(); + +function bindExport() { + let exportContainer = document.getElementById('export-buttom'); + if (exportContainer) { + exportContainer.parentElement.removeChild(exportContainer); + } + + exportContainer = document.createElement('div'); + exportContainer.id = 'export-buttom'; + exportContainer.style.position = 'absolute'; + exportContainer.style.bottom = '0'; + exportContainer.style.right = '0'; + + window['tableInstance'].getContainer().appendChild(exportContainer); + + const exportCsvButton = document.createElement('button'); + exportCsvButton.innerHTML = 'CSV-export'; + const exportExcelButton = document.createElement('button'); + exportExcelButton.innerHTML = 'Excel-export'; + exportContainer.appendChild(exportCsvButton); + exportContainer.appendChild(exportExcelButton); + + exportCsvButton.addEventListener('click', () => { + if (window.tableInstance) { + downloadCsv(exportVTableToCsv(window.tableInstance), 'export'); + } + }); + + exportExcelButton.addEventListener('click', async () => { + if (window.tableInstance) { + downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + } + }); +} +``` diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index f0bd83932..3e858ac01 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -394,15 +394,6 @@ "title": { "zh": "排序", "en": "Sort" - }, - "meta": { - "title": "Sort", - "keywords": "", - "category": "demo", - "group": "Basic Features", - "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/sort.gif", - "link": "'../../guide/basic_function/sort'", - "option": "" } }, { @@ -410,15 +401,6 @@ "title": { "zh": "预排序", "en": "Pre Sort" - }, - "meta": { - "title": "Pre Sort", - "keywords": "", - "category": "demo", - "group": "Basic Features", - "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pre-sort.png", - "link": "'../../guide/basic_function/sort'", - "option": "" } }, { @@ -426,15 +408,6 @@ "title": { "zh": "合并单元格", "en": "Merge Cells" - }, - "meta": { - "title": "Merge Cells", - "keywords": "", - "category": "demo", - "group": "Basic Features", - "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/merge.png", - "link": "", - "option": "" } }, { @@ -442,15 +415,6 @@ "title": { "zh": "自定义合并单元格", "en": "Custom Merge Cells" - }, - "meta": { - "title": "Custom Merge Cells", - "keywords": "", - "category": "demo", - "group": "Basic Features", - "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/custom-merge-custom.png", - "link": "", - "option": "" } }, { @@ -458,15 +422,6 @@ "title": { "zh": "适应容器高度", "en": "Row Height Mode - Adapt to Content" - }, - "meta": { - "title": "Row Height Mode - Adapt to Content", - "keywords": "", - "category": "demo", - "group": "Basic Features", - "cover": "https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/width-mode-adaptive.png", - "link": "'../../guide/basic_function/row_height_column_width'", - "option": "" } }, { @@ -1289,6 +1244,20 @@ "link": "'../../guide/export/excel'", "option": "" } + }, + { + "path": "table-export-ignore-icon", + "title": { + "zh": "表格导出", + "en": "table export" + } + }, + { + "path": "table-export-format", + "title": { + "zh": "表格导出", + "en": "table export" + } } ] }, diff --git a/docs/assets/demo/zh/basic-functionality/pre-sort.md b/docs/assets/demo/zh/basic-functionality/pre-sort.md index ce2c6c964..625cadeaa 100644 --- a/docs/assets/demo/zh/basic-functionality/pre-sort.md +++ b/docs/assets/demo/zh/basic-functionality/pre-sort.md @@ -4,7 +4,7 @@ group: Basic Features title: 预排序 cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/pre-sort.png order: 3-2 -link: '../guide/basic_function/sort' +link: '../guide/basic_function/sort/list_sort' --- # 预排序 @@ -36,54 +36,54 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/test-demo-data field: 'id', title: 'ID', width: 80, - sort: true, + sort: true }, { field: 'email1', title: 'email(pre-sorted)', width: 250, - sort: true, + sort: true }, { field: 'hobbies', title: 'hobbies(unsorted)', width: 200, - sort: true, + sort: true }, { field: 'birthday', title: 'birthday', - width: 120, + width: 120 }, { field: 'sex', title: 'sex', - width: 100, + width: 100 }, { field: 'tel', title: 'telephone', - width: 150, + width: 150 }, { field: 'work', title: 'job', - width: 200, + width: 200 }, { field: 'city', title: 'city', - width: 150, - }, + width: 150 + } ]; const option = { records: data.data, columns }; - const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID),option); + const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window['tableInstance'] = tableInstance; - - data.sort.forEach((item) => { + + data.sort.forEach(item => { tableInstance.setSortedIndexMap(item.key, item.value); }); }); diff --git a/docs/assets/demo/zh/basic-functionality/sort.md b/docs/assets/demo/zh/basic-functionality/sort.md index 8c3c346e0..b2b218558 100644 --- a/docs/assets/demo/zh/basic-functionality/sort.md +++ b/docs/assets/demo/zh/basic-functionality/sort.md @@ -4,7 +4,7 @@ group: Basic Features title: 排序 cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/sort.gif order: 3-2 -link: '../guide/basic_function/sort' +link: '../guide/basic_function/sort/list_sort' option: ListTable-columns-text#sort --- @@ -14,103 +14,101 @@ option: ListTable-columns-text#sort ## 关键配置 -- `columns[x].sort` 设置为true 按默认规则排序,或者设置函数形式指定排序规则 - ``` +- `columns[x].sort` 设置为 true 按默认规则排序,或者设置函数形式指定排序规则 + ``` sort: (v1: any, v2: any, order: 'desc'|'asc'|'normal') => { if (order === 'desc') { return v1 === v2 ? 0 : v1 > v2 ? -1 : 1; } return v1 === v2 ? 0 : v1 > v2 ? 1 : -1; } - ``` + ``` ## 代码演示 ```javascript livedemo template=vtable - -let tableInstance; - fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') - .then((res) => res.json()) - .then((data) => { - -const columns =[ - { - "field": "Order ID", - "title": "Order ID", - "width": "auto", - "sort":true - }, - { - "field": "Customer ID", - "title": "Customer ID", - "width": "auto", - "sort":true - }, - { - "field": "Product Name", - "title": "Product Name", - "width": "auto" - }, - { - "field": "Category", - "title": "Category", - "width": "auto" - }, - { - "field": "Sub-Category", - "title": "Sub-Category", - "width": "auto" - }, - { - "field": "Region", - "title": "Region", - "width": "auto" - }, - { - "field": "City", - "title": "City", - "width": "auto" - }, - { - "field": "Order Date", - "title": "Order Date", - "width": "auto" - }, - { - "field": "Quantity", - "title": "Quantity", - "width": "auto", - "sort":true - }, - { - "field": "Sales", - "title": "Sales", - "width": "auto", - "sort":(v1, v2, order) => { - if (order === 'desc') { - return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? -1 : 1; +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') + .then(res => res.json()) + .then(data => { + const columns = [ + { + field: 'Order ID', + title: 'Order ID', + width: 'auto', + sort: true + }, + { + field: 'Customer ID', + title: 'Customer ID', + width: 'auto', + sort: true + }, + { + field: 'Product Name', + title: 'Product Name', + width: 'auto' + }, + { + field: 'Category', + title: 'Category', + width: 'auto' + }, + { + field: 'Sub-Category', + title: 'Sub-Category', + width: 'auto' + }, + { + field: 'Region', + title: 'Region', + width: 'auto' + }, + { + field: 'City', + title: 'City', + width: 'auto' + }, + { + field: 'Order Date', + title: 'Order Date', + width: 'auto' + }, + { + field: 'Quantity', + title: 'Quantity', + width: 'auto', + sort: true + }, + { + field: 'Sales', + title: 'Sales', + width: 'auto', + sort: (v1, v2, order) => { + if (order === 'desc') { + return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? -1 : 1; + } + return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? 1 : -1; } - return Number(v1) === Number(v2) ? 0 : Number(v1) > Number(v2) ? 1 : -1; }, - }, - { - "field": "Profit", - "title": "Profit", - "width": "auto", - "sort":true - } -]; + { + field: 'Profit', + title: 'Profit', + width: 'auto', + sort: true + } + ]; -const option = { - records:data, - columns, - sortState:{ - field:"Sales", - order:'asc' - }, - widthMode:'standard' -}; -tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID),option); -window['tableInstance'] = tableInstance; - }) + const option = { + records: data, + columns, + sortState: { + field: 'Sales', + order: 'asc' + }, + widthMode: 'standard' + }; + tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); ``` diff --git a/docs/assets/demo/zh/cell-type/list-table-chart.md b/docs/assets/demo/zh/cell-type/list-table-chart.md index d8da3789f..a8d0e8743 100644 --- a/docs/assets/demo/zh/cell-type/list-table-chart.md +++ b/docs/assets/demo/zh/cell-type/list-table-chart.md @@ -9,245 +9,245 @@ option: ListTable-columns-chart#cellType # 基本表格集成图表 -将vchart图表库结合渲染到表格中,丰富可视化展示形式,提升多图表渲染性能。该示例引用了vchart的条形进度条,具体可参考:https://visactor.io/vchart/demo/progress/linear-progress-with-target-value +将 vchart 图表库结合渲染到表格中,丰富可视化展示形式,提升多图表渲染性能。该示例引用了 vchart 的条形进度条,具体可参考:https://visactor.io/vchart/demo/progress/linear-progress-with-target-value ## 关键配置 -- `VTable.register.chartModule('vchart', VChart)` 注册绘制图表的图表库 目前支持VChart -- `cellType: 'chart'` 指定类型chart +- `VTable.register.chartModule('vchart', VChart)` 注册绘制图表的图表库 目前支持 VChart +- `cellType: 'chart'` 指定类型 chart - `chartModule: 'vchart'` 指定注册的图表库名称 -- `chartSpec: {}` 图表spec +- `chartSpec: {}` 图表 spec ## 代码演示 ```javascript livedemo template=vtable - VTable.register.chartModule('vchart', VChart); - const records = [ - { - projectName: 'Project No.1', - startTime: '2023/5/1', - endTime: '2023/5/10', - estimateDays: 10, - goal: 0.6, - progress: [ - { - value: 0.5, - label: '50%', - goal: 0.6 - } - ], - master:'Julin' +VTable.register.chartModule('vchart', VChart); +const records = [ + { + projectName: 'Project No.1', + startTime: '2023/5/1', + endTime: '2023/5/10', + estimateDays: 10, + goal: 0.6, + progress: [ + { + value: 0.5, + label: '50%', + goal: 0.6 + } + ], + master: 'Julin' + }, + { + projectName: 'Project No.2', + startTime: '2023/5/1', + endTime: '2023/5/5', + estimateDays: 5, + goal: 0.5, + progress: [ + { + value: 0.5, + label: '50%', + goal: 0.5 + } + ], + master: 'Jack' + }, + { + projectName: 'Project No.3', + startTime: '2023/5/7', + endTime: '2023/5/8', + estimateDays: 3, + goal: 0.2, + progress: [ + { + value: 0.3, + label: '30%', + goal: 0.2 + } + ], + master: 'Mary' + }, + { + projectName: 'Project No.4', + startTime: '2023/5/11', + endTime: '2023/5/12', + estimateDays: 2, + goal: 0.8, + progress: [ + { + value: 0.9, + label: '90%', + goal: 0.8 + } + ], + master: 'Porry' + }, + { + projectName: 'Project No.5', + startTime: '2023/5/0', + endTime: '2023/5/10', + estimateDays: 2, + goal: 1, + progress: [ + { + value: 0.8, + label: '80%', + goal: 1 + } + ], + master: 'Sheery' + } +]; +const columns = [ + { + field: 'projectName', + title: 'Project Name', + width: 'auto', + style: { + color: '#ff689d', + fontWeight: 'bold' + } + }, + { + field: 'progress', + title: 'Schedule', + width: 300, + cellType: 'chart', + chartModule: 'vchart', + style: { + padding: 1 }, - { - projectName: 'Project No.2', - startTime: '2023/5/1', - endTime: '2023/5/5', - estimateDays: 5, - goal: 0.5, - progress: [ - { - value: 0.5, - label: '50%', - goal: 0.5 + chartSpec: { + type: 'linearProgress', + progress: { + style: { + fill: '#32a645', + lineCap: '' } - ], - master:'Jack' - }, - { - projectName: 'Project No.3', - startTime: '2023/5/7', - endTime: '2023/5/8', - estimateDays: 3, - goal: 0.2, - progress: [ + }, + data: { + id: 'id0' + }, + direction: 'horizontal', + xField: 'value', + yField: 'label', + seriesField: 'type', + cornerRadius: 20, + bandWidth: 12, + padding: 10, + axes: [ { - value: 0.3, - label: '30%', - goal: 0.2 - } - ], - master:'Mary' - }, - { - projectName: 'Project No.4', - startTime: '2023/5/11', - endTime: '2023/5/12', - estimateDays: 2, - goal: 0.8, - progress: [ + orient: 'right', + type: 'band', + domainLine: { visible: false }, + tick: { visible: false }, + label: { + formatMethod: val => val, + style: { + fontSize: 14, + fontWeight: 'bold', + fill: '#32a645' + } + }, + maxWidth: '60%' // 配置坐标轴的最大空间 + }, { - value: 0.9, - label: '90%', - goal: 0.8 + orient: 'bottom', + label: { visible: true, inside: true }, + type: 'linear', + visible: false, + grid: { + visible: false + } } ], - master:'Porry' - }, - { - projectName: 'Project No.5', - startTime: '2023/5/0', - endTime: '2023/5/10', - estimateDays: 2, - goal: 1, - progress: [ + extensionMark: [ { - value: 0.8, - label: '80%', - goal: 1 - } - ], - master:'Sheery' - } - ]; - const columns = [ - { - field: 'projectName', - title: 'Project Name', - width: 'auto', - style: { - color: '#ff689d', - fontWeight: 'bold' - } - }, - { - field: 'progress', - title: 'Schedule', - width: 300, - cellType: 'chart', - chartModule: 'vchart', - style: { - padding: 1 - }, - chartSpec: { - type: 'linearProgress', - progress: { + type: 'rule', + dataId: 'id0', + visible: true, style: { - fill: '#32a645', - lineCap: '' + x: (datum, ctx, elements, dataView) => { + debugger; + return ctx.valueToX([datum.goal]); + }, + y: (datum, ctx, elements, dataView) => { + return ctx.valueToY([datum.label]) - 5; + }, + x1: (datum, ctx, elements, dataView) => { + return ctx.valueToX([datum.goal]); + }, + y1: (datum, ctx, elements, dataView) => { + return ctx.valueToY([datum.label]) + 5; + }, + stroke: 'red', + lineWidth: 2 } }, - data: { - id: 'id0' - }, - direction: 'horizontal', - xField: 'value', - yField: 'label', - seriesField: 'type', - height: 150, - cornerRadius: 20, - bandWidth: 12, - axes: [ - { - orient: 'right', - type: 'band', - domainLine: { visible: false }, - tick: { visible: false }, - label: { - formatMethod: val => val, - style: { - fontSize: 14, - fontWeight: 'bold', - fill: '#32a645' - } + { + type: 'symbol', + dataId: 'id0', + visible: true, + style: { + symbolType: 'triangleDown', + x: (datum, ctx, elements, dataView) => { + return ctx.valueToX([datum.goal]); }, - maxWidth: '60%' // 配置坐标轴的最大空间 - }, - { - orient: 'bottom', - label: { visible: true, inside: true }, - type: 'linear', - visible: false, - grid: { - visible: false - } - } - ], - extensionMark: [ - { - type: 'rule', - dataId: 'id0', - visible: true, - style: { - x: (datum, ctx, elements, dataView) => { - debugger; - return ctx.valueToX([datum.goal]); - }, - y: (datum, ctx, elements, dataView) => { - return ctx.valueToY([datum.label]) - 5; - }, - x1: (datum, ctx, elements, dataView) => { - return ctx.valueToX([datum.goal]); - }, - y1: (datum, ctx, elements, dataView) => { - return ctx.valueToY([datum.label]) + 5; - }, - stroke: 'red', - lineWidth: 2 - } - }, - { - type: 'symbol', - dataId: 'id0', - visible: true, - style: { - symbolType: 'triangleDown', - x: (datum, ctx, elements, dataView) => { - return ctx.valueToX([datum.goal]); - }, - y: (datum, ctx, elements, dataView) => { - return ctx.valueToY([datum.label]) - 10; - }, - size: 15, - scaleY: 0.5, - fill: 'red' - } + y: (datum, ctx, elements, dataView) => { + return ctx.valueToY([datum.label]) - 10; + }, + size: 15, + scaleY: 0.5, + fill: 'red' } - ] - } - }, - { - field: 'goal', - title: 'Target', - width: 'auto', - fieldFormat(rec) { - return rec.goal * 100 + '%'; - }, - style: { - color: 'red', - fontWeight: 'bold' - } - }, - { - field: 'startTime', - title: 'Start Time', - width: 'auto' - }, - { - field: 'endTime', - title: 'End Time', - width: 'auto' + } + ] + } + }, + { + field: 'goal', + title: 'Target', + width: 'auto', + fieldFormat(rec) { + return rec.goal * 100 + '%'; }, - { - field: 'master', - title: 'Master', - width: 'auto', - style: { - color: 'purple', - fontWeight: 'bold' - } + style: { + color: 'red', + fontWeight: 'bold' + } + }, + { + field: 'startTime', + title: 'Start Time', + width: 'auto' + }, + { + field: 'endTime', + title: 'End Time', + width: 'auto' + }, + { + field: 'master', + title: 'Master', + width: 'auto', + style: { + color: 'purple', + fontWeight: 'bold' } - ]; + } +]; - const option = { - records, - columns, - widthMode: 'standard', - hover: { - highlightMode: 'cross' - }, - defaultRowHeight: 60, - autoFillWidth:true, - }; -const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID),option); +const option = { + records, + columns, + widthMode: 'standard', + hover: { + highlightMode: 'cross' + }, + defaultRowHeight: 60, + autoFillWidth: true +}; +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); window['tableInstance'] = tableInstance; ``` diff --git a/docs/assets/demo/zh/component/tooltip.md b/docs/assets/demo/zh/component/tooltip.md index d8e47db74..b058a9120 100644 --- a/docs/assets/demo/zh/component/tooltip.md +++ b/docs/assets/demo/zh/component/tooltip.md @@ -9,108 +9,151 @@ option: ListTable#tooltip.isShowOverflowTextTooltip # tooltip -在该示例中,配置了tooltip.isShowOverflowTextTooltip为true,超长显示不了被省略的文字被hover时将提示出来。 -同时通过监听`mouseenter_cell`事件,鼠标移入符合提示条件【第一列订单号】的单元格时,调用接口showTooltip来显示提示信息。 +在该示例中,展示了四种场景的 tooltip 提示。 + +1. 配置了 `tooltip.isShowOverflowTextTooltip` 为 `true` 开启溢出文字提示,超长显示不了被省略的文字被 hover 时将提示出来。该示例 `Product Name`列中的单元格文本有被省略,可以 hover 到单元格上出现提示信息。 + +2. 表头的描述信息,通过配置 `description` 来显示提示信息。 + +3. 在该示例也展示了通过接口主动显示 tooltip 的用法。通过监听`mouseenter_cell`事件,鼠标移入第一列订单号的单元格时,调用接口 `showTooltip` 来显示提示信息。 + +4. 自定义 icon 的提示信息,`orderId` 列配置 `headerIcon` 为`order`, 图标`order`的配置中配置了 `tooltip` 来显示提示信息。 + +提示信息支持 hover 上去进行选中复制,当内容过多时可以配置最大宽高可进行滚动交互 ## 关键配置 -`tooltip.isShowOverflowTextTooltip` 开启超长省略文字的提示 --`showTooltip` 显示tooltip的调用接口 +-`showTooltip` 显示 tooltip 的调用接口 ## 代码演示 ```javascript livedemo template=vtable +VTable.register.icon('order', { + type: 'svg', + svg: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/order.svg', + width: 22, + height: 22, + name: 'order', + positionType: VTable.TYPES.IconPosition.right, + marginRight: 0, + hover: { + width: 22, + height: 22, + bgColor: 'rgba(101, 117, 168, 0.1)' + }, + tooltip: { + // 气泡框,按钮的的解释信息 + title: + 'Order ID is the unique identifier for each order.\n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique identifier for each order. \n It is a unique', + style: { bgColor: 'black', arrowMark: true, color: 'white', maxHeight: 100, maxWidth: 200 }, + disappearDelay: 100 + }, + cursor: 'pointer' +}); +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') + .then(res => res.json()) + .then(data => { + const columns = [ + { + field: 'Order ID', + title: 'Order ID', + width: 'auto', + headerIcon: 'order', + description: 'Order ID is the unique identifier for each order.\n It is a unique identifier for each order.' + }, + { + field: 'Customer ID', + title: 'Customer ID', + width: 'auto', + description: + 'Customer ID is the unique identifier for each customer.\n It is a unique identifier for each customer.' + }, + { + field: 'Product Name', + title: 'Product Name', + width: '200', + description: 'Product Name is the name of the product.' + }, + { + field: 'Category', + title: 'Category', + width: 'auto', + description: 'Category is the category of the product.' + }, + { + field: 'Sub-Category', + title: 'Sub-Category', + width: 'auto', + description: 'Sub-Category is the sub-category of the product.' + }, + { + field: 'Region', + title: 'Region', + width: 'auto', + description: 'Region is the region of the order produced.' + }, + { + field: 'City', + title: 'City', + width: 'auto', + description: 'City is the city of the order produced.' + }, + { + field: 'Order Date', + title: 'Order Date', + width: 'auto', + description: 'Order Date is the date of the order produced.' + }, + { + field: 'Quantity', + title: 'Quantity', + width: 'auto', + description: 'Quantity is the quantity of the order.' + }, + { + field: 'Sales', + title: 'Sales', + width: 'auto', + description: 'Sales is the sales of the order.' + }, + { + field: 'Profit', + title: 'Profit', + width: 'auto', + description: 'Profit is the profit of the order.' + } + ]; -let tableInstance; - fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_data.json') - .then((res) => res.json()) - .then((data) => { - -const columns =[ - { - "field": "Order ID", - "title": "Order ID", - "width": "auto" - }, - { - "field": "Customer ID", - "title": "Customer ID", - "width": "auto" - }, - { - "field": "Product Name", - "title": "Product Name", - "width": "200" - }, - { - "field": "Category", - "title": "Category", - "width": "auto" - }, - { - "field": "Sub-Category", - "title": "Sub-Category", - "width": "auto" - }, - { - "field": "Region", - "title": "Region", - "width": "auto" - }, - { - "field": "City", - "title": "City", - "width": "auto" - }, - { - "field": "Order Date", - "title": "Order Date", - "width": "auto" - }, - { - "field": "Quantity", - "title": "Quantity", - "width": "auto" - }, - { - "field": "Sales", - "title": "Sales", - "width": "auto" - }, - { - "field": "Profit", - "title": "Profit", - "width": "auto" - } -]; - - const option = { - records:data, - columns, - widthMode:'standard', - tooltip:{ - isShowOverflowTextTooltip: true, - } - }; - tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID),option); - window['tableInstance'] = tableInstance; - tableInstance.on('mouseenter_cell', (args) => { - const { col, row, targetIcon } = args; - if(col===0&&row>=1){ - const rect = tableInstance.getVisibleCellRangeRelativeRect({ col, row }); - tableInstance.showTooltip(col, row, { - content: 'Order ID:'+tableInstance.getCellValue(col,row), - referencePosition: { rect, placement: VTable.TYPES.Placement.right }, //TODO - className: 'defineTooltip', - style: { - bgColor: 'black', - color: 'white', - font: 'normal bold normal 14px/1 STKaiti', - arrowMark: true, - }, - }); - } + const option = { + records: data, + columns, + widthMode: 'standard', + tooltip: { + isShowOverflowTextTooltip: true + } + }; + tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + tableInstance.on('mouseenter_cell', args => { + const { col, row, targetIcon } = args; + if (col === 0 && row >= 1) { + const rect = tableInstance.getVisibleCellRangeRelativeRect({ col, row }); + tableInstance.showTooltip(col, row, { + content: 'Order ID:' + tableInstance.getCellValue(col, row), + referencePosition: { rect, placement: VTable.TYPES.Placement.right }, //TODO + className: 'defineTooltip', + disappearDelay: 100, + style: { + bgColor: 'black', + color: 'white', + font: 'normal bold normal 14px/1 STKaiti', + arrowMark: true + } + }); + } }); -}) + }); ``` diff --git a/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-dimension.md b/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-dimension.md index 45f6c5afe..b6e45f970 100644 --- a/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-dimension.md +++ b/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-dimension.md @@ -9,7 +9,7 @@ option: PivotTable#dataConfig.sortRules # 透视分析表维度值排序 -透视表按某个维度的维度值进行排序,在 dataConfig 中配置 sortRules,可配置多个排序规则,先配的优先级较高。 +透视表按某个维度的维度值进行排序,在 dataConfig 中配置 sortRules,可配置多个排序规则,先配的优先级较高。在这个示例中 rows 行维度'Sub-Category'配置了 sort:true,会在显示维度名称的角头单元格中显示排序图标,点击图标按维度值进行排序。 ## 关键配置 @@ -40,6 +40,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American { dimensionKey: 'Sub-Category', title: 'Sub-Catogery', + sort: true, headerStyle: { textStick: true }, @@ -133,8 +134,8 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American sortBy: ['Office Supplies', 'Technology', 'Furniture'] }, { - sortField: 'Sub-Category', - sortBy: ['Chairs', 'Tables', 'Labels', 'Art', 'Paper', 'Appliances'] + sortField: 'Segment', + sortBy: ['Home Office', 'Consumer', 'Corporate'] } ] }, diff --git a/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md b/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md index c9b970c06..a487e9640 100644 --- a/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md +++ b/docs/assets/demo/zh/data-analysis/pivot-analysis-sort-indicator.md @@ -9,7 +9,7 @@ option: PivotTable#dataConfig.sortRules # 透视分析表按指标值排序 -透视表按某个维度的维度值进行排序,在 dataConfig 中配置 sortRules,可配置多个排序规则,先配的优先级较高。 +透视表按某个维度的维度值进行排序,在 dataConfig 中配置 sortRules,可配置多个排序规则,先配的优先级较高。在这个示例中 indicators 指标配置了 sort:true,会在显示指标名称的表头单元格显示排序图标,点击图标按指标值进行排序。 ## 关键配置 @@ -75,7 +75,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American indicatorKey: 'Quantity', title: 'Quantity', width: 'auto', - showSort: false, + sort: true, headerStyle: { fontWeight: 'normal' }, @@ -100,7 +100,7 @@ fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American indicatorKey: 'Sales', title: 'Sales', width: 'auto', - showSort: false, + sort: true, headerStyle: { fontWeight: 'normal' }, diff --git a/docs/assets/demo/zh/export/table-export-format.md b/docs/assets/demo/zh/export/table-export-format.md new file mode 100644 index 000000000..27806c3a1 --- /dev/null +++ b/docs/assets/demo/zh/export/table-export-format.md @@ -0,0 +1,240 @@ +--- +category: examples +group: export +title: 表格导出(自定义导出) +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/checkbox-demo.png +order: 4-6 +link: '../../guide/export/excel' +# option: ListTable +--- + +# 表格导出(自定义导出) + +默认情况下,表格导出时,会将导出单元格的内文字或图片输出到Excel中,如果需要自定义导出内容,可以设置`formatExportOutput`为一个函数,函数的参数为单元格信息,函数的返回值为导出字符串,如果返回`undefined`,则按照默认导出逻辑处理。 + +## 代码演示 + +```javascript livedemo template=vtable +// 使用时需要引入插件包@visactor/vtable-export +// import { +// downloadCsv, +// exportVTableToCsv, +// downloadExcel, +// exportVTableToExcel, +// } from "@visactor/vtable-export"; +// umd引入时导出工具会挂载到VTable.export + +const data = [ + { + 类别: '办公用品', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '信封', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色信封', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色信封', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '器具', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '订书机', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '计算器', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] + }, + { + 类别: '技术', + 销售额: '229.696', + 数量: '20', + 利润: '90.704', + children: [ + { + 类别: '设备', // 对应原子类别 + 销售额: '225.44', + 数量: '5', + 利润: '462.56' + }, + { + 类别: '配件', // 对应原子类别 + 销售额: '375.92', + 数量: '8', + 利润: '550.2' + }, + { + 类别: '复印机', // 对应原子类别 + 销售额: '425.44', + 数量: '7', + 利润: '342.56' + }, + { + 类别: '电话', // 对应原子类别 + 销售额: '175.92', + 数量: '6', + 利润: '750.2' + } + ] + }, + { + 类别: '家具', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '桌子', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色桌子', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色桌子', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '椅子', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '老板椅', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '沙发椅', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] + }, + { + 类别: '生活家电(懒加载)', + 销售额: '229.696', + 数量: '20', + 利润: '90.704' + } +]; +const option = { + container: document.getElementById(CONTAINER_ID), + columns: [ + { + field: '类别', + tree: true, + title: '类别', + width: 'auto', + sort: true + }, + { + field: '销售额', + title: '销售额', + width: 'auto', + sort: true + // tree: true, + }, + { + field: '利润', + title: '利润', + width: 'auto', + sort: true + } + ], + showPin: true, //显示VTable内置冻结列图标 + widthMode: 'standard', + allowFrozenColCount: 2, + records: data, + + hierarchyIndent: 20, + hierarchyExpandLevel: 2, + hierarchyTextStartAlignment: true, + sortState: { + field: '销售额', + order: 'asc' + }, + theme: VTable.themes.BRIGHT, + defaultRowHeight: 32 +}; +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window.tableInstance = tableInstance; + +bindExport(); + +function bindExport() { + let exportContainer = document.getElementById('export-buttom'); + if (exportContainer) { + exportContainer.parentElement.removeChild(exportContainer); + } + + exportContainer = document.createElement('div'); + exportContainer.id = 'export-buttom'; + exportContainer.style.position = 'absolute'; + exportContainer.style.bottom = '0'; + exportContainer.style.right = '0'; + + window['tableInstance'].getContainer().appendChild(exportContainer); + + const exportCsvButton = document.createElement('button'); + exportCsvButton.innerHTML = 'CSV-export'; + const exportExcelButton = document.createElement('button'); + exportExcelButton.innerHTML = 'Excel-export'; + exportContainer.appendChild(exportCsvButton); + exportContainer.appendChild(exportExcelButton); + + exportCsvButton.addEventListener('click', () => { + if (window.tableInstance) { + downloadCsv(exportVTableToCsv(window.tableInstance), 'export'); + } + }); + + exportExcelButton.addEventListener('click', async () => { + if (window.tableInstance) { + downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + } + }); +} +``` diff --git a/docs/assets/demo/zh/export/table-export-ignore-icon.md b/docs/assets/demo/zh/export/table-export-ignore-icon.md new file mode 100644 index 000000000..a435862a4 --- /dev/null +++ b/docs/assets/demo/zh/export/table-export-ignore-icon.md @@ -0,0 +1,108 @@ +--- +category: examples +group: export +title: 表格导出(忽略图标) +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/preview/export-tree.png +order: 4-6 +link: '../../guide/export/excel' +# option: ListTable +--- + +# 表格导出(忽略图标) + +默认情况下,单元格中有图标时,图标和文字会统一当做图片被导出;如果不需要导出图标,可以设置`ignoreIcon`为true,只输出文字。 + +## 代码演示 + +```javascript livedemo template=vtable +// 使用时需要引入插件包@visactor/vtable-export +// import { +// downloadCsv, +// exportVTableToCsv, +// downloadExcel, +// exportVTableToExcel, +// } from "@visactor/vtable-export"; +// umd引入时导出工具会挂载到VTable.export + +const records = [ + { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, + { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, + { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, + { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, + { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } +]; + +const columns = [ + { + field: 'isCheck', + title: '', + width: 60, + headerType: 'checkbox', + cellType: 'checkbox' + }, + { + field: 'productName', + title: 'productName', + width: 120 + }, + { + field: 'price', + title: 'checkbox', + width: 120, + cellType: 'checkbox', + disable: true, + checked: true + }, + { + field: 'check', + title: 'checkbox', + width: 120, + cellType: 'checkbox' + // disable: true + } +]; +const option = { + records, + columns +}; +const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); +window.tableInstance = tableInstance; + +bindExport(); + +function bindExport() { + let exportContainer = document.getElementById('export-buttom'); + if (exportContainer) { + exportContainer.parentElement.removeChild(exportContainer); + } + + exportContainer = document.createElement('div'); + exportContainer.id = 'export-buttom'; + exportContainer.style.position = 'absolute'; + exportContainer.style.bottom = '0'; + exportContainer.style.right = '0'; + + window['tableInstance'].getContainer().appendChild(exportContainer); + + const exportCsvButton = document.createElement('button'); + exportCsvButton.innerHTML = 'CSV-export'; + const exportExcelButton = document.createElement('button'); + exportExcelButton.innerHTML = 'Excel-export'; + exportContainer.appendChild(exportCsvButton); + exportContainer.appendChild(exportExcelButton); + + exportCsvButton.addEventListener('click', () => { + if (window.tableInstance) { + downloadCsv(exportVTableToCsv(window.tableInstance), 'export'); + } + }); + + exportExcelButton.addEventListener('click', async () => { + if (window.tableInstance) { + downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + } + }); +} +``` diff --git a/docs/assets/faq/en/5-How to customize the content of a tooltip in a pop-up box.md b/docs/assets/faq/en/5-How to customize the content of a tooltip in a pop-up box.md index 4f67e7746..ebf1a2903 100644 --- a/docs/assets/faq/en/5-How to customize the content of a tooltip in a pop-up box.md +++ b/docs/assets/faq/en/5-How to customize the content of a tooltip in a pop-up box.md @@ -6,7 +6,7 @@ When hovering the mouse over a cell, I want to display contextual information ab ## Solution -One flexible approach is to listen to the `mouseenter_cell` and `mouseleave_cell` events of the VTable instance. Show or hide the custom DOM elements accordingly, and calculate the position to display the tooltip based on the `cellRange` parameter from the VTable event. Demo: https://visactor.io/vtable/demo/example/component/tooltip_custom_content +One flexible approach is to listen to the `mouseenter_cell` and `mouseleave_cell` events of the VTable instance. Show or hide the custom DOM elements accordingly, and calculate the position to display the tooltip based on the `cellRange` parameter from the VTable event. Demo: https://visactor.io/vtable/demo/component/tooltip_custom_content ## Code Example @@ -23,12 +23,12 @@ tableInstance.on('mouseleave_cell', args => { ## Results -[Online demo](https://visactor.io/vtable/demo/example/component/tooltip_custom_content) +[Online demo](https://visactor.io/vtable/demo/component/tooltip_custom_content) ![result](/vtable/faq/5-0.png) ## Quote -- [Table tooltip demo](https://visactor.io/vtable/demo/example/component/tooltip_custom_content) +- [Table tooltip demo](https://visactor.io/vtable/demo/component/tooltip_custom_content) - [Tooltip Tutorial](https://visactor.io/vtable/guide/components/tooltip) - [github](https://github.com/VisActor/VTable) diff --git a/docs/assets/faq/en/6-How to sort table contents by data records.md b/docs/assets/faq/en/6-How to sort table contents by data records.md index 71a2e6426..b6d40f4bb 100644 --- a/docs/assets/faq/en/6-How to sort table contents by data records.md +++ b/docs/assets/faq/en/6-How to sort table contents by data records.md @@ -66,5 +66,5 @@ instance.updateSortState({ ## Quote - [Table Sort demo](https://visactor.io/vtable/demo/basic-functionality/sort) -- [Sort Tutorial](https://visactor.io/vtable/guide/basic_function/sort) +- [Sort Tutorial](https://visactor.io/vtable/guide/basic_function/sort/list_sort) - [github](https://github.com/VisActor/VTable) diff --git a/docs/assets/faq/zh/5-How to customize the content of a tooltip in a pop-up box.md b/docs/assets/faq/zh/5-How to customize the content of a tooltip in a pop-up box.md index 5b70b6cac..d81fd454b 100644 --- a/docs/assets/faq/zh/5-How to customize the content of a tooltip in a pop-up box.md +++ b/docs/assets/faq/zh/5-How to customize the content of a tooltip in a pop-up box.md @@ -6,7 +6,7 @@ ## 解决方案 -提供一种比较灵活的方式:监听 VTable 实例的事件 `mouseenter_cell` 和 `mouseleave_cell` 事件,将自定义的 dom 展示和隐藏,并依据 VTable 事件参数中的 `cellRange` 计算展示 tooltip 的位置。具体可以参考 demo:https://visactor.io/vtable/demo/example/component/tooltip_custom_content +提供一种比较灵活的方式:监听 VTable 实例的事件 `mouseenter_cell` 和 `mouseleave_cell` 事件,将自定义的 dom 展示和隐藏,并依据 VTable 事件参数中的 `cellRange` 计算展示 tooltip 的位置。具体可以参考 demo:https://visactor.io/vtable/demo/component/tooltip_custom_content ## 代码示例 @@ -23,12 +23,12 @@ tableInstance.on('mouseleave_cell', args => { ## 结果展示 -[在线效果参考](https://visactor.io/vtable/demo/example/component/tooltip_custom_content) +[在线效果参考](https://visactor.io/vtable/demo/component/tooltip_custom_content) ![result](/vtable/faq/5-0.png) ## 相关文档 -- [表格 tooltip demo](https://visactor.io/vtable/demo/example/component/tooltip_custom_content) +- [表格 tooltip demo](https://visactor.io/vtable/demo/component/tooltip_custom_content) - [Tooltip 教程](https://visactor.io/vtable/guide/components/tooltip) - [github](https://github.com/VisActor/VTable) diff --git a/docs/assets/faq/zh/6-How to sort table contents by data records.md b/docs/assets/faq/zh/6-How to sort table contents by data records.md index 6346032b8..40a33b56e 100644 --- a/docs/assets/faq/zh/6-How to sort table contents by data records.md +++ b/docs/assets/faq/zh/6-How to sort table contents by data records.md @@ -62,5 +62,5 @@ instance.updateSortState({ ## 相关文档 - [表格排序 demo](https://visactor.io/vtable/demo/basic-functionality/sort) -- [排序功能教程](https://visactor.io/vtable/guide/basic_function/sort) +- [排序功能教程](https://visactor.io/vtable/guide/basic_function/sort/list_sort) - [github](https://github.com/VisActor/VTable) diff --git a/docs/assets/guide/en/basic_function/sort.md b/docs/assets/guide/en/basic_function/sort/list_sort.md similarity index 98% rename from docs/assets/guide/en/basic_function/sort.md rename to docs/assets/guide/en/basic_function/sort/list_sort.md index af31c5f5b..04045c93d 100644 --- a/docs/assets/guide/en/basic_function/sort.md +++ b/docs/assets/guide/en/basic_function/sort/list_sort.md @@ -1,11 +1,9 @@ -# Table sorting function +# ListTable sorting function In the process of data analytics, the sorting (sorting) function is very important for the organization and analysis of data. By sorting, users can quickly arrange the data they care about in front, improve the efficiency of data search and analysis, and quickly find outliers and patterns in the data. VTable provides rich sorting functions, users can easily open on demand, customize sorting rules, set initial sorting status, etc. -**Note**: This tutorial is only for the basic table ListTable. The pivot table sorting tutorial can be asynchronously accessed: https://visactor.io/vtable/guide/table_type/Pivot_table/pivot_table_dataAnalysis - ## Enable sorting To use the sorting function of VTable, you need to configure the table columns first. exist `columns` The configuration items for each column need to be set according to cellType (column type). In this tutorial, we mainly focus on sorting-related configurations. diff --git a/docs/assets/guide/en/basic_function/sort/pivot_sort.md b/docs/assets/guide/en/basic_function/sort/pivot_sort.md new file mode 100644 index 000000000..076285318 --- /dev/null +++ b/docs/assets/guide/en/basic_function/sort/pivot_sort.md @@ -0,0 +1,323 @@ +# Pivot table sorting function + +The sorting capability of a pivot table can be implemented in the following ways: + +1. To customize the tree structure of the pivot table header, you can pass rowTree and columnTree in to display the table according to this structure. In this case, even if sortRule is configured, it will not work. +2. Add `sort:true` in the dimension or indicator configuration to enable sorting. The sort button will be displayed and clicking the button will trigger sorting. +3. Sort by interface: Call the interface `updateSortRules` to sort. +4. Other special requirements: only display the sorting status, do not use the VTable sorting logic + +The first way to organize the table header tree structure by yourself has been mentioned when introducing the pivot table. You can refer to the tutorial: https://visactor.io/vtable/guide/table_type/Pivot_table/pivot_table_tree. + +**Note that several sorting methods should not be mixed** + +Next, we will mainly introduce the following implementation methods and precautions. + +## Configure sort to enable sorting + +### Sort by dimension value + +The sort configuration can be configured in rows or columns. In this case, the header cell displaying the dimension name will display a sort button, and clicking the button will trigger the sort. + +The following is an example of configuring sort in rows to enable sorting: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'Category', + title: 'Category', + sort: true + }, + { + dimensionKey: 'Sub-Category', + title: 'Sub-Catogery', + sort: true + } + ], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales' + }, + { + indicatorKey: 'Profit', + title: 'Profit' + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` + +In the above code, `sort` is `true`, which means that the dimension values corresponding to the row headers can be sorted, and the cells in the corner headers will display the sort icon. + +### Sort by indicator value + +The sort configuration can be configured in indicators. At this time, the row header or column header cell displaying the indicator name will display a sort button, and clicking the button will trigger sorting. + +Here is an example of configuring sort in indicators to enable sorting: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: ['Category', 'Sub-Category'], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + sort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + sort: true + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` + +In the above code, `sort` is `true`, which means that sorting is supported by indicator value, and the cells in the row header or column header will display the sort icon. + +### Initialize sorting status + +Please configure data analysis dataConfig.sortRule to set the initial sorting state. + +The following example: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: ['Category', 'Sub-Category'], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + sort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + sort: true + } + ], + dataConfig: { + sortRules: [ + { + sortField: 'Sub-Category', + sortByIndicator: 'Sales', + sortType: VTable.TYPES.SortType.DESC, + query: ['Central', 'Corporate'] + } + ] + }, + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` + +This example configures the initial sorting rule, which sorts the indicator values in descending order according to the column header dimension path of `['Central', 'Corporate', 'Sales']`. At the same time, the sorting icon in the corresponding header cell changes to the descending state icon. + +### Update sorting through the interface + +The update sorting interface of the pivot table is `updateSortRules`, which can be called to update the sorting status. + +Here is an example of updating the order through the interface: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: ['Category', 'Sub-Category'], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + sort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + sort: true + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + tableInstance.updateSortRules([ + { + sortField: 'Sub-Category', + sortByIndicator: 'Sales', + sortType: VTable.TYPES.SortType.DESC, + query: ['Central', 'Corporate'] + } + ]); + }); +``` + +### Listen for sort icon click events + +The sort icon click event is monitored as `pivot_sort_click`. + +## Sorting by interface + +If you need to sort through the interface, you can update the sorting status by calling the `updateSortRules` interface. + +## Show only sort icons + +If there is a special setting panel in the business scenario, and there are special sorting options for users to operate, but the corresponding sorting status needs to be displayed in the table, you can configure `showSort: true` to display the sorting status. If there is a need to monitor icon clicks, you can monitor the event `pivot_sort_click`. + +At the same time, you can set pivotSortState on option to set the state of the initial sort icon. + +Let’s look at the usage example: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'Category', + title: 'Category', + showSort: true + }, + { + dimensionKey: 'Sub-Category', + title: 'Sub-Catogery', + showSort: true + } + ], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + showSort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + showSort: true + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + pivotSortState: [ + { + dimensions: [ + { + dimensionKey: 'Category', + value: 'Furniture', + isPivotCorner: false, + indicatorKey: undefined + } + ], + order: 'desc' + }, + { + dimensions: [ + { + dimensionKey: 'Region', + value: 'Central', + isPivotCorner: false, + indicatorKey: undefined + }, + { + dimensionKey: 'Segment', + value: 'Consumer', + isPivotCorner: false, + indicatorKey: undefined + }, + { + indicatorKey: 'Sales', + value: 'Sales', + isPivotCorner: false + } + ], + order: 'asc' + } + ], + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + tableInstance.on('pivot_sort_click', e => { + console.log(e); + // 执行业务逻辑 ... + // 如果执行业务逻辑后还需要更新排序状态,可以先调用updateOption来更新配置,目前还未提供专门更新的接口 + }); + }); +``` + +In the above example, pivotSortState is configured with two sorting rules. It will display descending icons on cells with dimension path ['Furniture'] in the row header, and ascending icons on cells with dimension path ['Central', 'Consumer', 'Sales'] in the column header. + +## other + +Here I would like to emphasize again: **Do not mix several sorting methods**. For example, do not use the sortRule method when you customize the table header tree structure or configure showSort; do not use the pivotSortState configuration when you configure sort. + +In addition, the current sorting method is not very perfect, for example + +1. Configure the sorting method of `sort:true`. Sometimes you need to set a custom sorting function to execute the sorting logic +2. Missing `pivotSortState` configuration state update interface + +We will add to these later. diff --git a/docs/assets/guide/en/components/tooltip.md b/docs/assets/guide/en/components/tooltip.md index b0f0beac9..19b94f7bf 100644 --- a/docs/assets/guide/en/components/tooltip.md +++ b/docs/assets/guide/en/components/tooltip.md @@ -4,11 +4,11 @@ In table components, a tooltip is a common user interface element used to provid ## Tooltip usage scenarios -* Data interpretation and description: Data in some tables may require additional interpretation or description. Tooltip can be used to display these interpretations to help users understand the meaning, units, calculation methods or other relevant information of the data. +- Data interpretation and description: Data in some tables may require additional interpretation or description. Tooltip can be used to display these interpretations to help users understand the meaning, units, calculation methods or other relevant information of the data. -* Overflow content: When the text or data in the table exceeds the width of the cell, you can use tooltip to display the full content to prevent truncation or hide important information. +- Overflow content: When the text or data in the table exceeds the width of the cell, you can use tooltip to display the full content to prevent truncation or hide important information. -* Description of Interactive Elements: If the table contains interactive elements (such as links, buttons, or icons), Tooltip can be used to provide functional descriptions or action hints for those elements. +- Description of Interactive Elements: If the table contains interactive elements (such as links, buttons, or icons), Tooltip can be used to provide functional descriptions or action hints for those elements. ## Introduction to configuration items @@ -26,12 +26,72 @@ The configuration items are: }; } -## Turn on overflow content prompt +## Tooltip prompt box style settings -VTable defaults to tooltip for overflow content: isShowOverflowTextTooltip defaults to true. +The style configuration of tooltip can be set through theme.tooltipStyle. The specific configuration is as follows: + +``` +export type TooltipStyle = { + fontFamily?: string; + fontSize?: number; + color?: string; + padding?: number[]; + bgColor?: string; + maxWidth?: number; + maxHeight?: number; +}; + +``` + +## Enable overflow content prompt + +By default, VTable enables the tooltip of overflow content: isShowOverflowTextTooltip defaults to true. If you need to delay disappearance so that the mouse can move to the tooltip content, you can configure overflowTextTooltipDisappearDelay. ![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/c0de7ff0a101bd4cb25c8170e.gif) +## Custom icon hover prompt + +For example, the configuration of the table header icon is as follows: + +``` +const tableInstance = new VTable.ListTable({ + columns: [ + { + field: 'orderID', + title: '订单编号', + headerIcon: { + type: 'svg', //指定svg格式图标,其他还支持path,image + svg: ` + + `, + width: 20, + height: 20, + name: 'filter', //定义图标的名称,在内部会作为缓存的key值 + positionType: VTable.TYPES.IconPosition.absoluteRight, // 指定位置,可以在文本的前后,或者在绝对定位在单元格的左侧右侧 + visibleTime: 'mouseenter_cell', // 显示时机, 'always' | 'mouseenter_cell' | 'click_cell' + hover: { + // 热区大小 + width: 26, + height: 26, + bgColor: 'rgba(22,44,66,0.5)' + }, + tooltip: { + style: { arrowMark: false }, + // 气泡框,按钮的的解释信息 + title: '过滤', + placement: VTable.TYPES.Placement.right, + disappearDelay: 1000, + } + } + } + ] +}); +``` + +The tooltip in headerIcon is the prompt box when the mouse hovers over the icon. At the same time, disappearDelay is configured to delay the disappearance of the pop-up box so that the mouse can move to the tooltip content. + +For detailed information about icon configuration, please refer to the tutorial: https://visactor.io/vtable/guide/custom_define/custom_icon. + ## Display tooltip custom information through the interface The interface showTooltip can actively display tooltip information, which is used as follows: (listen for cell hover events, call the interface) @@ -57,3 +117,28 @@ The interface showTooltip can actively display tooltip information, which is use Effect: ![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/ffc3a9b5518762d274121ff05.gif) + +## Icon tooltip configuration + +When customizing the icon, you can display the prompt information by configuring the tooltip as follows: + +``` +VTable.register.icon('order', { + ... //其他配置 + tooltip: { + // 气泡框,按钮的的解释信息 + title:'Order ID is the unique identifier for each order', + style: { + fontSize: 14, + fontFamily: 'Arial', + padding: [10,10,10,10], + bgColor: 'black', + arrowMark: true, + color: 'white', + maxHeight: 100, + maxWidth: 200 + }, + disappearDelay: 1000 + } +}) +``` diff --git a/docs/assets/guide/en/custom_define/custom_icon.md b/docs/assets/guide/en/custom_define/custom_icon.md index 3b3c10cca..05f5f9d47 100644 --- a/docs/assets/guide/en/custom_define/custom_icon.md +++ b/docs/assets/guide/en/custom_define/custom_icon.md @@ -10,7 +10,7 @@ We can configure the cell icons displayed in the header and body through icon an - `icon` The icon used to configure the body cell. -The specific content of the configuration is[ColumnIconOption]() Type objects can also dynamically set the icon style of the cell by passing a custom function. +The specific configuration content is an object of type `ColumnIconOption`. You can also pass a custom function to dynamically set the icon style of the cell. For the specific definition of ColumnIconOption, please refer to: https://visactor.io/vtable/option/ListTable-columns-text#icon ### Header icon configuration example @@ -192,8 +192,7 @@ VTable.register.icon('frozenCurrent', { }); ``` -The effect after replacement is as follows: -TODO +The effect after replacement is as follows: https://visactor.io/vtable/demo/custom-render/custom-icon In the same way, we can replace other functional icons. Several icons related to internal functions are built into VTable, such as sorting, fixed columns, drop-down menu icons, Expand folding icons, etc. @@ -229,6 +228,6 @@ The list of resettable internal icons is as follows: ] ``` -At the same time, the icons registered in your own business do not need to configure `funcType`. +**At the same time, the icons registered in your own business do not need to configure `funcType`.** At this point, the tutorial on how to use icons in VTable, register and replace function icons is all introduced. I hope this tutorial can help you better understand and use VTable, and create a more beautiful and practical data lake visualization table diff --git a/docs/assets/guide/en/data_analysis/list_table_dataAnalysis.md b/docs/assets/guide/en/data_analysis/list_table_dataAnalysis.md index 03e35fb91..053b01af1 100644 --- a/docs/assets/guide/en/data_analysis/list_table_dataAnalysis.md +++ b/docs/assets/guide/en/data_analysis/list_table_dataAnalysis.md @@ -4,7 +4,7 @@ Currently supported capabilities include sorting, filtering, and data aggregatio # Data sorting -For details, please refer to the tutorial: https://visactor.io/vtable/guide/basic_function/sort +For details, please refer to the tutorial: https://visactor.io/vtable/guide/basic_function/sort/list_sort # Data filtering diff --git a/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md b/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md index 85d9fac6b..2e830f8ed 100644 --- a/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md +++ b/docs/assets/guide/en/data_analysis/pivot_table_dataAnalysis.md @@ -68,7 +68,7 @@ dataConfig application example: ### 1. Totals -[option description](../../../option/PivotTable#dataConfig.totals) +[option description](../../option/PivotTable#dataConfig.totals) Configuration example: ``` @@ -97,8 +97,13 @@ Online demo:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-total ### 2. Sorting rules -[option description](../../../option/PivotTable#dataConfig.sortRules) -Configuration example: +VTable's pivot table supports four sorting methods: natural sorting of dimension values, specified dimension value order, indicator value sorting, and custom sorting. + +For definitions, please refer to: + +[option description](../../option/PivotTable#dataConfig.sortRules) [Usage tutorial](../../guide/basic_function/sort/pivot_sort) + +The following is an example of the indicator value sorting configuration: ``` sortRules: [ @@ -118,7 +123,7 @@ Online demo:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-sort- ### 3. Filter rules -[option description](../../../option/PivotTable#dataConfig.filterRules) +[option description](../../option/PivotTable#dataConfig.filterRules) Configuration example: ``` @@ -135,7 +140,7 @@ Online demo:https://visactor.io/vtable/demo/data-analysis/pivot-analysis-filte ### 4. Aggregation method -[option description](../../../option/PivotTable#dataConfig.aggregationRules) +[option description](../../option/PivotTable#dataConfig.aggregationRules) Configuration example: ``` @@ -200,7 +205,7 @@ The sales indicator in this record is a non-numeric value, and it is required to ### 5. Derive Field -[option description](../../../option/PivotTable#dataConfig.derivedFieldRules) +[option description](../../option/PivotTable#dataConfig.derivedFieldRules) Configuration example: ``` diff --git a/docs/assets/guide/en/export/excel.md b/docs/assets/guide/en/export/excel.md index 681942d2f..5f0135e27 100644 --- a/docs/assets/guide/en/export/excel.md +++ b/docs/assets/guide/en/export/excel.md @@ -17,10 +17,10 @@ import { downloadExcel, exportVTableToExcel } from '@visactor/vtable-export'; const tableInstance = new VTable.ListTable(option); // donload csv file -downloadExcel(exportVTableToExcel(tableInstance), 'export-csv'); +downloadExcel(exportVTableToExcel(tableInstance, optionForExport), 'export-csv'); ``` -* `exportVTableToExcel`: Table output tool, outputs table instances as an ArrayBuffer in Excel format +* `exportVTableToExcel`: Table output tool, outputs table instances as an ArrayBuffer in Excel format; option is an optional parameter, see below for configuration items * `downloadExcel`: Download tool to download the ArrayBuffer in Excel format as a file in the browser environment * If it is a server environment, you can process the Excel format ArrayBuffer converted by `exportVTableToExcel` yourself. * The excel export function is currently being improved. Currently, it only supports the export of text-type cells, and will support more types such as sparkline in the future. @@ -40,4 +40,40 @@ Find the corresponding tool in the global variable `VTable.export` and use the s ```js const { downloadCsv, exportVTableToCsv } = VTable.export; // ...... +``` + +## Options + +### ignoreIcon + +By default, when the cell has an icon, the icon and text will be treated as an image when exporting. If you do not need to export the icon, you can set `ignoreIcon` to true, and only the text will be output. + +### formatExportOutput + +By default, when exporting, the text or image inside the exported cell will be output to Excel. If you need to customize the export content, you can set `formatExportOutput` to a function, and the return value of the function is the exported string. If the return value is `undefined`, the default export logic will be processed. + +```ts +type CellInfo = { + cellType: string; + cellValue: string; + table: IVTable; + col: number; + row: number; +}; + +type ExportVTableToExcelOptions = { + ignoreIcon?: boolean; + formatExportOutput?: (cellInfo: CellInfo) => string | undefined; +}; +``` + +```js +const excelOption = { + formatExportOutput: ({ cellType, cellValue, table, col, row }) => { + if (cellType === 'checkbox') { + return table.getCellCheckboxState(col, row) ? 'true' : 'false'; + } + } +}; +downloadExcel(await exportVTableToExcel(tableInstance, excelOption)); ``` \ No newline at end of file diff --git a/docs/assets/guide/menu.json b/docs/assets/guide/menu.json index 2ded133a8..c8b44d150 100644 --- a/docs/assets/guide/menu.json +++ b/docs/assets/guide/menu.json @@ -226,7 +226,23 @@ "title": { "zh": "排序", "en": "sort" - } + }, + "children": [ + { + "path": "list_sort", + "title": { + "zh": "基本表格排序", + "en": "sort In ListTable" + } + }, + { + "path": "pivot_sort", + "title": { + "zh": "透视表格排序", + "en": "sort In PivotTable" + } + } + ] }, { "path": "frozen_column", diff --git a/docs/assets/guide/zh/basic_function/sort.md b/docs/assets/guide/zh/basic_function/sort/list_sort.md similarity index 97% rename from docs/assets/guide/zh/basic_function/sort.md rename to docs/assets/guide/zh/basic_function/sort/list_sort.md index 0e1d3c411..2db9f030d 100644 --- a/docs/assets/guide/zh/basic_function/sort.md +++ b/docs/assets/guide/zh/basic_function/sort/list_sort.md @@ -1,11 +1,9 @@ -# 表格排序功能 +# 基本表格排序功能 在数据分析过程中,排序( 排序 )功能对数据的组织和协助分析非常重要。通过排序,用户可以快速将关心的数据排列在前面,提高数据查找和分析的效率,同时也能快速发现数据中的异常点和规律。 VTable 提供了丰富的排序功能,用户可以轻松地按需开启、自定义排序规则、设定初始排序状态等。 -**注**:该教程进针对基本表格 ListTable,透视表的排序教程可异步至:https://visactor.io/vtable/guide/table_type/Pivot_table/pivot_table_dataAnalysis - ## 开启排序 要使用 VTable 的排序功能,需要先对表格列进行配置。在 `columns` 中,每一列的配置项需要根据 cellType(列类型)进行设置。在本教程中,我们主要关注排序相关的配置。 @@ -482,7 +480,7 @@ const listTable = new ListTable({ ## 预排序 -在大数据量的情况下,首次排序可能会耗时较长,可以通过预排序来提升排序功能的性能。通过setSortedIndexMap方法,设置预排序的数据字段和排序顺序。 +在大数据量的情况下,首次排序可能会耗时较长,可以通过预排序来提升排序功能的性能。通过 setSortedIndexMap 方法,设置预排序的数据字段和排序顺序。 ```js interface ISortedMapItem { diff --git a/docs/assets/guide/zh/basic_function/sort/pivot_sort.md b/docs/assets/guide/zh/basic_function/sort/pivot_sort.md new file mode 100644 index 000000000..774282327 --- /dev/null +++ b/docs/assets/guide/zh/basic_function/sort/pivot_sort.md @@ -0,0 +1,323 @@ +# 透视表排序功能 + +透视表的排序能力以下几种实现方式: + +1. 透视表自定义表头的树形结构,rowTree 和 columnTree 都自行传入即可按照这个结构进行展示。这个时候即便配置了 sortRule 也不会起作用。 +2. 在维度或者指标配置中添加`sort:true`来开启排序,此时会显示排序按钮,点击按钮会触发排序。 +3. 通过接口的方式进行排序:自行调用接口`updateSortRules`来排序。 +4. 其他特殊需求:仅显示排序状态,不使用 VTable 的排序逻辑 + +第一种自行组织表头树形结构的方式在介绍透视表时已经讲过,可以参考教程:https://visactor.io/vtable/guide/table_type/Pivot_table/pivot_table_tree。 + +**注意几种排序方式不要混用** + +接下来主要介绍后面的几种实现方式和注意事项。 + +## 配置 sort 开启排序 + +### 按维度值排序 + +sort 的配置可以在 rows 或者 columns 中配置,此时显示维度名称的角头单元格会显示排序按钮,点击按钮会触发排序。 + +以下是一个在 rows 中配置 sort 开启排序功能的例子: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'Category', + title: 'Category', + sort: true + }, + { + dimensionKey: 'Sub-Category', + title: 'Sub-Catogery', + sort: true + } + ], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales' + }, + { + indicatorKey: 'Profit', + title: 'Profit' + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` + +在上述代码中,`sort` 为 `true`,表示行表头对应维度值支持排序,角头的单元格会显示排序图标。 + +### 按指标值排序 + +sort 的配置可以在 indicators 中配置,此时显示指标名称的行表头或者列表头单元格会显示排序按钮,点击按钮会触发排序。 + +以下是一个在 indicators 中配置 sort 开启排序功能的例子: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: ['Category', 'Sub-Category'], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + sort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + sort: true + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` + +在上述代码中,`sort` 为 `true`,表示可以按指标值支持排序,行表头或者列表头的单元格会显示排序图标。 + +### 初始化排序状态 + +请配置数据分析 dataConfig.sortRule,可以设置初始排序状态。 + +如下示例: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: ['Category', 'Sub-Category'], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + sort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + sort: true + } + ], + dataConfig: { + sortRules: [ + { + sortField: 'Sub-Category', + sortByIndicator: 'Sales', + sortType: VTable.TYPES.SortType.DESC, + query: ['Central', 'Corporate'] + } + ] + }, + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + }); +``` + +该示例配置了初始排序规则,会按照列表头维度路径为`['Central', 'Corporate', 'Sales']` 指标值进行降序排序,同时相应的表头单元格中的排序图标变为了降序状态图标。 + +### 通过接口更新排序 + +透视表的更新排序接口为`updateSortRules`,通过调用该接口可以更新排序状态。 + +以下是一个通过接口更新排序的例子: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: ['Category', 'Sub-Category'], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + sort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + sort: true + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + tableInstance.updateSortRules([ + { + sortField: 'Sub-Category', + sortByIndicator: 'Sales', + sortType: VTable.TYPES.SortType.DESC, + query: ['Central', 'Corporate'] + } + ]); + }); +``` + +### 监听排序图标点击事件 + +监听排序图标点击事件为`pivot_sort_click`。 + +## 通过接口进行排序 + +如果需要通过接口进行排序,可以通过调用`updateSortRules`接口来更新排序状态。 + +## 仅显示排序图标 + +如果业务场景中有专门的设置面板,有专门的排序选项提供给用户进行操作,但是需要在表格中显示对应的排序状态,那么可以配置`showSort: true`来显示排序状态。如果有监听图标点击需求,可以监听事件`pivot_sort_click`。 + +同时,可以设置 option 上的 pivotSortState 来设置初始排序图标的状态。 + +下面看用法示例: + +```javascript livedemo template=vtable +let tableInstance; +fetch('https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/North_American_Superstore_Pivot_Chart_data.json') + .then(res => res.json()) + .then(data => { + const option = { + records: data, + rows: [ + { + dimensionKey: 'Category', + title: 'Category', + showSort: true + }, + { + dimensionKey: 'Sub-Category', + title: 'Sub-Catogery', + showSort: true + } + ], + columns: ['Region', 'Segment'], + indicators: [ + { + indicatorKey: 'Sales', + title: 'Sales', + showSort: true + }, + { + indicatorKey: 'Profit', + title: 'Profit', + showSort: true + } + ], + corner: { + titleOnDimension: 'row', + headerStyle: { + textStick: true + } + }, + pivotSortState: [ + { + dimensions: [ + { + dimensionKey: 'Category', + value: 'Furniture', + isPivotCorner: false, + indicatorKey: undefined + } + ], + order: 'desc' + }, + { + dimensions: [ + { + dimensionKey: 'Region', + value: 'Central', + isPivotCorner: false, + indicatorKey: undefined + }, + { + dimensionKey: 'Segment', + value: 'Consumer', + isPivotCorner: false, + indicatorKey: undefined + }, + { + indicatorKey: 'Sales', + value: 'Sales', + isPivotCorner: false + } + ], + order: 'asc' + } + ], + defaultColWidth: 130 + }; + tableInstance = new VTable.PivotTable(document.getElementById(CONTAINER_ID), option); + window['tableInstance'] = tableInstance; + tableInstance.on('pivot_sort_click', e => { + console.log(e); + // 执行业务逻辑 ... + // 如果执行业务逻辑后还需要更新排序状态,可以先调用updateOption来更新配置,目前还未提供专门更新的接口 + }); + }); +``` + +上述示例中 pivotSortState 配置了两个排序规则,会在行表头维度路径为`['Furniture']`的单元格上显示降序图标,同时会在列表头维度路径为`['Central', 'Consumer', 'Sales']`的单元格上显示升序图标。 + +## 其他 + +这里再强调一下:**几种排序方式不要混用**,例如:sortRule 的方式不要在自定义表头树结构的情况和配置 showSort 的情况下使用;pivotSortState 的配置也不要在配置 sort 的情况下使用。 + +另外,目前排序的方式并不是非常完善的,例如 + +1. 配置`sort:true`的排序方式,有时候需要设置自定义排序函数来执行排序逻辑 +2. 缺失`pivotSortState`配置状态的更新接口 + +这些后续我们都将补充。 diff --git a/docs/assets/guide/zh/components/tooltip.md b/docs/assets/guide/zh/components/tooltip.md index a5f685847..0c5a7a881 100644 --- a/docs/assets/guide/zh/components/tooltip.md +++ b/docs/assets/guide/zh/components/tooltip.md @@ -1,43 +1,106 @@ -# tooltip介绍 +# tooltip 介绍 在表格组件中,tooltip(信息提示)是一种常见的用户界面元素,用于提供关于特定表格单元格或数据的额外信息。它以一个小型弹出窗口或浮动框的形式出现,当用户将鼠标悬停在特定单元格上或与特定元素交互时,显示相关的提示文本。 -## tooltip的使用场景 +## tooltip 的使用场景 -- 数据解释和描述:某些表格中的数据可能需要额外的解释或描述。Tooltip可以用于显示这些解释,帮助用户理解数据的含义、单位、计算方法或其他相关信息。 +- 数据解释和描述:某些表格中的数据可能需要额外的解释或描述。Tooltip 可以用于显示这些解释,帮助用户理解数据的含义、单位、计算方法或其他相关信息。 -- 溢出内容:当表格中的文本或数据超出单元格的宽度时,可以使用tooltip显示完整的内容,以防止截断或隐藏重要信息。 +- 溢出内容:当表格中的文本或数据超出单元格的宽度时,可以使用 tooltip 显示完整的内容,以防止截断或隐藏重要信息。 -- 可交互元素说明:如果表格中包含可交互的元素(如链接、按钮或图标),Tooltip可以用于提供这些元素的功能说明或操作提示。 +- 可交互元素说明:如果表格中包含可交互的元素(如链接、按钮或图标),Tooltip 可以用于提供这些元素的功能说明或操作提示。 ## 配置项介绍 配置项为: + ``` { /** 提示弹框的相关配置。消失时机:显示后鼠标移动到指定区域外或者进入新的单元格后自动消失*/ tooltip: { /** 渲染方式:如使用html具有较好灵活行,上层可以覆盖样式;canvas具有较好的跨平台展示稳定性 */ - renderMode: 'html' | 'canvas'; - /** 代替原来hover:isShowTooltip配置 */ + renderMode?: 'html' | 'canvas'; + /** 是否显示缩略文字提示框 */ isShowOverflowTextTooltip: boolean; + /** 缩略文字提示框 延迟消失时间 */ + overflowTextTooltipDisappearDelay?: number; /** 弹框是否需要限定在表格区域内 */ confine: boolean; }; } ``` -## 开启溢出内容提示 +## tooltip 提示框的样式设置 + +tooltip 的样式配置可以通过 theme.tooltipStyle 来进行设置,具体配置如下: + +``` +export type TooltipStyle = { + fontFamily?: string; + fontSize?: number; + color?: string; + padding?: number[]; + bgColor?: string; + maxWidth?: number; + maxHeight?: number; +}; + +``` +## 开启溢出内容提示 -VTable默认开启溢出内容的tooltip:isShowOverflowTextTooltip默认为true。 +VTable 默认开启溢出内容的 tooltip:isShowOverflowTextTooltip 默认为 true。如果需要延迟消失以使得鼠标可以移动到 tooltip 内容上,可以配置 overflowTextTooltipDisappearDelay。 ![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/c0de7ff0a101bd4cb25c8170e.gif) -## 通过接口显示tooltip自定义信息 +## 自定义图标 hover 提示 + +如配置表头 icon 图标具体如下: + +``` +const tableInstance = new VTable.ListTable({ + columns: [ + { + field: 'orderID', + title: '订单编号', + headerIcon: { + type: 'svg', //指定svg格式图标,其他还支持path,image + svg: ` + + `, + width: 20, + height: 20, + name: 'filter', //定义图标的名称,在内部会作为缓存的key值 + positionType: VTable.TYPES.IconPosition.absoluteRight, // 指定位置,可以在文本的前后,或者在绝对定位在单元格的左侧右侧 + visibleTime: 'mouseenter_cell', // 显示时机, 'always' | 'mouseenter_cell' | 'click_cell' + hover: { + // 热区大小 + width: 26, + height: 26, + bgColor: 'rgba(22,44,66,0.5)' + }, + tooltip: { + style: { arrowMark: false }, + // 气泡框,按钮的的解释信息 + title: '过滤', + placement: VTable.TYPES.Placement.right, + disappearDelay: 1000, + } + } + } + ] +}); +``` + +其中 headerIcon 中的 tooltip 即是鼠标 hover 到 icon 上的提示框,同时配置了 disappearDelay 使弹出框可以延迟消失以便得鼠标可以移动到 tooltip 内容上。 + +关于 icon 的配置具体参考教程:https://visactor.io/vtable/guide/custom_define/custom_icon。 -接口showTooltip可主动显示tooltip信息,如下使用方式:(监听单元格hover事件,调用接口) +## 通过接口显示 tooltip 自定义信息 + +接口 showTooltip 可主动显示 tooltip 信息,如下使用方式:(监听单元格 hover 事件,调用接口) [参考接口说明](https://visactor.io/vtable/option/Methods#showTooltip) + ``` tableInstance.on('mouseenter_cell', (args) => { const { col, row, targetIcon } = args; @@ -57,5 +120,31 @@ VTable默认开启溢出内容的tooltip:isShowOverflowTextTooltip默认为tru } }); ``` + 效果: ![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/ffc3a9b5518762d274121ff05.gif) + +## icon 的 tooltip 配置 + +当自定义 icon 时,可以通过配置 tooltip 来显示提示信息,如下使用方式: + +``` +VTable.register.icon('order', { + ... //其他配置 + tooltip: { + // 气泡框,按钮的的解释信息 + title:'Order ID is the unique identifier for each order', + style: { + fontSize: 14, + fontFamily: 'Arial', + padding: [10,10,10,10], + bgColor: 'black', + arrowMark: true, + color: 'white', + maxHeight: 100, + maxWidth: 200 + }, + disappearDelay: 1000 + } +}) +``` diff --git a/docs/assets/guide/zh/custom_define/custom_icon.md b/docs/assets/guide/zh/custom_define/custom_icon.md index 50261a077..600730526 100644 --- a/docs/assets/guide/zh/custom_define/custom_icon.md +++ b/docs/assets/guide/zh/custom_define/custom_icon.md @@ -10,7 +10,7 @@ - `icon` 则用于配置 body 单元格的图标。 -配置具体内容为[ColumnIconOption]() 类型的对象,也可以通过传递一个自定义函数,动态设置单元格的图标样式。 +配置具体内容为`ColumnIconOption`类型的对象,也可以通过传递一个自定义函数,动态设置单元格的图标样式。ColumnIconOption 具体定义可以参考:https://visactor.io/vtable/option/ListTable-columns-text#icon ### 表头图标配置示例 @@ -192,8 +192,7 @@ VTable.register.icon('frozenCurrent', { }); ``` -替换后的效果如: -TODO +替换后的效果在 demo 中查看:https://visactor.io/vtable/demo/custom-render/custom-icon 同样的方法,我们可以替换其他功能性图标。在 VTable 中内置了几种关联内部功能的图标,如排序,固定列,下拉菜单图标,展开折叠图标等。 @@ -229,6 +228,6 @@ TODO ] ``` -同时自己业务中注册的图标,不需要配置`funcType`。 +**注意:同时自己业务中注册的图标,不需要配置`funcType`。** 至此,关于 VTable 中 icon 的使用方法、注册和替换功能图标的教程就全部介绍完毕。希望本教程能帮助您更好地理解和使用 VTable,打造出更加优美、实用的数据可视化表格 diff --git a/docs/assets/guide/zh/data_analysis/list_table_dataAnalysis.md b/docs/assets/guide/zh/data_analysis/list_table_dataAnalysis.md index a7336debe..439f5dbe7 100644 --- a/docs/assets/guide/zh/data_analysis/list_table_dataAnalysis.md +++ b/docs/assets/guide/zh/data_analysis/list_table_dataAnalysis.md @@ -4,7 +4,7 @@ # 数据排序 -具体可参阅教程:https://visactor.io/vtable/guide/basic_function/sort +具体可参阅教程:https://visactor.io/vtable/guide/basic_function/sort/list_sort # 数据过滤 diff --git a/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md b/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md index e0d53f579..e0cb369dd 100644 --- a/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md +++ b/docs/assets/guide/zh/data_analysis/pivot_table_dataAnalysis.md @@ -68,7 +68,7 @@ dataConfig 应用举例: ### 1. 数据汇总规则 -[option 说明](../../../option/PivotTable#dataConfig.totals) +[option 说明](../../option/PivotTable#dataConfig.totals) 配置示例: ``` @@ -97,8 +97,13 @@ dataConfig: { ### 2. 排序规则 -[option 说明](../../../option/PivotTable#dataConfig.sortRules) -配置示例: +VTable 的透视表支持四种排序方式:维度值自然排序、指定维度值顺序,指标值排序、自定义排序。 + +定义可以参考: + +[option 说明](../../option/PivotTable#dataConfig.sortRules) [使用教程](../../guide/basic_function/sort/pivot_sort) + +指标值排序配置示例如下: ``` sortRules: [ @@ -118,7 +123,7 @@ dataConfig: { ### 3. 过滤规则 -[option 说明](../../../option/PivotTable#dataConfig.filterRules) +[option 说明](../../option/PivotTable#dataConfig.filterRules) 配置示例: ``` @@ -135,7 +140,7 @@ filterRules: [ ### 4. 聚合方式 -[option 说明](../../../option/PivotTable#dataConfig.aggregationRules) +[option 说明](../../option/PivotTable#dataConfig.aggregationRules) 配置示例: ``` @@ -201,7 +206,7 @@ dataConfig:{ ### 5. 派生字段 -[option 说明](../../../option/PivotTable#dataConfig.derivedFieldRules) +[option 说明](../../option/PivotTable#dataConfig.derivedFieldRules) 配置示例: ``` diff --git a/docs/assets/guide/zh/export/excel.md b/docs/assets/guide/zh/export/excel.md index afe10713b..52c5b1e69 100644 --- a/docs/assets/guide/zh/export/excel.md +++ b/docs/assets/guide/zh/export/excel.md @@ -17,13 +17,13 @@ import { downloadExcel, exportVTableToExcel } from '@visactor/vtable-export'; const tableInstance = new VTable.ListTable(option); // donload csv file -downloadExcel(exportVTableToExcel(tableInstance), 'export-csv'); +downloadExcel(exportVTableToExcel(tableInstance, optionForExport), 'export-csv'); ``` -* `exportVTableToExcel`:表格输出工具,将表格实例输出为一个Excel格式的ArrayBuffer +* `exportVTableToExcel`:表格输出工具,将表格实例输出为一个Excel格式的ArrayBuffer;option为可选参数,详见下方配置项 * `downloadExcel`:下载工具,在浏览器环境中将Excel格式的ArrayBuffer下载为文件 * 如果是服务端环境,可以自行处理`exportVTableToExcel`转换出的Excel格式的ArrayBuffer -* 目前excel导出功能正在完善中,目前只支持文字类型的单元格导出,后续会支持迷你图等更多类型。 +* 目前excel导出功能正在完善中,目前只支持文字类型的单元格导出,后续会支持迷你图等更多类型 参考[demo](../../demo/export/table-export) @@ -40,4 +40,40 @@ downloadExcel(exportVTableToExcel(tableInstance), 'export-csv'); ```js const { downloadCsv, exportVTableToCsv } = VTable.export; // ...... +``` + +## 配置项 + +### ignoreIcon + +默认情况下,单元格中有图标时,图标和文字会统一当做图片被导出;如果不需要导出图标,可以设置`ignoreIcon`为true,只输出文字 + +### formatExportOutput + +默认情况下,表格导出时,会将导出单元格的内文字或图片输出到Excel中,如果需要自定义导出内容,可以设置`formatExportOutput`为一个函数,函数的参数为单元格信息,函数的返回值为导出字符串,如果返回`undefined`,则按照默认导出逻辑处理 + +```ts +type CellInfo = { + cellType: string; + cellValue: string; + table: IVTable; + col: number; + row: number; +}; + +type ExportVTableToExcelOptions = { + ignoreIcon?: boolean; + formatExportOutput?: (cellInfo: CellInfo) => string | undefined; +}; +``` + +```js +const excelOption = { + formatExportOutput: ({ cellType, cellValue, table, col, row }) => { + if (cellType === 'checkbox') { + return table.getCellCheckboxState(col, row) ? 'true' : 'false'; + } + } +}; +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 efeb32c37..7e73ffd4c 100644 --- a/docs/assets/option/en/common/option-secondary.md +++ b/docs/assets/option/en/common/option-secondary.md @@ -213,6 +213,14 @@ Do not respond to mouse select interaction. Separately set the header not to respond to mouse select interaction. +##${prefix} blankAreaClickDeselect(boolean) = false + +Whether to cancel the selection when clicking the blank area. + +##${prefix} outsideClickDeselect(boolean) = true + +Whether to cancel the selection when clicking outside the table. + #${prefix} theme(Object) {{ use: common-theme( @@ -291,7 +299,11 @@ Html is currently more complete, default using html rendering method. Currently ##${prefix} isShowOverflowTextTooltip (boolean) -Replace the original hover:isShowTooltip configuration. Temporarily need to set renderMode to html to display, canvas has not been developed yet. +Whether to display overflow text content tooltip when hovering over the cell. Temporarily, renderMode needs to be configured as html to display, and canvas has not been developed yet. + +##${prefix} overflowTextTooltipDisappearDelay (number) + +The overflow text tooltip delays disappearance time. If you need to delay disappearance so that the mouse can move to the tooltip content, you can configure this configuration item. ##${prefix} confine (boolean) = true @@ -495,6 +507,7 @@ animationAppear?: boolean | { ``` You can configure true to enable the default animation, or you can configure the animation parameters: + - `type` The type of the entry animation, currently supports `all` and `one-by-one`, and the default is `one-by-one` - `direction` The direction of the entry animation, currently supports `row` and `column`, and the default is `row` - `duration` The duration of a single animation, in milliseconds, for `one-by-one`, it is the duration of one animation, and the default is 500 diff --git a/docs/assets/option/en/common/style.md b/docs/assets/option/en/common/style.md index bd9464c97..2cbbca695 100644 --- a/docs/assets/option/en/common/style.md +++ b/docs/assets/option/en/common/style.md @@ -25,6 +25,12 @@ Define the text color of the cell prefix = ${prefix}, ) }} +#${prefix} strokeColor(ColorPropertyDefine) +Define the text stroke color of the cell +{{ use: common-color( + prefix = ${prefix}, +) }} + #${prefix} fontSize(FontSizePropertyDefine) Define the text size of the cell {{ use: common-font-size( diff --git a/docs/assets/option/en/icon/base-icon.md b/docs/assets/option/en/icon/base-icon.md index 3b9fe2f04..e7d847e33 100644 --- a/docs/assets/option/en/icon/base-icon.md +++ b/docs/assets/option/en/icon/base-icon.md @@ -20,10 +20,6 @@ IconPosition enumeration type. * */ export enum IconPosition { - /**The icon in front of the text line content follows the text positioning and wraps with the text */ - inlineFront = 'inlineFront', - /**The icon after the text line content, positioned with the text, and wrapped with the text. For example, the sort chart is placed in the first line of the text content */ - inlineEnd = 'inlineEnd', /**Button on the left side of the cell and affected by padding */ left = 'left', /**The button on the right side of the cell is affected by padding, such as the pin chart */ @@ -35,7 +31,12 @@ export enum IconPosition { /**The icon on the right side of the cell content block follows the text positioning and does not wrap with the text */ contentRight = 'contentRight', /**Free positioning in the cell */ - absolute = 'absolute' + absolute = 'absolute', + + /**The icon in front of the text line content follows the text positioning and wraps with the text */ + inlineFront = 'inlineFront', + /**The icon after the text line content, positioned with the text, and wrapped with the text. For example, the sort chart is placed in the first line of the text content */ + inlineEnd = 'inlineEnd', } ``` @@ -112,6 +113,9 @@ Placement enumeration definition: } ``` +#${prefix} disappearDelay (number) +The delay time for the tooltip to disappear. If you need to move the mouse to the tooltip, please configure this parameter. + #${prefix} style (Object) The style of the tooltip. If not configured, the theme style will be used. @@ -130,5 +134,11 @@ Tooltip background color. ##${prefix} arrowMark (boolean) Whether the tooltip displays an arrow. +##${prefix} maxWidth (number) +The maximum width of the tooltip. + +##${prefix} maxHeight (number) +The maximum height of the tooltip. + ${prefix} interactive (boolean) Whether it is interactive, default is true. Currently known non-interactive buttons are dropdown menu states. diff --git a/docs/assets/option/en/table/listTable.md b/docs/assets/option/en/table/listTable.md index b3b330d36..5b768db54 100644 --- a/docs/assets/option/en/table/listTable.md +++ b/docs/assets/option/en/table/listTable.md @@ -32,6 +32,7 @@ Whether to transpose, default is false Whether to display the table header. + ## pagination(IPagination) Pagination configuration. diff --git a/docs/assets/option/en/table/pivotChart.md b/docs/assets/option/en/table/pivotChart.md index 162e5c344..28be1ed1a 100644 --- a/docs/assets/option/en/table/pivotChart.md +++ b/docs/assets/option/en/table/pivotChart.md @@ -51,7 +51,7 @@ The currently supported data formats are, taking the sales of large supermarkets ## columnTree(Array) -List header tree, type:`IDimensionHeaderNode|IIndicatorHeaderNode[]`. Among them, IDimensionHeaderNode refers to the dimension value node of the dimension non-indicator, and IIndicatorHeaderNode refers to the indicator name node. +List header tree, type:`(IDimensionHeaderNode|IIndicatorHeaderNode)[]`. Among them, IDimensionHeaderNode refers to the dimension value node of the dimension non-indicator, and IIndicatorHeaderNode refers to the indicator name node. ** The specific configuration items of IDimensionHeaderNode are as follows:** @@ -63,8 +63,8 @@ export interface IDimensionHeaderNode { dimensionKey: string | number; /** dimension member value */ value: string; - /** Subdimension tree structure under dimension members */ - children?: IDimensionHeaderNode|IIndicatorHeaderNode[]; + /** Subdimension tree structure under dimension members. */ + children?: (IDimensionHeaderNode|IIndicatorHeaderNode)[] ; /** The collapsed state is used with the tree structure display. Note: only valid in rowTree */ hierarchyState?: HierarchyState; } diff --git a/docs/assets/option/en/table/pivotTable.md b/docs/assets/option/en/table/pivotTable.md index 9777fed0f..ef4ce6339 100644 --- a/docs/assets/option/en/table/pivotTable.md +++ b/docs/assets/option/en/table/pivotTable.md @@ -237,7 +237,7 @@ export interface DerivedFieldRule { ## columnTree(Array) -Column header tree, type: `IDimensionHeaderNode|IIndicatorHeaderNode[]`. Among them, IDimensionHeaderNode refers to the dimension value node of non-indicator dimensions, and IIndicatorHeaderNode refers to the indicator name node. +Column header tree, type: `(IDimensionHeaderNode|IIndicatorHeaderNode)[]`. Among them, IDimensionHeaderNode refers to the dimension value node of non-indicator dimensions, and IIndicatorHeaderNode refers to the indicator name node. ** Specific configuration of IDimensionHeaderNode is as follows: ** @@ -249,8 +249,8 @@ export interface IDimensionHeaderNode { dimensionKey: string | number; /** Dimension member value */ value: string; - /** The tree structure of the sub-dimensions under the member */ - children?: IDimensionHeaderNode|IIndicatorHeaderNode[]; + /** The tree structure of the sub-dimensions under the member. true is generally used to display the fold and expand buttons and to perform lazy loading to obtain data. */ + children?: (IDimensionHeaderNode|IIndicatorHeaderNode)[] | true; /** Collapse status Used with tree structure display. Note: only valid in rowTree */ hierarchyState?: HierarchyState; /** Whether it is a virtual node. If configured to true, this dimension field will be ignored when analyzing based on records data */ diff --git a/docs/assets/option/zh/common/option-secondary.md b/docs/assets/option/zh/common/option-secondary.md index f01c5db48..660fe7d7f 100644 --- a/docs/assets/option/zh/common/option-secondary.md +++ b/docs/assets/option/zh/common/option-secondary.md @@ -208,6 +208,14 @@ hover 交互响应模式:十字交叉、整列、整行或者单个单元格 单独设置表头不响应鼠标 select 交互。 +##${prefix} blankAreaClickDeselect(boolean) = false + +点击空白区域是否取消选中。 + +##${prefix} outsideClickDeselect(boolean) = true + +点击外部区域是否取消选中。 + #${prefix} theme(Object) {{ use: common-theme( @@ -284,7 +292,11 @@ html 目前实现较完整,先默认使用 html 渲染方式。目前暂不支 ##${prefix} isShowOverflowTextTooltip (boolean) -代替原来 hover:isShowTooltip 配置。暂时需要将 renderMode 配置为 html 才能显示,canvas 的还未开发。 +是否需要在 hover 到单元格时显示溢出文本内容 tooltip。暂时需要将 renderMode 配置为 html 才能显示,canvas 的还未开发。 + +##${prefix} overflowTextTooltipDisappearDelay (number) + +溢出文本 tooltip 延时消失时间,如果需要延迟消失以使得鼠标可以移动到 tooltip 内容上,可以配置该配置项。 ##${prefix} confine (boolean) = true @@ -491,7 +503,8 @@ animationAppear?: boolean | { }; ``` -可以配置true开启默认动画,也可以配置动画的参数: +可以配置 true 开启默认动画,也可以配置动画的参数: + - `type` 入场动画的类型,目前支持 `all` 和 `one-by-one`两种,默认为 `one-by-one` - `direction` 入场动画的方向,目前支持 `row` 和 `column`两种,默认为 `row` - `duration` 单个动画的时长,单位为毫秒,`one-by-one` 时,为一次动画的时长,默认为 500 diff --git a/docs/assets/option/zh/common/style.md b/docs/assets/option/zh/common/style.md index ca8dfc3ee..969ce580c 100644 --- a/docs/assets/option/zh/common/style.md +++ b/docs/assets/option/zh/common/style.md @@ -25,6 +25,12 @@ prefix = ${prefix}, ) }} +#${prefix} strokeColor(ColorPropertyDefine) +定义单元格的文字描边颜色 +{{ use: common-color( + prefix = ${prefix}, +) }} + #${prefix} fontSize(FontSizePropertyDefine) 定义单元格的文字大小 {{ use: common-font-size( diff --git a/docs/assets/option/zh/icon/base-icon.md b/docs/assets/option/zh/icon/base-icon.md index 08f06a2d5..04e1bdb01 100644 --- a/docs/assets/option/zh/icon/base-icon.md +++ b/docs/assets/option/zh/icon/base-icon.md @@ -20,10 +20,6 @@ IconPosition 枚举类型。 * */ export enum IconPosition { - /**文本行内容前面的图标,跟随文本定位,随文本折行 */ - inlineFront = 'inlineFront', - /**文本行内容后面的图标,跟随文本定位,随文本折行。如sort图表 放在文本内容的第一行 */ - inlineEnd = 'inlineEnd', /**单元格左侧按钮 且受padding影响 */ left = 'left', /**单元格右侧按钮 受padding影响 如pin图表 */ @@ -35,7 +31,12 @@ export enum IconPosition { /**在单元格内容块的右侧的图标,跟随文本定位,不随文本折行 */ contentRight = 'contentRight', /**在单元格中自由定位 */ - absolute = 'absolute' + absolute = 'absolute', + + /**文本行内容前面的图标,跟随文本定位,随文本折行 */ + inlineFront = 'inlineFront', + /**文本行内容后面的图标,跟随文本定位,随文本折行。如sort图表 放在文本内容的第一行 */ + inlineEnd = 'inlineEnd', } ``` @@ -112,6 +113,9 @@ Placement 枚举类型定义: } ``` +#${prefix} disappearDelay (number) +提示框消失延迟时间,如果有需要鼠标移动到 tooltip 上的需求,请配置上这个参数。 + #${prefix} style (Object) 气泡框的样式。如果不配置,会使用 theme 中的样式。 @@ -130,5 +134,11 @@ Placement 枚举类型定义: ##${prefix} arrowMark (boolean) 气泡框是否显示箭头。 +##${prefix} maxWidth (number) +tooltip 的最大宽度。 + +##${prefix} maxHeight (number) +tooltip 的最大高度。 + ${prefix} interactive (boolean) 是否可交互,默认为 true。目前已知不可交互按钮为下拉菜单状态。 diff --git a/docs/assets/option/zh/table/pivotChart.md b/docs/assets/option/zh/table/pivotChart.md index fddfbd9db..5e1461337 100644 --- a/docs/assets/option/zh/table/pivotChart.md +++ b/docs/assets/option/zh/table/pivotChart.md @@ -51,7 +51,7 @@ ## columnTree(Array) -列表头树,类型为:`IDimensionHeaderNode|IIndicatorHeaderNode[]`。其中 IDimensionHeaderNode 指的是维度非指标的维度值节点,IIndicatorHeaderNode 指的是指标名称节点。 +列表头树,类型为:`(IDimensionHeaderNode|IIndicatorHeaderNode)[]`。其中 IDimensionHeaderNode 指的是维度非指标的维度值节点,IIndicatorHeaderNode 指的是指标名称节点。 ** IDimensionHeaderNode 具体配置项如下:** @@ -64,7 +64,7 @@ export interface IDimensionHeaderNode { /** 维度成员值 */ value: string; /** 维度成员下的子维度树结构 */ - children?: IDimensionHeaderNode|IIndicatorHeaderNode[]; + children?: (IDimensionHeaderNode|IIndicatorHeaderNode)[]; /** 折叠状态 配合树形结构展示使用。注意:仅在rowTree中有效 */ hierarchyState?: HierarchyState; } diff --git a/docs/assets/option/zh/table/pivotTable.md b/docs/assets/option/zh/table/pivotTable.md index 441f85d39..8d8b64a1b 100644 --- a/docs/assets/option/zh/table/pivotTable.md +++ b/docs/assets/option/zh/table/pivotTable.md @@ -241,7 +241,7 @@ export interface DerivedFieldRule { ## columnTree(Array) -列表头树,类型为:`IDimensionHeaderNode|IIndicatorHeaderNode[]`。其中 IDimensionHeaderNode 指的是维度非指标的维度值节点,IIndicatorHeaderNode 指的是指标名称节点。 +列表头树,类型为:`(IDimensionHeaderNode|IIndicatorHeaderNode)[]`。其中 IDimensionHeaderNode 指的是维度非指标的维度值节点,IIndicatorHeaderNode 指的是指标名称节点。 ** IDimensionHeaderNode 具体配置项如下:** @@ -253,8 +253,8 @@ export interface IDimensionHeaderNode { dimensionKey: string | number; /** 维度成员值 */ value: string; - /** 维度成员下的子维度树结构 */ - children?: IDimensionHeaderNode|IIndicatorHeaderNode[]; + /** 维度成员下的子维度树结构。 true一般是用在显示折叠展开按钮,进行懒加载获取数据的场景中。 */ + children?: (IDimensionHeaderNode|IIndicatorHeaderNode)[] | true; /** 折叠状态 配合树形结构展示使用。注意:仅在rowTree中有效 */ hierarchyState?: HierarchyState; /** 是否为虚拟节点。 如果配置为true,则在基于records数据做分析时会忽略该维度字段 */ diff --git a/packages/openinula-vtable/package.json b/packages/openinula-vtable/package.json index bee4c09a1..dcdc28bcf 100644 --- a/packages/openinula-vtable/package.json +++ b/packages/openinula-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/openinula-vtable", - "version": "1.2.0", + "version": "1.3.2", "description": "The openinula version of VTable", "keywords": [ "openinula", diff --git a/packages/react-vtable/package.json b/packages/react-vtable/package.json index 435415299..a53afa788 100644 --- a/packages/react-vtable/package.json +++ b/packages/react-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vtable", - "version": "1.2.0", + "version": "1.3.2", "description": "The react version of VTable", "keywords": [ "react", diff --git a/packages/vtable-editors/package.json b/packages/vtable-editors/package.json index 934005722..b9bcf90ed 100644 --- a/packages/vtable-editors/package.json +++ b/packages/vtable-editors/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-editors", - "version": "1.2.0", + "version": "1.3.2", "description": "", "sideEffects": false, "main": "cjs/index.js", diff --git a/packages/vtable-export/demo/list/list-checkbox.ts b/packages/vtable-export/demo/list/list-checkbox.ts new file mode 100644 index 000000000..b16b650c6 --- /dev/null +++ b/packages/vtable-export/demo/list/list-checkbox.ts @@ -0,0 +1,80 @@ +import * as VTable from '@visactor/vtable'; +const CONTAINER_ID = 'vTable'; +const generatePersons = count => { + return Array.from(new Array(count)).map((_, i) => ({ + id: i + 1, + email1: `${i + 1}@xxx.com`, + name: `小明${i + 1}`, + lastName: '王', + date1: '2022年9月1日', + tel: '000-0000-0000', + sex: i % 2 === 0 ? 'boy' : 'girl', + work: i % 2 === 0 ? 'back-end engineer' : 'front-end engineer', + city: 'beijing' + })); +}; + +export function createTable() { + const records = [ + { productName: 'aaaa', price: 20, check: { text: 'unchecked', checked: false, disable: false } }, + { productName: 'bbbb', price: 18, check: { text: 'checked', checked: true, disable: false } }, + { productName: 'cccc', price: 16, check: { text: 'disable', checked: true, disable: true } }, + { productName: 'cccc', price: 14, check: { text: 'disable', checked: false, disable: true } }, + { productName: 'eeee', price: 12, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'ffff', price: 10, check: { text: 'checked', checked: false, disable: false } }, + { productName: 'gggg', price: 10, check: { text: 'checked', checked: false, disable: false } } + ]; + + const columns = [ + { + field: 'isCheck', + title: '', + width: 60, + headerType: 'checkbox', + cellType: 'checkbox' + }, + { + field: 'productName', + title: 'productName', + width: 120 + }, + { + field: 'price', + title: 'checkbox', + width: 120, + cellType: 'checkbox', + disable: true, + checked: true + }, + { + field: 'check', + title: 'checkbox', + width: 120, + cellType: 'checkbox' + // disable: true + } + ]; + const option = { + records, + columns + }; + const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); + window.tableInstance = tableInstance; + window.excelOption = { + formatExportOutput: ({ cellType, cellValue, table, col, row }) => { + if (cellType === 'checkbox') { + return table.getCellCheckboxState(col, row) ? 'true' : 'false'; + } + } + }; + // tableInstance.on('sort_click', args => { + // tableInstance.updateSortState( + // { + // field: args.field, + // order: Date.now() % 3 === 0 ? 'desc' : Date.now() % 3 === 1 ? 'asc' : 'normal' + // }, + // false + // ); + // return false; //return false代表不执行内部排序逻辑 + // }); +} diff --git a/packages/vtable-export/demo/list/list-tree.ts b/packages/vtable-export/demo/list/list-tree.ts new file mode 100644 index 000000000..07a80b307 --- /dev/null +++ b/packages/vtable-export/demo/list/list-tree.ts @@ -0,0 +1,208 @@ +import * as VTable from '@visactor/vtable'; +const CONTAINER_ID = 'vTable'; +const generatePersons = count => { + return Array.from(new Array(count)).map((_, i) => ({ + id: i + 1, + email1: `${i + 1}@xxx.com`, + name: `小明${i + 1}`, + lastName: '王', + date1: '2022年9月1日', + tel: '000-0000-0000', + sex: i % 2 === 0 ? 'boy' : 'girl', + work: i % 2 === 0 ? 'back-end engineer' : 'front-end engineer', + city: 'beijing' + })); +}; + +export function createTable() { + const data = [ + { + 类别: '办公用品', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '信封', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色信封', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色信封', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '器具', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '订书机', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '计算器', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] + }, + { + 类别: '技术', + 销售额: '229.696', + 数量: '20', + 利润: '90.704', + children: [ + { + 类别: '设备', // 对应原子类别 + 销售额: '225.44', + 数量: '5', + 利润: '462.56' + }, + { + 类别: '配件', // 对应原子类别 + 销售额: '375.92', + 数量: '8', + 利润: '550.2' + }, + { + 类别: '复印机', // 对应原子类别 + 销售额: '425.44', + 数量: '7', + 利润: '342.56' + }, + { + 类别: '电话', // 对应原子类别 + 销售额: '175.92', + 数量: '6', + 利润: '750.2' + } + ] + }, + { + 类别: '家具', + 销售额: '129.696', + 数量: '2', + 利润: '-60.704', + children: [ + { + 类别: '桌子', // 对应原子类别 + 销售额: '125.44', + 数量: '2', + 利润: '42.56', + children: [ + { + 类别: '黄色桌子', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '白色桌子', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + }, + { + 类别: '椅子', // 对应原子类别 + 销售额: '1375.92', + 数量: '3', + 利润: '550.2', + children: [ + { + 类别: '老板椅', + 销售额: '125.44', + 数量: '2', + 利润: '42.56' + }, + { + 类别: '沙发椅', + 销售额: '1375.92', + 数量: '3', + 利润: '550.2' + } + ] + } + ] + }, + { + 类别: '生活家电(懒加载)', + 销售额: '229.696', + 数量: '20', + 利润: '90.704' + } + ]; + const option = { + container: document.getElementById(CONTAINER_ID), + columns: [ + { + field: '类别', + tree: true, + title: '类别', + width: 'auto', + sort: true + }, + { + field: '销售额', + title: '销售额', + width: 'auto', + sort: true + // tree: true, + }, + { + field: '利润', + title: '利润', + width: 'auto', + sort: true + } + ], + showPin: true, //显示VTable内置冻结列图标 + widthMode: 'standard', + allowFrozenColCount: 2, + records: data, + + hierarchyIndent: 20, + hierarchyExpandLevel: 2, + hierarchyTextStartAlignment: true, + sortState: { + field: '销售额', + order: 'asc' + }, + theme: VTable.themes.BRIGHT, + defaultRowHeight: 32 + }; + const tableInstance = new VTable.ListTable(document.getElementById(CONTAINER_ID), option); + window.tableInstance = tableInstance; + window.excelOption = { + ignoreIcon: true + }; + // tableInstance.on('sort_click', args => { + // tableInstance.updateSortState( + // { + // field: args.field, + // order: Date.now() % 3 === 0 ? 'desc' : Date.now() % 3 === 1 ? 'asc' : 'normal' + // }, + // false + // ); + // return false; //return false代表不执行内部排序逻辑 + // }); +} diff --git a/packages/vtable-export/demo/main.ts b/packages/vtable-export/demo/main.ts index 80f9319c8..1d4579990 100644 --- a/packages/vtable-export/demo/main.ts +++ b/packages/vtable-export/demo/main.ts @@ -143,13 +143,13 @@ function bindExport() { exportCsvButton.addEventListener('click', () => { if (window.tableInstance) { - downloadCsv(exportVTableToCsv(window.tableInstance), 'export'); + downloadCsv(exportVTableToCsv(window.tableInstance, window.csvOption), 'export'); } }); exportExcelButton.addEventListener('click', async () => { if (window.tableInstance) { - downloadExcel(await exportVTableToExcel(window.tableInstance), 'export'); + downloadExcel(await exportVTableToExcel(window.tableInstance, window.excelOption), 'export'); } }); } diff --git a/packages/vtable-export/demo/menu.ts b/packages/vtable-export/demo/menu.ts index 75e9b4918..01ac1cfe1 100644 --- a/packages/vtable-export/demo/menu.ts +++ b/packages/vtable-export/demo/menu.ts @@ -29,6 +29,14 @@ export const menus = [ { path: 'list', name: 'list-large' + }, + { + path: 'list', + name: 'list-tree' + }, + { + path: 'list', + name: 'list-checkbox' } ] }, diff --git a/packages/vtable-export/package.json b/packages/vtable-export/package.json index 18ba6cc88..05d524594 100644 --- a/packages/vtable-export/package.json +++ b/packages/vtable-export/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-export", - "version": "1.2.0", + "version": "1.3.2", "description": "The export util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-export/src/csv/index.ts b/packages/vtable-export/src/csv/index.ts index 325efb9d0..c5ce305cc 100644 --- a/packages/vtable-export/src/csv/index.ts +++ b/packages/vtable-export/src/csv/index.ts @@ -1,4 +1,5 @@ import type * as VTable from '@visactor/vtable'; +import type { CellInfo } from '../excel'; type IVTable = VTable.ListTable | VTable.PivotTable | VTable.PivotChart; type CellRange = VTable.TYPES.CellRange; @@ -6,7 +7,11 @@ type CellRange = VTable.TYPES.CellRange; const newLine = '\r\n'; const separator = ','; -export function exportVTableToCsv(tableInstance: IVTable): string { +export type ExportVTableToCsvOptions = { + formatExportOutput?: (cellInfo: CellInfo) => string | undefined; +}; + +export function exportVTableToCsv(tableInstance: IVTable, option?: ExportVTableToCsvOptions): string { const minRow = 0; const maxRow = tableInstance.rowCount - 1; const minCol = 0; @@ -15,7 +20,7 @@ export function exportVTableToCsv(tableInstance: IVTable): string { let copyValue = ''; for (let row = minRow; row <= maxRow; row++) { for (let col = minCol; col <= maxCol; col++) { - const copyCellValue = getCopyCellValue(col, row, tableInstance); + const copyCellValue = getCopyCellValue(col, row, tableInstance, option); if (typeof Promise !== 'undefined' && copyCellValue instanceof Promise) { // not support async } else { @@ -33,7 +38,22 @@ export function exportVTableToCsv(tableInstance: IVTable): string { return copyValue; } -function getCopyCellValue(col: number, row: number, tableInstance: IVTable): string | Promise | void { +function getCopyCellValue( + col: number, + row: number, + tableInstance: IVTable, + option?: ExportVTableToCsvOptions +): string | Promise | void { + if (option?.formatExportOutput) { + const cellInfo = { cellType: '', cellValue: '', table: tableInstance, col, row }; + const formattedValue = option.formatExportOutput(cellInfo); + if (formattedValue !== undefined) { + if (typeof formattedValue === 'string') { + return '"' + formattedValue + '"'; + } + return formattedValue; + } + } const cellRange: CellRange = tableInstance.getCellRange(col, row); const copyStartCol = cellRange.start.col; const copyStartRow = cellRange.start.row; diff --git a/packages/vtable-export/src/excel/index.ts b/packages/vtable-export/src/excel/index.ts index 83bf050f1..6f2ad25c6 100644 --- a/packages/vtable-export/src/excel/index.ts +++ b/packages/vtable-export/src/excel/index.ts @@ -6,7 +6,20 @@ import { updateCell, renderChart, graphicUtil } from '@visactor/vtable'; import { isArray } from '@visactor/vutils'; import type { ColumnDefine, IRowSeriesNumber } from '@visactor/vtable/src/ts-types'; -export async function exportVTableToExcel(tableInstance: IVTable) { +export type CellInfo = { + cellType: string; + cellValue: string; + table: IVTable; + col: number; + row: number; +}; + +export type ExportVTableToExcelOptions = { + ignoreIcon?: boolean; + formatExportOutput?: (cellInfo: CellInfo) => string | undefined; +}; + +export async function exportVTableToExcel(tableInstance: IVTable, options?: ExportVTableToExcelOptions) { const workbook = new ExcelJS.Workbook(); const worksheet = workbook.addWorksheet('sheet1'); worksheet.properties.defaultRowHeight = 40; @@ -30,7 +43,7 @@ export async function exportVTableToExcel(tableInstance: IVTable) { worksheetRow.height = rowHeight; } - addCell(col, row, tableInstance, worksheet, workbook); + addCell(col, row, tableInstance, worksheet, workbook, options); const cellRange = tableInstance.getCellRange(col, row); if (cellRange.start.col !== cellRange.end.col || cellRange.start.row !== cellRange.end.row) { @@ -85,7 +98,8 @@ function addCell( row: number, tableInstance: IVTable, worksheet: ExcelJS.Worksheet, - workbook: ExcelJS.Workbook + workbook: ExcelJS.Workbook, + options?: ExportVTableToExcelOptions ) { const { layoutMap } = tableInstance.internalProps; const cellType = tableInstance.getCellType(col, row); @@ -113,13 +127,27 @@ function addCell( customLayout = (define as ColumnDefine)?.customLayout; } + if (options?.formatExportOutput) { + const cellInfo = { cellType, cellValue, table: tableInstance, col, row }; + const formattedValue = options.formatExportOutput(cellInfo); + if (formattedValue !== undefined) { + const cell = worksheet.getCell(encodeCellAddress(col, row)); + cell.value = formattedValue; + cell.font = getCellFont(cellStyle, cellType); + cell.fill = getCellFill(cellStyle); + cell.border = getCellBorder(cellStyle); + cell.alignment = getCellAlignment(cellStyle); + return; + } + } + if ( cellType === 'image' || cellType === 'video' || cellType === 'progressbar' || cellType === 'sparkline' || layoutMap.isAxisCell(col, row) || - (isArray(icons) && icons.length) || + (!options?.ignoreIcon && isArray(icons) && icons.length) || customRender || customLayout ) { diff --git a/packages/vtable-search/README.md b/packages/vtable-search/README.md index 969fd10dd..456ef1467 100644 --- a/packages/vtable-search/README.md +++ b/packages/vtable-search/README.md @@ -22,51 +22,32 @@ VTable is not just a high-performance multidimensional data analysis table, but ## Installation -[npm package](https://www.npmjs.com/package/@visactor/vtable-export) +[npm package](https://www.npmjs.com/package/@visactor/vtable-search) ```bash // npm -npm install @visactor/vtable-export +npm install @visactor/vtable-search // yarn -yarn add @visactor/vtable-export +yarn add @visactor/vtable-search ``` ## Quick Start ```jsx -import * as VTable from '@visactor/vtable'; -import { downloadCsv, exportVTableToCsv, downloadExcel, exportVTableToExcel } from '@visactor/vtable-export'; - const option = { - header: [ - { - field: "0", - caption: "name", - }, - { - field: "1", - caption: "age", - }, - { - field: "2", - caption: "gender", - }, - { - field: "3", - caption: "hobby", - }, - ], - records: new Array(1000).fill(["John", 18, "male", "🏀"]), + container: document.getElementById(CONTAINER_ID), + records, + columns, }; - const tableInstance = new VTable.ListTable(option); +window.tableInstance = tableInstance; -// donload csv file -downloadCsv(exportVTableToCsv(tableInstance), 'export-csv'); - -// donload excel file -downloadExcel(await exportVTableToExcel(tableInstance), 'export-excel'); +const search = new SearchComponent({ + table: tableInstance, + autoJump: true +}); +window.search = search; ``` diff --git a/packages/vtable-search/package.json b/packages/vtable-search/package.json index c0b74c512..160440955 100644 --- a/packages/vtable-search/package.json +++ b/packages/vtable-search/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-search", - "version": "1.2.0", + "version": "1.3.2", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable/CHANGELOG.json b/packages/vtable/CHANGELOG.json index e92ee9f52..b81762de2 100644 --- a/packages/vtable/CHANGELOG.json +++ b/packages/vtable/CHANGELOG.json @@ -1,6 +1,104 @@ { "name": "@visactor/vtable", "entries": [ + { + "version": "1.3.2", + "tag": "@visactor/vtable_v1.3.2", + "date": "Mon, 17 Jun 2024 11:51:18 GMT", + "comments": { + "none": [ + { + "comment": "fix: cellIsInVisualView api error #1864\n\n" + }, + { + "comment": "refactor: tooltip support scroll #1887\n\n" + }, + { + "comment": "fix: if set style autoWrapText, this config not wort when resize column width #1892\n\n" + }, + { + "comment": "refactor: when not records pivot table can show corner header #1895\n\n" + }, + { + "comment": "refactor: when rowTree children not set value can supplement indicators #1924\n\n" + }, + { + "comment": "feat: add blankAreaClickDeselect and outsideClickDeselect config\n\n" + } + ] + } + }, + { + "version": "1.3.1", + "tag": "@visactor/vtable_v1.3.1", + "date": "Thu, 13 Jun 2024 12:35:49 GMT", + "comments": { + "none": [ + { + "comment": "fix: fix frozenColCount large than colCount error #1872\n\n" + }, + { + "comment": "docs: update changlog of rush\n\n" + }, + { + "comment": "fix: fix merge cell size update #1869" + }, + { + "comment": "fix: optimize row height update when useOneRowHeightFillAll" + } + ] + } + }, + { + "version": "1.3.0", + "tag": "@visactor/vtable_v1.3.0", + "date": "Wed, 12 Jun 2024 12:30:16 GMT", + "comments": { + "none": [ + { + "comment": "refactor: memory release logic optimization #1856\n\n" + }, + { + "comment": "refactor: arrow key with shift ctrl key to select cells #1873\n\n" + }, + { + "comment": "fix: icon inlineEnd inlineFront x position compute error #1882\n\n" + }, + { + "comment": "fix: drill down icon can not be click #1899\n\n" + }, + { + "comment": "feat: add ignoreIcon&formatExportOutput config in vtable-export #1813" + }, + { + "comment": "feat: add textArea editor\n\n" + }, + { + "comment": "feat: add strokeColor style #1847" + }, + { + "comment": "feat: add dx&dy in title component #1874" + }, + { + "comment": "fix: fix frozenColCount large than colCount error #1872" + }, + { + "comment": "feat: add shrinkSparklineFirst config #1862" + }, + { + "comment": "fix: fix ellipsis error in _disableColumnAndRowSizeRound mode #1884" + } + ], + "minor": [ + { + "comment": "feat: tooltip disappear delay time #1848\n\n" + }, + { + "comment": "feat: add sort config for pivotTable #1865\n\n" + } + ] + } + }, { "version": "1.2.0", "tag": "@visactor/vtable_v1.2.0", diff --git a/packages/vtable/CHANGELOG.md b/packages/vtable/CHANGELOG.md index 13db94fdf..9cae5822a 100644 --- a/packages/vtable/CHANGELOG.md +++ b/packages/vtable/CHANGELOG.md @@ -1,6 +1,80 @@ # Change Log - @visactor/vtable -This log was last generated on Thu, 06 Jun 2024 02:34:10 GMT and should not be manually modified. +This log was last generated on Mon, 17 Jun 2024 11:51:18 GMT and should not be manually modified. + +## 1.3.2 +Mon, 17 Jun 2024 11:51:18 GMT + +### Updates + +- fix: cellIsInVisualView api error #1864 + + +- refactor: tooltip support scroll #1887 + + +- fix: if set style autoWrapText, this config not wort when resize column width #1892 + + +- refactor: when not records pivot table can show corner header #1895 + + +- refactor: when rowTree children not set value can supplement indicators #1924 + + +- feat: add blankAreaClickDeselect and outsideClickDeselect config + + + +## 1.3.1 +Thu, 13 Jun 2024 12:35:49 GMT + +### Updates + +- fix: fix frozenColCount large than colCount error #1872 + + +- docs: update changlog of rush + + +- fix: fix merge cell size update #1869 +- fix: optimize row height update when useOneRowHeightFillAll + +## 1.3.0 +Wed, 12 Jun 2024 12:30:16 GMT + +### Minor changes + +- feat: tooltip disappear delay time #1848 + + +- feat: add sort config for pivotTable #1865 + + + +### Updates + +- refactor: memory release logic optimization #1856 + + +- refactor: arrow key with shift ctrl key to select cells #1873 + + +- fix: icon inlineEnd inlineFront x position compute error #1882 + + +- fix: drill down icon can not be click #1899 + + +- feat: add ignoreIcon&formatExportOutput config in vtable-export #1813 +- feat: add textArea editor + + +- feat: add strokeColor style #1847 +- feat: add dx&dy in title component #1874 +- fix: fix frozenColCount large than colCount error #1872 +- feat: add shrinkSparklineFirst config #1862 +- fix: fix ellipsis error in _disableColumnAndRowSizeRound mode #1884 ## 1.2.0 Thu, 06 Jun 2024 02:34:10 GMT diff --git a/packages/vtable/__tests__/listTable.test.ts b/packages/vtable/__tests__/listTable.test.ts index ca62d6569..b4215bcaf 100644 --- a/packages/vtable/__tests__/listTable.test.ts +++ b/packages/vtable/__tests__/listTable.test.ts @@ -118,6 +118,7 @@ describe('listTable init test', () => { expect(listTable.getScrollLeft()).toBe(901); expect(listTable.getScrollTop()).toBe(720); expect(listTable.getCellStyle(6, 16)).toStrictEqual({ + strokeColor: undefined, textAlign: 'left', textBaseline: 'middle', bgColor: '#FFF', diff --git a/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts new file mode 100644 index 000000000..b4f5d8240 --- /dev/null +++ b/packages/vtable/__tests__/options/listTable-api-with-frozen.test.ts @@ -0,0 +1,119 @@ +// @ts-nocheck +// 有问题可对照demo unitTestListTable +import records from '../data/marketsales.json'; +import { ListTable } from '../../src/ListTable'; +import { createDiv } from '../dom'; +global.__VERSION__ = 'none'; +describe('listTable init 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] + } + }, + { + field: '邮寄方式', + caption: '邮寄方式' + }, + { + field: '省/自治区', + caption: '省/自治区' + }, + { + field: '产品名称', + caption: '产品名称' + }, + { + field: '类别', + caption: '类别' + }, + { + field: '子类别', + caption: '子类别' + }, + { + field: '销售额', + caption: '销售额' + }, + { + field: '数量', + caption: '数量' + }, + { + field: '折扣', + caption: '折扣' + }, + { + field: '利润', + caption: '利润' + } + ]; + const option = { + columns, + defaultColWidth: 150, + frozenColCount: 2, + rightFrozenColCount: 2, + bottomFrozenRowCount: 2 + }; + + option.container = containerDom; + option.records = records; + const listTable = new ListTable(option); + test('listTable cellIsInVisualView', () => { + expect(listTable.cellIsInVisualView(3, 0)).toBe(true); + expect(listTable.cellIsInVisualView(0, 3)).toBe(true); + expect(listTable.cellIsInVisualView(5, 3)).toBe(false); + expect(listTable.cellIsInVisualView(4, 3)).toBe(false); + expect(listTable.cellIsInVisualView(3, 3)).toBe(true); + expect(listTable.cellIsInVisualView(3, 39)).toBe(true); + expect(listTable.cellIsInVisualView(3, 38)).toBe(true); + + expect(listTable.cellIsInVisualView(12, 3)).toBe(true); + expect(listTable.cellIsInVisualView(11, 3)).toBe(true); + expect(listTable.cellIsInVisualView(10, 3)).toBe(false); + + expect(listTable.cellIsInVisualView(3, 37)).toBe(false); + }); + + test('listTable scroll cellIsInVisualView', () => { + listTable.scrollTop = 100; + listTable.scrollLeft = 100; + expect(listTable.cellIsInVisualView(3, 5)).toBe(true); + expect(listTable.cellIsInVisualView(2, 5)).toBe(false); + expect(listTable.cellIsInVisualView(5, 5)).toBe(false); + expect(listTable.cellIsInVisualView(4, 5)).toBe(true); + expect(listTable.cellIsInVisualView(12, 5)).toBe(true); + + expect(listTable.cellIsInVisualView(3, 19)).toBe(true); + expect(listTable.cellIsInVisualView(3, 20)).toBe(false); + expect(listTable.cellIsInVisualView(2, 19)).toBe(false); + + expect(listTable.cellIsInVisualView(3, 39)).toBe(true); + expect(listTable.cellIsInVisualView(3, 38)).toBe(true); + }); +}); diff --git a/packages/vtable/examples/icon/icon-register.ts b/packages/vtable/examples/icon/icon-register.ts index e782ac19f..66e9344ec 100644 --- a/packages/vtable/examples/icon/icon-register.ts +++ b/packages/vtable/examples/icon/icon-register.ts @@ -88,8 +88,10 @@ export function createTable() { `, tooltip: { // 气泡框,按钮的的解释信息 - title: '更多操作', - style: { bgColor: 'black', arrowMark: true, color: 'white' } + title: + '更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作更多操作操作 更多操作更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作 更多操作更多操作', + style: { bgColor: 'black', arrowMark: true, color: 'white', maxHeight: 100, maxWidth: 100 }, + disappearDelay: 100 } }); VTable.register.icon('phone', { @@ -318,7 +320,8 @@ export function createTable() { style: { arrowMark: true }, placement: VTable.TYPES.Placement.top, // 气泡框,按钮的的解释信息 - title: '对象定义形式 非注册' + title: '对象定义形式 非注册', + disappearDelay: 100 } } ], @@ -394,7 +397,8 @@ export function createTable() { return `这是第${rec.id}号`; }, title: 'ID说明', - description: '这是一个ID详细描述', + description: `这是一个ID详细描述\n这是一个ID详细描述 +这是一个ID详细描述`, sort: (v1, v2, order) => { if (order === 'desc') { return v1 === v2 ? 0 : v1 > v2 ? -1 : 1; @@ -421,12 +425,18 @@ export function createTable() { allowFrozenColCount: 8, tooltip: { renderMode: 'html', - isShowOverflowTextTooltip: false + isShowOverflowTextTooltip: true, + overflowTextTooltipDisappearDelay: 100 }, heightMode: 'autoHeight', title: { text: 'title', orient: 'top' + }, + theme: { + tooltipStyle: { + maxWidth: 200 + } } }; diff --git a/packages/vtable/examples/interactive/tooltip.ts b/packages/vtable/examples/interactive/tooltip.ts index dad3b4334..5c7518d7e 100644 --- a/packages/vtable/examples/interactive/tooltip.ts +++ b/packages/vtable/examples/interactive/tooltip.ts @@ -39,7 +39,11 @@ export function createTable() { return `已完成${rec.progress}%`; }, title: 'progress', - description: '这是一个标题的详细描述', + description: `这是一个标题的详细描述,这是一个标题的详细描述, +这是一个标题的详细描述,这是一个标题的详细描述,这是一个标题的详细描述,这是一个标题的详细描述, +这是一个标题的详细描述,这是一个标题的详细描述, 这是一个标题的详细描述,这是一个标题的详细描述, +这是一个标题的详细描述,这是一个标题的详细描述,这是一个标题的详细描述,这是一个标题的详细描述, +这是一个标题的详细描述,这是一个标题的详细描述`, width: 150, showSort: true //显示VTable内置排序图标 }, @@ -86,7 +90,8 @@ export function createTable() { allowFrozenColCount: 2, tooltip: { renderMode: 'html', - isShowOverflowTextTooltip: true + isShowOverflowTextTooltip: true, + overflowTextTooltipDisappearDelay: 1000 } }; diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index 20c1ba406..cdba02a22 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -520,6 +520,10 @@ export const menus = [ { path: 'pivot-analysis', name: 'pivot-analysis-str' + }, + { + path: 'pivot-analysis', + name: 'pivot-empty-tip' } ] }, diff --git a/packages/vtable/examples/pivot-analysis/pivot-empty-tip.ts b/packages/vtable/examples/pivot-analysis/pivot-empty-tip.ts new file mode 100644 index 000000000..947a0efcd --- /dev/null +++ b/packages/vtable/examples/pivot-analysis/pivot-empty-tip.ts @@ -0,0 +1,56 @@ +import * as VTable from '../../src'; +const PivotTable = VTable.PivotTable; +const CONTAINER_ID = 'vTable'; + +export function createTable() { + const option: VTable.PivotTableConstructorOptions = { + rows: ['province', 'city'], + columns: ['category', 'sub_category'], + indicators: ['sales', 'number'], + + indicatorTitle: '指标名称', + indicatorsAsCol: false, + corner: { titleOnDimension: 'column' }, + columnResizeType: 'all', + records: [ + { + sales: 891, + number: 7789, + province: '浙江省', + city: '杭州市', + category: '家具', + sub_category: '桌子' + } + ], + emptyTip: true, + widthMode: 'autoWidth' // 宽度模式:standard 标准模式; adaptive 自动填满容器 + }; + + const instance = new PivotTable(document.getElementById(CONTAINER_ID)!, option); + window.tableInstance = instance; + + setTimeout(() => { + instance.updateOption({ + rows: ['province', 'city'], + columns: ['category', 'sub_category'], + indicators: ['sales', 'number'], + + indicatorTitle: '指标名称', + indicatorsAsCol: false, + corner: { titleOnDimension: 'column' }, + columnResizeType: 'all', + records: [ + // { + // sales: 891, + // number: 7789, + // province: '浙江省', + // city: '杭州市', + // category: '家具', + // sub_category: '桌子' + // } + ], + emptyTip: true, + widthMode: 'autoWidth' // 宽度模式:standard 标准模式; adaptive 自动填满容器 + }); + }, 1000); +} diff --git a/packages/vtable/package.json b/packages/vtable/package.json index 7b8292213..30000491f 100644 --- a/packages/vtable/package.json +++ b/packages/vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable", - "version": "1.2.0", + "version": "1.3.2", "description": "canvas table width high performance", "keywords": [ "grid", diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index b03bd306a..1c213561d 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -88,6 +88,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.editorManager = new EditManeger(this); } this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; if (options.dataSource) { _setDataSource(this, options.dataSource); @@ -164,6 +165,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.internalProps.headerHelper.setTableColumnsEditor(); this._hasAutoImageColumn = undefined; this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; this.scenegraph.clearCells(); this.headerStyleCache = new Map(); this.bodyStyleCache = new Map(); @@ -190,6 +192,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.internalProps.columns = header; this.options.header = header; this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; //需要异步等待其他事情都完成后再绘制 this.renderAsync(); } @@ -408,6 +411,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.transpose = options.transpose ?? false; // 更新表头 this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // this.hasMedia = null; // 避免重复绑定 // 清空目前数据 @@ -459,6 +463,7 @@ export class ListTable extends BaseTable implements ListTableAPI { pagination.perPageCount && (this.pagination.perPageCount = pagination.perPageCount || this.pagination.perPageCount); this.internalProps.layoutMap.clearCellRangeMap(); + this.internalProps.useOneRowHeightFillAll = false; // 清空单元格内容 this.scenegraph.clearCells(); //数据源缓存数据更新 @@ -796,6 +801,7 @@ export class ListTable extends BaseTable implements ListTableAPI { this.clearCellStyleCache(); this.internalProps.layoutMap.clearCellRangeMap(); + this.internalProps.useOneRowHeightFillAll = false; this.scenegraph.updateHierarchyIcon(col, row); this.scenegraph.updateRow(diffPositions.removeCellPositions, diffPositions.addCellPositions); if (checkHasChart) { @@ -891,6 +897,7 @@ export class ListTable extends BaseTable implements ListTableAPI { // clear cell range cache this.internalProps.layoutMap.clearCellRangeMap(); + this.internalProps.useOneRowHeightFillAll = false; this.scenegraph.sortCell(); } } @@ -926,7 +933,7 @@ export class ListTable extends BaseTable implements ListTableAPI { const cellType = this.getCellType(col, row); if (isValid(field) && cellType === 'checkbox') { const dataIndex = this.dataSource.getIndexKey(this.getRecordShowIndexByCell(col, row)); - return this.stateManager.checkedState[dataIndex as number][field as string | number]; + return this.stateManager.checkedState[dataIndex as number]?.[field as string | number]; } return undefined; } diff --git a/packages/vtable/src/PivotChart.ts b/packages/vtable/src/PivotChart.ts index a36189f74..866484242 100644 --- a/packages/vtable/src/PivotChart.ts +++ b/packages/vtable/src/PivotChart.ts @@ -23,7 +23,7 @@ import type { PivotChartAPI } from './ts-types'; import { AggregationType } from './ts-types'; -import { HierarchyState } from './ts-types'; +import type { HierarchyState } from './ts-types'; import { getField } from './data/DataSource'; import { PivotHeaderLayoutMap } from './layout/pivot-header-layout'; import { PIVOT_CHART_EVENT_TYPE } from './ts-types/pivot-table/PIVOT_TABLE_EVENT_TYPE'; @@ -223,6 +223,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { this.internalProps.layoutMap = new PivotHeaderLayoutMap(this, this.dataset, columnDimensionTree, rowDimensionTree); this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // this.internalProps.frozenColCount = this.options.frozenColCount || this.rowHeaderLevelCount; // 生成单元格场景树 this.scenegraph.createSceneGraph(); @@ -438,6 +439,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { // 更新表头 this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // this.hasMedia = null; // 避免重复绑定 // 清空目前数据 @@ -750,48 +752,21 @@ export class PivotChart extends BaseTable implements PivotChartAPI { * @param sortRules */ updateSortRules(sortRules: SortRules) { - this.internalProps.dataConfig.sortRules = sortRules; + if (this.internalProps.dataConfig) { + this.internalProps.dataConfig.sortRules = sortRules; + } else { + this.internalProps.dataConfig = { sortRules }; + } this.dataset.updateSortRules(sortRules); this.internalProps.layoutMap.resetHeaderTree(); // 清空单元格内容 this.scenegraph.clearCells(); this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // 生成单元格场景树 this.scenegraph.createSceneGraph(); this.render(); } - updatePivotSortState( - pivotSortStateConfig: { - dimensions: IDimensionInfo[]; - order: SortOrder; - }[] - ) { - // // dimensions: IDimensionInfo[], order: SortOrder - // // 清空当前 pivot sort 状态 - // const cells = this.pivotSortState.map((cell) => ({ col: cell.col, row: cell.row })); - // this.pivotSortState.length = 0; - // cells.map((cell) => { - // this.invalidateCellRange(this.getCellRange(cell.col, cell.row)); - // }); - - // 更新 pivot sort 状态 - for (let i = 0; i < pivotSortStateConfig.length; i++) { - const { dimensions, order } = pivotSortStateConfig[i]; - const cellAddress = (this.internalProps.layoutMap as PivotHeaderLayoutMap).getPivotCellAdress(dimensions); - - cellAddress && - this.pivotSortState.push({ - col: cellAddress.col, - row: cellAddress.row, - order - }); - } - - // // 更新相关单元格样式 - // this.pivotSortState.map((cell) => { - // this.invalidateCellRange(this.getCellRange(cell.col, cell.row)); - // }); - } getPivotSortState(col: number, row: number): SortOrder { if (!this.pivotSortState) { @@ -856,31 +831,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { * @param row */ toggleHierarchyState(col: number, row: number) { - const hierarchyState = this.getHierarchyState(col, row); - if (hierarchyState === HierarchyState.expand) { - this.fireListeners(PIVOT_CHART_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, { - col: col, - row: row, - hierarchyState: HierarchyState.collapse - }); - } else if (hierarchyState === HierarchyState.collapse) { - this.fireListeners(PIVOT_CHART_EVENT_TYPE.TREE_HIERARCHY_STATE_CHANGE, { - col: col, - row: row, - hierarchyState: HierarchyState.expand, - originData: this.getCellOriginRecord(col, row) - }); - } - - const result = (this.internalProps.layoutMap as PivotHeaderLayoutMap).toggleHierarchyState(col, row); - //影响行数 - this.refreshRowColCount(); - // this.scenegraph.clearCells(); - // this.scenegraph.createSceneGraph(); - // this.invalidate(); - this.clearCellStyleCache(); - this.scenegraph.updateHierarchyIcon(col, row); - this.scenegraph.updateRow(result.removeCellPositions, result.addCellPositions); + //nothing } /** * 通过表头的维度值路径来计算单元格位置 getCellAddressByHeaderPaths接口更强大一些 不限表头 不限参数格式 @@ -1439,6 +1390,7 @@ export class PivotChart extends BaseTable implements PivotChartAPI { // 更新表头 this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // 清空单元格内容 this.scenegraph.clearCells(); diff --git a/packages/vtable/src/PivotTable.ts b/packages/vtable/src/PivotTable.ts index d3153166f..b368a734b 100644 --- a/packages/vtable/src/PivotTable.ts +++ b/packages/vtable/src/PivotTable.ts @@ -16,9 +16,11 @@ import type { IPagination, CellLocation, IIndicator, - ColumnDefine + ColumnDefine, + SortByIndicatorRule, + SortTypeRule } from './ts-types'; -import { HierarchyState } from './ts-types'; +import { HierarchyState, SortType } from './ts-types'; import { PivotHeaderLayoutMap } from './layout/pivot-header-layout'; import { FlatDataToObjects } from './dataset/flatDataToObject'; import { PIVOT_TABLE_EVENT_TYPE } from './ts-types/pivot-table/PIVOT_TABLE_EVENT_TYPE'; @@ -46,7 +48,10 @@ export class PivotTable extends BaseTable implements PivotTableAPI { layoutNodeId: { seqId: number } = { seqId: 0 }; declare internalProps: PivotTableProtected; declare options: PivotTableConstructorOptions; - pivotSortState: PivotSortState[]; + pivotSortState: { + dimensions: IDimensionInfo[]; + order: SortOrder; + }[]; editorManager: EditManeger; dataset?: Dataset; //数据处理对象 开启数据透视分析的表 flatDataToObjects?: FlatDataToObjects; //数据处理对象 聚合后的flat数据 转成便于查询的行列二维数组 @@ -182,14 +187,18 @@ export class PivotTable extends BaseTable implements PivotTableAPI { rowDimensionTree ); } - this.pivotSortState = []; - if (options.pivotSortState) { - this.updatePivotSortState(options.pivotSortState); + this._changePivotSortStateBySortRules(); + if ((options.pivotSortState?.length ?? 0) > 0) { + this.pivotSortState = []; + this.pivotSortState = options.pivotSortState; + // this.updatePivotSortState(options.pivotSortState); } if (Env.mode !== 'node') { this.editorManager = new EditManeger(this); } + this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; this.stateManager.initCheckedState(records); // this.internalProps.frozenColCount = this.options.frozenColCount || this.rowHeaderLevelCount; @@ -374,12 +383,17 @@ export class PivotTable extends BaseTable implements PivotTableAPI { } internalProps.layoutMap = new PivotHeaderLayoutMap(this, this.dataset, columnDimensionTree, rowDimensionTree); } - this.pivotSortState = []; - if (options.pivotSortState) { - this.updatePivotSortState(options.pivotSortState); + this._changePivotSortStateBySortRules(); + + if ((options.pivotSortState?.length ?? 0) > 0) { + this.pivotSortState = []; + this.pivotSortState = options.pivotSortState; + // this.updatePivotSortState(options.pivotSortState); } + // 更新表头 this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // this.hasMedia = null; // 避免重复绑定 // 清空目前数据 @@ -960,16 +974,67 @@ export class PivotTable extends BaseTable implements PivotTableAPI { * @param sortRules */ updateSortRules(sortRules: SortRules) { - this.internalProps.dataConfig.sortRules = sortRules; + if (this.internalProps.dataConfig) { + this.internalProps.dataConfig.sortRules = sortRules; + } else { + this.internalProps.dataConfig = { sortRules }; + } this.dataset.updateSortRules(sortRules); + this._changePivotSortStateBySortRules(); this.internalProps.layoutMap.resetHeaderTree(); // 清空单元格内容 this.scenegraph.clearCells(); this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // 生成单元格场景树 this.scenegraph.createSceneGraph(); this.render(); } + _changePivotSortStateBySortRules() { + this.pivotSortState = []; + const sortRules = this.internalProps.dataConfig?.sortRules ?? []; + for (let i = 0; i < sortRules.length; i++) { + const sortRule = sortRules[i]; + if ((sortRule as SortByIndicatorRule).sortType) { + const dimensions: IDimensionInfo[] = []; + if ((sortRule as SortByIndicatorRule).sortByIndicator) { + if ( + (sortRule as SortByIndicatorRule).sortField === + (this.dataset.indicatorsAsCol + ? this.dataset.rows[this.dataset.rows.length - 1] + : this.dataset.columns[this.dataset.columns.length - 1]) + ) { + for (let j = 0; j < (sortRule as SortByIndicatorRule).query.length; j++) { + dimensions.push({ + dimensionKey: this.dataset.indicatorsAsCol ? this.dataset.columns[j] : this.dataset.rows[j], + value: (sortRule as SortByIndicatorRule).query[j] + }); + } + dimensions.push({ + indicatorKey: (sortRule as SortByIndicatorRule).sortByIndicator, + value: + this.internalProps.layoutMap.getIndicatorInfo((sortRule as SortByIndicatorRule).sortByIndicator) + ?.title ?? (sortRule as SortByIndicatorRule).sortByIndicator + }); + } + } else { + dimensions.push({ + dimensionKey: (sortRule as SortTypeRule).sortField, + isPivotCorner: true, + value: (sortRule as SortTypeRule).sortField + }); + } + this.pivotSortState.push({ + dimensions, + order: (sortRule as SortByIndicatorRule).sortType + }); + // this.changePivotSortState({ + // dimensions, + // order: (sortRule as SortByIndicatorRule).sortType + // }); + } + } + } /** * 更新排序状态 * @param pivotSortStateConfig.dimensions 排序状态维度对应关系;pivotSortStateConfig.order 排序状态 @@ -980,31 +1045,120 @@ export class PivotTable extends BaseTable implements PivotTableAPI { order: SortOrder; }[] ) { - // // dimensions: IDimensionInfo[], order: SortOrder - // // 清空当前 pivot sort 状态 - // const cells = this.pivotSortState.map((cell) => ({ col: cell.col, row: cell.row })); - // this.pivotSortState.length = 0; - // cells.map((cell) => { - // this.invalidateCellRange(this.getCellRange(cell.col, cell.row)); - // }); + this.pivotSortState = pivotSortStateConfig; + } + // changePivotSortState(pivotSortState: { dimensions: IDimensionInfo[]; order: SortOrder }) { + // let isExist = false; + // for (let i = 0; i < this.pivotSortState.length; i++) { + // const pivotSortStateItem = this.pivotSortState[i]; + // const dimensions = pivotSortStateItem.dimensions; + // const isEqual = dimensions.every( + // (item, index) => + // (item.dimensionKey === pivotSortState.dimensions[index].dimensionKey || + // item.indicatorKey === pivotSortState.dimensions[index].indicatorKey) && + // item.value === pivotSortState.dimensions[index].value && + // ((isValid(item.isPivotCorner ?? pivotSortState.dimensions[index].isPivotCorner) && + // item.isPivotCorner === pivotSortState.dimensions[index].isPivotCorner) || + // (!isValid(item.isPivotCorner) && !isValid(pivotSortState.dimensions[index].isPivotCorner))) + // ); + // if (isEqual) { + // isExist = true; + // pivotSortStateItem.order = pivotSortState.order; + // break; + // } + // } + // if (!isExist) { + // this.pivotSortState.push(pivotSortState); + // } + // } + /** 如果单元格所在维度或者指标配置了sort自动 可以通过该接口进行排序 */ + sort(col: number, row: number, order: SortOrder) { + let dimensions: IDimensionInfo[]; + if ((this as PivotTable).isCornerHeader(col, row)) { + const dimensionInfo = (this as PivotTable).getHeaderDefine(col, row) as any; + dimensions = []; + const dimension: IDimensionInfo = { + isPivotCorner: true, + dimensionKey: dimensionInfo.value, + value: dimensionInfo.value + }; + dimensions.push(dimension); + } else if ((this as PivotTable).isColumnHeader(col, row)) { + dimensions = (this as PivotTable).getCellHeaderPaths(col, row).colHeaderPaths as IDimensionInfo[]; + } else { + dimensions = (this as PivotTable).getCellHeaderPaths(col, row).rowHeaderPaths as IDimensionInfo[]; + } - // 更新 pivot sort 状态 - for (let i = 0; i < pivotSortStateConfig.length; i++) { - const { dimensions, order } = pivotSortStateConfig[i]; - const cellAddress = (this.internalProps.layoutMap as PivotHeaderLayoutMap).getPivotCellAdress(dimensions); + const sortIndicator = dimensions[dimensions.length - 1].indicatorKey; - cellAddress && - this.pivotSortState.push({ - col: cellAddress.col, - row: cellAddress.row, - order - }); - } + const headerDefine = this.getHeaderDefine(col, row) as any; + if (headerDefine.sort) { + if ((this as PivotTable).dataset.sortRules) { + for (let i = (this as PivotTable).dataset.sortRules.length - 1; i >= 0; i--) { + const sortRule = (this as PivotTable).dataset.sortRules[i]; + if (headerDefine.dimensionKey && sortRule.sortField === headerDefine.dimensionKey) { + (this as PivotTable).dataset.sortRules.splice(i, 1); + } else if ( + sortIndicator && + // headerDefine.indicatorKey === sortIndicator && + // sortIndicator === (sortRule as SortByIndicatorRule).sortByIndicator && + sortRule.sortField === + (this.dataset.indicatorsAsCol + ? this.dataset.rows[this.dataset.rows.length - 1] + : this.dataset.columns[this.dataset.columns.length - 1]) + ) { + (this as PivotTable).dataset.sortRules.splice(i, 1); + } + } + if (sortIndicator) { + (this as PivotTable).dataset.sortRules.push({ + sortField: this.dataset.indicatorsAsCol + ? this.dataset.rows[this.dataset.rows.length - 1] + : this.dataset.columns[this.dataset.columns.length - 1], + sortType: SortType[order], + sortByIndicator: sortIndicator, + query: dimensions.reduce((arr, dimension) => { + if (dimension.dimensionKey) { + arr.push(dimension.value); + } + return arr; + }, []) + }); + } else { + (this as PivotTable).dataset.sortRules.push({ + sortField: headerDefine.dimensionKey, + sortType: SortType[order] + }); + } + } else { + if (sortIndicator) { + (this as PivotTable).dataset.sortRules = [ + { + sortField: this.dataset.indicatorsAsCol + ? this.dataset.rows[this.dataset.rows.length - 1] + : this.dataset.columns[this.dataset.columns.length - 1], + sortType: SortType[order], + sortByIndicator: sortIndicator, + query: dimensions.reduce((arr, dimension) => { + if (dimension.dimensionKey) { + arr.push(dimension.value); + } + return arr; + }, []) + } + ]; + } else { + (this as PivotTable).dataset.sortRules = [ + { + sortField: headerDefine.dimensionKey, + sortType: SortType[order] + } + ]; + } + } - // // 更新相关单元格样式 - // this.pivotSortState.map((cell) => { - // this.invalidateCellRange(this.getCellRange(cell.col, cell.row)); - // }); + (this as PivotTable).updateSortRules((this as PivotTable).dataset.sortRules); + } } getPivotSortState(col: number, row: number): SortOrder { @@ -1013,9 +1167,13 @@ export class PivotTable extends BaseTable implements PivotTableAPI { } const cellRange = this.getCellRange(col, row); for (let i = 0; i < this.pivotSortState.length; i++) { - const { col: sortCol, row: sortRow, order } = this.pivotSortState[i]; + const pivotState = this.pivotSortState[i]; + const dimensions = pivotState.dimensions; + const cell = this.getCellAddressByHeaderPaths(dimensions); + // const { col: sortCol, row: sortRow, order } = this.pivotSortState[i]; + const order = pivotState.order; - if (cellInRange(cellRange, sortCol, sortRow)) { + if (cell && cellInRange(cellRange, cell.col, cell.row)) { return order; } } @@ -1317,12 +1475,14 @@ export class PivotTable extends BaseTable implements PivotTableAPI { internalProps.layoutMap = new PivotHeaderLayoutMap(this, this.dataset, columnDimensionTree, rowDimensionTree); this.pivotSortState = []; if (options.pivotSortState) { - this.updatePivotSortState(options.pivotSortState); + this.pivotSortState = options.pivotSortState; + // this.updatePivotSortState(options.pivotSortState); } } // 更新表头 this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; // 清空单元格内容 this.scenegraph.clearCells(); diff --git a/packages/vtable/src/body-helper/style/Style.ts b/packages/vtable/src/body-helper/style/Style.ts index 10ca22e75..6d48253f9 100644 --- a/packages/vtable/src/body-helper/style/Style.ts +++ b/packages/vtable/src/body-helper/style/Style.ts @@ -29,6 +29,7 @@ const STYLE_EVENT_TYPE = { export class Style extends EventTarget implements ColumnStyle { private _color?: ColorPropertyDefine; + private _strokeColor?: ColorPropertyDefine; private _fontSize?: FontSizePropertyDefine; private _fontFamily?: FontFamilyPropertyDefine; private _fontWeight?: FontWeightPropertyDefine; @@ -67,6 +68,7 @@ export class Style extends EventTarget implements ColumnStyle { this._textAlign = style?.textAlign ?? bodyStyle?.textAlign; //|| "left"; this._textBaseline = style?.textBaseline ?? bodyStyle?.textBaseline; //|| "middle"; this._color = style?.color ?? bodyStyle?.color; + this._strokeColor = style?.strokeColor ?? bodyStyle?.strokeColor; // icon为文字前后可添加的图表 this._fontSize = style.fontSize ?? bodyStyle?.fontSize; this._fontFamily = style.fontFamily ?? bodyStyle?.fontFamily; @@ -101,6 +103,13 @@ export class Style extends EventTarget implements ColumnStyle { this._color = color; // this.doChangeStyle(); } + get strokeColor(): ColorPropertyDefine | undefined { + return this._strokeColor; + } + set strokeColor(strokeColor: ColorPropertyDefine | undefined) { + this._strokeColor = strokeColor; + // this.doChangeStyle(); + } get fontSize(): FontSizePropertyDefine | undefined { return this._fontSize; } diff --git a/packages/vtable/src/components/empty-tip/empty-tip.ts b/packages/vtable/src/components/empty-tip/empty-tip.ts index f898201b2..971b61d8a 100644 --- a/packages/vtable/src/components/empty-tip/empty-tip.ts +++ b/packages/vtable/src/components/empty-tip/empty-tip.ts @@ -62,11 +62,13 @@ export class EmptyTip { ? this.table.getFrozenRowsHeight() : 0; const width = - (this.table.columnHeaderLevelCount > 0 ? this.table.getDrawRange().width : this.table.tableNoFrameWidth) - - leftHeaderWidth; + (this.table.columnHeaderLevelCount > 0 && this.table.isListTable() + ? this.table.getDrawRange().width + : this.table.tableNoFrameWidth) - leftHeaderWidth; const height = - (this.table.rowHeaderLevelCount > 0 ? this.table.getDrawRange().height : this.table.tableNoFrameHeight) - - topHeaderHeight; + (this.table.rowHeaderLevelCount > 0 && this.table.isListTable() + ? this.table.getDrawRange().height + : this.table.tableNoFrameHeight) - topHeaderHeight; this._emptyTipComponent.setAttributes({ spaceBetweenTextAndIcon: this._emptyTipOption.spaceBetweenTextAndIcon, x: this.table.tableX + leftHeaderWidth, @@ -123,11 +125,13 @@ export class EmptyTip { ? this.table.getFrozenRowsHeight() : 0; const width = - (this.table.columnHeaderLevelCount > 0 ? this.table.getDrawRange().width : this.table.tableNoFrameWidth) - - leftHeaderWidth; + (this.table.columnHeaderLevelCount > 0 && this.table.isListTable() + ? this.table.getDrawRange().width + : this.table.tableNoFrameWidth) - leftHeaderWidth; const height = - (this.table.rowHeaderLevelCount > 0 ? this.table.getDrawRange().height : this.table.tableNoFrameHeight) - - topHeaderHeight; + (this.table.rowHeaderLevelCount > 0 && this.table.isListTable() + ? this.table.getDrawRange().height + : this.table.tableNoFrameHeight) - topHeaderHeight; return { spaceBetweenTextAndIcon: this._emptyTipOption.spaceBetweenTextAndIcon, diff --git a/packages/vtable/src/components/title/title.ts b/packages/vtable/src/components/title/title.ts index 83581623f..a0fcd07c7 100644 --- a/packages/vtable/src/components/title/title.ts +++ b/packages/vtable/src/components/title/title.ts @@ -178,7 +178,9 @@ export class Title { subtextStyle: { width: realWidth, ...this._titleOption.subtextStyle - } + }, + dx: this._titleOption.dx ?? 0, + dy: this._titleOption.dy ?? 0 } as TitleAttrs; } } diff --git a/packages/vtable/src/components/tooltip/TooltipHandler.ts b/packages/vtable/src/components/tooltip/TooltipHandler.ts index 475b47a3a..89afbf690 100644 --- a/packages/vtable/src/components/tooltip/TooltipHandler.ts +++ b/packages/vtable/src/components/tooltip/TooltipHandler.ts @@ -195,7 +195,8 @@ export class TooltipHandler { placement: Placement.bottom, rect }, - style: { arrowMark: false } + disappearDelay: table.internalProps.tooltip.overflowTextTooltipDisappearDelay ?? 0, + style: table.theme.tooltipStyle }; } else if (table.internalProps.tooltip?.isShowOverflowTextTooltip) { const overflowText = table.getCellOverflowText(col, row); @@ -210,6 +211,7 @@ export class TooltipHandler { placement: Placement.bottom, rect }, + disappearDelay: table.internalProps.tooltip.overflowTextTooltipDisappearDelay ?? 0, style: table.theme.tooltipStyle }; } diff --git a/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts b/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts index c84185ad6..bcaeff0c7 100644 --- a/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts +++ b/packages/vtable/src/components/tooltip/logic/BubbleTooltipElement.ts @@ -18,15 +18,29 @@ export class BubbleTooltipElement { private _rootElement?: HTMLElement; private _messageElement?: HTMLElement; private _triangleElement?: HTMLElement; + private _disappearDelay?: number; // 提示框延迟多久消失 + private _disappearDelayId?: any; constructor() { this._handler = new EventHandler(); const rootElement = (this._rootElement = createElement('div', [TOOLTIP_CLASS, HIDDEN_CLASS])); - const messageElement = createElement('span', [CONTENT_CLASS]); + const messageElement = createElement('div', [CONTENT_CLASS]); const triangle = createElement('span', [TRIANGLE_CLASS]); rootElement.appendChild(triangle); rootElement.appendChild(messageElement); this._messageElement = rootElement.querySelector(`.${CONTENT_CLASS}`) || undefined; this._triangleElement = rootElement.querySelector(`.${TRIANGLE_CLASS}`) || undefined; + + rootElement.addEventListener('mousemove', () => { + this._disappearDelayId && clearTimeout(this._disappearDelayId); + }); + rootElement.addEventListener('mouseleave', () => { + this._disappearDelay = undefined; + this.unbindFromCell(); + }); + + messageElement.addEventListener('wheel', e => { + e.stopPropagation(); + }); } bindToCell( table: BaseTableAPI, @@ -35,6 +49,8 @@ export class BubbleTooltipElement { tooltipInstanceInfo: TooltipOptions, confine: boolean ): boolean { + this._disappearDelay = tooltipInstanceInfo?.disappearDelay; + this._disappearDelayId && clearTimeout(this._disappearDelayId); const rootElement = this._rootElement; const messageElement = this._messageElement; const triangle = this._triangleElement; @@ -57,6 +73,10 @@ export class BubbleTooltipElement { tooltipInstanceInfo?.style?.color && (messageElement.style.color = tooltipInstanceInfo?.style?.color); tooltipInstanceInfo?.style?.padding && (messageElement.style.padding = `${tooltipInstanceInfo?.style?.padding.join('px ')}px`); + tooltipInstanceInfo?.style?.maxHeight && + (messageElement.style.maxHeight = `${tooltipInstanceInfo?.style?.maxHeight}px`); + tooltipInstanceInfo?.style?.maxWidth && + (messageElement.style.maxWidth = `${tooltipInstanceInfo?.style?.maxWidth}px`); messageElement && (messageElement.textContent = tooltipInstanceInfo?.content); const binded = this._bindToCell( table, @@ -100,10 +120,20 @@ export class BubbleTooltipElement { } } unbindFromCell(): void { - const rootElement = this._rootElement; - if (rootElement?.parentElement) { - rootElement.classList.remove(SHOWN_CLASS); - rootElement.classList.add(HIDDEN_CLASS); + if (this._disappearDelay) { + this._disappearDelayId = setTimeout(() => { + const rootElement = this._rootElement; + if (rootElement?.parentElement) { + rootElement.classList.remove(SHOWN_CLASS); + rootElement.classList.add(HIDDEN_CLASS); + } + }, this._disappearDelay ?? 0); + } else { + const rootElement = this._rootElement; + if (rootElement?.parentElement) { + rootElement.classList.remove(SHOWN_CLASS); + rootElement.classList.add(HIDDEN_CLASS); + } } } _canBindToCell(table: BaseTableAPI, col: number, row: number): boolean { diff --git a/packages/vtable/src/components/tooltip/logic/BubbleTooltipElementStyle.ts b/packages/vtable/src/components/tooltip/logic/BubbleTooltipElementStyle.ts index 6c29a4166..dc4f3d1a0 100644 --- a/packages/vtable/src/components/tooltip/logic/BubbleTooltipElementStyle.ts +++ b/packages/vtable/src/components/tooltip/logic/BubbleTooltipElementStyle.ts @@ -18,8 +18,8 @@ export function importStyle() { .vtable__bubble-tooltip-element { position: absolute; - pointer-events: none; - user-select: none; + // pointer-events: none; + //user-select: none; max-width: 300px; z-index: 99999; @@ -33,6 +33,8 @@ export function importStyle() { } .vtable__bubble-tooltip-element--hidden { opacity: 0; + pointer-events: none; + user-select: none; /* transform: translate(-50%, -50%); */ transition: opacity 75ms linear; } @@ -51,12 +53,30 @@ export function importStyle() { white-space: pre-wrap; margin: 0; box-sizing: border-box; - overflow: hidden; + overflow: auto; word-wrap: break-word; position: relative; background-color: #FFF; z-index: 2; - border-radius: 4px + border-radius: 4px; +} +/* WebKit Microsoft Edge(新版): */ +.vtable__bubble-tooltip-element__content::-webkit-scrollbar { + width: 0; + height: 0; + background-color: transparent; +} +/* Opera Firefox */ +.vtable__bubble-tooltip-element__content > scrollbar-track { + width: 0; + height: 0; + background-color: transparent; +} +/* Internet Explorer 11 和 Microsoft Edge(旧版) */ +.vtable__bubble-tooltip-element__content > scrollbar { + width: 0; + height: 0; + background-color: transparent; } .vtable__bubble-tooltip-element__triangle { /* font-size: .75rem; */ diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 12070fae5..8a6faa10f 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -1625,6 +1625,10 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { const scrollLeft = this.scrollLeft; if (this.isLeftFrozenColumn(startCol) && this.isRightFrozenColumn(endCol)) { width = this.tableNoFrameWidth - (this.getColsWidth(startCol + 1, this.colCount - 1) ?? 0) - absoluteLeft; + // width = + // this.tableNoFrameWidth - + // (this.getColsWidth(0, startCol - 1) ?? 0) - + // (this.getColsWidth(endCol + 1, this.colCount - 1) ?? 0); } else if (this.isLeftFrozenColumn(startCol) && !this.isLeftFrozenColumn(endCol)) { width = Math.max(width - scrollLeft, this.getColsWidth(startCol, this.frozenColCount - 1)); } else if (!this.isRightFrozenColumn(startCol) && this.isRightFrozenColumn(endCol)) { @@ -1642,6 +1646,10 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { const scrollTop = this.scrollTop; if (this.isTopFrozenRow(startRow) && this.isBottomFrozenRow(endRow)) { height = this.tableNoFrameHeight - (this.getRowsHeight(startRow + 1, this.rowCount - 1) ?? 0) - absoluteTop; + // height = + // this.tableNoFrameHeight - + // (this.getRowsHeight(0, startRow - 1) ?? 0) - + // (this.getRowsHeight(endRow + 1, this.rowCount - 1) ?? 0); } else if (this.isTopFrozenRow(startRow) && !this.isTopFrozenRow(endRow)) { height = Math.max(height - scrollTop, this.getRowsHeight(startRow, this.frozenRowCount - 1)); } else if (!this.isBottomFrozenRow(startRow) && this.isBottomFrozenRow(endRow)) { @@ -2333,6 +2341,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { renderWithRecreateCells() { const oldHoverState = { col: this.stateManager.hover.cellPos.col, row: this.stateManager.hover.cellPos.row }; this.refreshHeader(); + this.internalProps.useOneRowHeightFillAll = false; this.scenegraph.clearCells(); this.clearCellStyleCache(); this.scenegraph.createSceneGraph(); @@ -2719,8 +2728,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * @param col * @param row */ - selectCell(col: number, row: number) { - this.stateManager.updateSelectPos(col, row); + selectCell(col: number, row: number, isShift?: boolean, isCtrl?: boolean) { + this.stateManager.updateSelectPos(col, row, isShift, isCtrl); this.stateManager.endSelectCells(); } /** @@ -3287,8 +3296,11 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { if (this.isPivotTable() && !this.isBottomFrozenRow(row) && !this.isRightFrozenColumn(col)) { // use dimensionKey&indicatorKey to cache style object in pivot table const define = this.getHeaderDefine(col, row) as any; + const isCorner = this.isCornerHeader(col, row); cacheKey = define?.dimensionKey - ? `dim-${define.dimensionKey}` + ? isCorner + ? `dim-cor-${define.dimensionKey}` + : `dim-${define.dimensionKey}` : define?.indicatorKey ? `ind-${define.indicatorKey}` : `${col}-${row}`; @@ -3711,6 +3723,9 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { ? getProp('bgColor', actStyle, col, row, this) : (theme.group.fill as string), color: isBoolean(theme.text.fill) ? getProp('color', actStyle, col, row, this) : (theme.text.fill as string), + strokeColor: isBoolean(theme.text.stroke) + ? getProp('strokeColor', actStyle, col, row, this) + : (theme.text.stroke as string), fontFamily: theme.text.fontFamily, fontSize: theme.text.fontSize, fontWeight: theme.text.fontWeight, @@ -4084,28 +4099,58 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { if (col < this.frozenColCount && row < this.frozenRowCount) { return true; } - - const colHeaderRangeRect = this.getCellRangeRelativeRect({ - start: { - col: 0, - row: 0 - }, - end: { - col: this.colCount - 1, - row: this.columnHeaderLevelCount - } - }); - const rowHeaderRangeRect = this.getCellRangeRelativeRect({ - start: { - col: 0, - row: 0 - }, - end: { - col: this.rowHeaderLevelCount, - row: this.rowCount - 1 - } - }); - + let colHeaderRangeRect; + if (this.frozenRowCount >= 1) { + colHeaderRangeRect = this.getCellRangeRelativeRect({ + start: { + col: 0, + row: 0 + }, + end: { + col: this.colCount - 1, + row: this.frozenRowCount - 1 + } + }); + } + let rowHeaderRangeRect; + if (this.frozenColCount >= 1) { + rowHeaderRangeRect = this.getCellRangeRelativeRect({ + start: { + col: 0, + row: 0 + }, + end: { + col: this.frozenColCount - 1, + row: this.rowCount - 1 + } + }); + } + let bottomFrozenRangeRect; + if (this.bottomFrozenRowCount >= 1) { + bottomFrozenRangeRect = this.getCellRangeRelativeRect({ + start: { + col: 0, + row: this.rowCount - this.bottomFrozenRowCount + }, + end: { + col: this.colCount - 1, + row: this.rowCount - 1 + } + }); + } + let rightFrozenRangeRect; + if (this.rightFrozenColCount >= 1) { + rightFrozenRangeRect = this.getCellRangeRelativeRect({ + start: { + col: this.colCount - this.rightFrozenColCount, + row: 0 + }, + end: { + col: this.colCount - 1, + row: this.rowCount - 1 + } + }); + } if ( rect.top >= drawRange.top && rect.bottom <= drawRange.bottom && @@ -4113,12 +4158,14 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { rect.right <= drawRange.right ) { // return true; - if (this.isHeader(col, row)) { + if (this.isFrozenCell(col, row)) { return true; } else if ( // body cell drawRange do not intersect colHeaderRangeRect&rowHeaderRangeRect - drawRange.top >= colHeaderRangeRect.bottom && - drawRange.left >= rowHeaderRangeRect.right + rect.top >= (colHeaderRangeRect?.bottom ?? rect.top) && + rect.left >= (rowHeaderRangeRect?.right ?? rect.left) && + rect.bottom <= (bottomFrozenRangeRect?.top ?? rect.bottom) && + rect.right <= (rightFrozenRangeRect?.left ?? rect.right) ) { return true; } diff --git a/packages/vtable/src/core/tableHelper.ts b/packages/vtable/src/core/tableHelper.ts index 833de32e0..ecbaae88a 100644 --- a/packages/vtable/src/core/tableHelper.ts +++ b/packages/vtable/src/core/tableHelper.ts @@ -248,6 +248,7 @@ export function getStyleTheme( const textAlign = getProp('textAlign', headerStyle, col, row, table); const textBaseline = getProp('textBaseline', headerStyle, col, row, table); const color = getProp('color', headerStyle, col, row, table); + const strokeColor = getProp('strokeColor', headerStyle, col, row, table); const lineHeight = getProp('lineHeight', headerStyle, col, row, table); const underline = getProp('underline', headerStyle, col, row, table); // boolean @@ -292,6 +293,7 @@ export function getStyleTheme( fontStyle, fontVariant, fill: color, + stroke: strokeColor ?? false, textAlign, textBaseline, lineHeight: lineHeight ?? fontSize, diff --git a/packages/vtable/src/dataset/dataset.ts b/packages/vtable/src/dataset/dataset.ts index 3fe1d486b..7407f69b9 100644 --- a/packages/vtable/src/dataset/dataset.ts +++ b/packages/vtable/src/dataset/dataset.ts @@ -115,7 +115,9 @@ export class Dataset { collectedValues: Record> = {}; cacheCollectedValues: Record> = {}; rows: string[]; + rowsHasValue: boolean[]; //rows中的key是否有在records中体现 columns: string[]; + columnsHasValue: boolean[]; //columns中的key是否有在records中体现 indicatorKeys: string[]; customRowTree?: IHeaderTreeDefine[]; customColTree?: IHeaderTreeDefine[]; @@ -198,6 +200,8 @@ export class Dataset { this.rowFlatKeys = {}; this.colKeys = []; this.rowKeys = []; + this.rowsHasValue = []; + this.columnsHasValue = []; if (records) { //处理数据 this.records = records; @@ -256,7 +260,9 @@ export class Dataset { if (this.rowHierarchyType === 'tree') { this.rowHeaderTree = this.ArrToTree1( this.rowKeys, - this.rows, + this.rows.filter((key, index) => { + return this.rowsHasValue[index]; + }), this.indicatorsAsCol ? undefined : this.indicators, this.totals?.row?.showGrandTotals || (!this.indicatorsAsCol && this.columns.length === 0) || @@ -266,7 +272,9 @@ export class Dataset { } else { this.rowHeaderTree = this.ArrToTree( this.rowKeys, - this.rows, + this.rows.filter((key, index) => { + return this.rowsHasValue[index]; + }), this.indicatorsAsCol ? undefined : this.indicators, this.rowsIsTotal, this.totals?.row?.showGrandTotals || (this.indicatorsAsCol && this.rows.length === 0), @@ -285,7 +293,9 @@ export class Dataset { } else { this.colHeaderTree = this.ArrToTree( this.colKeys, - this.columns, + this.columns.filter((key, index) => { + return this.columnsHasValue[index]; + }), this.indicatorsAsCol ? this.indicators : undefined, this.colsIsTotal, this.totals?.column?.showGrandTotals || (!this.indicatorsAsCol && this.columns.length === 0), // || this.rows.length === 0,//todo 这里原有逻辑暂时注释掉 @@ -558,6 +568,7 @@ export class Dataset { for (let l = 0, len1 = this.rows.length; l < len1; l++) { const rowAttr = this.rows[l]; if (rowAttr in record) { + this.rowsHasValue[l] = true; rowKey.push(record[rowAttr]); } else if (rowAttr !== IndicatorDimensionKeyPlaceholder) { //如果数据中缺失某个维度的值 可以认为是用户传入的汇总数据 @@ -589,6 +600,7 @@ export class Dataset { for (let n = 0, len2 = this.columns.length; n < len2; n++) { const colAttr = this.columns[n]; if (colAttr in record) { + this.columnsHasValue[n] = true; colKey.push(record[colAttr]); } else if (colAttr !== IndicatorDimensionKeyPlaceholder) { //如果数据中缺失某个维度的值 可以认为是用户传入的汇总数据 @@ -767,7 +779,9 @@ export class Dataset { if (this.rowHierarchyType === 'tree') { this.rowHeaderTree = this.ArrToTree1( this.rowKeys, - this.rows, + this.rows.filter((key, index) => { + return this.rowsHasValue[index]; + }), this.indicatorsAsCol ? undefined : this.indicators, this.totals?.row?.showGrandTotals || (!this.indicatorsAsCol && this.columns.length === 0) || @@ -777,7 +791,9 @@ export class Dataset { } else { this.rowHeaderTree = this.ArrToTree( this.rowKeys, - this.rows, + this.rows.filter((key, index) => { + return this.rowsHasValue[index]; + }), this.indicatorsAsCol ? undefined : this.indicators, this.rowsIsTotal, this.totals?.row?.showGrandTotals || (this.indicatorsAsCol && this.rows.length === 0), @@ -792,7 +808,9 @@ export class Dataset { if (!this.customColTree) { this.colHeaderTree = this.ArrToTree( this.colKeys, - this.columns, + this.columns.filter((key, index) => { + return this.columnsHasValue[index]; + }), this.indicatorsAsCol ? this.indicators : undefined, this.colsIsTotal, this.totals?.column?.showGrandTotals || (!this.indicatorsAsCol && this.columns.length === 0), // || this.rows.length === 0,//todo 这里原有逻辑暂时注释掉 diff --git a/packages/vtable/src/event/event.ts b/packages/vtable/src/event/event.ts index fd8ed281a..294849caa 100644 --- a/packages/vtable/src/event/event.ts +++ b/packages/vtable/src/event/event.ts @@ -55,6 +55,8 @@ export class EventManager { //报错已绑定过的事件 后续清除绑定 globalEventListeners: { name: string; env: 'document' | 'body' | 'window'; callback: (e?: any) => void }[] = []; inertiaScroll: InertiaScroll; + + bindSparklineHoverEvent: boolean; constructor(table: BaseTableAPI) { this.table = table; this.handleTextStickBindId = []; @@ -102,6 +104,9 @@ export class EventManager { }); this.handleTextStickBindId = []; } + + // chart hover + bindSparklineHoverEvent(this.table); }, 0); } bindSelfEvent() { diff --git a/packages/vtable/src/event/listener/container-dom.ts b/packages/vtable/src/event/listener/container-dom.ts index a77069653..7f095cc0c 100644 --- a/packages/vtable/src/event/listener/container-dom.ts +++ b/packages/vtable/src/event/listener/container-dom.ts @@ -57,22 +57,54 @@ export function bindContainerDomListener(eventManager: EventManager) { // 处理向上箭头键 if (e.key === 'ArrowUp') { - targetCol = stateManager.select.cellPos.col; - targetRow = Math.min(table.rowCount - 1, Math.max(0, stateManager.select.cellPos.row - 1)); + if (e.ctrlKey || e.metaKey) { + targetCol = stateManager.select.cellPos.col; + targetRow = 0; + } else if (e.shiftKey) { + targetCol = stateManager.select.cellPos.col; + targetRow = Math.min(table.rowCount - 1, Math.max(0, stateManager.select.cellPos.row - 1)); + } else { + targetCol = stateManager.select.cellPos.col; + targetRow = Math.min(table.rowCount - 1, Math.max(0, stateManager.select.cellPos.row - 1)); + } } else if (e.key === 'ArrowDown') { // 处理向下箭头键 - targetCol = stateManager.select.cellPos.col; - targetRow = Math.min(table.rowCount - 1, Math.max(0, stateManager.select.cellPos.row + 1)); + if (e.ctrlKey || e.metaKey) { + targetCol = stateManager.select.cellPos.col; + targetRow = table.rowCount - 1; + } else if (e.shiftKey) { + targetCol = stateManager.select.cellPos.col; + targetRow = Math.min(table.rowCount - 1, Math.max(0, stateManager.select.cellPos.row + 1)); + } else { + targetCol = stateManager.select.cellPos.col; + targetRow = Math.min(table.rowCount - 1, Math.max(0, stateManager.select.cellPos.row + 1)); + } } else if (e.key === 'ArrowLeft') { // 处理向左箭头键 - targetRow = stateManager.select.cellPos.row; - targetCol = Math.min(table.colCount - 1, Math.max(0, stateManager.select.cellPos.col - 1)); + if (e.ctrlKey || e.metaKey) { + targetCol = 0; + targetRow = stateManager.select.cellPos.row; + } else if (e.shiftKey) { + targetRow = stateManager.select.cellPos.row; + targetCol = Math.min(table.colCount - 1, Math.max(0, stateManager.select.cellPos.col - 1)); + } else { + targetRow = stateManager.select.cellPos.row; + targetCol = Math.min(table.colCount - 1, Math.max(0, stateManager.select.cellPos.col - 1)); + } } else if (e.key === 'ArrowRight') { // 处理向右箭头键 - targetRow = stateManager.select.cellPos.row; - targetCol = Math.min(table.colCount - 1, Math.max(0, stateManager.select.cellPos.col + 1)); + if (e.ctrlKey || e.metaKey) { + targetCol = table.colCount - 1; + targetRow = stateManager.select.cellPos.row; + } else if (e.shiftKey) { + targetRow = stateManager.select.cellPos.row; + targetCol = Math.min(table.colCount - 1, Math.max(0, stateManager.select.cellPos.col + 1)); + } else { + targetRow = stateManager.select.cellPos.row; + targetCol = Math.min(table.colCount - 1, Math.max(0, stateManager.select.cellPos.col + 1)); + } } - table.selectCell(targetCol, targetRow); + table.selectCell(targetCol, targetRow, e.shiftKey); if ( (table.options.keyboardOptions?.moveEditCellOnArrowKeys ?? false) && (table as ListTableAPI).editorManager.editingEditor @@ -617,14 +649,11 @@ export function bindContainerDomListener(eventManager: EventManager) { } } table.stateManager.updateInteractionState(InteractionState.grabing); - table.stateManager.updateSelectPos( - table.getTargetColAtConsiderRightFrozen(selectX, considerFrozenX).col, - table.getTargetRowAtConsiderBottomFrozen(selectY, considerFrozenY).row, - false, - false, - false, - true - ); + const targetCol = table.getTargetColAtConsiderRightFrozen(selectX, considerFrozenX); + const targetRow = table.getTargetRowAtConsiderBottomFrozen(selectY, considerFrozenY); + if (isValid(targetCol) && isValid(targetRow)) { + table.stateManager.updateSelectPos(targetCol.col, targetRow.row, false, false, false, true); + } }); } else if (table.eventManager.inertiaScroll.isInertiaScrolling()) { table.eventManager.inertiaScroll.endInertia(); diff --git a/packages/vtable/src/event/listener/table-group.ts b/packages/vtable/src/event/listener/table-group.ts index 73a070b27..4882d89d6 100644 --- a/packages/vtable/src/event/listener/table-group.ts +++ b/packages/vtable/src/event/listener/table-group.ts @@ -12,7 +12,7 @@ import type { SceneEvent } from '../util'; import { getCellEventArgsSet, regIndexReg } from '../util'; import { TABLE_EVENT_TYPE } from '../../core/TABLE_EVENT_TYPE'; import type { Group } from '../../scenegraph/graphic/group'; -import { isValid, last } from '@visactor/vutils'; +import { isValid } from '@visactor/vutils'; import { getIconAndPositionFromTarget } from '../../scenegraph/utils/icon'; import { cellInRanges } from '../../tools/helper'; import { Rect } from '../../tools/Rect'; @@ -338,7 +338,9 @@ export function bindTableGroupListener(eventManager: EventManager) { stateManager.updateInteractionState(InteractionState.default); eventManager.dealTableHover(); //点击到表格外部不需要取消选中状态 - // eventManager.dealTableSelect(); + if (table.options.select?.outsideClickDeselect) { + eventManager.dealTableSelect(); + } }); table.scenegraph.tableGroup.addEventListener('pointerdown', (e: FederatedPointerEvent) => { @@ -381,6 +383,8 @@ export function bindTableGroupListener(eventManager: EventManager) { const hitIcon = (eventArgsSet?.eventArgs?.target as any)?.role?.startsWith('icon') ? eventArgsSet.eventArgs.target + : (e.target as any).role?.startsWith('icon') + ? e.target : undefined; eventManager.downIcon = hitIcon; if (!hitIcon || (hitIcon.attribute as IIconGraphicAttribute).interactive === false) { @@ -669,8 +673,15 @@ export function bindTableGroupListener(eventManager: EventManager) { stateManager.hideMenu(); } (table as ListTableAPI).editorManager?.completeEdit(e.nativeEvent); + + const hitIcon = (e.target as any).role?.startsWith('icon') ? e.target : undefined; + eventManager.downIcon = hitIcon; // 处理列宽调整 这里和tableGroup.addEventListener('pointerdown' 逻辑一样 - if (!eventManager.checkCellFillhandle(eventArgsSet) && eventManager.checkColumnResize(eventArgsSet, true)) { + if ( + !hitIcon && + !eventManager.checkCellFillhandle(eventArgsSet) && + eventManager.checkColumnResize(eventArgsSet, true) + ) { // eventManager.startColumnResize(e); // eventManager._resizing = true; table.scenegraph.updateChartState(null); @@ -720,8 +731,13 @@ export function bindTableGroupListener(eventManager: EventManager) { ) { stateManager.updateInteractionState(InteractionState.default); eventManager.dealTableHover(); - eventManager.dealTableSelect(); stateManager.endSelectCells(); + + // 点击空白区域取消选中 + if (table.options.select?.blankAreaClickDeselect ?? true) { + eventManager.dealTableSelect(); + } + stateManager.updateCursor(); table.scenegraph.updateChartState(null); } else if (table.eventManager.isDraging && stateManager.isSelecting()) { diff --git a/packages/vtable/src/event/sparkline-event.ts b/packages/vtable/src/event/sparkline-event.ts index 2e4f81d41..a6f849e44 100644 --- a/packages/vtable/src/event/sparkline-event.ts +++ b/packages/vtable/src/event/sparkline-event.ts @@ -5,6 +5,10 @@ import type { MousePointerCellEvent } from '../ts-types'; import type { BaseTableAPI } from '../ts-types/base-table'; export function bindSparklineHoverEvent(table: BaseTableAPI) { + if (table.eventManager.bindSparklineHoverEvent) { + return; + } + // 判断是否有sparkline 类型 let hasSparkLine = false; if (table.isPivotTable()) { @@ -23,6 +27,8 @@ export function bindSparklineHoverEvent(table: BaseTableAPI) { return; } + table.eventManager.bindSparklineHoverEvent = true; + table.on(TABLE_EVENT_TYPE.MOUSEMOVE_CELL, (e: MousePointerCellEvent) => { const { col, row, x, y } = e; const type = table.getBodyColumnType(col, row); diff --git a/packages/vtable/src/header-helper/header-helper.ts b/packages/vtable/src/header-helper/header-helper.ts index da7a12996..ffb15e64c 100644 --- a/packages/vtable/src/header-helper/header-helper.ts +++ b/packages/vtable/src/header-helper/header-helper.ts @@ -59,11 +59,25 @@ export class HeaderHelper { const icons: ColumnIconOption[] = []; if (this._table.isPivotTable()) { // 透视表显示排序按钮 - const { showSort } = this._table.internalProps.layoutMap.getHeader(col, row) as HeaderData; + const { showSort, sort } = this._table.internalProps.layoutMap.getHeader(col, row) as HeaderData; if (showSort) { - const order = (this._table as PivotTableAPI).getPivotSortState(col, row); - const sortIcon = order === 'asc' ? this.downIcon : order === 'desc' ? this.upIcon : this.normalIcon; + let order = (this._table as PivotTableAPI).getPivotSortState(col, row) as string; + if (order) { + order = order.toUpperCase(); + } + const sortIcon = order === 'ASC' ? this.downIcon : order === 'DESC' ? this.upIcon : this.normalIcon; + if (sortIcon) { + icons.push(sortIcon); + } + } else if (sort) { + // 处理配置了sort的情况 + const sortIcon = this.getSortIconForPivotTable( + (this._table as PivotTableAPI).getPivotSortState(col, row), + this._table, + col, + row + ); if (sortIcon) { icons.push(sortIcon); } @@ -202,6 +216,26 @@ export class HeaderHelper { return icon; } + getSortIconForPivotTable( + order: SortOrder | undefined, + _table: BaseTableAPI, + col: number, + row: number + ): ColumnIconOption | null { + const headerC = _table.getHeaderDefine(col, row) as any; + if ( + !headerC || + headerC.showSort === false || + (!isValid(headerC.showSort) && !headerC.sort) || + (headerC.columns && headerC.columns.length > 0) + ) { + return null; + } + const icon = order === 'ASC' ? this.downIcon : order === 'DESC' ? this.upIcon : this.normalIcon; + // const icon = order === 'ASC' ? this.downIcon : this.upIcon; + return icon; + } + private getDropDownStateIcons(_table: BaseTableAPI, col: number, row: number): ColumnIconOption[] { const headerC = _table.getHeaderDefine(col, row) as any; const headerL = _table._getHeaderLayoutMap(col, row); diff --git a/packages/vtable/src/header-helper/style/Style.ts b/packages/vtable/src/header-helper/style/Style.ts index 1c49d268b..52cef9f31 100644 --- a/packages/vtable/src/header-helper/style/Style.ts +++ b/packages/vtable/src/header-helper/style/Style.ts @@ -27,6 +27,7 @@ const EVENT_TYPE = { }; export class Style extends EventTarget implements ColumnStyle { private _color?: ColorPropertyDefine; + private _strokeColor?: ColorPropertyDefine; private _fontSize?: FontSizePropertyDefine; private _fontFamily?: FontFamilyPropertyDefine; private _fontWeight?: FontWeightPropertyDefine; @@ -72,6 +73,7 @@ export class Style extends EventTarget implements ColumnStyle { constructor(style: IStyleOption = {}, headerStyle: IStyleOption = {}) { super(); this._color = style.color ?? headerStyle?.color; + this._strokeColor = style?.strokeColor ?? headerStyle?.strokeColor; // icon为文字前后可添加的图标 // this._icon = style.icon; this._fontSize = style.fontSize ?? headerStyle?.fontSize; @@ -121,6 +123,13 @@ export class Style extends EventTarget implements ColumnStyle { this._color = color; //this.doChangeStyle(); } + get strokeColor(): ColorPropertyDefine | undefined { + return this._strokeColor; + } + set strokeColor(strokeColor: ColorPropertyDefine | undefined) { + this._strokeColor = strokeColor; + // this.doChangeStyle(); + } get fontSize(): FontSizePropertyDefine | undefined { return this._fontSize; } diff --git a/packages/vtable/src/layout/layout-helper.ts b/packages/vtable/src/layout/layout-helper.ts index 08833bfdc..8a951c456 100644 --- a/packages/vtable/src/layout/layout-helper.ts +++ b/packages/vtable/src/layout/layout-helper.ts @@ -1,6 +1,5 @@ import { isArray, isString } from '@visactor/vutils'; import type { PivotTable } from '../PivotTable'; -import { IndicatorDimensionKeyPlaceholder } from '../tools/global'; import { AggregationType } from '../ts-types'; import type { BaseTableAPI } from '../ts-types/base-table'; import type { diff --git a/packages/vtable/src/layout/pivot-header-layout.ts b/packages/vtable/src/layout/pivot-header-layout.ts index 4bc348ac6..8fe03ac33 100644 --- a/packages/vtable/src/layout/pivot-header-layout.ts +++ b/packages/vtable/src/layout/pivot-header-layout.ts @@ -222,15 +222,8 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.resetRowHeaderLevelCount(); - //生成列表头单元格 - this._generateColHeaderIds(); - - this.colIndex = 0; - //生成行表头单元格 - this._generateRowHeaderIds(); - if (this._table.isPivotChart()) { - this.hasTwoIndicatorAxes = this._indicators.some(indicatorObject => { + this.hasTwoIndicatorAxes = this.indicatorsDefine.some((indicatorObject: any) => { if ( indicatorObject.chartSpec && indicatorObject.chartSpec.series && @@ -258,6 +251,13 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { // this.colAttrs[this.colAttrs.length-1]===this.indicatorDimensionKey&&this.colAttrs.pop(); // this.rowAttrs[this.rowAttrs.length-1]===this.indicatorDimensionKey&&this.rowAttrs.pop(); + //生成列表头单元格 + this._generateColHeaderIds(); + + this.colIndex = 0; + //生成行表头单元格 + this._generateRowHeaderIds(); + this._rowHeaderCellFullPathIds_FULL = transpose(this._rowHeaderCellFullPathIds_FULL); if ((table as PivotTable).options.rowHierarchyType === 'tree' && this.extensionRows?.length >= 1) { this.generateExtensionRowTree(); @@ -279,7 +279,25 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.sharedVar.seqId = Math.max(this.sharedVar.seqId, this._headerObjects.length); //生成cornerHeaderObjs及_cornerHeaderCellIds if (this.cornerSetting.titleOnDimension === 'column') { - const colDimensionKeys = this.columnDimensionTree.dimensionKeysIncludeVirtual.valueArr(); + let colDimensionKeys = this.columnDimensionTree.dimensionKeysIncludeVirtual.valueArr(); + //#region 处理需求 当没有数据时仍然显示角头维度名称 + if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree + ) { + colDimensionKeys = this.columnsDefine.map(define => { + if (typeof define === 'string') { + return define; + } + return define.dimensionKey; + }); + if (this.indicatorsAsCol) { + colDimensionKeys.push(this.indicatorDimensionKey); + } + } + //#endregion this.cornerHeaderObjs = this._addCornerHeaders( this.columnHeaderTitle ? [''].concat(colDimensionKeys) : colDimensionKeys, this.columnsDefine @@ -300,7 +318,25 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.rowsDefine.concat(extensionRowDimensions) ); } else { - const rowDimensionKeys = this.rowDimensionTree.dimensionKeysIncludeVirtual.valueArr(); + //#region 处理需求 当没有数据时仍然显示角头维度名称 + let rowDimensionKeys = this.rowDimensionTree.dimensionKeysIncludeVirtual.valueArr(); + if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree + ) { + rowDimensionKeys = this.rowsDefine.map(define => { + if (typeof define === 'string') { + return define; + } + return define.dimensionKey; + }); + if (!this.indicatorsAsCol) { + rowDimensionKeys.push(this.indicatorDimensionKey); + } + } + //#endregion this.cornerHeaderObjs = this._addCornerHeaders( this.rowHeaderTitle ? [''].concat(rowDimensionKeys) : rowDimensionKeys, this.rowsDefine @@ -448,9 +484,25 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { } _generateColHeaderIds() { if (this.columnDimensionTree.tree.children?.length >= 1) { + //#region 处理需求 当没有数据时仍然显示角头维度名称 + let startRow = 0; + if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree && + this.indicatorsAsCol && + this.columnDimensionTree.totalLevel < this.columnHeaderLevelCount + ) { + startRow = this.columnHeaderLevelCount - this.columnDimensionTree.totalLevel; + for (let i = 0; i < startRow; i++) { + this._columnHeaderCellFullPathIds.unshift([]); + } + } + //#endregion this._addHeaders( this._columnHeaderCellFullPathIds, - 0, + startRow, this.columnDimensionTree.tree.children, [], this.columnHeaderObjs @@ -460,7 +512,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { if (this.columnHeaderTitle) { this.sharedVar.seqId = Math.max(this.sharedVar.seqId, this._headerObjects.length); const id = ++this.sharedVar.seqId; - const firstRowIds = Array(this.colCount - this.rowHeaderLevelCount).fill(id); + const firstRowIds = Array(this.colCount - this.rowHeaderLevelCount - this.rightFrozenColCount).fill(id); this._columnHeaderCellFullPathIds.unshift(firstRowIds); const cell: HeaderData = { id, @@ -500,9 +552,25 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.rowHeaderObjs ); } else { + //#region 处理需求 当没有数据时仍然显示角头维度名称 + let startRow = 0; + if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree && + !this.indicatorsAsCol && + this.rowDimensionTree.totalLevel < this.rowHeaderLevelCount + ) { + startRow = this.rowHeaderLevelCount - this.rowDimensionTree.totalLevel; + for (let i = 0; i < startRow; i++) { + this._rowHeaderCellFullPathIds_FULL.unshift([]); + } + } + //#endregion this._addHeaders( this._rowHeaderCellFullPathIds_FULL, - 0, + startRow, this.rowDimensionTree.tree.children, [], this.rowHeaderObjs @@ -625,11 +693,15 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { : dimensionKey === 'axis' ? '' : (dimensionKey as string), - field: '维度名称', + field: dimensionKey, //'维度名称', style: this.cornerSetting.headerStyle, headerType: this.cornerSetting.headerType ?? 'text', + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, define: { - dimensionKey: '维度名称', + showSort: dimensionInfo?.showSortInCorner, + sort: dimensionInfo?.sort, + dimensionKey: dimensionKey, // '维度名称', id, value: dimensionKey, disableHeaderHover: !!this.cornerSetting.disableHeaderHover, @@ -1127,6 +1199,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { : this.columnDimensionTree.totalLevel : this.columnDimensionTree.totalLevel : this.columnDimensionTree.totalLevel; + if (this.columnHeaderTitle) { count += 1; } @@ -1138,6 +1211,27 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { ) { count -= 1; } + //#region 处理需求 当没有数据时仍然显示角头维度名称 + if (count === 0 && this.dataset && !this.dataset.customColTree && !this.dataset.customRowTree) { + if (this.cornerSetting.titleOnDimension === 'row') { + count = 1; + } else if ((this.dataset.records?.length ?? 0) === 0 && this.cornerSetting.titleOnDimension === 'column') { + count = this.columnsDefine.length ?? 0; + } + } else if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree + ) { + if (this.cornerSetting.titleOnDimension === 'column') { + count = this.columnsDefine.length ?? 0; + if (!this.hideIndicatorName && this.indicatorsAsCol) { + count++; + } + } + } + //#endregion this.columnHeaderLevelCount = count; return; } @@ -1181,6 +1275,27 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { // if (this._table.isPivotChart()&&this.indicatorsAsCol) { // count+=1; // } + //#region 处理需求 当没有数据时仍然显示角头维度名称 + if (count === 0 && this.dataset && !this.dataset.customColTree && !this.dataset.customRowTree) { + if (this.cornerSetting.titleOnDimension === 'column') { + count = 1; + } else if ((this.dataset.records?.length ?? 0) === 0 && this.cornerSetting.titleOnDimension === 'row') { + count = this.rowsDefine.length ?? 0; + } + } else if ( + this.dataset && + (this.dataset.records?.length ?? 0) === 0 && + !this.dataset.customColTree && + !this.dataset.customRowTree + ) { + if (this.cornerSetting.titleOnDimension === 'row') { + count = this.rowsDefine.length; + if (!this.hideIndicatorName && !this.indicatorsAsCol) { + count++; + } + } + } + //#endregion this.rowHeaderLevelCount = count; return; } @@ -1317,13 +1432,13 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { if (this.isSeriesNumber(col, row)) { return ''; } else if (this.isCornerHeader(col, row)) { - return this._cornerHeaderCellIds[row][col - this.leftRowSeriesNumberColumnCount]; + return this._cornerHeaderCellIds[row]?.[col - this.leftRowSeriesNumberColumnCount]; } else if (this.isColumnHeader(col, row)) { - return this._columnHeaderCellIds[row][col - this.rowHeaderLevelCount - this.leftRowSeriesNumberColumnCount]; + return this._columnHeaderCellIds[row]?.[col - this.rowHeaderLevelCount - this.leftRowSeriesNumberColumnCount]; } else if (this.isRowHeader(col, row)) { return this._rowHeaderCellIds[row - this.columnHeaderLevelCount]?.[col - this.leftRowSeriesNumberColumnCount]; } else if (this.isRightFrozenColumn(col, row)) { - return this._rowHeaderCellIds[row - this.columnHeaderLevelCount][this.rowHeaderLevelCount - 1]; + return this._rowHeaderCellIds[row - this.columnHeaderLevelCount]?.[this.rowHeaderLevelCount - 1]; } else if (this.isBottomFrozenRow(col, row)) { return this._columnHeaderCellIds[this.columnHeaderLevelCount - 1]?.[ col - this.rowHeaderLevelCount - this.leftRowSeriesNumberColumnCount @@ -2357,12 +2472,9 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { // } IPivotTableCellHeaderPaths | IDimensionInfo[] ): CellAddress | undefined { - let colHeaderPaths; - let rowHeaderPaths: { - dimensionKey?: string; - indicatorKey?: string; - value?: string; - }[]; + let colHeaderPaths: IDimensionInfo[]; + let rowHeaderPaths: IDimensionInfo[]; + let isCornerCell = false; let forceBody = false; if (Array.isArray(dimensionPaths)) { if (dimensionPaths.length > this.rowDimensionKeys.length + this.colDimensionKeys.length) { @@ -2404,6 +2516,32 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.fullRowDimensionKeys.indexOf(b.dimensionKey ?? this.indicatorDimensionKey) ); }); + + colHeaderPaths?.forEach(a => { + if (a.isPivotCorner) { + isCornerCell = true; + } + }); + rowHeaderPaths?.forEach(a => { + if (a.isPivotCorner) { + isCornerCell = true; + } + }); + if (isCornerCell) { + if (this.cornerSetting.titleOnDimension === 'row') { + for (let i = 0; i < this.rowDimensionKeys.length; i++) { + if (rowHeaderPaths[0].dimensionKey === this.rowDimensionKeys[i]) { + return { col: i + this.leftRowSeriesNumberColumnCount, row: 0 }; + } + } + } else { + for (let i = 0; i < this.colDimensionKeys.length; i++) { + if (colHeaderPaths[0].dimensionKey === this.colDimensionKeys[i]) { + return { col: 0, row: i }; + } + } + } + } let needLowestLevel = false; // needLowestLevel来标记是否需要 提供到最底层的维度层级信息 // 如果行列维度都有值 说明是匹配body单元格 那这个时候 维度层级应该是满的 if (colHeaderPaths?.length >= 1 && rowHeaderPaths?.length >= 1) { @@ -2518,7 +2656,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { } //树形展示的情况下 肯定是在第0列 } if (colDimensionFinded || forceBody) { - col = this.rowHeaderLevelCount; + col = this.rowHeaderLevelCount + this.leftRowSeriesNumberColumnCount; const { startInTotal, level } = (colDimensionFinded as ITreeLayoutHeadNode) ?? defaultDimension; col += startInTotal ?? 0; defaultRow = this.columnHeaderTitle ? level + 1 : level; @@ -2650,6 +2788,7 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { setPagination(pagination: IPagination): void { this.clearCellRangeMap(); + this._table.internalProps.useOneRowHeightFillAll = false; this.pagination = pagination; if ( @@ -3126,8 +3265,10 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { return totalCount; } resetHeaderTree() { + this.colIndex = 0; //和初始化代码逻辑一致 但未考虑透视图类型 this._rowHeaderCellFullPathIds_FULL = []; + this._columnHeaderCellFullPathIds = []; this._columnHeaderCellIds = []; const dataset = this.dataset; // if (dataset) { @@ -3141,20 +3282,24 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI { this.rowHierarchyType, this.rowHierarchyType === 'tree' ? this.rowExpandLevel : undefined ); + + this.resetColumnHeaderLevelCount(); + //生成列表头单元格 this._generateColHeaderIds(); this.colIndex = 0; //生成行表头单元格 this._generateRowHeaderIds(); - - this.resetColumnHeaderLevelCount(); this._rowHeaderCellFullPathIds_FULL = transpose(this._rowHeaderCellFullPathIds_FULL); this._headerObjectMap = this._headerObjects.reduce((o, e) => { o[e.id as number] = e; return o; }, {} as { [key: LayoutObjectId]: HeaderData }); + + this._CellHeaderPathMap = new Map(); + this._largeCellRangeCache.length = 0; this.generateCellIdsConsiderHideHeader(); this.setPagination(this.pagination); } diff --git a/packages/vtable/src/layout/row-height-map.ts b/packages/vtable/src/layout/row-height-map.ts index 9b5fb03c9..ff8fe142a 100644 --- a/packages/vtable/src/layout/row-height-map.ts +++ b/packages/vtable/src/layout/row-height-map.ts @@ -67,6 +67,10 @@ export class NumberRangeMap { put(position: number, newValue: number) { if (this.data.has(position)) { const oldValue = this.data.get(position); + + if (oldValue === newValue) { + return; + } this.data.set(position, newValue); const difference = newValue - oldValue; this.totalSum += difference; diff --git a/packages/vtable/src/layout/tree-helper.ts b/packages/vtable/src/layout/tree-helper.ts index c593561fb..6292ae6c2 100644 --- a/packages/vtable/src/layout/tree-helper.ts +++ b/packages/vtable/src/layout/tree-helper.ts @@ -442,7 +442,7 @@ export function dealHeader( headerType: indicatorInfo?.headerType ?? dimensionInfo?.headerType ?? 'text', headerIcon: indicatorInfo?.headerIcon ?? dimensionInfo?.headerIcon, // define: hd, - define: Object.assign({}, hd, indicatorInfo ?? dimensionInfo), + define: Object.assign({}, hd, indicatorInfo ?? Object.assign({}, dimensionInfo, { sort: undefined })), fieldFormat: indicatorInfo?.headerFormat ?? dimensionInfo?.headerFormat, // iconPositionList:[] dropDownMenu: indicatorInfo?.dropDownMenu ?? dimensionInfo?.dropDownMenu, @@ -456,6 +456,7 @@ export function dealHeader( minWidth: (dimensionInfo as IRowDimension)?.minWidth, maxWidth: (dimensionInfo as IRowDimension)?.maxWidth, showSort: indicatorInfo?.showSort ?? dimensionInfo?.showSort, + sort: indicatorInfo?.sort, description: dimensionInfo?.description }; diff --git a/packages/vtable/src/scenegraph/graphic/chart.ts b/packages/vtable/src/scenegraph/graphic/chart.ts index f13d987f2..e4e221b6d 100644 --- a/packages/vtable/src/scenegraph/graphic/chart.ts +++ b/packages/vtable/src/scenegraph/graphic/chart.ts @@ -149,7 +149,7 @@ export class Chart extends Group { */ deactivate() { this.active = false; - // move active chart view box out of broswer view + // move active chart view box out of browser view // to avoid async render when chart is releasd this.activeChartInstance?.updateViewBox( { diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index fd703ab26..5d8e256d1 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -500,7 +500,7 @@ export function updateCell(col: number, row: number, table: BaseTableAPI, addNew lineClamp, wordBreak: 'break-word', // widthLimit: autoColWidth ? -1 : colWidth - (padding[1] + padding[3]), - heightLimit: cellHeight - (padding[0] + padding[2]), + heightLimit: cellHeight - Math.floor(padding[0] + padding[2]), pickable: false, dx: textAlign === 'left' ? hierarchyOffset : 0, x @@ -739,7 +739,7 @@ export function dealWithMergeCellSize( for (let row = range.start.row; row <= range.end.row; row++) { const cellGroup = table.scenegraph.getCell(col, row, true); - if (cellGroup.role === 'cell' && range.start.row !== range.end.row && cellGroup.contentWidth !== cellWidth) { + if (cellGroup.role === 'cell' && range.start.row !== range.end.row && cellGroup.contentHeight !== cellHeight) { updateCellContentHeight( cellGroup, cellHeight, @@ -752,7 +752,7 @@ export function dealWithMergeCellSize( // 'middle' ); } - if (cellGroup.role === 'cell' && range.start.col !== range.end.col && cellGroup.contentHeight !== cellHeight) { + if (cellGroup.role === 'cell' && range.start.col !== range.end.col && cellGroup.contentWidth !== cellWidth) { updateCellContentWidth( cellGroup, cellWidth, 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 f3b15e7bd..6e7c77ca5 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 @@ -191,7 +191,7 @@ function createCheckbox( lineClamp, wordBreak: 'break-word', // widthLimit: autoColWidth ? -1 : colWidth - (padding[1] + padding[3]), - heightLimit: autoRowHeight ? -1 : cellHeight - (padding[0] + padding[2]), + heightLimit: autoRowHeight ? -1 : cellHeight - Math.floor(padding[0] + padding[2]), pickable: false, dx: hierarchyOffset, whiteSpace: text.length === 1 && !autoWrapText ? 'no-wrap' : 'normal' 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 86aaf777c..eb30a4449 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 @@ -160,7 +160,7 @@ function createRadio( lineClamp, wordBreak: 'break-word', // widthLimit: autoColWidth ? -1 : colWidth - (padding[1] + padding[3]), - heightLimit: autoRowHeight ? -1 : cellHeight - (padding[0] + padding[2]), + heightLimit: autoRowHeight ? -1 : cellHeight - Math.floor(padding[0] + padding[2]), pickable: false, dx: hierarchyOffset // whiteSpace: text.length === 1 && !autoWrapText ? 'no-wrap' : 'normal' diff --git a/packages/vtable/src/scenegraph/icon/icon-update.ts b/packages/vtable/src/scenegraph/icon/icon-update.ts index c034b1221..7da4ee306 100644 --- a/packages/vtable/src/scenegraph/icon/icon-update.ts +++ b/packages/vtable/src/scenegraph/icon/icon-update.ts @@ -137,7 +137,7 @@ export function setIconHoverStyle(baseIcon: Icon, col: number, row: number, cell iconBack.setAttributes({ x: (icon.attribute.x ?? 0) + - (icon.attribute.dx ?? 0) + + // (icon.attribute.dx ?? 0) + (icon.AABBBounds.width() - icon.backgroundWidth) / 2, y: (icon.attribute.y ?? 0) + (icon.AABBBounds.height() - icon.backgroundHeight) / 2, dx: icon.attribute.dx ?? 0, @@ -152,7 +152,7 @@ export function setIconHoverStyle(baseIcon: Icon, col: number, row: number, cell iconBack = createRect({ x: (icon.attribute.x ?? 0) + - (icon.attribute.dx ?? 0) + + // (icon.attribute.dx ?? 0) + (icon.AABBBounds.width() - icon.backgroundWidth) / 2, y: (icon.attribute.y ?? 0) + (icon.AABBBounds.height() - icon.backgroundHeight) / 2, dx: icon.attribute.dx ?? 0, @@ -194,6 +194,7 @@ export function setIconHoverStyle(baseIcon: Icon, col: number, row: number, cell }, placement: baseIcon.tooltip.placement }, + disappearDelay: baseIcon.tooltip.disappearDelay, style: Object.assign({}, scene.table.internalProps.theme?.tooltipStyle, baseIcon.tooltip?.style) }; if (!scene.table.internalProps.tooltipHandler.isBinded(tooltipOptions)) { diff --git a/packages/vtable/src/scenegraph/layout/compute-col-width.ts b/packages/vtable/src/scenegraph/layout/compute-col-width.ts index 02d05c7dd..dee0da2db 100644 --- a/packages/vtable/src/scenegraph/layout/compute-col-width.ts +++ b/packages/vtable/src/scenegraph/layout/compute-col-width.ts @@ -691,6 +691,8 @@ export function getAdaptiveWidth( ) { let actualWidth = 0; const adaptiveColumns: number[] = []; + const sparklineColumns = []; + let totalSparklineAbleWidth = 0; for (let col = startCol; col < endColPlus1; col++) { const width = update ? newWidths[col] : table.getColWidth(col); const maxWidth = table.getMaxColWidth(col); @@ -702,10 +704,40 @@ export function getAdaptiveWidth( // fixed width, do not adaptive totalDrawWidth -= width; } + + if (table.options.customConfig?.shrinkSparklineFirst) { + const bodyCellType = table.getBodyColumnType(col, 0); + if (bodyCellType === 'sparkline') { + sparklineColumns.push({ col, width }); + totalSparklineAbleWidth += width - table.defaultColWidth; + } + } } const factor = totalDrawWidth / actualWidth; + if ( + table.options.customConfig?.shrinkSparklineFirst && + factor < 1 && + totalDrawWidth - actualWidth < totalSparklineAbleWidth + ) { + // only shrink sparkline column + for (let i = 0; i < sparklineColumns.length; i++) { + const { col, width } = sparklineColumns[i]; + const deltaWidth = (actualWidth - totalDrawWidth) / sparklineColumns.length; + const colWidth = Math.floor(width - deltaWidth); + + if (update) { + newWidths[col] = table._adjustColWidth(col, colWidth); + } else if (fromScenegraph) { + table.scenegraph.setColWidth(col, table._adjustColWidth(col, colWidth)); + } else { + table._setColWidth(col, table._adjustColWidth(col, colWidth), false, true); + } + } + return; + } + for (let i = 0; i < adaptiveColumns.length; i++) { const col = adaptiveColumns[i]; let colWidth; diff --git a/packages/vtable/src/scenegraph/layout/compute-row-height.ts b/packages/vtable/src/scenegraph/layout/compute-row-height.ts index 1f77dd187..afa7d8f05 100644 --- a/packages/vtable/src/scenegraph/layout/compute-row-height.ts +++ b/packages/vtable/src/scenegraph/layout/compute-row-height.ts @@ -502,6 +502,9 @@ function fillRowsHeight( table: BaseTableAPI, newHeights: number[] | undefined ) { + if (table.internalProps.useOneRowHeightFillAll) { + return; + } for (let row = startRow; row <= endRow; row++) { if (newHeights) { newHeights[row] = height; @@ -509,6 +512,7 @@ function fillRowsHeight( table._setRowHeight(row, height); } } + table.internalProps.useOneRowHeightFillAll = true; } /** diff --git a/packages/vtable/src/scenegraph/layout/frozen.ts b/packages/vtable/src/scenegraph/layout/frozen.ts index 62dd6b7e1..e028d6c9a 100644 --- a/packages/vtable/src/scenegraph/layout/frozen.ts +++ b/packages/vtable/src/scenegraph/layout/frozen.ts @@ -17,6 +17,9 @@ export function dealFrozen(scene: Scenegraph) { } } else if (scene.table.frozenColCount < scene.table.rowHeaderLevelCount) { // move columnGroup from rowHeaderGroup into bodyGroup(from cornerHeaderGroup into colHeaderGroup) + scene.bodyGroup.setAttribute('height', scene.rowHeaderGroup.attribute.height); + scene.bodyGroup.setAttribute('y', scene.rowHeaderGroup.attribute.y); + scene.colHeaderGroup.setAttribute('height', scene.cornerHeaderGroup.attribute.height); for (let i = 0; i < scene.table.rowHeaderLevelCount - scene.table.frozenColCount; i++) { moveColumnFromRowHeaderToBody(scene); moveColumnFromCornerHeaderToColHeader(scene); @@ -47,6 +50,9 @@ export function dealFrozen(scene: Scenegraph) { export function resetFrozen(scene: Scenegraph) { if (scene.frozenColCount > scene.table.frozenColCount) { // move columnGroup from rowHeaderGroup into bodyGroup(from cornerHeaderGroup into colHeaderGroup) + scene.bodyGroup.setAttribute('height', scene.rowHeaderGroup.attribute.height); + scene.bodyGroup.setAttribute('y', scene.rowHeaderGroup.attribute.y); + scene.colHeaderGroup.setAttribute('height', scene.cornerHeaderGroup.attribute.height); for (let i = 0; i < scene.frozenColCount - scene.table.frozenColCount; i++) { moveColumnFromRowHeaderToBody(scene); moveColumnFromCornerHeaderToColHeader(scene); diff --git a/packages/vtable/src/scenegraph/layout/update-width.ts b/packages/vtable/src/scenegraph/layout/update-width.ts index 23aacadaa..cb8199392 100644 --- a/packages/vtable/src/scenegraph/layout/update-width.ts +++ b/packages/vtable/src/scenegraph/layout/update-width.ts @@ -429,7 +429,10 @@ function updateCellWidth( ); isHeightChange = isHeightChange || cellChange; } - + if (!autoWrapText) { + const style = scene.table._getCellStyle(col, row); + autoWrapText = style.autoWrapText; + } return autoRowHeight && autoWrapText ? isHeightChange : false; } diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index e2f376af7..caabf5c01 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -809,6 +809,7 @@ export class Scenegraph { } recalculateRowHeights() { + this.table.internalProps.useOneRowHeightFillAll = false; computeRowsHeight(this.table, 0, this.table.rowCount - 1, true, true); } @@ -1802,6 +1803,7 @@ export class Scenegraph { updateRow(removeCells: CellAddress[], addCells: CellAddress[], updateCells: CellAddress[] = []) { this.table.internalProps.layoutMap.clearCellRangeMap(); + this.table.internalProps.useOneRowHeightFillAll = false; const addRows = deduplication(addCells.map(cell => cell.row)).sort((a, b) => a - b); const updateRows = deduplication(updateCells.map(cell => cell.row)).sort((a, b) => a - b); //这个值是后续为了autoFillHeight判断逻辑中用到的 判断是否更新前是未填满的情况 diff --git a/packages/vtable/src/scenegraph/utils/text-icon-layout.ts b/packages/vtable/src/scenegraph/utils/text-icon-layout.ts index 560006c60..985d79e07 100644 --- a/packages/vtable/src/scenegraph/utils/text-icon-layout.ts +++ b/packages/vtable/src/scenegraph/utils/text-icon-layout.ts @@ -11,7 +11,7 @@ import type { Scenegraph } from '../scenegraph'; import { getCellMergeInfo } from './get-cell-merge'; import { getHierarchyOffset } from './get-hierarchy-offset'; import type { BaseTableAPI } from '../../ts-types/base-table'; -import { isNil, isNumber, isValid } from '@visactor/vutils'; +import { isNil, isNumber, isValid, isValidNumber } from '@visactor/vutils'; import { isMergeCellGroup } from './is-merge-cell-group'; import { breakString } from './break-string'; @@ -103,7 +103,7 @@ export function createCellContent( heightLimit: autoRowHeight && !table.options.customConfig?.multilinesForXTable ? -1 - : cellHeight - (padding[0] + padding[2]), + : cellHeight - Math.floor(padding[0] + padding[2]), pickable: false, dx: (textAlign === 'left' ? hierarchyOffset : 0) + _contentOffset, whiteSpace: text.length === 1 && !autoWrapText ? 'no-wrap' : 'normal' @@ -221,7 +221,7 @@ export function createCellContent( heightLimit: autoRowHeight && !table.options.customConfig?.multilinesForXTable ? -1 - : cellHeight - (padding[0] + padding[2]), + : cellHeight - Math.floor(padding[0] + padding[2]), pickable: false, autoWrapText, lineClamp, @@ -545,6 +545,9 @@ export function updateCellContentWidth( textBaseline: CanvasTextBaseline, scene: Scenegraph ): boolean { + if (isValidNumber(cellGroup.contentWidth)) { + detaX = distWidth - (cellGroup.contentWidth ?? cellGroup.attribute.width); + } let leftIconWidth = 0; let leftIconHeight = 0; let rightIconWidth = 0; @@ -600,7 +603,7 @@ export function updateCellContentWidth( child.setAttribute('x', child.attribute.x + detaX); } else if (child.role === 'icon-absolute-right') { child.setAttribute('x', child.attribute.x + detaX); - } else if (child.name === 'content' || child.name === 'text') { + } else if (child.name === 'content' || (child.name === 'text' && child.type !== 'richtext')) { const childTextAlign = child.attribute.textAlign ?? textAlign; if (childTextAlign === 'center') { child.setAttribute( @@ -673,7 +676,7 @@ export function updateCellContentHeight( textAlign: CanvasTextAlign, textBaseline: CanvasTextBaseline ) { - const newHeight = distHeight - (padding[0] + padding[2]); + const newHeight = distHeight - Math.floor(padding[0] + padding[2]); const textMark = cellGroup.getChildByName('text'); diff --git a/packages/vtable/src/state/cell-move/index.ts b/packages/vtable/src/state/cell-move/index.ts index 635c17baa..a8dac8bae 100644 --- a/packages/vtable/src/state/cell-move/index.ts +++ b/packages/vtable/src/state/cell-move/index.ts @@ -129,6 +129,7 @@ export function endMoveCol(state: StateManager) { // 更新状态 if (moveContext) { + state.table.internalProps.useOneRowHeightFillAll = false; state.table.internalProps.layoutMap.clearCellRangeMap(); const sourceMergeInfo = state.table.getCellRange(state.columnMove.colSource, state.columnMove.rowSource); const targetMergeInfo = state.table.getCellRange(state.columnMove.colTarget, state.columnMove.rowTarget); diff --git a/packages/vtable/src/state/select/update-position.ts b/packages/vtable/src/state/select/update-position.ts index 9561f5ffb..aa4ff9322 100644 --- a/packages/vtable/src/state/select/update-position.ts +++ b/packages/vtable/src/state/select/update-position.ts @@ -95,6 +95,10 @@ export function updateSelectPosition( ) { const currentRange = state.select.ranges[state.select.ranges.length - 1]; if (isShift && currentRange) { + if (!isCtrl) { + cellPos.col = col; + cellPos.row = row; + } if (state.select.headerSelectMode !== 'cell' && table.isColumnHeader(col, row)) { const startCol = Math.min(currentRange.start.col, currentRange.end.col, col); const endCol = Math.max(currentRange.start.col, currentRange.end.col, col); diff --git a/packages/vtable/src/state/sort/index.ts b/packages/vtable/src/state/sort/index.ts index 149e1f697..16d3f6c28 100644 --- a/packages/vtable/src/state/sort/index.ts +++ b/packages/vtable/src/state/sort/index.ts @@ -67,6 +67,7 @@ export function dealSort(col: number, row: number, table: ListTableAPI, event: E } // clear cell range cache + table.internalProps.useOneRowHeightFillAll = false; table.internalProps.layoutMap.clearCellRangeMap(); table.scenegraph.sortCell(); diff --git a/packages/vtable/src/state/state.ts b/packages/vtable/src/state/state.ts index a275c9f62..767261b2d 100644 --- a/packages/vtable/src/state/state.ts +++ b/packages/vtable/src/state/state.ts @@ -6,7 +6,6 @@ import type { CellAddress, CellPosition, CellRange, - CheckboxColumnDefine, DropDownMenuHighlightInfo, IDimensionInfo, ListTableAPI, @@ -15,7 +14,7 @@ import type { SortOrder, SortState } from '../ts-types'; -import { HighlightScope, InteractionState } from '../ts-types'; +import { HighlightScope, InteractionState, SortType } from '../ts-types'; import { IconFuncTypeEnum } from '../ts-types'; import { checkMultiCellInSelect } from './common/check-in-select'; import { updateHoverPosition } from './hover/update-position'; @@ -49,6 +48,7 @@ import { } from './checkbox/checkbox'; import { updateResizeRow } from './resize/update-resize-row'; import { deleteAllSelectingBorder } from '../scenegraph/select/delete-select-border'; +import type { PivotTable } from '../PivotTable'; export class StateManager { table: BaseTableAPI; @@ -572,7 +572,8 @@ export class StateManager { this.table.internalProps.theme?.tooltipStyle, inlineIcon.tooltip?.style, inlineIcon.attribute?.tooltip?.style - ) + ), + disappearDelay: inlineIcon.attribute.tooltip.disappearDelay }; if (!this.table.internalProps.tooltipHandler.isBinded(tooltipOptions)) { this.table.showTooltip(col, row, tooltipOptions); @@ -761,7 +762,7 @@ export class StateManager { checkFrozen(): boolean { // 判断固定列的总宽度 是否过大 - const originalFrozenColCount = + let originalFrozenColCount = this.table.isListTable() && !this.table.internalProps.transpose ? this.table.options.frozenColCount : this.table.isPivotChart() @@ -771,6 +772,9 @@ export class StateManager { this.table.options.frozenColCount ?? 0 ); if (originalFrozenColCount) { + if (originalFrozenColCount > this.table.colCount) { + originalFrozenColCount = this.table.colCount; + } if (this.table.tableNoFrameWidth - this.table.getColsWidth(0, originalFrozenColCount - 1) <= 120) { this.table._setFrozenColCount(0); this.setFrozenCol(-1); @@ -1173,7 +1177,13 @@ export class StateManager { triggerSort(col: number, row: number, iconMark: Icon, event: Event) { if (this.table.isPivotTable()) { // 透视表不执行sort操作 - const order = (this.table as PivotTableAPI).getPivotSortState(col, row); + const sortState = (this.table as PivotTableAPI).getPivotSortState(col, row); + + const order = sortState ? (sortState.toUpperCase() as SortOrder) : 'DESC'; + // const new_order = order === 'ASC' ? 'DESC' : order === 'DESC' ? 'NORMAL' : 'ASC'; + const new_order = order === 'ASC' ? 'DESC' : 'ASC'; + (this.table as PivotTable).sort(col, row, new_order); + // // 触发透视表排序按钮点击 this.table.fireListeners(PIVOT_TABLE_EVENT_TYPE.PIVOT_SORT_CLICK, { col: col, diff --git a/packages/vtable/src/themes/theme.ts b/packages/vtable/src/themes/theme.ts index cf9447610..c924f2b19 100644 --- a/packages/vtable/src/themes/theme.ts +++ b/packages/vtable/src/themes/theme.ts @@ -493,6 +493,12 @@ export class TableTheme implements ITableThemeDefine { }, get color(): string | undefined { return tooltip.color ?? '#FFF'; + }, + get maxWidth(): number | undefined { + return tooltip.maxWidth; + }, + get maxHeight(): number | undefined { + return tooltip.maxHeight; } }; } @@ -749,6 +755,9 @@ export class TableTheme implements ITableThemeDefine { get color(): ColorPropertyDefine | undefined { return style.color; }, + get strokeColor(): ColorPropertyDefine | undefined { + return style.strokeColor; + }, get borderColor(): ColorsPropertyDefine | undefined { return style.borderColor; }, diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index f19dd9e9b..dcb2d878a 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -197,6 +197,8 @@ export interface IBaseTableProtected { renderMode: 'html' | 'canvas'; /** 代替原来hover:isShowTooltip配置 */ isShowOverflowTextTooltip: boolean; + /** 缩略文字提示框 延迟消失时间 */ + overflowTextTooltipDisappearDelay?: number; /** 弹框是否需要限定在表格区域内 */ confine: boolean; }; @@ -249,6 +251,9 @@ export interface IBaseTableProtected { * 设置为 'none' 时, 表格滚动到顶部/底部时, 不再触发父容器滚动 * */ overscrollBehavior?: 'auto' | 'none'; + + // 已使用一行的高度填充所有行 + useOneRowHeightFillAll?: boolean; } export interface BaseTableConstructorOptions { // /** 指定表格的行数 */ @@ -315,7 +320,7 @@ export interface BaseTableConstructorOptions { /** hover交互配置 */ hover?: { /** hover交互响应模式:十字交叉 整列 整行 或者单个单元格 */ - highlightMode: 'cross' | 'column' | 'row' | 'cell'; + highlightMode?: 'cross' | 'column' | 'row' | 'cell'; /** 不响应鼠标hover交互 */ disableHover?: boolean; /** 单独设置表头不响应鼠标hover交互 */ @@ -326,13 +331,17 @@ export interface BaseTableConstructorOptions { /** 选择单元格交互配置 */ select?: { /** 高亮范围模式:十字交叉 整列 整行 或者单个单元格。默认`cell` */ - highlightMode: 'cross' | 'column' | 'row' | 'cell'; + highlightMode?: 'cross' | 'column' | 'row' | 'cell'; /** 点击表头单元格时连带body整行或整列选中 或仅选中当前单元格,默认或整行或整列选中*/ headerSelectMode?: 'inline' | 'cell'; /** 不响应鼠标select交互 */ disableSelect?: boolean; /** 单独设置表头不响应鼠标select交互 */ disableHeaderSelect?: boolean; + /** 点击空白区域是否取消选中 */ + blankAreaClickDeselect?: boolean; + /** 点击外部区域是否取消选中 */ + outsideClickDeselect?: boolean; // }; /** 下拉菜单的相关配置。消失时机:显示后点击菜单区域外自动消失*/ menu?: { @@ -349,8 +358,10 @@ export interface BaseTableConstructorOptions { tooltip?: { /** html目前实现较完整 先默认html渲染方式 */ renderMode?: 'html'; // 目前暂不支持canvas方案 - /** 代替原来hover:isShowTooltip配置 暂时需要将renderMode配置为html才能显示,canvas的还未开发*/ + /** 是否显示缩略文字提示框。 代替原来hover:isShowTooltip配置 暂时需要将renderMode配置为html才能显示,canvas的还未开发*/ isShowOverflowTextTooltip?: boolean; + /** 缩略文字提示框 延迟消失时间 */ + overflowTextTooltipDisappearDelay?: number; /** 是否将 tooltip 框限制在画布区域内,默认开启。针对renderMode:"html"有效 */ confine?: boolean; }; @@ -412,7 +423,7 @@ export interface BaseTableConstructorOptions { customMergeCell?: CustomMergeCell; // #region for nodejs - mode?: 'node' | 'broswer'; + mode?: 'node' | 'browser'; modeParams?: any; canvasWidth?: number; canvasHeight?: number; @@ -441,6 +452,8 @@ export interface BaseTableConstructorOptions { /** 禁用行高列宽计算取整数逻辑 对齐xTable */ _disableColumnAndRowSizeRound?: boolean; imageMargin?: number; + // adaptive 模式下优先缩小迷你图 + shrinkSparklineFirst?: boolean; }; // 部分特殊配置,兼容xTable等作用 animationAppear?: boolean | IAnimationAppear; @@ -616,7 +629,7 @@ export interface BaseTableAPI { getFrozenColsWidth: () => number; getBottomFrozenRowsHeight: () => number; getRightFrozenColsWidth: () => number; - selectCell: (col: number, row: number) => void; + selectCell: (col: number, row: number, isShift?: boolean, isCtrl?: boolean) => void; selectCells: (cellRanges: CellRange[]) => void; getAllRowsHeight: () => number; getAllColsWidth: () => number; diff --git a/packages/vtable/src/ts-types/column/style.ts b/packages/vtable/src/ts-types/column/style.ts index fcfdd6f1c..102c2020c 100644 --- a/packages/vtable/src/ts-types/column/style.ts +++ b/packages/vtable/src/ts-types/column/style.ts @@ -56,6 +56,7 @@ export interface IStyleOption { textAlign?: TextAlignType; textBaseline?: TextBaselineType; color?: ColorPropertyDefine; + strokeColor?: ColorPropertyDefine; fontSize?: FontSizePropertyDefine; fontFamily?: FontFamilyPropertyDefine; diff --git a/packages/vtable/src/ts-types/common.ts b/packages/vtable/src/ts-types/common.ts index 65e20f36f..9c321c270 100644 --- a/packages/vtable/src/ts-types/common.ts +++ b/packages/vtable/src/ts-types/common.ts @@ -82,19 +82,9 @@ export type IListTableCellHeaderPaths = { }; export type IPivotTableCellHeaderPaths = { /** 列表头各级path表头信息 */ - readonly colHeaderPaths?: { - dimensionKey?: string; - indicatorKey?: string; - value?: string; - virtual?: boolean; - }[]; + readonly colHeaderPaths?: IDimensionInfo[]; /** 行表头各级path表头信息 */ - readonly rowHeaderPaths?: { - dimensionKey?: string; - indicatorKey?: string; - value?: string; - virtual?: boolean; - }[]; + readonly rowHeaderPaths?: IDimensionInfo[]; cellLocation: CellLocation; }; @@ -103,6 +93,7 @@ export interface IDimensionInfo { value?: string; indicatorKey?: string; isPivotCorner?: boolean; + virtual?: boolean; } /** @@ -135,7 +126,7 @@ export enum HighlightScope { 'none' = 'none' } -export type SortOrder = 'asc' | 'desc' | 'normal'; +export type SortOrder = 'asc' | 'desc' | 'normal' | 'ASC' | 'DESC' | 'NORMAL'; export type CustomCellStyle = { id: string; diff --git a/packages/vtable/src/ts-types/component/title.ts b/packages/vtable/src/ts-types/component/title.ts index 0e706174e..7aa2952c7 100644 --- a/packages/vtable/src/ts-types/component/title.ts +++ b/packages/vtable/src/ts-types/component/title.ts @@ -150,6 +150,9 @@ export type ITitle = { character?: IRichTextCharacter[]; } & Partial & Partial; + + dx?: number; + dy?: number; }; export interface IPadding { top?: number; diff --git a/packages/vtable/src/ts-types/icon.ts b/packages/vtable/src/ts-types/icon.ts index 4a008f0ab..26c289dee 100644 --- a/packages/vtable/src/ts-types/icon.ts +++ b/packages/vtable/src/ts-types/icon.ts @@ -63,7 +63,10 @@ export interface IIconBase { padding?: number[]; bgColor?: string; arrowMark?: boolean; + maxWidth?: number; + maxHeight?: number; }; + disappearDelay?: number; }; /** * 是否可交互 默认为true 目前已知不可交互按钮:下拉菜单状态 diff --git a/packages/vtable/src/ts-types/list-table/layout-map/api.ts b/packages/vtable/src/ts-types/list-table/layout-map/api.ts index b1af5c8d2..8a6b32ba3 100644 --- a/packages/vtable/src/ts-types/list-table/layout-map/api.ts +++ b/packages/vtable/src/ts-types/list-table/layout-map/api.ts @@ -20,7 +20,8 @@ import type { SparklineSpec, HierarchyState, Aggregation, - IRowSeriesNumber + IRowSeriesNumber, + SortOption } from '../../'; import type { Aggregator } from '../../../dataset/statistics-helper'; import type { BaseTableAPI } from '../../base-table'; @@ -82,6 +83,7 @@ export interface HeaderData extends WidthData { columnWidthComputeMode?: 'normal' | 'only-header' | 'only-body'; showSort?: boolean; + sort?: SortOption; /** * 表头描述 鼠标hover会提示该信息 diff --git a/packages/vtable/src/ts-types/pivot-table/dimension/basic-dimension.ts b/packages/vtable/src/ts-types/pivot-table/dimension/basic-dimension.ts index bcef1487a..8a2289bf5 100644 --- a/packages/vtable/src/ts-types/pivot-table/dimension/basic-dimension.ts +++ b/packages/vtable/src/ts-types/pivot-table/dimension/basic-dimension.ts @@ -3,7 +3,7 @@ import type { ICustomLayout } from '../../customLayout'; import type { FieldFormat } from '../../table-engine'; import type { ColumnIconOption } from '../../icon'; import type { MenuListItem } from '../../menu'; -import type { BaseCellInfo, CellInfo } from '../../common'; +import type { BaseCellInfo, CellInfo, SortOption } from '../../common'; import type { IEditor } from '@visactor/vtable-editors'; import type { BaseTableAPI } from '../../base-table'; @@ -37,8 +37,12 @@ export interface IBasicDimension { dropDownMenu?: MenuListItem[]; /** 角头单元格显示下拉按钮及下拉菜单*/ cornerDropDownMenu?: MenuListItem[]; - /** 是否显示排序icon */ + /** sort排序规则 */ + sort?: SortOption; + /** 显示sort排序icon。为了仅仅显示图标,无排序逻辑 */ showSort?: boolean; + /** 在角头的维度名称单元格中是否显示排序 */ + showSortInCorner?: boolean; /** 是否可以拖拽表头换位置 */ dragHeader?: boolean; /** 表头自定义渲染内容定义 */ diff --git a/packages/vtable/src/ts-types/pivot-table/indicator/basic-indicator.ts b/packages/vtable/src/ts-types/pivot-table/indicator/basic-indicator.ts index 341b7b5af..c0ca9628a 100644 --- a/packages/vtable/src/ts-types/pivot-table/indicator/basic-indicator.ts +++ b/packages/vtable/src/ts-types/pivot-table/indicator/basic-indicator.ts @@ -24,7 +24,10 @@ export interface IBasicHeaderIndicator { // sparklineSpec?: SparklineSpec | ((arg0: CustomRenderFunctionArg) => SparklineSpec); dropDownMenu?: MenuListItem[]; // 针对单独指标上配置下拉按钮 - showSort?: boolean; // 否显示排序icon + /** sort排序规则 */ + sort?: boolean; + /** 显示sort排序icon。为了仅仅显示图标,无排序逻辑 */ + showSort?: boolean; disableColumnResize?: boolean; // 是否禁用调整列宽,如果是转置表格或者是透视表的指标是行方向指定 那该配置不生效 /** 指标名称表头自定义渲染内容定义 */ diff --git a/packages/vtable/src/ts-types/style-define.ts b/packages/vtable/src/ts-types/style-define.ts index dc755bc3b..f8caeeb8e 100644 --- a/packages/vtable/src/ts-types/style-define.ts +++ b/packages/vtable/src/ts-types/style-define.ts @@ -81,6 +81,7 @@ export type CellStyle = { padding: PaddingsDef; textBaseline: CanvasTextBaseline; color: CanvasRenderingContext2D['fillStyle']; + strokeColor?: CanvasRenderingContext2D['fillStyle']; bgColor: CanvasRenderingContext2D['fillStyle']; // font: string; fontSize: number; diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index f2ce929dd..28472b9e3 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -424,7 +424,10 @@ export interface PivotTableAPI extends BaseTableAPI { options: PivotTableConstructorOptions; editorManager: EditManeger; // internalProps: PivotTableProtected; - pivotSortState: PivotSortState[]; + pivotSortState: { + dimensions: IDimensionInfo[]; + order: SortOrder; + }[]; isListTable: () => false; isPivotTable: () => true; getPivotSortState: (col: number, row: number) => SortOrder; diff --git a/packages/vtable/src/ts-types/theme.ts b/packages/vtable/src/ts-types/theme.ts index 069debd44..7579e0a78 100644 --- a/packages/vtable/src/ts-types/theme.ts +++ b/packages/vtable/src/ts-types/theme.ts @@ -73,6 +73,8 @@ export type TooltipStyle = { color?: string; padding?: number[]; bgColor?: string; + maxWidth?: number; + maxHeight?: number; /** !目前未实现该逻辑。触发行为:hover or click */ // trigger?: string | string[]; /**气泡框位置,可选 top left right bottom */ diff --git a/packages/vtable/src/ts-types/tooltip.ts b/packages/vtable/src/ts-types/tooltip.ts index ee3633a37..12ff04e68 100644 --- a/packages/vtable/src/ts-types/tooltip.ts +++ b/packages/vtable/src/ts-types/tooltip.ts @@ -26,5 +26,9 @@ export type TooltipOptions = { color?: string; padding?: number[]; arrowMark?: boolean; + maxWidth?: number; + maxHeight?: number; }; + /** 设置tooltip的消失时间 */ + disappearDelay?: number; };