| /* |
| * 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 * as zrUtil from 'zrender/src/core/util'; |
| import * as graphic from '../../util/graphic'; |
| import {getECData} from '../../util/innerStore'; |
| import {createTextStyle} from '../../label/labelStyle'; |
| import {getLayoutRect} from '../../util/layout'; |
| import ComponentModel from '../../model/Component'; |
| import { |
| ComponentOption, |
| BoxLayoutOptionMixin, |
| ZRTextAlign, |
| ZRTextVerticalAlign, |
| ZRColor, |
| BorderOptionMixin, |
| LabelOption |
| } from '../../util/types'; |
| import ComponentView from '../../view/Component'; |
| import GlobalModel from '../../model/Global'; |
| import ExtensionAPI from '../../core/ExtensionAPI'; |
| import {windowOpen} from '../../util/format'; |
| import { EChartsExtensionInstallRegisters } from '../../extension'; |
| |
| |
| export interface TitleOption extends ComponentOption, BoxLayoutOptionMixin, BorderOptionMixin { |
| |
| mainType?: 'title' |
| |
| show?: boolean |
| |
| text?: string |
| /** |
| * Link to url |
| */ |
| link?: string |
| target?: 'self' | 'blank' |
| |
| subtext?: string |
| sublink?: string |
| subtarget?: 'self' | 'blank' |
| |
| textAlign?: ZRTextAlign |
| textVerticalAlign?: ZRTextVerticalAlign |
| |
| /** |
| * @deprecated Use textVerticalAlign instead |
| */ |
| textBaseline?: ZRTextVerticalAlign |
| |
| backgroundColor?: ZRColor |
| /** |
| * Padding between text and border. |
| * Support to be a single number or an array. |
| */ |
| padding?: number | number[] |
| /** |
| * Gap between text and subtext |
| */ |
| itemGap?: number |
| |
| textStyle?: LabelOption |
| |
| subtextStyle?: LabelOption |
| |
| /** |
| * If trigger mouse or touch event |
| */ |
| triggerEvent?: boolean |
| |
| /** |
| * Radius of background border. |
| */ |
| borderRadius?: number | number[] |
| } |
| class TitleModel extends ComponentModel<TitleOption> { |
| static type = 'title' as const; |
| type = TitleModel.type; |
| |
| readonly layoutMode = {type: 'box', ignoreSize: true} as const; |
| |
| static defaultOption: TitleOption = { |
| zlevel: 0, |
| z: 6, |
| show: true, |
| |
| text: '', |
| target: 'blank', |
| subtext: '', |
| |
| subtarget: 'blank', |
| |
| left: 0, |
| top: 0, |
| |
| backgroundColor: 'rgba(0,0,0,0)', |
| |
| borderColor: '#ccc', |
| |
| borderWidth: 0, |
| |
| padding: 5, |
| |
| itemGap: 10, |
| textStyle: { |
| fontSize: 18, |
| fontWeight: 'bold', |
| color: '#464646' |
| }, |
| subtextStyle: { |
| fontSize: 12, |
| color: '#6E7079' |
| } |
| }; |
| } |
| |
| |
| // View |
| class TitleView extends ComponentView { |
| |
| static type = 'title' as const; |
| type = TitleView.type; |
| |
| |
| render(titleModel: TitleModel, ecModel: GlobalModel, api: ExtensionAPI) { |
| this.group.removeAll(); |
| |
| if (!titleModel.get('show')) { |
| return; |
| } |
| |
| const group = this.group; |
| |
| const textStyleModel = titleModel.getModel('textStyle'); |
| const subtextStyleModel = titleModel.getModel('subtextStyle'); |
| |
| let textAlign = titleModel.get('textAlign'); |
| let textVerticalAlign = zrUtil.retrieve2( |
| titleModel.get('textBaseline'), titleModel.get('textVerticalAlign') |
| ); |
| |
| const textEl = new graphic.Text({ |
| style: createTextStyle(textStyleModel, { |
| text: titleModel.get('text'), |
| fill: textStyleModel.getTextColor() |
| }, {disableBox: true}), |
| z2: 10 |
| }); |
| |
| const textRect = textEl.getBoundingRect(); |
| |
| const subText = titleModel.get('subtext'); |
| const subTextEl = new graphic.Text({ |
| style: createTextStyle(subtextStyleModel, { |
| text: subText, |
| fill: subtextStyleModel.getTextColor(), |
| y: textRect.height + titleModel.get('itemGap'), |
| verticalAlign: 'top' |
| }, {disableBox: true}), |
| z2: 10 |
| }); |
| |
| const link = titleModel.get('link'); |
| const sublink = titleModel.get('sublink'); |
| const triggerEvent = titleModel.get('triggerEvent', true); |
| |
| textEl.silent = !link && !triggerEvent; |
| subTextEl.silent = !sublink && !triggerEvent; |
| |
| if (link) { |
| textEl.on('click', function () { |
| windowOpen(link, '_' + titleModel.get('target')); |
| }); |
| } |
| if (sublink) { |
| subTextEl.on('click', function () { |
| windowOpen(sublink, '_' + titleModel.get('subtarget')); |
| }); |
| } |
| |
| getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent |
| ? { |
| componentType: 'title', |
| componentIndex: titleModel.componentIndex |
| } |
| : null; |
| |
| group.add(textEl); |
| subText && group.add(subTextEl); |
| // If no subText, but add subTextEl, there will be an empty line. |
| |
| let groupRect = group.getBoundingRect(); |
| const layoutOption = titleModel.getBoxLayoutParams(); |
| layoutOption.width = groupRect.width; |
| layoutOption.height = groupRect.height; |
| const layoutRect = getLayoutRect( |
| layoutOption, { |
| width: api.getWidth(), |
| height: api.getHeight() |
| }, titleModel.get('padding') |
| ); |
| // Adjust text align based on position |
| if (!textAlign) { |
| // Align left if title is on the left. center and right is same |
| textAlign = (titleModel.get('left') || titleModel.get('right')) as ZRTextAlign; |
| // @ts-ignore |
| if (textAlign === 'middle') { |
| textAlign = 'center'; |
| } |
| // Adjust layout by text align |
| if (textAlign === 'right') { |
| layoutRect.x += layoutRect.width; |
| } |
| else if (textAlign === 'center') { |
| layoutRect.x += layoutRect.width / 2; |
| } |
| } |
| if (!textVerticalAlign) { |
| textVerticalAlign = (titleModel.get('top') || titleModel.get('bottom')) as ZRTextVerticalAlign; |
| // @ts-ignore |
| if (textVerticalAlign === 'center') { |
| textVerticalAlign = 'middle'; |
| } |
| if (textVerticalAlign === 'bottom') { |
| layoutRect.y += layoutRect.height; |
| } |
| else if (textVerticalAlign === 'middle') { |
| layoutRect.y += layoutRect.height / 2; |
| } |
| |
| textVerticalAlign = textVerticalAlign || 'top'; |
| } |
| |
| group.x = layoutRect.x; |
| group.y = layoutRect.y; |
| group.markRedraw(); |
| const alignStyle = { |
| align: textAlign, |
| verticalAlign: textVerticalAlign |
| }; |
| textEl.setStyle(alignStyle); |
| subTextEl.setStyle(alignStyle); |
| |
| // Render background |
| // Get groupRect again because textAlign has been changed |
| groupRect = group.getBoundingRect(); |
| const padding = layoutRect.margin; |
| const style = titleModel.getItemStyle(['color', 'opacity']); |
| style.fill = titleModel.get('backgroundColor'); |
| const rect = new graphic.Rect({ |
| shape: { |
| x: groupRect.x - padding[3], |
| y: groupRect.y - padding[0], |
| width: groupRect.width + padding[1] + padding[3], |
| height: groupRect.height + padding[0] + padding[2], |
| r: titleModel.get('borderRadius') |
| }, |
| style: style, |
| subPixelOptimize: true, |
| silent: true |
| }); |
| |
| group.add(rect); |
| } |
| } |
| |
| |
| export function install(registers: EChartsExtensionInstallRegisters) { |
| registers.registerComponentModel(TitleModel); |
| registers.registerComponentView(TitleView); |
| } |