UNPKG

@pmndrs/uikit

Version:

Build performant 3D user interfaces with Three.js and yoga.

91 lines (90 loc) 3.54 kB
import { BreakallWrapper, NowrapWrapper, WordWrapper } from './wrapper/index.js'; import { getGlyphLayoutHeight } from './utils.js'; import { computed } from '@preact/signals-core'; import { MeasureMode } from 'yoga-layout/load'; import { readReactive } from '../utils.js'; import { computedInheritableProperty } from '../properties/index.js'; export function computedCustomLayouting(properties, fontSignal, textSignal, propertiesRef, defaultWordBreak) { const fontSize = computedInheritableProperty(properties, 'fontSize', 16); const letterSpacing = computedInheritableProperty(properties, 'letterSpacing', 0); const lineHeight = computedInheritableProperty(properties, 'lineHeight', '120%'); const wordBreak = computedInheritableProperty(properties, 'wordBreak', defaultWordBreak); return computed(() => { const font = fontSignal.value; if (font == null) { return undefined; } const textsValue = textSignal.value; let text = Array.isArray(textsValue) ? textsValue.map((t) => String(readReactive(t))).join('') : String(readReactive(textsValue)); //TODO: tab should be intergrated into the text layouting algorithm text = text.replaceAll('\t', ' '.repeat(4)); const layoutProperties = { font, fontSize: fontSize.value, letterSpacing: letterSpacing.value, lineHeight: lineHeight.value, text, wordBreak: wordBreak.value, }; propertiesRef.current = layoutProperties; const { width: minWidth } = measureGlyphLayout(layoutProperties, 0); const { height: minHeight } = measureGlyphLayout(layoutProperties, undefined); return { minHeight, minWidth, measure: (width, widthMode) => measureGlyphLayout(layoutProperties, widthMode === MeasureMode.Undefined ? undefined : width), }; }); } const wrappers = { 'keep-all': NowrapWrapper, 'break-all': BreakallWrapper, 'break-word': WordWrapper, }; const lineHelper = {}; export function measureGlyphLayout(properties, availableWidth) { const wrapper = wrappers[properties.wordBreak]; const text = properties.text; let width = 0; let lines = 0; let charIndex = 0; while (charIndex < text.length) { wrapper(properties, availableWidth, charIndex, lineHelper); width = Math.max(width, lineHelper.nonWhitespaceWidth); lines += 1; charIndex = lineHelper.charLength + lineHelper.charIndexOffset; } if (text[text.length - 1] === '\n') { lines += 1; } return { width, height: getGlyphLayoutHeight(lines, properties) }; } export function buildGlyphLayout(properties, availableWidth, availableHeight) { const lines = []; const wrapper = wrappers[properties.wordBreak]; const text = properties.text; let charIndex = 0; while (charIndex < text.length) { const line = {}; wrapper(properties, availableWidth, charIndex, line); lines.push(line); charIndex = line.charLength + line.charIndexOffset; } if (lines.length === 0 || text[text.length - 1] === '\n') { lines.push({ charLength: 0, nonWhitespaceWidth: 0, whitespacesBetween: 0, charIndexOffset: text.length, nonWhitespaceCharLength: 0, }); } return { lines, availableHeight, availableWidth, ...properties, }; }