import { TextStyle } from '../dataModels'; import { textStyleToCss } from '../cssUtils'; import { ComponentBase, ComponentState } from './componentBase'; export type TextState = ComponentState & { _type_: 'Text-builtin'; text?: string; selectable?: boolean; style?: 'heading1' | 'heading2' | 'heading3' | 'text' | 'dim' | TextStyle; justify?: 'left' | 'right' | 'center' | 'justify'; wrap?: boolean | 'ellipsize'; }; export class TextComponent extends ComponentBase { state: Required; private inner: HTMLElement; createElement(): HTMLElement { let element = document.createElement('div'); element.classList.add('rio-text'); this.inner = document.createElement('span'); element.appendChild(this.inner); return element; } updateElement( deltaState: TextState, latentComponents: Set ): void { super.updateElement(deltaState, latentComponents); // BEFORE WE DO ANYTHING ELSE, update the text style if (deltaState.style !== undefined) { // Change the element to

,

,

or as necessary let tagName: string = 'SPAN'; if (typeof deltaState.style === 'string') { tagName = { heading1: 'H1', heading2: 'H2', heading3: 'H3', }[deltaState.style] || 'SPAN'; } if (tagName !== this.inner.tagName) { let newInner = document.createElement(tagName); this.inner.remove(); this.element.appendChild(newInner); this.inner = newInner; // Turn the whole state into a deltaState so that the new // element is initialized correctly deltaState = { ...this.state, ...deltaState }; // Shut up the type checker if (deltaState.style === undefined) { return; } } // Now apply the style Object.assign(this.inner.style, textStyleToCss(deltaState.style)); } // Text content // // Make sure not to allow any linebreaks if the text is not multiline. if (deltaState.text !== undefined) { this.inner.textContent = deltaState.text; } // Wrap lines switch (deltaState.wrap) { case false: this.inner.style.whiteSpace = 'pre'; this.inner.style.textOverflow = 'clip'; this.inner.style.width = 'max-content'; break; case true: this.inner.style.whiteSpace = 'pre-wrap'; this.inner.style.textOverflow = 'clip'; this.inner.style.width = 'min-content'; break; case 'ellipsize': this.inner.style.whiteSpace = 'pre'; this.inner.style.textOverflow = 'ellipsis'; this.inner.style.width = '0'; // No `min-width: 100%` required break; } // Selectable if (deltaState.selectable !== undefined) { if (deltaState.selectable) { this.inner.style.pointerEvents = 'auto'; this.inner.style.userSelect = 'auto'; } else { this.inner.style.pointerEvents = 'none'; this.inner.style.userSelect = 'none'; } } // Text alignment if (deltaState.justify !== undefined) { this.inner.style.textAlign = deltaState.justify; } } }