DataZoomModel.ts 23.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

S
sushuang 已提交
20
import {__DEV__} from '../../config';
S
sushuang 已提交
21 22
import * as zrUtil from 'zrender/src/core/util';
import env from 'zrender/src/core/env';
S
sushuang 已提交
23 24 25
import * as modelUtil from '../../util/model';
import * as helper from './helper';
import AxisProxy from './AxisProxy';
P
pissang 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
import ComponentModel from '../../model/Component';
import {
    LayoutOrient,
    ComponentOption,
    Dictionary,
    LabelOption,
    SeriesOption,
    SeriesOnCartesianOptionMixin,
    SeriesOnPolarOptionMixin,
    SeriesOnSingleOptionMixin
} from '../../util/types';
import Model from '../../model/Model';
import GlobalModel from '../../model/Global';
import SeriesModel from '../../model/Series';
import { AxisBaseModel } from '../../coord/AxisBaseModel';
import { OptionAxisType, AxisBaseOption } from '../../coord/axisCommonTypes';
S
sushuang 已提交
42

S
sushuang 已提交
43 44 45
var each = zrUtil.each;
var eachAxisDim = helper.eachAxisDim;

P
pissang 已提交
46
export interface DataZoomOption extends ComponentOption {
S
sushuang 已提交
47

P
pissang 已提交
48 49 50 51
    /**
     * Default auto by axisIndex
     */
    orient?: LayoutOrient
S
sushuang 已提交
52

P
pissang 已提交
53 54 55 56
    /**
     * Default the first horizontal category axis.
     */
    xAxisIndex?: number | number[]
S
sushuang 已提交
57 58

    /**
P
pissang 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
     * Default the first vertical category axis.
     */
    yAxisIndex?: number | number[]

    radiusAxisIndex?: number | number[]
    angleAxisIndex?: number | number[]

    singleAxisIndex?: number | number[]

    /**
     * Possible values: 'filter' or 'empty' or 'weakFilter'.
     * 'filter': data items which are out of window will be removed. This option is
     *         applicable when filtering outliers. For each data item, it will be
     *         filtered if one of the relevant dimensions is out of the window.
     * 'weakFilter': data items which are out of window will be removed. This option
     *         is applicable when filtering outliers. For each data item, it will be
     *         filtered only if all  of the relevant dimensions are out of the same
     *         side of the window.
     * 'empty': data items which are out of window will be set to empty.
     *         This option is applicable when user should not neglect
     *         that there are some data items out of window.
     * 'none': Do not filter.
     * Taking line chart as an example, line will be broken in
     * the filtered points when filterModel is set to 'empty', but
     * be connected when set to 'filter'.
     */
    filterMode?: 'filter' | 'weakFilter' | 'empty' | 'none'

    /**
     * Dispatch action by the fixed rate, avoid frequency.
     * default 100. Do not throttle when use null/undefined.
     * If animation === true and animationDurationUpdate > 0,
     * default value is 100, otherwise 20.
S
sushuang 已提交
92
     */
P
pissang 已提交
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
    throttle?: number | null | undefined
    /**
     * Start percent. 0 ~ 100
     */
    start?: number
    /**
     * End percent. 0 ~ 100
     */
    end?: number
    /**
     * Start value. If startValue specified, start is ignored
     */
    startValue?: number
    /**
     * End value. If endValue specified, end is ignored.
     */
    endValue?: number
    /**
     * Min span percent, 0 - 100
     * The range of dataZoom can not be smaller than that.
     */
    minSpan?: number
    /**
     * Max span percent, 0 - 100
     * The range of dataZoom can not be larger than that.
     */
    maxSpan?: number

    minValueSpan?: number

    maxValueSpan?: number

    rangeMode?: ['value' | 'percent', 'value' | 'percent']

    realtime?: boolean

    // Available when type is slider
    textStyle?: LabelOption
}

type RangeOption = Pick<DataZoomOption, 'start' | 'end' | 'startValue' | 'endValue'>

type ExtendedAxisBaseModel = AxisBaseModel & {
    __dzAxisProxy: AxisProxy
}

interface SeriesModelOnAxis extends SeriesModel<
    SeriesOption & SeriesOnCartesianOptionMixin & SeriesOnPolarOptionMixin & SeriesOnSingleOptionMixin
> {}

class DataZoomModel<Opts extends DataZoomOption = DataZoomOption> extends ComponentModel<Opts> {
    static type = 'dataZoom'
    type = DataZoomModel.type

    static dependencies = [
        'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series'
    ]


    static defaultOption: DataZoomOption = {
S
sushuang 已提交
153 154
        zlevel: 0,
        z: 4,                   // Higher than normal component (z: 2).
P
pissang 已提交
155 156 157 158 159 160

        filterMode: 'filter',

        start: 0,
        end: 100
    }
S
sushuang 已提交
161 162

    /**
P
pissang 已提交
163
     * key like x_0, y_1
S
sushuang 已提交
164
     */
P
pissang 已提交
165
    private _dataIntervalByAxis: Dictionary<[number, number]> = {}
P
pah100 已提交
166

P
pissang 已提交
167
    private _dataInfo = {}
P
pah100 已提交
168

P
pissang 已提交
169
    private _axisProxies: Dictionary<AxisProxy> = {}
P
pah100 已提交
170

P
pissang 已提交
171
    private _autoThrottle = true
P
pah100 已提交
172

P
pissang 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    /**
     * It is `[rangeModeForMin, rangeModeForMax]`.
     * The optional values for `rangeMode`:
     * + `'value'` mode: the axis extent will always be determined by
     *     `dataZoom.startValue` and `dataZoom.endValue`, despite
     *     how data like and how `axis.min` and `axis.max` are.
     * + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`,
     *     where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`,
     *     and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`.
     *     Axis extent will be determined by the result of the percent of `[dMin, dMax]`.
     *
     * For example, when users are using dynamic data (update data periodically via `setOption`),
     * if in `'value`' mode, the window will be kept in a fixed value range despite how
     * data are appended, while if in `'percent'` mode, whe window range will be changed alone with
     * the appended data (suppose `axis.min` and `axis.max` are not specified).
     */
    private _rangePropMode: DataZoomOption['rangeMode'] = ['percent', 'percent']
190

P
pissang 已提交
191 192 193 194 195
    textStyleModel: Model<DataZoomOption['textStyle']>

    settledOption: Opts

    init(option: Opts, parentModel: Model, ecModel: GlobalModel) {
P
pah100 已提交
196

S
sushuang 已提交
197
        this._rangePropMode = ['percent', 'percent'];
P
pah100 已提交
198

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        var inputRawOption = retrieveRawOption(option);

        /**
         * Suppose a "main process" start at the point that model prepared (that is,
         * model initialized or merged or method called in `action`).
         * We should keep the `main process` idempotent, that is, given a set of values
         * on `option`, we get the same result.
         *
         * But sometimes, values on `option` will be updated for providing users
         * a "final calculated value" (`dataZoomProcessor` will do that). Those value
         * should not be the base/input of the `main process`.
         *
         * So in that case we should save and keep the input of the `main process`
         * separately, called `settledOption`.
         *
         * For example, consider the case:
         * (Step_1) brush zoom the grid by `toolbox.dataZoom`,
         *     where the original input `option.startValue`, `option.endValue` are earsed by
         *     calculated value.
         * (Step)2) click the legend to hide and show a series,
         *     where the new range is calculated by the earsed `startValue` and `endValue`,
         *     which brings incorrect result.
         *
         * @readOnly
         */
        this.settledOption = inputRawOption;
225

S
sushuang 已提交
226
        this.mergeDefaultAndTheme(option, ecModel);
P
pah100 已提交
227

228
        this.doInit(inputRawOption);
P
pissang 已提交
229
    }
230

S
sushuang 已提交
231 232 233
    /**
     * @override
     */
P
pissang 已提交
234
    mergeOption(newOption: Opts) {
235
        var inputRawOption = retrieveRawOption(newOption);
P
pah100 已提交
236

S
sushuang 已提交
237 238
        //FIX #2591
        zrUtil.merge(this.option, newOption, true);
239
        zrUtil.merge(this.settledOption, inputRawOption, true);
P
pah100 已提交
240

241
        this.doInit(inputRawOption);
P
pissang 已提交
242
    }
P
pah100 已提交
243

S
sushuang 已提交
244 245 246
    /**
     * @protected
     */
P
pissang 已提交
247
    doInit(inputRawOption: Opts) {
S
sushuang 已提交
248
        var thisOption = this.option;
249

S
sushuang 已提交
250 251 252 253
        // Disable realtime view update if canvas is not supported.
        if (!env.canvasSupported) {
            thisOption.realtime = false;
        }
P
pah100 已提交
254

255
        this._setDefaultThrottle(inputRawOption);
P
pah100 已提交
256

P
pissang 已提交
257
        this._updateRangeUse(inputRawOption);
258

259
        var settledOption = this.settledOption;
P
pissang 已提交
260
        each([['start', 'startValue'], ['end', 'endValue']] as const, function (names, index) {
S
sushuang 已提交
261 262 263 264
            // start/end has higher priority over startValue/endValue if they
            // both set, but we should make chart.setOption({endValue: 1000})
            // effective, rather than chart.setOption({endValue: 1000, end: null}).
            if (this._rangePropMode[index] === 'value') {
265
                thisOption[names[0]] = settledOption[names[0]] = null;
266
            }
S
sushuang 已提交
267 268
            // Otherwise do nothing and use the merge result.
        }, this);
P
pah100 已提交
269

S
sushuang 已提交
270
        this.textStyleModel = this.getModel('textStyle');
P
pah100 已提交
271

S
sushuang 已提交
272
        this._resetTarget();
P
pah100 已提交
273

S
sushuang 已提交
274
        this._giveAxisProxies();
P
pissang 已提交
275
    }
P
pah100 已提交
276

P
pissang 已提交
277
    private _giveAxisProxies() {
S
sushuang 已提交
278 279 280 281 282 283
        var axisProxies = this._axisProxies;

        this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) {
            var axisModel = this.dependentModels[dimNames.axis][axisIndex];

            // If exists, share axisProxy with other dataZoomModels.
P
pissang 已提交
284
            var axisProxy = (axisModel as ExtendedAxisBaseModel).__dzAxisProxy || (
S
sushuang 已提交
285
                // Use the first dataZoomModel as the main model of axisProxy.
P
pissang 已提交
286
                (axisModel as ExtendedAxisBaseModel).__dzAxisProxy = new AxisProxy(
S
sushuang 已提交
287 288 289
                    dimNames.name, axisIndex, this, ecModel
                )
            );
P
pah100 已提交
290
            // FIXME
S
sushuang 已提交
291
            // dispose __dzAxisProxy
292

S
sushuang 已提交
293 294
            axisProxies[dimNames.name + '_' + axisIndex] = axisProxy;
        }, this);
P
pissang 已提交
295
    }
296

P
pissang 已提交
297
    private _resetTarget() {
S
sushuang 已提交
298
        var thisOption = this.option;
P
pah100 已提交
299

S
sushuang 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
        var autoMode = this._judgeAutoMode();

        eachAxisDim(function (dimNames) {
            var axisIndexName = dimNames.axisIndex;
            thisOption[axisIndexName] = modelUtil.normalizeToArray(
                thisOption[axisIndexName]
            );
        }, this);

        if (autoMode === 'axisIndex') {
            this._autoSetAxisIndex();
        }
        else if (autoMode === 'orient') {
            this._autoSetOrient();
        }
P
pissang 已提交
315
    }
S
sushuang 已提交
316

P
pissang 已提交
317
    private _judgeAutoMode() {
S
sushuang 已提交
318 319 320 321 322 323 324 325 326 327 328 329
        // Auto set only works for setOption at the first time.
        // The following is user's reponsibility. So using merged
        // option is OK.
        var thisOption = this.option;

        var hasIndexSpecified = false;
        eachAxisDim(function (dimNames) {
            // When user set axisIndex as a empty array, we think that user specify axisIndex
            // but do not want use auto mode. Because empty array may be encountered when
            // some error occured.
            if (thisOption[dimNames.axisIndex] != null) {
                hasIndexSpecified = true;
P
pah100 已提交
330
            }
S
sushuang 已提交
331 332 333 334 335 336 337 338 339 340 341 342 343
        }, this);

        var orient = thisOption.orient;

        if (orient == null && hasIndexSpecified) {
            return 'orient';
        }
        else if (!hasIndexSpecified) {
            if (orient == null) {
                thisOption.orient = 'horizontal';
            }
            return 'axisIndex';
        }
P
pissang 已提交
344
    }
S
sushuang 已提交
345

P
pissang 已提交
346
    private _autoSetAxisIndex() {
S
sushuang 已提交
347 348 349 350 351 352 353 354 355 356
        var autoAxisIndex = true;
        var orient = this.get('orient', true);
        var thisOption = this.option;
        var dependentModels = this.dependentModels;

        if (autoAxisIndex) {
            // Find axis that parallel to dataZoom as default.
            var dimName = orient === 'vertical' ? 'y' : 'x';

            if (dependentModels[dimName + 'Axis'].length) {
P
pissang 已提交
357
                thisOption[dimName + 'AxisIndex' as 'xAxisIndex' | 'yAxisIndex'] = [0];
S
sushuang 已提交
358
                autoAxisIndex = false;
P
pah100 已提交
359
            }
S
sushuang 已提交
360
            else {
P
pissang 已提交
361 362 363
                each(dependentModels.singleAxis, function (
                    singleAxisModel: AxisBaseModel<{'orient': LayoutOrient} & AxisBaseOption>
                ) {
S
sushuang 已提交
364 365 366 367 368 369 370
                    if (autoAxisIndex && singleAxisModel.get('orient', true) === orient) {
                        thisOption.singleAxisIndex = [singleAxisModel.componentIndex];
                        autoAxisIndex = false;
                    }
                });
            }
        }
P
pah100 已提交
371

S
sushuang 已提交
372 373
        if (autoAxisIndex) {
            // Find the first category axis as default. (consider polar)
374
            eachAxisDim(function (dimNames) {
S
sushuang 已提交
375 376 377 378 379 380 381 382 383 384
                if (!autoAxisIndex) {
                    return;
                }
                var axisIndices = [];
                var axisModels = this.dependentModels[dimNames.axis];
                if (axisModels.length && !axisIndices.length) {
                    for (var i = 0, len = axisModels.length; i < len; i++) {
                        if (axisModels[i].get('type') === 'category') {
                            axisIndices.push(i);
                        }
P
pah100 已提交
385 386
                    }
                }
S
sushuang 已提交
387 388 389 390
                thisOption[dimNames.axisIndex] = axisIndices;
                if (axisIndices.length) {
                    autoAxisIndex = false;
                }
P
pah100 已提交
391
            }, this);
S
sushuang 已提交
392
        }
P
pah100 已提交
393

S
sushuang 已提交
394 395 396 397 398 399 400 401
        if (autoAxisIndex) {
            // FIXME
            // 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制),
            // 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)?

            // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified,
            // dataZoom component auto adopts series that reference to
            // both xAxis and yAxis which type is 'value'.
P
pissang 已提交
402
            this.ecModel.eachSeries(function (seriesModel: SeriesModelOnAxis) {
S
sushuang 已提交
403 404
                if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) {
                    eachAxisDim(function (dimNames) {
P
pissang 已提交
405
                        var axisIndices = thisOption[dimNames.axisIndex] as number[]; // Has been normalized to array
S
sushuang 已提交
406 407 408 409 410 411 412 413 414 415 416 417 418

                        var axisIndex = seriesModel.get(dimNames.axisIndex);
                        var axisId = seriesModel.get(dimNames.axisId);

                        var axisModel = seriesModel.ecModel.queryComponents({
                            mainType: dimNames.axis,
                            index: axisIndex,
                            id: axisId
                        })[0];

                        if (__DEV__) {
                            if (!axisModel) {
                                throw new Error(
P
pissang 已提交
419
                                    dimNames.axis + ' "' + zrUtil.retrieve<number | string>(
S
sushuang 已提交
420 421 422 423 424 425 426 427
                                        axisIndex,
                                        axisId,
                                        0
                                    ) + '" not found'
                                );
                            }
                        }
                        axisIndex = axisModel.componentIndex;
428

S
sushuang 已提交
429 430 431 432 433
                        if (zrUtil.indexOf(axisIndices, axisIndex) < 0) {
                            axisIndices.push(axisIndex);
                        }
                    });
                }
P
pah100 已提交
434
            }, this);
S
sushuang 已提交
435
        }
P
pissang 已提交
436
    }
S
sushuang 已提交
437

P
pissang 已提交
438 439
    private _autoSetOrient() {
        var dim: string;
S
sushuang 已提交
440 441 442 443 444 445 446

        // Find the first axis
        this.eachTargetAxis(function (dimNames) {
            !dim && (dim = dimNames.name);
        }, this);

        this.option.orient = dim === 'y' ? 'vertical' : 'horizontal';
P
pissang 已提交
447
    }
S
sushuang 已提交
448

P
pissang 已提交
449
    private _isSeriesHasAllAxesTypeOf(seriesModel: SeriesModelOnAxis, axisType: OptionAxisType) {
S
sushuang 已提交
450 451 452 453 454 455 456 457 458 459 460
        // FIXME
        // 需要series的xAxisIndex和yAxisIndex都首先自动设置上。
        // 例如series.type === scatter时。

        var is = true;
        eachAxisDim(function (dimNames) {
            var seriesAxisIndex = seriesModel.get(dimNames.axisIndex);
            var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex];

            if (!axisModel || axisModel.get('type') !== axisType) {
                is = false;
P
pah100 已提交
461
            }
S
sushuang 已提交
462 463
        }, this);
        return is;
P
pissang 已提交
464
    }
S
sushuang 已提交
465

P
pissang 已提交
466
    private _setDefaultThrottle(inputRawOption: DataZoomOption) {
S
sushuang 已提交
467
        // When first time user set throttle, auto throttle ends.
468
        if (inputRawOption.hasOwnProperty('throttle')) {
S
sushuang 已提交
469 470 471 472
            this._autoThrottle = false;
        }
        if (this._autoThrottle) {
            var globalOption = this.ecModel.option;
S
sushuang 已提交
473 474 475
            this.option.throttle = (
                globalOption.animation && globalOption.animationDurationUpdate > 0
            ) ? 100 : 20;
S
sushuang 已提交
476
        }
P
pissang 已提交
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
    }

    private _updateRangeUse(inputRawOption: RangeOption) {
        var rangePropMode = this._rangePropMode;
        var rangeModeInOption = this.get('rangeMode');

        each([['start', 'startValue'], ['end', 'endValue']] as const, function (names, index) {
            var percentSpecified = inputRawOption[names[0]] != null;
            var valueSpecified = inputRawOption[names[1]] != null;
            if (percentSpecified && !valueSpecified) {
                rangePropMode[index] = 'percent';
            }
            else if (!percentSpecified && valueSpecified) {
                rangePropMode[index] = 'value';
            }
            else if (rangeModeInOption) {
                rangePropMode[index] = rangeModeInOption[index];
            }
            else if (percentSpecified) { // percentSpecified && valueSpecified
                rangePropMode[index] = 'percent';
            }
            // else remain its original setting.
        });
    }
S
sushuang 已提交
501 502 503 504

    /**
     * @public
     */
P
pissang 已提交
505 506
    getFirstTargetAxisModel() {
        var firstAxisModel: AxisBaseModel;
S
sushuang 已提交
507 508
        eachAxisDim(function (dimNames) {
            if (firstAxisModel == null) {
P
pissang 已提交
509
                var indices = this.get(dimNames.axisIndex) as number[]; // Has been normalized to array
S
sushuang 已提交
510
                if (indices.length) {
P
pissang 已提交
511
                    firstAxisModel = this.dependentModels[dimNames.axis][indices[0]] as AxisBaseModel;
P
pah100 已提交
512 513
                }
            }
S
sushuang 已提交
514 515 516
        }, this);

        return firstAxisModel;
P
pissang 已提交
517
    }
S
sushuang 已提交
518 519 520 521 522

    /**
     * @public
     * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel
     */
P
pissang 已提交
523 524 525 526 527 528 529 530 531 532
    eachTargetAxis<Ctx>(
        callback: (
            this: Ctx,
            dimNames: Parameters<Parameters<typeof eachAxisDim>[0]>[0],
            axisIndex: number,
            dataZoomModel: this,
            ecModel: GlobalModel
        ) => void,
        context?: Ctx
    ) {
S
sushuang 已提交
533 534 535
        var ecModel = this.ecModel;
        eachAxisDim(function (dimNames) {
            each(
P
pissang 已提交
536
                this.get(dimNames.axisIndex) as number[],   // Has been normalized to array
S
sushuang 已提交
537 538 539 540 541 542
                function (axisIndex) {
                    callback.call(context, dimNames, axisIndex, this, ecModel);
                },
                this
            );
        }, this);
P
pissang 已提交
543
    }
S
sushuang 已提交
544 545

    /**
P
pissang 已提交
546
     * @return If not found, return null/undefined.
S
sushuang 已提交
547
     */
P
pissang 已提交
548
    getAxisProxy(dimName: string, axisIndex: number): AxisProxy {
S
sushuang 已提交
549
        return this._axisProxies[dimName + '_' + axisIndex];
P
pissang 已提交
550
    }
S
sushuang 已提交
551 552

    /**
P
pissang 已提交
553
     * @return If not found, return null/undefined.
S
sushuang 已提交
554
     */
P
pissang 已提交
555
    getAxisModel(dimName: string, axisIndex: number): AxisBaseModel {
S
sushuang 已提交
556 557
        var axisProxy = this.getAxisProxy(dimName, axisIndex);
        return axisProxy && axisProxy.getAxisModel();
P
pissang 已提交
558
    }
S
sushuang 已提交
559 560 561 562

    /**
     * If not specified, set to undefined.
     */
P
pissang 已提交
563
    setRawRange(opt: RangeOption) {
564 565
        var thisOption = this.option;
        var settledOption = this.settledOption;
P
pissang 已提交
566
        each([['start', 'startValue'], ['end', 'endValue']] as const, function (names) {
567 568 569 570 571 572 573 574 575
            // Consider the pair <start, startValue>:
            // If one has value and the other one is `null/undefined`, we both set them
            // to `settledOption`. This strategy enables the feature to clear the original
            // value in `settledOption` to `null/undefined`.
            // But if both of them are `null/undefined`, we do not set them to `settledOption`
            // and keep `settledOption` with the original value. This strategy enables users to
            // only set <end or endValue> but not set <start or startValue> when calling
            // `dispatchAction`.
            // The pair <end, endValue> is treated in the same way.
S
sushuang 已提交
576
            if (opt[names[0]] != null || opt[names[1]] != null) {
577 578
                thisOption[names[0]] = settledOption[names[0]] = opt[names[0]];
                thisOption[names[1]] = settledOption[names[1]] = opt[names[1]];
S
sushuang 已提交
579 580
            }
        }, this);
S
sushuang 已提交
581

P
pissang 已提交
582 583
        this._updateRangeUse(opt);
    }
584

P
pissang 已提交
585
    setCalculatedRange(opt: RangeOption) {
586
        var option = this.option;
P
pissang 已提交
587
        each(['start', 'startValue', 'end', 'endValue'] as const, function (name) {
588 589
            option[name] = opt[name];
        });
P
pissang 已提交
590
    }
S
sushuang 已提交
591 592 593 594 595

    /**
     * @public
     * @return {Array.<number>} [startPercent, endPercent]
     */
P
pissang 已提交
596
    getPercentRange() {
S
sushuang 已提交
597 598 599 600
        var axisProxy = this.findRepresentativeAxisProxy();
        if (axisProxy) {
            return axisProxy.getDataPercentWindow();
        }
P
pissang 已提交
601
    }
S
sushuang 已提交
602 603 604 605 606

    /**
     * @public
     * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0);
     *
P
pissang 已提交
607
     * @return [startValue, endValue] value can only be '-' or finite number.
S
sushuang 已提交
608
     */
P
pissang 已提交
609
    getValueRange(axisDimName: string, axisIndex: number): [number, number] {
S
sushuang 已提交
610 611 612 613
        if (axisDimName == null && axisIndex == null) {
            var axisProxy = this.findRepresentativeAxisProxy();
            if (axisProxy) {
                return axisProxy.getDataValueWindow();
614
            }
S
sushuang 已提交
615 616 617 618
        }
        else {
            return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow();
        }
P
pissang 已提交
619
    }
S
sushuang 已提交
620 621 622

    /**
     * @public
P
pissang 已提交
623
     * @param axisModel If axisModel given, find axisProxy
S
sushuang 已提交
624 625
     *      corresponding to the axisModel
     */
P
pissang 已提交
626
    findRepresentativeAxisProxy(axisModel?: AxisBaseModel): AxisProxy {
S
sushuang 已提交
627
        if (axisModel) {
P
pissang 已提交
628
            return (axisModel as ExtendedAxisBaseModel).__dzAxisProxy;
S
sushuang 已提交
629
        }
630

S
sushuang 已提交
631 632 633 634 635
        // Find the first hosted axisProxy
        var axisProxies = this._axisProxies;
        for (var key in axisProxies) {
            if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) {
                return axisProxies[key];
636
            }
S
sushuang 已提交
637
        }
P
pah100 已提交
638

S
sushuang 已提交
639 640 641 642 643 644 645 646
        // If no hosted axis find not hosted axisProxy.
        // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis,
        // and the option.start or option.end settings are different. The percentRange
        // should follow axisProxy.
        // (We encounter this problem in toolbox data zoom.)
        for (var key in axisProxies) {
            if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) {
                return axisProxies[key];
647
            }
648
        }
P
pissang 已提交
649
    }
P
pah100 已提交
650

S
sushuang 已提交
651 652 653
    /**
     * @return {Array.<string>}
     */
P
pissang 已提交
654
    getRangePropMode() {
S
sushuang 已提交
655
        return this._rangePropMode.slice();
656 657
    }

P
pissang 已提交
658
}
659 660 661 662 663
/**
 * Retrieve the those raw params from option, which will be cached separately.
 * becasue they will be overwritten by normalized/calculated values in the main
 * process.
 */
P
pissang 已提交
664 665
function retrieveRawOption<T extends DataZoomOption>(option: T) {
    var ret = {} as T;
S
sushuang 已提交
666
    each(
P
pissang 已提交
667
        ['start', 'end', 'startValue', 'endValue', 'throttle'] as const,
S
sushuang 已提交
668 669 670 671 672 673 674
        function (name) {
            option.hasOwnProperty(name) && (ret[name] = option[name]);
        }
    );
    return ret;
}

P
pissang 已提交
675
ComponentModel.registerClass(DataZoomModel);
P
pah100 已提交
676

S
sushuang 已提交
677
export default DataZoomModel;