This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch PR/plainheart_fix/alignTicks-precision
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit b094f987ddf00b43341c1bf73055c9a8babf9132
Author: 100pah <[email protected]>
AuthorDate: Tue Mar 10 17:39:48 2026 +0800

    fix(toolbox): Fix that toolbox theme cause corresponding icons are always 
displayed even if not required. See #21176 .
---
 src/component/toolbox/ToolboxModel.ts |  51 ++++++++++--
 src/model/Component.ts                |   4 +-
 test/lib/testHelper.js                |   2 +-
 test/toolbox-custom.html              | 153 ++++++++++++++++++++++++++++++++++
 4 files changed, 199 insertions(+), 11 deletions(-)

diff --git a/src/component/toolbox/ToolboxModel.ts 
b/src/component/toolbox/ToolboxModel.ts
index 49debe77b..27037e3ae 100644
--- a/src/component/toolbox/ToolboxModel.ts
+++ b/src/component/toolbox/ToolboxModel.ts
@@ -17,7 +17,6 @@
 * under the License.
 */
 
-import * as zrUtil from 'zrender/src/core/util';
 import * as featureManager from './featureManager';
 import ComponentModel from '../../model/Component';
 import {
@@ -31,9 +30,13 @@ import {
     CommonTooltipOption,
     Dictionary,
     ComponentOnCalendarOptionMixin,
-    ComponentOnMatrixOptionMixin
+    ComponentOnMatrixOptionMixin,
+    NullUndefined
 } from '../../util/types';
 import tokens from '../../visual/tokens';
+import type GlobalModel from '../../model/Global';
+import type Model from '../../model/Model';
+import { each, extend, merge } from 'zrender/src/core/util';
 
 
 export interface ToolboxTooltipFormatterParams {
@@ -93,19 +96,49 @@ class ToolboxModel extends ComponentModel<ToolboxOption> {
         ignoreSize: true
     } as const;
 
-    optionUpdated() {
-        super.optionUpdated.apply(this, arguments as any);
-        const {ecModel} = this;
+    private _themeFeatureOption: ToolboxOption['feature'];
+
+    init(option: ToolboxOption, parentModel: Model, ecModel: GlobalModel): 
void {
+        // An historical behavior:
+        //  An initial ec option
+        //       chart.setOption( {toolbox: {feature: { featureA: {}, 
featureB: {}, }} } )
+        //  indicates the declared toolbox features need to be enabled 
regardless of whether property
+        //  "show" is explicity specified. But the subsequent `setOption` in 
merge mode requires property
+        //  "show: false" to be explicity specified if intending to remove 
features, for example:
+        //       chart.setOption( {toolbox: {feature: { featureA: {show: 
false}, featureC: {} } )
+        // We keep backward compatibility and perform specific processing to 
prevent theme
+        // settings from breaking it.
+        const toolboxOptionInTheme = ecModel.getTheme().get('toolbox');
+        const themeFeatureOption = toolboxOptionInTheme ? 
toolboxOptionInTheme.feature : null;
+        if (themeFeatureOption) {
+            // Use extend - the first level of the feature option will be 
modified later.
+            this._themeFeatureOption = extend({}, themeFeatureOption);
+            toolboxOptionInTheme.feature = {};
+        }
 
-        zrUtil.each(this.option.feature, function (featureOpt, featureName) {
+        super.init(option, parentModel, ecModel); // merge theme is performed 
inside it.
+
+        if (themeFeatureOption) {
+            toolboxOptionInTheme.feature = themeFeatureOption; // Recover
+        }
+    }
+
+    optionUpdated() {
+        each(this.option.feature, function (featureOpt, featureName) {
+            const themeFeatureOption = this._themeFeatureOption;
             const Feature = featureManager.getFeature(featureName);
             if (Feature) {
                 if (Feature.getDefaultOption) {
-                    Feature.defaultOption = Feature.getDefaultOption(ecModel);
+                    Feature.defaultOption = 
Feature.getDefaultOption(this.ecModel);
+                }
+                if (themeFeatureOption && themeFeatureOption[featureName]) {
+                    merge(featureOpt, themeFeatureOption[featureName]);
+                    // Follow the previous behavior, theme is only be merged 
once.
+                    themeFeatureOption[featureName] = null;
                 }
-                zrUtil.merge(featureOpt, Feature.defaultOption);
+                merge(featureOpt, Feature.defaultOption);
             }
-        });
+        }, this);
     }
 
     static defaultOption: ToolboxOption = {
diff --git a/src/model/Component.ts b/src/model/Component.ts
index 3ff62785f..a2a938f25 100644
--- a/src/model/Component.ts
+++ b/src/model/Component.ts
@@ -191,7 +191,9 @@ class ComponentModel<Opt extends ComponentOption = 
ComponentOption> extends Mode
     /**
      * Called immediately after `init` or `mergeOption` of this instance 
called.
      */
-    optionUpdated(newCptOption: Opt, isInit: boolean): void {}
+    optionUpdated(newCptOption: Opt, isInit: boolean): void {
+        // MUST NOT do anything here.
+    }
 
     /**
      * [How to declare defaultOption]:
diff --git a/test/lib/testHelper.js b/test/lib/testHelper.js
index fee04790d..67b95a7bc 100644
--- a/test/lib/testHelper.js
+++ b/test/lib/testHelper.js
@@ -1819,7 +1819,7 @@
             if (theme == null && window.__ECHARTS__DEFAULT__THEME__) {
                 theme = window.__ECHARTS__DEFAULT__THEME__;
             }
-            if (theme) {
+            if (typeof theme === 'string') {
                 require(['theme/' + theme]);
             }
 
diff --git a/test/toolbox-custom.html b/test/toolbox-custom.html
index 5c89fad38..91eee32b1 100644
--- a/test/toolbox-custom.html
+++ b/test/toolbox-custom.html
@@ -30,7 +30,10 @@ under the License.
         <link rel="stylesheet" href="lib/reset.css" />
     </head>
     <body>
+
         <div id="main0"></div>
+        <div id="main_update"></div>
+
 
         <script>
         require(['echarts'], function (echarts) {
@@ -84,6 +87,156 @@ under the License.
         </script>
 
 
+
+
+        <script>
+        require([
+            'echarts',
+        ], function (echarts) {
+            var _ctx = {
+                featureList: [
+                    'saveAsImage',
+                    'myTool1',
+                ],
+            };
+            _ctx.lastFeatureList = _ctx.featureList;
+
+            var MY_THEME = {
+                toolbox: {
+                    feature: {
+                        // dataView panel theme is customized.
+                        dataView: {
+                            backgroundColor: 'blue',
+                            textColor: 'black',
+                            textareaColor: 'green',
+                            textareaBorderColor: 'green',
+                            buttonColor: 'black',
+                            buttonTextColor: 'green'
+                        }
+                    }
+                },
+            };
+
+            var allFeatures = {
+                restore: {},
+                saveAsImage: {},
+                dataZoom: {},
+                dataView: {},
+                myTool1: {
+                    title: 'myTool1',
+                    icon: 
'path://M432.45,595.444c0,2.177-4.661,6.82-11.305,6.82c-6.475,0-11.306-4.567-11.306-6.82s4.852-6.812,11.306-6.812C427.841,588.632,432.452,593.191,432.45,595.444L432.45,595.444z
 
M421.155,589.876c-3.009,0-5.448,2.495-5.448,5.572s2.439,5.572,5.448,5.572c3.01,0,5.449-2.495,5.449-5.572C426.604,592.371,424.165,589.876,421.155,589.876L421.155,589.876z
 
M421.146,591.891c-1.916,0-3.47,1.589-3.47,3.549c0,1.959,1.554,3.548,3.47,3.548s3.469-1.589,3.469-3.548C424.614,593.479,
 [...]
+                    onclick: function (){
+                        alert('myToolHandler1')
+                    }
+                },
+                myTool2: {
+                    show: true,
+                    title: 'myTool2',
+                    icon: 
'image://data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7',
+                    onclick: function () {
+                        alert('myToolHandler2');
+                    }
+                }
+            };
+
+            function createFeaturesOption(addShowProp) {
+                var featureOption = {};
+                var featureList = _ctx.featureList;
+                var lastFeatureList = _ctx.lastFeatureList;
+
+                featureList.forEach(function (featureName) {
+                    if (addShowProp) {
+                        featureOption[featureName] = Object.assign({}, 
allFeatures[featureName]);
+                        featureOption[featureName].show = true;
+                    }
+                    else {
+                        featureOption[featureName] = allFeatures[featureName];
+                    }
+                });
+
+                if (addShowProp) {
+                    lastFeatureList.forEach(function (lastFeatureName) {
+                        if (!featureOption[lastFeatureName]) {
+                            featureOption[lastFeatureName] = {show: false};
+                        }
+                    });
+                }
+
+                return featureOption;
+            }
+
+            function createOption() {
+                var option = {
+                    xAxis: {},
+                    yAxis: {},
+                    series: {
+                        type: 'line',
+                        data: [[11, 22], [33, 44]]
+                    },
+                    toolbox: {
+                        show: true,
+                        right: 20,
+                        feature: createFeaturesOption()
+                    },
+                    animation: false
+                };
+
+                return option;
+            }
+
+            var chart = testHelper.create(echarts, 'main_update', {
+                title: [
+                    'Init toolbox features in a theme where dataView panel is 
customized.',
+                    'In the init state, **dataView** button should not be 
displayed.',
+                ],
+                option: createOption(),
+                theme: MY_THEME,
+                inputsStyle: 'compact',
+                inputs: [{
+                    type: 'select',
+                    text: 'update (merge mode, no "show" specified, expect no 
remove):',
+                    values: [
+                        _ctx.featureList,
+                        [],
+                        ['restore', 'dataZoom'],
+                        ['restore', 'saveAsImage', 'dataView', 'myTool2']
+                    ],
+                    onchange() {
+                        _ctx.lastFeatureList = _ctx.featureList;
+                        _ctx.featureList = this.value;
+                        chart.setOption({
+                            toolbox: {
+                                feature: createFeaturesOption(false),
+                            }
+                        });
+                    }
+                }, {
+                    type: 'br',
+                }, {
+                    type: 'select',
+                    text: 'update (merge mode, "show" specified, expect 
removable):',
+                    values: [
+                        _ctx.featureList,
+                        [],
+                        ['restore', 'dataZoom'],
+                        ['restore', 
'saveAsImage','dataZoom','dataView','myTool1']
+                    ],
+                    onchange() {
+                        _ctx.lastFeatureList = _ctx.featureList;
+                        _ctx.featureList = this.value;
+                        chart.setOption({
+                            toolbox: {
+                                feature: createFeaturesOption(true),
+                            }
+                        });
+                    }
+                }]
+            });
+        });
+        </script>
+
+
+
     </body>
 </html>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to