diff --git a/common/changes/@visactor/vtable/fix-issue-4816-functional-icon-theme_2026-07-27-15-36.json b/common/changes/@visactor/vtable/fix-issue-4816-functional-icon-theme_2026-07-27-15-36.json new file mode 100644 index 0000000000..0f68b64d76 --- /dev/null +++ b/common/changes/@visactor/vtable/fix-issue-4816-functional-icon-theme_2026-07-27-15-36.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "fix: refresh functional icons after theme updates", + "type": "patch", + "packageName": "@visactor/vtable" + } + ], + "packageName": "@visactor/vtable", + "email": "biukam.w@gmail.com" +} diff --git a/packages/vtable/__tests__/functional-icons-theme.test.ts b/packages/vtable/__tests__/functional-icons-theme.test.ts new file mode 100644 index 0000000000..97dba2dc48 --- /dev/null +++ b/packages/vtable/__tests__/functional-icons-theme.test.ts @@ -0,0 +1,89 @@ +// @ts-nocheck +import * as VTable from '../src'; +import { createDiv, removeDom } from './dom'; + +global.__VERSION__ = 'none'; + +describe('functional icon theme updates', () => { + let container: HTMLElement; + let table: VTable.ListTable; + + const columns = [{ field: 'name', title: 'Name', sort: true, tree: true }]; + const records = [{ name: 'Parent', children: [{ name: 'Child' }] }]; + + const createTable = () => { + container = createDiv(); + container.style.width = '600px'; + container.style.height = '400px'; + table = new VTable.ListTable(container, { + columns, + records, + rowSeriesNumber: { dragOrder: true }, + theme: { + functionalIconsStyle: { + sort_color: '#111111', + collapse_color: '#222222', + dragReorder_color: '#333333' + } + } + }); + }; + + afterEach(() => { + table?.release(); + removeDom(container); + }); + + test('refreshes functional icons after updateOption', () => { + createTable(); + + table.updateOption({ + columns, + records, + rowSeriesNumber: { dragOrder: true }, + theme: { + functionalIconsStyle: { + sort_color: '#123456', + collapse_color: '#234567', + dragReorder_color: '#345678' + } + } + }); + + expect(table.internalProps.headerHelper.normalIcon.svg).toContain('#123456'); + expect(table.internalProps.bodyHelper.collapseIcon.svg).toContain('#234567'); + expect(table.internalProps.rowSeriesNumberHelper.dragReorderIconName.svg).toContain('#345678'); + }); + + test('refreshes functional icons after updateTheme', () => { + createTable(); + + table.updateTheme({ + functionalIconsStyle: { + sort_color: '#456789', + collapse_color: '#56789a', + dragReorder_color: '#6789ab' + } + }); + + expect(table.internalProps.headerHelper.normalIcon.svg).toContain('#456789'); + expect(table.internalProps.bodyHelper.collapseIcon.svg).toContain('#56789a'); + expect(table.internalProps.rowSeriesNumberHelper.dragReorderIconName.svg).toContain('#6789ab'); + }); + + test('refreshes functional icons after setting theme', () => { + createTable(); + + table.theme = { + functionalIconsStyle: { + sort_color: '#789abc', + collapse_color: '#89abcd', + dragReorder_color: '#9abcde' + } + }; + + expect(table.internalProps.headerHelper.normalIcon.svg).toContain('#789abc'); + expect(table.internalProps.bodyHelper.collapseIcon.svg).toContain('#89abcd'); + expect(table.internalProps.rowSeriesNumberHelper.dragReorderIconName.svg).toContain('#9abcde'); + }); +}); diff --git a/packages/vtable/examples/debug/issue-4816-functional-icons-theme.ts b/packages/vtable/examples/debug/issue-4816-functional-icons-theme.ts new file mode 100644 index 0000000000..64c49a8eea --- /dev/null +++ b/packages/vtable/examples/debug/issue-4816-functional-icons-theme.ts @@ -0,0 +1,133 @@ +import * as VTable from '../../src'; + +const CONTAINER_ID = 'vTable'; + +const columns: VTable.ColumnsDefine = [ + { + field: 'name', + title: 'Name', + width: 240, + sort: true, + tree: true + } +]; + +const records = [ + { + name: 'Parent', + children: [{ name: 'Child' }] + } +]; + +const initialIconColors = { + sort_color: '#111111', + collapse_color: '#222222', + dragReorder_color: '#333333' +}; + +const updatedIconColors = { + sort_color: '#123456', + collapse_color: '#234567', + dragReorder_color: '#345678' +}; + +const removeToolbar = () => { + document.getElementById('issue4816Toolbar')?.remove(); +}; + +const setStatus = (message: string, pass: boolean) => { + const status = document.getElementById('issue4816Status'); + if (!status) { + return; + } + status.textContent = message; + status.style.color = pass ? '#237804' : '#cf1322'; +}; + +const getIconSvgState = (tableInstance: VTable.ListTable) => { + const internalProps = (tableInstance as any).internalProps; + return { + sort: internalProps.headerHelper.normalIcon.svg, + collapse: internalProps.bodyHelper.collapseIcon.svg, + drag: internalProps.rowSeriesNumberHelper.dragReorderIconName.svg + }; +}; + +const includesColors = (iconState: ReturnType, colors: typeof updatedIconColors) => + iconState.sort.includes(colors.sort_color) && + iconState.collapse.includes(colors.collapse_color) && + iconState.drag.includes(colors.dragReorder_color); + +export function createTable() { + removeToolbar(); + + const container = document.getElementById(CONTAINER_ID)!; + container.style.width = '640px'; + container.style.height = '360px'; + + const toolbar = document.createElement('div'); + toolbar.id = 'issue4816Toolbar'; + toolbar.style.cssText = 'display:flex;gap:8px;align-items:center;height:48px;font-size:12px;'; + toolbar.innerHTML = ` + + + 预期:updateOption 后 sort/collapse/drag 图标 SVG 都使用新颜色。 + + `; + container.before(toolbar); + + const createOption = (functionalIconsStyle: typeof initialIconColors): VTable.ListTableConstructorOptions => ({ + columns, + records, + rowSeriesNumber: { dragOrder: true }, + theme: { + functionalIconsStyle + } + }); + + const tableInstance = new VTable.ListTable(container, createOption(initialIconColors)); + + const checkInitial = () => { + const iconState = getIconSvgState(tableInstance); + const pass = includesColors(iconState, initialIconColors); + const sortMatched = iconState.sort.includes(initialIconColors.sort_color); + const collapseMatched = iconState.collapse.includes(initialIconColors.collapse_color); + const dragMatched = iconState.drag.includes(initialIconColors.dragReorder_color); + + setStatus( + `${pass ? 'PASS' : 'FAIL'} | initial sort=${sortMatched}, collapse=${collapseMatched}, drag=${dragMatched}`, + pass + ); + }; + + const updateAndCheck = () => { + tableInstance.updateOption(createOption(updatedIconColors)); + const iconState = getIconSvgState(tableInstance); + const pass = includesColors(iconState, updatedIconColors); + const sortMatched = iconState.sort.includes(updatedIconColors.sort_color); + const collapseMatched = iconState.collapse.includes(updatedIconColors.collapse_color); + const dragMatched = iconState.drag.includes(updatedIconColors.dragReorder_color); + + setStatus( + `${pass ? 'PASS' : 'FAIL'} | updated sort=${sortMatched}, collapse=${collapseMatched}, drag=${dragMatched}`, + pass + ); + }; + + document.getElementById('issue4816CheckInitial')?.addEventListener('click', checkInitial); + document.getElementById('issue4816Update')?.addEventListener('click', updateAndCheck); + + checkInitial(); + + (window as any).tableInstance = tableInstance; + (window as any).issue4816Run = () => { + updateAndCheck(); + return document.getElementById('issue4816Status')?.textContent; + }; + + const release = tableInstance.release.bind(tableInstance); + tableInstance.release = () => { + removeToolbar(); + release(); + }; +} diff --git a/packages/vtable/examples/menu.ts b/packages/vtable/examples/menu.ts index 2474d4e3ae..f0eacdaba4 100644 --- a/packages/vtable/examples/menu.ts +++ b/packages/vtable/examples/menu.ts @@ -54,6 +54,10 @@ export const menus = [ path: 'debug', name: 'issue-5213-row-series-number-aggregation' }, + { + path: 'debug', + name: 'issue-4816-functional-icons-theme' + }, { path: 'debug', name: 'header-frame-border-null-color' diff --git a/packages/vtable/src/body-helper/body-helper.ts b/packages/vtable/src/body-helper/body-helper.ts index 9047a93878..9a5ab4e6b9 100644 --- a/packages/vtable/src/body-helper/body-helper.ts +++ b/packages/vtable/src/body-helper/body-helper.ts @@ -19,6 +19,10 @@ export class BodyHelper { _table: BaseTableAPI; constructor(_table: BaseTableAPI) { this._table = _table; + this.updateIcons(); + } + + updateIcons() { const regedIcons = registerIcons.get(); //展开折叠按钮 this.expandIcon = regedIcons[InternalIconName.expandIconName] as SvgIcon; diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 7230e26571..587f126314 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -503,7 +503,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { internalProps.focusedTable = false; internalProps.theme = themes.of(options.theme ?? themes.DEFAULT); //原来在listTable文件中 internalProps.theme.isPivot = this.isPivotTable(); - setIconColor(internalProps.theme.functionalIconsStyle); + this._updateFunctionalIcons(); if (container) { // 先清空 if (clearDOM) { @@ -2938,7 +2938,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { internalProps.theme = themes.of(options.theme ?? themes.DEFAULT); internalProps.theme.isPivot = this.isPivotTable(); - setIconColor(internalProps.theme.functionalIconsStyle); + this._updateFunctionalIcons(); this.scenegraph.updateStageBackground(); // this._updateSize(); //设置是否自动撑开的配置 @@ -3783,6 +3783,12 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { /** * 获取当前使用的主题 */ + private _updateFunctionalIcons() { + setIconColor(this.internalProps.theme.functionalIconsStyle); + this.internalProps.headerHelper?.updateIcons(); + this.internalProps.bodyHelper?.updateIcons(); + this.internalProps.rowSeriesNumberHelper?.updateIcons(); + } get theme(): TableTheme { return this.internalProps.theme; } @@ -3790,7 +3796,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { this.internalProps.theme = themes.of(theme ?? themes.DEFAULT); this.internalProps.theme.isPivot = this.isPivotTable(); this.options.theme = theme; - setIconColor(this.internalProps.theme.functionalIconsStyle); + this._updateFunctionalIcons(); } /** * 设置主题 @@ -3799,7 +3805,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { const oldHoverState = { col: this.stateManager.hover.cellPos.col, row: this.stateManager.hover.cellPos.row }; this.internalProps.theme = themes.of(theme ?? themes.DEFAULT); this.internalProps.theme.isPivot = this.isPivotTable(); - setIconColor(this.internalProps.theme.functionalIconsStyle); + this._updateFunctionalIcons(); this.options.theme = theme; this.scenegraph.updateComponent(); this.scenegraph.updateStageBackground(); diff --git a/packages/vtable/src/core/row-series-number-helper.ts b/packages/vtable/src/core/row-series-number-helper.ts index da83ab292b..3975a5d3e6 100644 --- a/packages/vtable/src/core/row-series-number-helper.ts +++ b/packages/vtable/src/core/row-series-number-helper.ts @@ -17,6 +17,10 @@ export class RowSeriesNumberHelper { _table: BaseTableAPI; constructor(_table: BaseTableAPI) { this._table = _table; + this.updateIcons(); + } + + updateIcons() { const regedIcons = registerIcons.get(); this.dragReorderIconName = regedIcons[InternalIconName.dragReorderIconName] as SvgIcon; diff --git a/packages/vtable/src/header-helper/header-helper.ts b/packages/vtable/src/header-helper/header-helper.ts index a448fe0033..4c883780f4 100644 --- a/packages/vtable/src/header-helper/header-helper.ts +++ b/packages/vtable/src/header-helper/header-helper.ts @@ -38,6 +38,10 @@ export class HeaderHelper { _table: BaseTableAPI; constructor(_table: BaseTableAPI) { this._table = _table; + this.updateIcons(); + } + + updateIcons() { const regedIcons = registerIcons.get(); //pin默认值 this.freezeIcon = regedIcons[InternalIconName.freezeIconName] as SvgIcon;