|  | /* | 
|  | * 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; |