blob: c7e0678a0294144416524448fcc34edda98c820d [file] [log] [blame]
/*
* 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);
}