Skip to content

Commit

Permalink
Merge pull request #1101 from ecomfe/feat/truncate-inquiry
Browse files Browse the repository at this point in the history
feat: support output the state `isTruncated` in `Text` element.
  • Loading branch information
plainheart authored Nov 23, 2024
2 parents 4055b18 + 92dbb39 commit 980b0ea
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 11 deletions.
10 changes: 10 additions & 0 deletions src/graphic/Text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ class ZRText extends Displayable<TextProps> implements GroupLike {
*/
innerTransformable: Transformable

// Be `true` if and only if the result text is modified due to overflow, due to
// settings on either `overflow` or `lineOverflow`. Based on this the caller can
// take some action like showing the original text in a particular tip.
// Only take effect after rendering. So do not visit it before it.
isTruncated: boolean

private _children: (ZRImage | Rect | TSpan)[] = []

private _childCursor: 0
Expand Down Expand Up @@ -497,6 +503,8 @@ class ZRText extends Displayable<TextProps> implements GroupLike {

const defaultStyle = this._defaultStyle;

this.isTruncated = !!contentBlock.isTruncated;

const baseX = style.x || 0;
const baseY = style.y || 0;
const textAlign = style.align || defaultStyle.align || 'left';
Expand Down Expand Up @@ -635,6 +643,8 @@ class ZRText extends Displayable<TextProps> implements GroupLike {
const textAlign = style.align || defaultStyle.align;
const verticalAlign = style.verticalAlign || defaultStyle.verticalAlign;

this.isTruncated = !!contentBlock.isTruncated;

const boxX = adjustTextX(baseX, outerWidth, textAlign);
const boxY = adjustTextY(baseY, outerHeight, verticalAlign);
let xLeft = boxX;
Expand Down
72 changes: 61 additions & 11 deletions src/graphic/helper/parseText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,42 @@ export function truncateText(
ellipsis?: string,
options?: InnerTruncateOption
): string {
const out = {} as Parameters<typeof truncateText2>[0];
truncateText2(out, text, containerWidth, font, ellipsis, options);
return out.text;
}

// PENDING: not sure whether `truncateText` is used outside zrender, since it has an `export`
// specifier. So keep it and perform the interface modification in `truncateText2`.
function truncateText2(
out: {text: string, isTruncated: boolean},
text: string,
containerWidth: number,
font: string,
ellipsis?: string,
options?: InnerTruncateOption
): void {
if (!containerWidth) {
return '';
out.text = '';
out.isTruncated = false;
return;
}

const textLines = (text + '').split('\n');
options = prepareTruncateOptions(containerWidth, font, ellipsis, options);

// FIXME
// It is not appropriate that every line has '...' when truncate multiple lines.
let isTruncated = false;
const truncateOut = {} as Parameters<typeof truncateSingleLine>[0];
for (let i = 0, len = textLines.length; i < len; i++) {
textLines[i] = truncateSingleLine(textLines[i], options as InnerPreparedTruncateOption);
truncateSingleLine(truncateOut, textLines[i], options as InnerPreparedTruncateOption);
textLines[i] = truncateOut.textLine;
isTruncated = isTruncated || truncateOut.isTruncated;
}

return textLines.join('\n');
out.text = textLines.join('\n');
out.isTruncated = isTruncated;
}

function prepareTruncateOptions(
Expand Down Expand Up @@ -104,19 +126,27 @@ function prepareTruncateOptions(
return preparedOpts;
}

function truncateSingleLine(textLine: string, options: InnerPreparedTruncateOption): string {
function truncateSingleLine(
out: {textLine: string, isTruncated: boolean},
textLine: string,
options: InnerPreparedTruncateOption
): void {
const containerWidth = options.containerWidth;
const font = options.font;
const contentWidth = options.contentWidth;

if (!containerWidth) {
return '';
out.textLine = '';
out.isTruncated = false;
return;
}

let lineWidth = getWidth(textLine, font);

if (lineWidth <= containerWidth) {
return textLine;
out.textLine = textLine;
out.isTruncated = false;
return;
}

for (let j = 0; ; j++) {
Expand All @@ -139,7 +169,8 @@ function truncateSingleLine(textLine: string, options: InnerPreparedTruncateOpti
textLine = options.placeholder;
}

return textLine;
out.textLine = textLine;
out.isTruncated = true;
}

function estimateLength(
Expand Down Expand Up @@ -174,6 +205,10 @@ export interface PlainTextContentBlock {
outerHeight: number

lines: string[]

// Be `true` if and only if the result text is modified due to overflow, due to
// settings on either `overflow` or `lineOverflow`
isTruncated: boolean
}

export function parsePlainText(
Expand All @@ -192,6 +227,7 @@ export function parsePlainText(
const bgColorDrawn = !!(style.backgroundColor);

const truncateLineOverflow = style.lineOverflow === 'truncate';
let isTruncated = false;

let width = style.width;
let lines: string[];
Expand All @@ -210,6 +246,7 @@ export function parsePlainText(
if (contentHeight > height && truncateLineOverflow) {
const lineCount = Math.floor(height / lineHeight);

isTruncated = isTruncated || (lines.length > lineCount);
lines = lines.slice(0, lineCount);

// TODO If show ellipse for line truncate
Expand All @@ -228,8 +265,11 @@ export function parsePlainText(
placeholder: style.placeholder
});
// Having every line has '...' when truncate multiple lines.
const singleOut = {} as Parameters<typeof truncateSingleLine>[0];
for (let i = 0; i < lines.length; i++) {
lines[i] = truncateSingleLine(lines[i], options);
truncateSingleLine(singleOut, lines[i], options);
lines[i] = singleOut.textLine;
isTruncated = isTruncated || singleOut.isTruncated;
}
}

Expand Down Expand Up @@ -265,7 +305,8 @@ export function parsePlainText(
calculatedLineHeight: calculatedLineHeight,
contentWidth: contentWidth,
contentHeight: contentHeight,
width: width
width: width,
isTruncated: isTruncated
};
}

Expand Down Expand Up @@ -314,6 +355,9 @@ export class RichTextContentBlock {
outerWidth: number = 0
outerHeight: number = 0
lines: RichTextLine[] = []
// Be `true` if and only if the result text is modified due to overflow, due to
// settings on either `overflow` or `lineOverflow`
isTruncated: boolean = false
}

type WrapInfo = {
Expand All @@ -326,7 +370,7 @@ type WrapInfo = {
* Also consider 'bbbb{a|xxx\nzzz}xxxx\naaaa'.
* If styleName is undefined, it is plain text.
*/
export function parseRichText(text: string, style: TextStyleProps) {
export function parseRichText(text: string, style: TextStyleProps): RichTextContentBlock {
const contentBlock = new RichTextContentBlock();

text != null && (text += '');
Expand Down Expand Up @@ -366,6 +410,7 @@ export function parseRichText(text: string, style: TextStyleProps) {

const truncate = overflow === 'truncate';
const truncateLine = style.lineOverflow === 'truncate';
const tmpTruncateOut = {} as Parameters<typeof truncateText2>[0];

// let prevToken: RichTextToken;

Expand Down Expand Up @@ -412,6 +457,7 @@ export function parseRichText(text: string, style: TextStyleProps) {
if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) {
// TODO Add ellipsis on the previous token.
// prevToken.text =
const originalLength = contentBlock.lines.length;
if (j > 0) {
line.tokens = line.tokens.slice(0, j);
finishLine(line, lineWidth, lineHeight);
Expand All @@ -420,6 +466,7 @@ export function parseRichText(text: string, style: TextStyleProps) {
else {
contentBlock.lines = contentBlock.lines.slice(0, i);
}
contentBlock.isTruncated = contentBlock.isTruncated || (contentBlock.lines.length < originalLength);
break outer;
}

Expand Down Expand Up @@ -461,10 +508,13 @@ export function parseRichText(text: string, style: TextStyleProps) {
token.width = token.contentWidth = 0;
}
else {
token.text = truncateText(
truncateText2(
tmpTruncateOut,
token.text, remainTruncWidth - paddingH, font, style.ellipsis,
{minChar: style.truncateMinChar}
);
token.text = tmpTruncateOut.text;
contentBlock.isTruncated = contentBlock.isTruncated || tmpTruncateOut.isTruncated;
token.width = token.contentWidth = getWidth(token.text, font);
}
}
Expand Down
16 changes: 16 additions & 0 deletions test/text-overflow.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
height: 100%;
margin: 0;
}
#message {
position: relative;
padding: 20px 0;
}
</style>
<div id="message"></div>
<div id="main"></div>

<script>
Expand Down Expand Up @@ -123,6 +128,17 @@
console.time('render');
zr.refreshImmediately();
console.timeEnd('render');

const msgHTML = [];
textElementList.forEach((text, idx) => {
msgHTML.push('[text block ' + idx + '] text.isTruncated: ' + text.isTruncated);
});
updateMessageDisplay(msgHTML.join('<br/>'));
}

function updateMessageDisplay(unescapedHTML) {
const dom = document.getElementById('message');
dom.innerHTML = unescapedHTML;
}

update();
Expand Down

0 comments on commit 980b0ea

Please sign in to comment.