/* * 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 { 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); }