@visactor/vchart
Version:
charts lib based @visactor/VGrammar
224 lines (210 loc) • 13.3 kB
JavaScript
import { BaseTooltipHandler } from "./base";
import { getDomStyle, getTextStyle, setStyleToDom } from "./utils/style";
import { TOOLTIP_CONTAINER_EL_CLASS_NAME, DEFAULT_TOOLTIP_Z_INDEX, TOOLTIP_PREFIX, TOOLTIP_CONTENT_BOX_CLASS_NAME, TOOLTIP_TITLE_CLASS_NAME } from "./constants";
import { isValid } from "@visactor/vutils";
import { domDocument } from "../../../util/env";
import { registerComponentPlugin } from "../register";
import { TooltipHandlerType } from "../../../component/tooltip/constant";
import { getSvgHtml } from "./utils/svg";
import { formatContent } from "./utils/common";
import { token } from "../../../theme/token";
import { calcLayoutNumber } from "../../../util/space";
export class DomTooltipHandler extends BaseTooltipHandler {
getVisibility() {
return !!this._rootDom && "visible" === this._rootDom.style.visibility;
}
setVisibility(_value) {
_value !== this.getVisibility() && this._rootDom && (this._rootDom.style.visibility = _value ? "visible" : "hidden");
}
getRootDom() {
return this._rootDom;
}
constructor() {
super(DomTooltipHandler.type), this.type = TooltipHandlerType.dom, this._tooltipContainer = null == domDocument ? void 0 : domDocument.body;
}
onAdd(service) {
super.onAdd(service), this._initStyle(), this.initEl();
}
initEl() {
const parentElement = this._component.getSpec().parentElement;
if (domDocument && parentElement && parentElement.children && parentElement.children.length) {
for (let i = 0; i < parentElement.children.length; i++) if (parentElement.children[i].classList.contains(TOOLTIP_CONTAINER_EL_CLASS_NAME)) {
this._container = parentElement.children[i];
break;
}
this._container || (this._container = domDocument.createElement("div"), this._container.style.position = "relative",
this._container.style.zIndex = DEFAULT_TOOLTIP_Z_INDEX, this._container.classList.add(TOOLTIP_CONTAINER_EL_CLASS_NAME),
parentElement.appendChild(this._container));
}
}
initRootDom() {
var _a;
const tooltipSpec = this._component.getSpec(), tooltipElement = document.createElement("div"), themeFontFamily = null === (_a = this._chartOption) || void 0 === _a ? void 0 : _a.getTheme("fontFamily");
setStyleToDom(tooltipElement, Object.assign({
left: "0",
top: "0",
pointerEvents: "none",
padding: "12px",
position: "absolute",
zIndex: DEFAULT_TOOLTIP_Z_INDEX,
fontFamily: null != themeFontFamily ? themeFontFamily : token.fontFamily,
fontSize: "11px",
borderRadius: "3px",
borderStyle: "solid",
lineHeight: "initial",
background: "#fff",
boxShadow: "2px 2px 4px rgba(0, 0, 0, 0.1)",
maxWidth: "100wh",
maxHeight: "100vh",
visibility: "hidden"
}, this._domStyle.panel)), tooltipElement.classList.add(tooltipSpec.className),
tooltipElement.setAttribute("vchart-tooltip-id", `${this.id}`), this._container.appendChild(tooltipElement),
this._rootDom = tooltipElement;
}
_getTooltipBoxSize(actualTooltip, changePositionOnly) {
var _a;
this._rootDom || this.initRootDom(), changePositionOnly || this._updateDomStringByCol(actualTooltip),
this._updateDomStyle("height", changePositionOnly);
const rect = null === (_a = this._rootDom) || void 0 === _a ? void 0 : _a.getBoundingClientRect();
return {
width: null == rect ? void 0 : rect.width,
height: null == rect ? void 0 : rect.height
};
}
_removeTooltip() {
this._rootDom && this._rootDom.parentNode && (this._rootDom.parentNode.removeChild(this._rootDom),
this._rootDom = null), this._container = null;
}
_updateTooltip(visible, params) {
var _a, _b;
if (visible && this._rootDom) {
const {tooltipSpec: tooltipSpec, activeTooltipSpec: activeTooltipSpec} = params;
params.changePositionOnly || (this._tooltipActual = activeTooltipSpec);
const el = this._rootDom;
if (el) {
const {x: x = 0, y: y = 0} = null !== (_a = activeTooltipSpec.position) && void 0 !== _a ? _a : {};
let position = {
x: x,
y: y
};
const currentVisible = this.getVisibility();
tooltipSpec.updateElement && (this._updatePosition(null !== (_b = this._cacheCustomTooltipPosition) && void 0 !== _b ? _b : {
x: x,
y: y
}), tooltipSpec.updateElement(el, activeTooltipSpec, params), position = this._getActualTooltipPosition(activeTooltipSpec, params, {
width: el.offsetWidth,
height: el.offsetHeight
}), this._cacheCustomTooltipPosition = position), !currentVisible && visible && (this._rootDom.style.transition = "none",
this._updatePosition(position, !1), this._rootDom.getBoundingClientRect()), this._updatePosition(position);
}
this.setVisibility(visible);
} else this.setVisibility(visible), this._cacheCustomTooltipPosition = void 0;
}
_initStyle() {
const tooltipSpec = this._component.getSpec();
this._domStyle = getDomStyle(tooltipSpec);
}
_updateDomStringByCol(actualTooltip) {
var _a;
const {title: title = {}, content: content} = actualTooltip, hasContent = content && content.length, rowStyle = this._domStyle.row, chilren = [ ...this._rootDom.children ];
let titleDom = chilren.find((child => child.className.includes(TOOLTIP_TITLE_CLASS_NAME))), contentDom = chilren.find((child => child.className.includes(TOOLTIP_CONTENT_BOX_CLASS_NAME)));
if (!titleDom && !1 !== title.visible) {
titleDom = document.createElement("h2");
const span = document.createElement("span");
titleDom.appendChild(span), titleDom.classList.add(TOOLTIP_TITLE_CLASS_NAME), this._rootDom.firstChild ? this._rootDom.insertBefore(titleDom, this._rootDom.firstChild) : this._rootDom.appendChild(titleDom);
}
titleDom && !1 !== title.visible ? (setStyleToDom(titleDom, Object.assign(Object.assign(Object.assign({}, this._domStyle.title), hasContent ? rowStyle : {
marginBottom: "0px"
}), {
marginTop: "0px"
})), titleDom.firstChild.innerHTML = `${null !== (_a = title.value) && void 0 !== _a ? _a : ""}`) : titleDom && !1 === title.visible && titleDom.parentNode.removeChild(titleDom);
const columns = [ "shape", "key", "value" ];
if (!contentDom && hasContent && (contentDom = document.createElement("div"), columns.forEach((col => {
const colDiv = document.createElement("div");
colDiv.classList.add(`${TOOLTIP_PREFIX}-column`), colDiv.classList.add(`${TOOLTIP_PREFIX}-${col}-column`),
colDiv.setAttribute("data-col", col), contentDom.appendChild(colDiv);
})), contentDom.classList.add(TOOLTIP_CONTENT_BOX_CLASS_NAME), this._rootDom.appendChild(contentDom)),
contentDom && hasContent) {
const columnDivs = [ ...contentDom.children ];
setStyleToDom(contentDom, {
whiteSpace: "nowrap"
}), columnDivs.forEach(((colDiv, index) => {
const colName = colDiv.getAttribute("data-col");
if (colName && columns.includes(colName)) {
const hideColumn = "shape" === colName && content.every((c => !c.hasShape || !c.shapeType));
setStyleToDom(colDiv, Object.assign(Object.assign({}, this._domStyle[colName]), {
display: hideColumn ? "none" : "inline-block",
verticalAlign: "top"
}));
const rows = [ ...colDiv.children ];
rows.slice(content.length).forEach((extraRow => {
extraRow.parentNode.removeChild(extraRow);
})), content.forEach(((entry, index) => {
let row = rows[index];
row || (row = document.createElement("div"), row.classList.add(`${TOOLTIP_PREFIX}-${colName}`),
colDiv.appendChild(row));
const styleByRow = Object.assign({}, rowStyle);
index === content.length - 1 && (styleByRow.marginBottom = "0px"), styleByRow.display = !1 === entry.visible ? "none" : "block",
styleByRow.height = "initial", "key" === colName ? (row.innerHTML = formatContent(entry.key),
entry.keyStyle && getTextStyle(entry.keyStyle, styleByRow)) : "value" === colName ? (row.innerHTML = formatContent(entry.value),
entry.valueStyle && getTextStyle(entry.valueStyle, styleByRow)) : "shape" === colName && (row.innerHTML = getSvgHtml(entry, `${this.id}_${index}`)),
setStyleToDom(row, styleByRow);
}));
}
}));
} else contentDom && !hasContent && contentDom.parentNode.removeChild(contentDom);
}
_updateDomStyle(sizeKey = "width", refreshSize) {
var _a, _b, _c, _d, _e;
const rootDom = this._rootDom, contentDom = [ ...rootDom.children ].find((child => child.className.includes(TOOLTIP_CONTENT_BOX_CLASS_NAME))), titleDom = [ ...rootDom.children ].find((child => child.className.includes(TOOLTIP_TITLE_CLASS_NAME)));
if (contentDom) {
const tooltipSpec = this._component.getSpec(), contentStyle = {}, titleLabel = null === (_a = tooltipSpec.style) || void 0 === _a ? void 0 : _a.titleLabel;
if (titleLabel && titleLabel.autoWidth && !1 !== titleLabel.multiLine && titleDom) {
const maxWidth = [ ...contentDom.children ].reduce(((res, col) => "height" === sizeKey ? res + col.getBoundingClientRect().width : Math.max(res, col.getBoundingClientRect().width)), 0);
maxWidth > 0 && (titleDom.style.maxWidth = `${maxWidth}px`, titleDom.style.maxWidth = `${Math.ceil(contentDom.getBoundingClientRect().width)}px`);
}
if (isValid(null === (_b = null == tooltipSpec ? void 0 : tooltipSpec.style) || void 0 === _b ? void 0 : _b.maxContentHeight)) {
const titleHeight = titleDom && titleDom.className.includes(TOOLTIP_TITLE_CLASS_NAME) ? titleDom.getBoundingClientRect().height + (null !== (_c = tooltipSpec.style.spaceRow) && void 0 !== _c ? _c : 0) : 0, viewRect = this._chartOption.getChartViewRect(), maxHeight = calcLayoutNumber(tooltipSpec.style.maxContentHeight, Math.min(viewRect.height, document.body.clientHeight) - titleHeight - (this._domStyle.panelPadding ? this._domStyle.panelPadding[0] + this._domStyle.panelPadding[1] : 0));
maxHeight > 0 && (contentStyle.maxHeight = `${maxHeight}px`, contentStyle.overflowY = "auto",
contentStyle.width = `calc(100% + ${this._domStyle.panelPadding ? this._domStyle.panelPadding[1] + "px" : "10px"})`,
setStyleToDom(contentDom, contentStyle));
}
const rows = contentDom.children, widthByCol = [];
if (rows) {
for (let i = 0; i < rows.length; i++) {
const cols = null !== (_d = rows[i].children) && void 0 !== _d ? _d : [];
for (let j = 0; j < cols.length; j++) {
refreshSize && (cols[j].style[sizeKey] = "initial");
const width = cols[j].getBoundingClientRect()[sizeKey];
(void 0 === widthByCol[j] || widthByCol[j] < width) && (widthByCol[j] = width);
}
}
for (let i = 0; i < rows.length; i++) {
const cols = null !== (_e = rows[i].children) && void 0 !== _e ? _e : [];
for (let j = 0; j < cols.length; j++) cols[j].style[sizeKey] = `${widthByCol[j]}px`;
}
}
}
}
_getParentElement(spec) {
var _a;
return null !== (_a = this._container) && void 0 !== _a ? _a : super._getParentElement(spec);
}
isTooltipShown() {
return this.getVisibility();
}
reInit() {
super.reInit(), this._initStyle(), this._rootDom && setStyleToDom(this._rootDom, this._domStyle.panel),
this.getVisibility() && (this._updateDomStringByCol(this._tooltipActual), this._updateDomStyle("height", !1));
}
_updatePosition({x: x, y: y}, resetTransition = !0) {
this._rootDom && (this._rootDom.style.transform = `translate3d(${x}px, ${y}px, 0)`,
resetTransition && "" !== this._rootDom.style.transition && (this._rootDom.style.transition = "",
Object.assign(this._rootDom.style, this._domStyle.panel)));
}
}
DomTooltipHandler.type = TooltipHandlerType.dom;
export const registerDomTooltipHandler = () => {
registerComponentPlugin(DomTooltipHandler);
};
//# sourceMappingURL=dom-tooltip-handler.js.map