| /* | 
 | * 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. | 
 | */ | 
 |  | 
 | import ComponentModel from '../../model/Component'; | 
 | import SeriesData from '../../data/SeriesData'; | 
 | import { | 
 |     ComponentOption, | 
 |     BoxLayoutOptionMixin, | 
 |     LayoutOrient, | 
 |     SymbolOptionMixin, | 
 |     LineStyleOption, | 
 |     ItemStyleOption, | 
 |     LabelOption, | 
 |     OptionDataValue, | 
 |     ZRColor, | 
 |     ColorString, | 
 |     CommonTooltipOption, | 
 |     CallbackDataParams, | 
 |     ZREasing | 
 | } from '../../util/types'; | 
 | import Model from '../../model/Model'; | 
 | import GlobalModel, { GlobalModelSetOptionOpts } from '../../model/Global'; | 
 | import { each, isObject, clone } from 'zrender/src/core/util'; | 
 | import { convertOptionIdName, getDataItemValue } from '../../util/model'; | 
 |  | 
 |  | 
 | export interface TimelineControlStyle extends ItemStyleOption { | 
 |     show?: boolean | 
 |     showPlayBtn?: boolean | 
 |     showPrevBtn?: boolean | 
 |     showNextBtn?: boolean | 
 |     itemSize?: number | 
 |     itemGap?: number | 
 |     position?: 'left' | 'right' | 'top' | 'bottom' | 
 |     playIcon?: string | 
 |     stopIcon?: string | 
 |     prevIcon?: string | 
 |     nextIcon?: string | 
 |  | 
 |     // Can be a percent value relative to itemSize | 
 |     playBtnSize?: number | string | 
 |     stopBtnSize?: number | string | 
 |     nextBtnSize?: number | string | 
 |     prevBtnSize?: number | string | 
 | } | 
 |  | 
 | export interface TimelineCheckpointStyle extends ItemStyleOption, | 
 |     SymbolOptionMixin { | 
 |     animation?: boolean | 
 |     animationDuration?: number | 
 |     animationEasing?: ZREasing | 
 | } | 
 |  | 
 | interface TimelineLineStyleOption extends LineStyleOption { | 
 |     show?: boolean | 
 | } | 
 |  | 
 | interface TimelineLabelOption extends Omit<LabelOption, 'position'> { | 
 |     show?: boolean | 
 |     // number can be distance to the timeline axis. sign will determine the side. | 
 |     position?: 'auto' | 'left' | 'right' | 'top' | 'bottom' | number | 
 |     interval?: 'auto' | number | 
 |     formatter?: string | ((value: string | number, index: number) => string) | 
 | } | 
 |  | 
 | export interface TimelineDataItemOption extends SymbolOptionMixin { | 
 |     value?: OptionDataValue | 
 |     itemStyle?: ItemStyleOption | 
 |     label?: TimelineLabelOption | 
 |     checkpointStyle?: TimelineCheckpointStyle | 
 |  | 
 |     emphasis?: { | 
 |         itemStyle?: ItemStyleOption | 
 |         label?: TimelineLabelOption | 
 |         checkpointStyle?: TimelineCheckpointStyle | 
 |     } | 
 |  | 
 |     // Style in progress | 
 |     progress?: { | 
 |         lineStyle?: TimelineLineStyleOption | 
 |         itemStyle?: ItemStyleOption | 
 |         label?: TimelineLabelOption | 
 |     } | 
 |  | 
 |     tooltip?: boolean | 
 | } | 
 |  | 
 | export interface TimelineOption extends ComponentOption, BoxLayoutOptionMixin, SymbolOptionMixin { | 
 |     mainType?: 'timeline' | 
 |  | 
 |     backgroundColor?: ZRColor | 
 |     borderColor?: ColorString | 
 |     borderWidth?: number | 
 |  | 
 |     tooltip?: CommonTooltipOption<CallbackDataParams> & { | 
 |         trigger?: 'item' | 
 |     } | 
 |  | 
 |     show?: boolean | 
 |  | 
 |     axisType?: 'category' | 'time' | 'value' | 
 |  | 
 |     currentIndex?: number | 
 |  | 
 |     autoPlay?: boolean | 
 |  | 
 |     rewind?: boolean | 
 |  | 
 |     loop?: boolean | 
 |  | 
 |     playInterval?: number | 
 |  | 
 |     realtime?: boolean | 
 |  | 
 |     controlPosition?: 'left' | 'right' | 'top' | 'bottom' | 
 |  | 
 |     padding?: number | number[] | 
 |  | 
 |     orient?: LayoutOrient | 
 |  | 
 |     inverse?: boolean | 
 |  | 
 |     // If not specified, options will be changed by "normalMerge". | 
 |     // If specified, options will be changed by "replaceMerge". | 
 |     replaceMerge?: GlobalModelSetOptionOpts['replaceMerge'] | 
 |  | 
 |     lineStyle?: TimelineLineStyleOption | 
 |     itemStyle?: ItemStyleOption | 
 |     checkpointStyle?: TimelineCheckpointStyle | 
 |     controlStyle?: TimelineControlStyle | 
 |     label?: TimelineLabelOption | 
 |  | 
 |     emphasis?: { | 
 |         lineStyle?: TimelineLineStyleOption | 
 |         itemStyle?: ItemStyleOption | 
 |         checkpointStyle?: TimelineCheckpointStyle | 
 |         controlStyle?: TimelineControlStyle | 
 |         label?: TimelineLabelOption | 
 |     } | 
 |  | 
 |  | 
 |     // Style in progress | 
 |     progress?: { | 
 |         lineStyle?: TimelineLineStyleOption | 
 |         itemStyle?: ItemStyleOption | 
 |         label?: TimelineLabelOption | 
 |     } | 
 |  | 
 |     data?: (OptionDataValue | TimelineDataItemOption)[] | 
 | } | 
 | class TimelineModel extends ComponentModel<TimelineOption> { | 
 |  | 
 |     static type = 'timeline'; | 
 |     type = TimelineModel.type; | 
 |  | 
 |     layoutMode = 'box'; | 
 |  | 
 |     private _data: SeriesData<TimelineModel>; | 
 |  | 
 |     private _names: string[]; | 
 |  | 
 |     /** | 
 |      * @override | 
 |      */ | 
 |     init(option: TimelineOption, parentModel: Model, ecModel: GlobalModel) { | 
 |         this.mergeDefaultAndTheme(option, ecModel); | 
 |         this._initData(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * @override | 
 |      */ | 
 |     mergeOption(option: TimelineOption) { | 
 |         super.mergeOption.apply(this, arguments as any); | 
 |         this._initData(); | 
 |     } | 
 |  | 
 |     setCurrentIndex(currentIndex: number) { | 
 |         if (currentIndex == null) { | 
 |             currentIndex = this.option.currentIndex; | 
 |         } | 
 |         const count = this._data.count(); | 
 |  | 
 |         if (this.option.loop) { | 
 |             currentIndex = (currentIndex % count + count) % count; | 
 |         } | 
 |         else { | 
 |             currentIndex >= count && (currentIndex = count - 1); | 
 |             currentIndex < 0 && (currentIndex = 0); | 
 |         } | 
 |  | 
 |         this.option.currentIndex = currentIndex; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {number} currentIndex | 
 |      */ | 
 |     getCurrentIndex() { | 
 |         return this.option.currentIndex; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {boolean} | 
 |      */ | 
 |     isIndexMax() { | 
 |         return this.getCurrentIndex() >= this._data.count() - 1; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @param {boolean} state true: play, false: stop | 
 |      */ | 
 |     setPlayState(state: boolean) { | 
 |         this.option.autoPlay = !!state; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {boolean} true: play, false: stop | 
 |      */ | 
 |     getPlayState() { | 
 |         return !!this.option.autoPlay; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @private | 
 |      */ | 
 |     _initData() { | 
 |         const thisOption = this.option; | 
 |         const dataArr = thisOption.data || []; | 
 |         const axisType = thisOption.axisType; | 
 |         const names: string[] = this._names = []; | 
 |  | 
 |         let processedDataArr: TimelineOption['data']; | 
 |         if (axisType === 'category') { | 
 |             processedDataArr = []; | 
 |             each(dataArr, function (item, index) { | 
 |                 const value = convertOptionIdName(getDataItemValue(item), ''); | 
 |                 let newItem; | 
 |  | 
 |                 if (isObject(item)) { | 
 |                     newItem = clone(item); | 
 |                     (newItem as TimelineDataItemOption).value = index; | 
 |                 } | 
 |                 else { | 
 |                     newItem = index; | 
 |                 } | 
 |  | 
 |                 processedDataArr.push(newItem); | 
 |  | 
 |                 names.push(value); | 
 |             }); | 
 |         } | 
 |         else { | 
 |             processedDataArr = dataArr; | 
 |         } | 
 |  | 
 |         const dimType = ({ | 
 |             category: 'ordinal', | 
 |             time: 'time', | 
 |             value: 'number' | 
 |         })[axisType] || 'number'; | 
 |  | 
 |         const data = this._data = new SeriesData([{ | 
 |             name: 'value', type: dimType | 
 |         }], this); | 
 |  | 
 |         data.initData(processedDataArr, names); | 
 |     } | 
 |  | 
 |     getData() { | 
 |         return this._data; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @public | 
 |      * @return {Array.<string>} categoreis | 
 |      */ | 
 |     getCategories() { | 
 |         if (this.get('axisType') === 'category') { | 
 |             return this._names.slice(); | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * @protected | 
 |      */ | 
 |     static defaultOption: TimelineOption = { | 
 |  | 
 |         // zlevel: 0,                  // 一级层叠 | 
 |         z: 4,                       // 二级层叠 | 
 |         show: true, | 
 |  | 
 |         axisType: 'time',  // 模式是时间类型,支持 value, category | 
 |  | 
 |         realtime: true, | 
 |  | 
 |         left: '20%', | 
 |         top: null, | 
 |         right: '20%', | 
 |         bottom: 0, | 
 |         width: null, | 
 |         height: 40, | 
 |         padding: 5, | 
 |  | 
 |         controlPosition: 'left',           // 'left' 'right' 'top' 'bottom' 'none' | 
 |         autoPlay: false, | 
 |         rewind: false,                     // 反向播放 | 
 |         loop: true, | 
 |         playInterval: 2000,                // 播放时间间隔,单位ms | 
 |  | 
 |         currentIndex: 0, | 
 |  | 
 |         itemStyle: {}, | 
 |         label: { | 
 |             color: '#000' | 
 |         }, | 
 |  | 
 |         data: [] | 
 |     }; | 
 |  | 
 | } | 
 |  | 
 | export default TimelineModel; |