@antv/s2
Version:
effective spreadsheet render core lib
280 lines • 13.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCellWidth = exports.drawCustomContent = exports.getContentAreaForMultiData = exports.getEmptyPlaceholder = exports.isUnchangedValue = exports.isZeroOrEmptyValue = exports.isUpDataValue = exports.replaceEmptyFieldValue = exports.getDisplayText = void 0;
const lodash_1 = require("lodash");
const constant_1 = require("../common/constant");
const interface_1 = require("../common/interface");
const g_renders_1 = require("../utils/g-renders");
const cell_1 = require("./cell/cell");
const condition_1 = require("./condition/condition");
const g_mini_charts_1 = require("./g-mini-charts");
const layout_1 = require("./layout");
const getDisplayText = (text, placeholder) => {
const displayText = (0, layout_1.resolveNillString)(text);
const emptyPlaceholder = placeholder !== null && placeholder !== void 0 ? placeholder : constant_1.EMPTY_PLACEHOLDER;
const isInvalidNumber = (0, lodash_1.isNumber)(displayText) && Number.isNaN(displayText);
// 对应维度缺少维度数据时, 会使用 EMPTY_FIELD_VALUE 填充, 实际渲染时统一转成 "-"
const isEmptyString = displayText === '' || displayText === constant_1.EMPTY_FIELD_VALUE;
const isEmptyText = (0, lodash_1.isNil)(displayText) || isInvalidNumber || isEmptyString;
return isEmptyText ? emptyPlaceholder : (0, layout_1.resolveNillString)(displayText);
};
exports.getDisplayText = getDisplayText;
const replaceEmptyFieldValue = (value) => value === constant_1.EMPTY_FIELD_VALUE ? constant_1.EMPTY_PLACEHOLDER : value;
exports.replaceEmptyFieldValue = replaceEmptyFieldValue;
/**
* To decide whether the data is positive or negative.
* Two cases needed to be considered since the derived value could be number or string.
* @param value
*/
const isUpDataValue = (value) => {
if ((0, lodash_1.isNumber)(value)) {
return value >= 0;
}
return !!value && !(0, lodash_1.trim)(value).startsWith('-');
};
exports.isUpDataValue = isUpDataValue;
/**
* Determines whether the data is actually equal to 0 or empty or nil
* example: "0.00%" => true
* @param value
*/
const isZeroOrEmptyValue = (value) => {
return ((0, lodash_1.isNil)(value) ||
value === '' ||
Number(String(value).replace(/[^0-9.]+/g, '')) === 0);
};
exports.isZeroOrEmptyValue = isZeroOrEmptyValue;
/**
* Determines whether the data is actually equal to 0 or empty or nil or equals to compareValue
* example: "0.00%" => true
* @param value
* @param compareValue
*/
const isUnchangedValue = (value, compareValue) => {
return (0, exports.isZeroOrEmptyValue)(value) || value === compareValue;
};
exports.isUnchangedValue = isUnchangedValue;
/**
* 根据单元格对齐方式计算文本的 x 坐标
* @param x 单元格的 x 坐标
* @param padding @Padding
* @param extraWidth 额外的宽度
* @param textAlign 文本对齐方式
*/
const calX = (x, padding, extraWidth, textAlign = 'left') => {
const { right = 0, left = 0 } = padding;
const extra = extraWidth || 0;
if (textAlign === 'left') {
return x + right / 2 + extra;
}
if (textAlign === 'right') {
return x - right / 2 - extra;
}
return x + left / 2 + extra;
};
/**
* 返回需要绘制的 cell 主题
* @param cell 目标 cell
* @returns cell 主题和具体 text 主题
*/
const getDrawStyle = (cell) => {
var _a, _b;
const { isTotals } = cell.getMeta();
const isMeasureField = (_b = (_a = cell).isMeasureField) === null || _b === void 0 ? void 0 : _b.call(_a);
const cellStyle = cell.getStyle(cell.cellType || constant_1.CellType.DATA_CELL);
let textStyle;
if (isMeasureField) {
textStyle = cellStyle === null || cellStyle === void 0 ? void 0 : cellStyle.measureText;
}
else if (isTotals) {
textStyle = cellStyle === null || cellStyle === void 0 ? void 0 : cellStyle.bolderText;
}
else {
textStyle = cellStyle === null || cellStyle === void 0 ? void 0 : cellStyle.text;
}
return {
cellStyle,
textStyle,
};
};
/**
* 获取当前文字的绘制样式
*/
const getCurrentTextStyle = ({ rowIndex, colIndex, meta, data, textStyle, textCondition, cell, }) => {
var _a;
const style = (_a = textCondition === null || textCondition === void 0 ? void 0 : textCondition.mapping) === null || _a === void 0 ? void 0 : _a.call(textCondition, data, {
rowIndex,
colIndex,
meta,
}, cell);
return Object.assign(Object.assign({}, textStyle), style);
};
/**
* 获取自定义空值占位符
*/
const getEmptyPlaceholder = (meta, placeHolder) => (0, lodash_1.isFunction)(placeHolder === null || placeHolder === void 0 ? void 0 : placeHolder.cell) ? placeHolder === null || placeHolder === void 0 ? void 0 : placeHolder.cell(meta) : placeHolder === null || placeHolder === void 0 ? void 0 : placeHolder.cell;
exports.getEmptyPlaceholder = getEmptyPlaceholder;
/**
* @desc 获取多指标情况下每一个指标的内容包围盒
* --------------------------------------------
* | text icon | text icon | text icon |
* |-------------|-------------|-------------|
* | text icon | text icon | text icon |
* --------------------------------------------
* @param box SimpleBBox 整体绘制内容包围盒
* @param textValues SimpleDataItem[][] 指标集合
* @param widthPercent number[] 每行指标的宽度百分比
*/
const getContentAreaForMultiData = (box, textValues, widthPercent) => {
const { x, y, width, height } = box;
const avgHeight = height / (0, lodash_1.size)(textValues);
const boxes = [];
let curX;
let curY;
let avgWidth;
let totalWidth = 0;
const percents = (0, lodash_1.map)(widthPercent, (item) => (item > 1 ? item / 100 : item));
for (let i = 0; i < (0, lodash_1.size)(textValues); i++) {
curY = y + avgHeight * i;
const rows = [];
curX = x;
totalWidth = 0;
for (let j = 0; j < (0, lodash_1.size)(textValues[i]); j++) {
// 指标个数相同,任取其一即可
avgWidth = !(0, lodash_1.isEmpty)(percents)
? width * percents[j]
: width / (0, lodash_1.size)(textValues[0]);
curX = calX(x, { left: 0, right: 0 }, totalWidth, 'left');
totalWidth += avgWidth;
rows.push({
x: curX,
y: curY,
width: avgWidth,
height: avgHeight,
});
}
boxes.push(rows);
}
return boxes;
};
exports.getContentAreaForMultiData = getContentAreaForMultiData;
/**
* @desc draw text shape of object
* @param cell
* @multiData 自定义文本内容
* @useCondition 是否使用条件格式
*/
// eslint-disable-next-line max-lines-per-function
const drawCustomContent = (cell, multiData, useCondition = true) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
const { x, y, height: totalTextHeight, width: totalTextWidth, } = cell.getBBoxByType(interface_1.CellClipBox.CONTENT_BOX);
const meta = cell.getMeta();
const text = multiData || meta.fieldValue;
const { values: textValues } = text;
const { options } = meta.spreadsheet;
// 趋势分析表默认只作用一个条件(因为指标挂行头,每列都不一样,直接在回调里判断是否需要染色即可)
const textCondition = (_b = (_a = options === null || options === void 0 ? void 0 : options.conditions) === null || _a === void 0 ? void 0 : _a.text) === null || _b === void 0 ? void 0 : _b[0];
const iconCondition = (_d = (_c = options === null || options === void 0 ? void 0 : options.conditions) === null || _c === void 0 ? void 0 : _c.icon) === null || _d === void 0 ? void 0 : _d[0];
if (!(0, lodash_1.isArray)(textValues)) {
(0, g_mini_charts_1.renderMiniChart)(cell, textValues);
return;
}
const widthPercent = (_g = (_f = (_e = options.style) === null || _e === void 0 ? void 0 : _e.dataCell) === null || _f === void 0 ? void 0 : _f.valuesCfg) === null || _g === void 0 ? void 0 : _g.widthPercent;
let labelHeight = 0;
// 绘制单元格主标题
if (text === null || text === void 0 ? void 0 : text.label) {
const dataCellStyle = cell.getStyle(constant_1.CellType.DATA_CELL);
const labelStyle = dataCellStyle.bolderText;
labelHeight = totalTextHeight / (textValues.length + 1);
cell.renderTextShape(Object.assign(Object.assign({}, labelStyle), { x, y: y + labelHeight / 2, text: text.label, maxLines: 1, wordWrapWidth: totalTextWidth, wordWrap: true, textOverflow: 'ellipsis' }), { shallowRender: true });
}
// 绘制指标
const { cellStyle, textStyle } = getDrawStyle(cell);
const iconStyle = cellStyle === null || cellStyle === void 0 ? void 0 : cellStyle.icon;
const iconCfg = iconCondition &&
iconCondition.mapping && {
name: '',
size: iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.size,
margin: iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.margin,
position: (0, condition_1.getIconPosition)(iconCondition),
};
let curText;
const contentBoxes = (0, exports.getContentAreaForMultiData)({
x,
y: y + labelHeight,
height: totalTextHeight - labelHeight,
width: totalTextWidth,
}, textValues, widthPercent);
for (let i = 0; i < textValues.length; i++) {
const measures = (0, lodash_1.clone)(textValues[i]);
for (let j = 0; j < measures.length; j++) {
curText = measures[j];
const curStyle = useCondition
? getCurrentTextStyle({
rowIndex: i,
colIndex: j,
meta,
data: curText,
textStyle,
textCondition,
cell,
})
: textStyle;
const maxTextWidth = contentBoxes[i][j].width -
(iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.size) -
((iconCfg === null || iconCfg === void 0 ? void 0 : iconCfg.position) === 'left'
? (_h = iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.margin) === null || _h === void 0 ? void 0 : _h.right
: (_j = iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.margin) === null || _j === void 0 ? void 0 : _j.left);
const groupedIcons = {
left: [],
right: [],
};
if (iconCfg) {
groupedIcons[iconCfg.position].push(iconCfg);
}
cell.renderTextShape(Object.assign(Object.assign({}, curStyle), { x: 0, y: 0,
// 多列文本不换行
maxLines: 1, text: curText, wordWrapWidth: maxTextWidth }), {
shallowRender: true,
});
const actualTextWidth = cell.getActualTextWidth();
const { textX, leftIconX, rightIconX } = (0, cell_1.getHorizontalTextIconPosition)({
bbox: contentBoxes[i][j],
textAlign: curStyle.textAlign,
textWidth: actualTextWidth,
iconStyle,
groupedIcons,
});
const textY = (0, cell_1.getVerticalTextPosition)(contentBoxes[i][j], curStyle.textBaseline);
cell.updateTextPosition({ x: textX, y: textY });
// 绘制条件格式的 icon
if (iconCondition && useCondition) {
const attrs = (_k = iconCondition === null || iconCondition === void 0 ? void 0 : iconCondition.mapping) === null || _k === void 0 ? void 0 : _k.call(iconCondition, curText, {
rowIndex: i,
colIndex: j,
meta: cell === null || cell === void 0 ? void 0 : cell.getMeta(),
}, cell);
const iconX = (iconCfg === null || iconCfg === void 0 ? void 0 : iconCfg.position) === 'left' ? leftIconX : rightIconX;
const iconY = (0, cell_1.getVerticalIconPosition)(iconStyle.size, textY, curStyle.fontSize, curStyle.textBaseline);
if (attrs) {
const iconShape = (0, g_renders_1.renderIcon)(cell, {
x: iconX,
y: iconY,
name: attrs.icon,
width: iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.size,
height: iconStyle === null || iconStyle === void 0 ? void 0 : iconStyle.size,
fill: attrs.fill,
});
cell.addConditionIconShape(iconShape);
}
}
}
}
};
exports.drawCustomContent = drawCustomContent;
/**
* 根据 dataCell 配置获取当前单元格宽度
*/
const getCellWidth = (dataCell, labelSize = 1) => (dataCell === null || dataCell === void 0 ? void 0 : dataCell.width) * labelSize;
exports.getCellWidth = getCellWidth;
//# sourceMappingURL=text.js.map