| /* | |
| * 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. | |
| */ | |
| // TODO depends on DataZoom and Brush | |
| import * as zrUtil from 'zrender/src/core/util'; | |
| import BrushController, { BrushControllerEvents, BrushDimensionMinMax } from '../../helper/BrushController'; | |
| import BrushTargetManager, { BrushTargetInfoCartesian2D } from '../../helper/BrushTargetManager'; | |
| import * as history from '../../dataZoom/history'; | |
| import sliderMove from '../../helper/sliderMove'; | |
| import { | |
| ToolboxFeature, | |
| ToolboxFeatureModel, | |
| ToolboxFeatureOption | |
| } from '../featureManager'; | |
| import GlobalModel from '../../../model/Global'; | |
| import ExtensionAPI from '../../../core/ExtensionAPI'; | |
| import { Payload, Dictionary, ComponentOption, ItemStyleOption } from '../../../util/types'; | |
| import Cartesian2D from '../../../coord/cartesian/Cartesian2D'; | |
| import CartesianAxisModel from '../../../coord/cartesian/AxisModel'; | |
| import DataZoomModel from '../../dataZoom/DataZoomModel'; | |
| import { | |
| DataZoomPayloadBatchItem, DataZoomAxisDimension | |
| } from '../../dataZoom/helper'; | |
| import { | |
| ModelFinderObject, ModelFinderIndexQuery, makeInternalComponentId, | |
| ModelFinderIdQuery, parseFinder, ParsedModelFinderKnown | |
| } from '../../../util/model'; | |
| import ToolboxModel from '../ToolboxModel'; | |
| import { registerInternalOptionCreator } from '../../../model/internalComponentCreator'; | |
| import ComponentModel from '../../../model/Component'; | |
| const each = zrUtil.each; | |
| const DATA_ZOOM_ID_BASE = makeInternalComponentId('toolbox-dataZoom_'); | |
| const ICON_TYPES = ['zoom', 'back'] as const; | |
| type IconType = typeof ICON_TYPES[number]; | |
| export interface ToolboxDataZoomFeatureOption extends ToolboxFeatureOption { | |
| type?: IconType[] | |
| icon?: {[key in IconType]?: string} | |
| title?: {[key in IconType]?: string} | |
| // TODO: TYPE Use type in dataZoom | |
| filterMode?: 'filter' | 'weakFilter' | 'empty' | 'none' | |
| // Backward compat: false means 'none' | |
| xAxisIndex?: ModelFinderIndexQuery | |
| yAxisIndex?: ModelFinderIndexQuery | |
| xAxisId?: ModelFinderIdQuery | |
| yAxisId?: ModelFinderIdQuery, | |
| brushStyle?: ItemStyleOption | |
| } | |
| type ToolboxDataZoomFeatureModel = ToolboxFeatureModel<ToolboxDataZoomFeatureOption>; | |
| class DataZoomFeature extends ToolboxFeature<ToolboxDataZoomFeatureOption> { | |
| _brushController: BrushController; | |
| _isZoomActive: boolean; | |
| render( | |
| featureModel: ToolboxDataZoomFeatureModel, | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI, | |
| payload: Payload | |
| ) { | |
| if (!this._brushController) { | |
| this._brushController = new BrushController(api.getZr()); | |
| this._brushController.on('brush', zrUtil.bind(this._onBrush, this)) | |
| .mount(); | |
| } | |
| updateZoomBtnStatus(featureModel, ecModel, this, payload, api); | |
| updateBackBtnStatus(featureModel, ecModel); | |
| } | |
| onclick( | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI, | |
| type: IconType | |
| ) { | |
| handlers[type].call(this); | |
| } | |
| remove( | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI | |
| ) { | |
| this._brushController && this._brushController.unmount(); | |
| } | |
| dispose( | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI | |
| ) { | |
| this._brushController && this._brushController.dispose(); | |
| } | |
| private _onBrush(eventParam: BrushControllerEvents['brush']): void { | |
| const areas = eventParam.areas; | |
| if (!eventParam.isEnd || !areas.length) { | |
| return; | |
| } | |
| const snapshot: history.DataZoomStoreSnapshot = {}; | |
| const ecModel = this.ecModel; | |
| this._brushController.updateCovers([]); // remove cover | |
| const brushTargetManager = new BrushTargetManager( | |
| makeAxisFinder(this.model), | |
| ecModel, | |
| {include: ['grid']} | |
| ); | |
| brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys: Cartesian2D) { | |
| if (coordSys.type !== 'cartesian2d') { | |
| return; | |
| } | |
| const brushType = area.brushType; | |
| if (brushType === 'rect') { | |
| setBatch('x', coordSys, (coordRange as BrushDimensionMinMax[])[0]); | |
| setBatch('y', coordSys, (coordRange as BrushDimensionMinMax[])[1]); | |
| } | |
| else { | |
| setBatch( | |
| ({lineX: 'x', lineY: 'y'} as const)[brushType as 'lineX' | 'lineY'], | |
| coordSys, | |
| coordRange as BrushDimensionMinMax | |
| ); | |
| } | |
| }); | |
| history.push(ecModel, snapshot); | |
| this._dispatchZoomAction(snapshot); | |
| function setBatch(dimName: DataZoomAxisDimension, coordSys: Cartesian2D, minMax: number[]) { | |
| const axis = coordSys.getAxis(dimName); | |
| const axisModel = axis.model; | |
| const dataZoomModel = findDataZoom(dimName, axisModel, ecModel); | |
| // Restrict range. | |
| const minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); | |
| if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { | |
| minMax = sliderMove( | |
| 0, minMax.slice(), axis.scale.getExtent(), 0, | |
| minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan | |
| ); | |
| } | |
| dataZoomModel && (snapshot[dataZoomModel.id] = { | |
| dataZoomId: dataZoomModel.id, | |
| startValue: minMax[0], | |
| endValue: minMax[1] | |
| }); | |
| } | |
| function findDataZoom( | |
| dimName: DataZoomAxisDimension, axisModel: CartesianAxisModel, ecModel: GlobalModel | |
| ): DataZoomModel { | |
| let found; | |
| ecModel.eachComponent({mainType: 'dataZoom', subType: 'select'}, function (dzModel: DataZoomModel) { | |
| const has = dzModel.getAxisModel(dimName, axisModel.componentIndex); | |
| has && (found = dzModel); | |
| }); | |
| return found; | |
| } | |
| }; | |
| _dispatchZoomAction(snapshot: history.DataZoomStoreSnapshot): void { | |
| const batch: DataZoomPayloadBatchItem[] = []; | |
| // Convert from hash map to array. | |
| each(snapshot, function (batchItem, dataZoomId) { | |
| batch.push(zrUtil.clone(batchItem)); | |
| }); | |
| batch.length && this.api.dispatchAction({ | |
| type: 'dataZoom', | |
| from: this.uid, | |
| batch: batch | |
| }); | |
| } | |
| static getDefaultOption(ecModel: GlobalModel) { | |
| const defaultOption: ToolboxDataZoomFeatureOption = { | |
| show: true, | |
| filterMode: 'filter', | |
| // Icon group | |
| icon: { | |
| zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1', | |
| back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26' | |
| }, | |
| // `zoom`, `back` | |
| title: ecModel.getLocaleModel().get(['toolbox', 'dataZoom', 'title']), | |
| brushStyle: { | |
| borderWidth: 0, | |
| color: 'rgba(210,219,238,0.2)' | |
| } | |
| }; | |
| return defaultOption; | |
| } | |
| } | |
| const handlers: { [key in IconType]: (this: DataZoomFeature) => void } = { | |
| zoom: function () { | |
| const nextActive = !this._isZoomActive; | |
| this.api.dispatchAction({ | |
| type: 'takeGlobalCursor', | |
| key: 'dataZoomSelect', | |
| dataZoomSelectActive: nextActive | |
| }); | |
| }, | |
| back: function () { | |
| this._dispatchZoomAction(history.pop(this.ecModel)); | |
| } | |
| }; | |
| function makeAxisFinder(dzFeatureModel: ToolboxDataZoomFeatureModel): ModelFinderObject { | |
| const setting = { | |
| xAxisIndex: dzFeatureModel.get('xAxisIndex', true), | |
| yAxisIndex: dzFeatureModel.get('yAxisIndex', true), | |
| xAxisId: dzFeatureModel.get('xAxisId', true), | |
| yAxisId: dzFeatureModel.get('yAxisId', true) | |
| } as ModelFinderObject; | |
| // If both `xAxisIndex` `xAxisId` not set, it means 'all'. | |
| // If both `yAxisIndex` `yAxisId` not set, it means 'all'. | |
| // Some old cases set like this below to close yAxis control but leave xAxis control: | |
| // `{ feature: { dataZoom: { yAxisIndex: false } }`. | |
| if (setting.xAxisIndex == null && setting.xAxisId == null) { | |
| setting.xAxisIndex = 'all'; | |
| } | |
| if (setting.yAxisIndex == null && setting.yAxisId == null) { | |
| setting.yAxisIndex = 'all'; | |
| } | |
| return setting; | |
| } | |
| function updateBackBtnStatus( | |
| featureModel: ToolboxDataZoomFeatureModel, | |
| ecModel: GlobalModel | |
| ) { | |
| featureModel.setIconStatus( | |
| 'back', | |
| history.count(ecModel) > 1 ? 'emphasis' : 'normal' | |
| ); | |
| } | |
| function updateZoomBtnStatus( | |
| featureModel: ToolboxDataZoomFeatureModel, | |
| ecModel: GlobalModel, | |
| view: DataZoomFeature, | |
| payload: Payload, | |
| api: ExtensionAPI | |
| ) { | |
| let zoomActive = view._isZoomActive; | |
| if (payload && payload.type === 'takeGlobalCursor') { | |
| zoomActive = payload.key === 'dataZoomSelect' | |
| ? payload.dataZoomSelectActive : false; | |
| } | |
| view._isZoomActive = zoomActive; | |
| featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal'); | |
| const brushTargetManager = new BrushTargetManager( | |
| makeAxisFinder(featureModel), | |
| ecModel, | |
| {include: ['grid']} | |
| ); | |
| const panels = brushTargetManager.makePanelOpts(api, function (targetInfo: BrushTargetInfoCartesian2D) { | |
| return (targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared) | |
| ? 'lineX' | |
| : (!targetInfo.xAxisDeclared && targetInfo.yAxisDeclared) | |
| ? 'lineY' | |
| : 'rect'; | |
| }); | |
| view._brushController | |
| .setPanels(panels) | |
| .enableBrush( | |
| (zoomActive && panels.length) | |
| ? { | |
| brushType: 'auto', | |
| brushStyle: featureModel.getModel('brushStyle').getItemStyle() | |
| } | |
| : false | |
| ); | |
| } | |
| registerInternalOptionCreator('dataZoom', function (ecModel: GlobalModel): ComponentOption[] { | |
| const toolboxModel = ecModel.getComponent('toolbox', 0) as ToolboxModel; | |
| const featureDataZoomPath = ['feature', 'dataZoom'] as const; | |
| if (!toolboxModel || toolboxModel.get(featureDataZoomPath) == null) { | |
| return; | |
| } | |
| const dzFeatureModel = toolboxModel.getModel(featureDataZoomPath as any) as ToolboxDataZoomFeatureModel; | |
| const dzOptions = [] as ComponentOption[]; | |
| const finder = makeAxisFinder(dzFeatureModel); | |
| const finderResult = parseFinder(ecModel, finder) as ParsedModelFinderKnown; | |
| each(finderResult.xAxisModels, axisModel => buildInternalOptions(axisModel, 'xAxis', 'xAxisIndex')); | |
| each(finderResult.yAxisModels, axisModel => buildInternalOptions(axisModel, 'yAxis', 'yAxisIndex')); | |
| function buildInternalOptions( | |
| axisModel: ComponentModel, | |
| axisMainType: 'xAxis' | 'yAxis', | |
| axisIndexPropName: 'xAxisIndex' | 'yAxisIndex' | |
| ) { | |
| const axisIndex = axisModel.componentIndex; | |
| const newOpt = { | |
| type: 'select', | |
| $fromToolbox: true, | |
| // Default to be filter | |
| filterMode: dzFeatureModel.get('filterMode', true) || 'filter', | |
| // Id for merge mapping. | |
| id: DATA_ZOOM_ID_BASE + axisMainType + axisIndex | |
| } as Dictionary<unknown>; | |
| newOpt[axisIndexPropName] = axisIndex; | |
| dzOptions.push(newOpt); | |
| } | |
| return dzOptions; | |
| }); | |
| export default DataZoomFeature; |