Update MaxSizedBox.tsx (#2233)
Co-authored-by: Jacob Richman <jacob314@gmail.com> Co-authored-by: Scott Densmore <scottdensmore@mac.com> Co-authored-by: Pascal Birchler <pascalb@google.com>
This commit is contained in:
parent
222e362fc2
commit
615748657a
|
@ -248,6 +248,89 @@ Line 3`);
|
||||||
🐶`);
|
🐶`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('falls back to an ellipsis when width is extremely small', () => {
|
||||||
|
const { lastFrame } = render(
|
||||||
|
<OverflowProvider>
|
||||||
|
<MaxSizedBox maxWidth={2} maxHeight={2}>
|
||||||
|
<Box>
|
||||||
|
<Text>No</Text>
|
||||||
|
<Text wrap="wrap">wrap</Text>
|
||||||
|
</Box>
|
||||||
|
</MaxSizedBox>
|
||||||
|
</OverflowProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(lastFrame()).equals('N…');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('truncates long non-wrapping text with ellipsis', () => {
|
||||||
|
const { lastFrame } = render(
|
||||||
|
<OverflowProvider>
|
||||||
|
<MaxSizedBox maxWidth={3} maxHeight={2}>
|
||||||
|
<Box>
|
||||||
|
<Text>ABCDE</Text>
|
||||||
|
<Text wrap="wrap">wrap</Text>
|
||||||
|
</Box>
|
||||||
|
</MaxSizedBox>
|
||||||
|
</OverflowProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(lastFrame()).equals('AB…');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('truncates non-wrapping text containing line breaks', () => {
|
||||||
|
const { lastFrame } = render(
|
||||||
|
<OverflowProvider>
|
||||||
|
<MaxSizedBox maxWidth={3} maxHeight={2}>
|
||||||
|
<Box>
|
||||||
|
<Text>{'A\nBCDE'}</Text>
|
||||||
|
<Text wrap="wrap">wrap</Text>
|
||||||
|
</Box>
|
||||||
|
</MaxSizedBox>
|
||||||
|
</OverflowProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(lastFrame()).equals(`A\n…`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('truncates emoji characters correctly with ellipsis', () => {
|
||||||
|
const { lastFrame } = render(
|
||||||
|
<OverflowProvider>
|
||||||
|
<MaxSizedBox maxWidth={3} maxHeight={2}>
|
||||||
|
<Box>
|
||||||
|
<Text>🐶🐶🐶</Text>
|
||||||
|
<Text wrap="wrap">wrap</Text>
|
||||||
|
</Box>
|
||||||
|
</MaxSizedBox>
|
||||||
|
</OverflowProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(lastFrame()).equals(`🐶…`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows ellipsis for multiple rows with long non-wrapping text', () => {
|
||||||
|
const { lastFrame } = render(
|
||||||
|
<OverflowProvider>
|
||||||
|
<MaxSizedBox maxWidth={3} maxHeight={3}>
|
||||||
|
<Box>
|
||||||
|
<Text>AAA</Text>
|
||||||
|
<Text wrap="wrap">first</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text>BBB</Text>
|
||||||
|
<Text wrap="wrap">second</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text>CCC</Text>
|
||||||
|
<Text wrap="wrap">third</Text>
|
||||||
|
</Box>
|
||||||
|
</MaxSizedBox>
|
||||||
|
</OverflowProvider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(lastFrame()).equals(`AA…\nBB…\nCC…`);
|
||||||
|
});
|
||||||
|
|
||||||
it('accounts for additionalHiddenLinesCount', () => {
|
it('accounts for additionalHiddenLinesCount', () => {
|
||||||
const { lastFrame } = render(
|
const { lastFrame } = render(
|
||||||
<OverflowProvider>
|
<OverflowProvider>
|
||||||
|
|
|
@ -432,8 +432,85 @@ function layoutInkElementAsStyledText(
|
||||||
const availableWidth = maxWidth - noWrappingWidth;
|
const availableWidth = maxWidth - noWrappingWidth;
|
||||||
|
|
||||||
if (availableWidth < 1) {
|
if (availableWidth < 1) {
|
||||||
// No room to render the wrapping segments. TODO(jacob314): consider an alternative fallback strategy.
|
// No room to render the wrapping segments. Truncate the non-wrapping
|
||||||
output.push(nonWrappingContent);
|
// content and append an ellipsis so the line always fits within maxWidth.
|
||||||
|
|
||||||
|
// Handle line breaks in non-wrapping content when truncating
|
||||||
|
const lines: StyledText[][] = [];
|
||||||
|
let currentLine: StyledText[] = [];
|
||||||
|
let currentLineWidth = 0;
|
||||||
|
|
||||||
|
for (const segment of nonWrappingContent) {
|
||||||
|
const textLines = segment.text.split('\n');
|
||||||
|
textLines.forEach((text, index) => {
|
||||||
|
if (index > 0) {
|
||||||
|
// New line encountered, finish current line and start new one
|
||||||
|
lines.push(currentLine);
|
||||||
|
currentLine = [];
|
||||||
|
currentLineWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
const textWidth = stringWidth(text);
|
||||||
|
|
||||||
|
// When there's no room for wrapping content, be very conservative
|
||||||
|
// For lines after the first line break, show only ellipsis if the text would be truncated
|
||||||
|
if (index > 0 && textWidth > 0) {
|
||||||
|
// This is content after a line break - just show ellipsis to indicate truncation
|
||||||
|
currentLine.push({ text: '…', props: {} });
|
||||||
|
currentLineWidth = stringWidth('…');
|
||||||
|
} else {
|
||||||
|
// This is the first line or a continuation, try to fit what we can
|
||||||
|
const maxContentWidth = Math.max(0, maxWidth - stringWidth('…'));
|
||||||
|
|
||||||
|
if (textWidth <= maxContentWidth && currentLineWidth === 0) {
|
||||||
|
// Text fits completely on this line
|
||||||
|
currentLine.push({ text, props: segment.props });
|
||||||
|
currentLineWidth += textWidth;
|
||||||
|
} else {
|
||||||
|
// Text needs truncation
|
||||||
|
const codePoints = toCodePoints(text);
|
||||||
|
let truncatedWidth = currentLineWidth;
|
||||||
|
let sliceEndIndex = 0;
|
||||||
|
|
||||||
|
for (const char of codePoints) {
|
||||||
|
const charWidth = stringWidth(char);
|
||||||
|
if (truncatedWidth + charWidth > maxContentWidth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
truncatedWidth += charWidth;
|
||||||
|
sliceEndIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slice = codePoints.slice(0, sliceEndIndex).join('');
|
||||||
|
if (slice) {
|
||||||
|
currentLine.push({ text: slice, props: segment.props });
|
||||||
|
}
|
||||||
|
currentLine.push({ text: '…', props: {} });
|
||||||
|
currentLineWidth = truncatedWidth + stringWidth('…');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the last line if it has content or if the last segment ended with \n
|
||||||
|
if (
|
||||||
|
currentLine.length > 0 ||
|
||||||
|
(nonWrappingContent.length > 0 &&
|
||||||
|
nonWrappingContent[nonWrappingContent.length - 1].text.endsWith('\n'))
|
||||||
|
) {
|
||||||
|
lines.push(currentLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have any lines yet, add an ellipsis line
|
||||||
|
if (lines.length === 0) {
|
||||||
|
lines.push([{ text: '…', props: {} }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
output.push(line);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue