mirror of
https://github.com/rio-labs/rio.git
synced 2026-05-13 06:48:49 -05:00
change how multi-line text works
This commit is contained in:
@@ -3,22 +3,21 @@ import { textStyleToCss } from '../cssUtils';
|
||||
import { ComponentBase, ComponentState } from './componentBase';
|
||||
import { LayoutContext } from '../layouting';
|
||||
import { getTextDimensions } from '../layoutHelpers';
|
||||
import { firstDefined } from '../utils';
|
||||
|
||||
export type TextState = ComponentState & {
|
||||
_type_: 'Text-builtin';
|
||||
text?: string;
|
||||
multiline?: boolean;
|
||||
selectable?: boolean;
|
||||
style?: 'heading1' | 'heading2' | 'heading3' | 'text' | 'dim' | TextStyle;
|
||||
text_align: number | 'justify';
|
||||
justify?: 'left' | 'right' | 'center' | 'justify';
|
||||
line_overflow?: 'none' | 'wrap' | 'ellipsize';
|
||||
};
|
||||
|
||||
export class TextComponent extends ComponentBase {
|
||||
state: Required<TextState>;
|
||||
|
||||
private inner: HTMLElement;
|
||||
private cachedSingleLineTextDimensions: [number, number];
|
||||
private cachedNoWrapDimensions: [number, number];
|
||||
|
||||
createElement(): HTMLElement {
|
||||
let element = document.createElement('div');
|
||||
@@ -37,28 +36,24 @@ export class TextComponent extends ComponentBase {
|
||||
// Text content
|
||||
//
|
||||
// Make sure not to allow any linebreaks if the text is not multiline.
|
||||
if (
|
||||
deltaState.text !== undefined ||
|
||||
deltaState.multiline !== undefined
|
||||
) {
|
||||
let text = firstDefined(deltaState.text, this.state.text);
|
||||
let multiline = firstDefined(
|
||||
deltaState.multiline,
|
||||
this.state.multiline
|
||||
);
|
||||
|
||||
if (!multiline) {
|
||||
text = text.replace(/\n/g, ' ');
|
||||
}
|
||||
|
||||
this.inner.textContent = text;
|
||||
if (deltaState.text !== undefined) {
|
||||
this.inner.textContent = deltaState.text;
|
||||
}
|
||||
|
||||
// Multiline
|
||||
if (deltaState.multiline !== undefined) {
|
||||
this.inner.style.whiteSpace = deltaState.multiline
|
||||
? 'pre-wrap'
|
||||
: 'pre';
|
||||
// Wrap lines
|
||||
switch (deltaState.line_overflow) {
|
||||
case 'none':
|
||||
this.inner.style.whiteSpace = 'pre';
|
||||
this.inner.style.textOverflow = 'clip';
|
||||
break;
|
||||
case 'wrap':
|
||||
this.inner.style.whiteSpace = 'pre-wrap';
|
||||
this.inner.style.textOverflow = 'clip';
|
||||
break;
|
||||
case 'ellipsize':
|
||||
this.inner.style.whiteSpace = 'pre';
|
||||
this.inner.style.textOverflow = 'ellipsis';
|
||||
break;
|
||||
}
|
||||
|
||||
// Selectable
|
||||
@@ -74,56 +69,45 @@ export class TextComponent extends ComponentBase {
|
||||
}
|
||||
|
||||
// Text alignment
|
||||
if (deltaState.text_align === 0) {
|
||||
this.inner.style.textAlign = 'left';
|
||||
} else if (deltaState.text_align === 0.5) {
|
||||
this.inner.style.textAlign = 'center';
|
||||
} else if (deltaState.text_align === 1) {
|
||||
this.inner.style.textAlign = 'right';
|
||||
} else if (deltaState.text_align === 'justify') {
|
||||
this.inner.style.textAlign = 'justify';
|
||||
if (deltaState.justify !== undefined) {
|
||||
this.inner.style.textAlign = deltaState.justify;
|
||||
}
|
||||
|
||||
if (
|
||||
deltaState.text !== undefined ||
|
||||
deltaState.multiline !== undefined ||
|
||||
deltaState.line_overflow !== undefined ||
|
||||
deltaState.style !== undefined
|
||||
) {
|
||||
this.makeLayoutDirty();
|
||||
|
||||
// If it's single-line, compute and cache the text dimensions
|
||||
let multiline = firstDefined(
|
||||
deltaState.multiline,
|
||||
this.state.multiline
|
||||
// Compute and cache the dimensions that our text requires if line
|
||||
// wrapping is disabled
|
||||
this.cachedNoWrapDimensions = getTextDimensions(
|
||||
this.element.textContent!,
|
||||
this.state.style
|
||||
);
|
||||
|
||||
if (!multiline) {
|
||||
this.cachedSingleLineTextDimensions = getTextDimensions(
|
||||
this.element.textContent!,
|
||||
this.state.style
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNaturalWidth(ctx: LayoutContext): void {
|
||||
if (this.state.multiline) {
|
||||
this.naturalWidth = 0;
|
||||
if (this.state.line_overflow === 'none') {
|
||||
this.naturalWidth = this.cachedNoWrapDimensions[0];
|
||||
} else {
|
||||
[this.naturalWidth, this.naturalHeight] =
|
||||
this.cachedSingleLineTextDimensions;
|
||||
this.naturalWidth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateNaturalHeight(ctx: LayoutContext): void {
|
||||
if (this.state.multiline) {
|
||||
if (this.state.line_overflow === 'wrap') {
|
||||
// Calculate how much height we need given the allocated width
|
||||
this.naturalHeight = getTextDimensions(
|
||||
this.state.text,
|
||||
this.state.style,
|
||||
this.allocatedWidth
|
||||
)[1];
|
||||
} else {
|
||||
// 'wrap' and 'ellipsize' both require the same height
|
||||
this.naturalHeight = this.cachedNoWrapDimensions[1];
|
||||
}
|
||||
|
||||
// Single-line case is already handled in `updateNaturalWidth`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,6 +652,7 @@ select {
|
||||
|
||||
.rio-text > div {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Rectangle
|
||||
|
||||
+5
-11
@@ -51,9 +51,12 @@ class Text(FundamentalComponent):
|
||||
"""
|
||||
|
||||
text: str
|
||||
selectable: bool
|
||||
style: Literal["heading1", "heading2", "heading3", "text", "dim"] | rio.TextStyle
|
||||
selectable: bool = True
|
||||
style: (
|
||||
Literal["heading1", "heading2", "heading3", "text", "dim"] | rio.TextStyle
|
||||
) = "text"
|
||||
justify: Literal["left", "right", "center", "justify"] = "left"
|
||||
line_overflow: Literal["none", "wrap", "ellipsize"] = "none"
|
||||
|
||||
def _custom_serialize(self) -> JsonDoc:
|
||||
# Serialization doesn't handle unions. Hence the custom serialization
|
||||
@@ -65,17 +68,8 @@ class Text(FundamentalComponent):
|
||||
|
||||
return {
|
||||
"style": style,
|
||||
"text_align": self._text_align,
|
||||
}
|
||||
|
||||
def get_debug_details(self) -> dict[str, Any]:
|
||||
result = super().get_debug_details()
|
||||
|
||||
# Pretend `text-align` is the same as `align_x`
|
||||
result["align_x"] = self._text_align
|
||||
|
||||
return result
|
||||
|
||||
def __repr__(self) -> str:
|
||||
if len(self.text) > 40:
|
||||
text = self.text[:40] + "..."
|
||||
|
||||
Reference in New Issue
Block a user