UNPKG

@antv/s2

Version:

effective spreadsheet render core lib

280 lines 13.2 kB
"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