LeCodesdocs

Элементы контента UI

Листовые элементы, которые что-то отображают: UIText, UIImage, UIVideo. У каждого одно изменяемое свойство контента (.text, .src, .player), живущее на элементе — контент никогда не стиль. Для редактируемого текста см. UIInput в interactive.md.

Обзор

TypeScript
import photo from './photo.jpg'

const caption = UIText("Loading…").style({ color: "white", fontSize: 16 })

const screen = UIScreen(
  UIImage(photo).style({ width: "100%", height: 220, borderRadius: 16, objectFit: "cover" }),
  caption,
).style({ bgColor: "black", p: 16, pt: "safe-top", gap: 12 })

screen.open()
caption.text = "Sunset over the harbor"     // перерисовывается сразу

UIText

TypeScript
UIText(text: string): UIText

text.text = "Updated"       // get/set отображаемой строки

Специфичные для текста стили (поверх общих стилей элемента):

TypeScript
textAlign:      "start" | "center" | "end" | "left" | "right"   // по умолчанию left
fontFamily:     string            // системные: "serif", "sans-serif", "monospaced"; кастомные через font() (см. fonts.md)
fontSize:       UIValue           // по умолчанию 14
lineHeight:     UIValue | "normal"
fontWeight:     number | "normal" | "bold"     // 400, 700, …
fontStyle:      "normal" | "italic"
color:          Color
textDecoration: "underline" | "line-through" | "none"
letterSpacing:  UIValue
lineClamp:      number            // обрезать до N строк; 0 / не задано = без ограничения
textOverflow:   "ellipsis" | "clip"            // по умолчанию ellipsis
Note

lineHeight: 1.5 значит 1.5 px — голое число это px везде. Множитель это "1.5em" (разрешается относительно собственного fontSize этого элемента; наследования нет).

Обрезка текста до N строк

lineClamp ограничивает текст N строками, и элемент меряется в эту высоту — окружающая раскладка перетекает вокруг обрезанного бокса, а не вокруг полной строки:

TypeScript
UIText(article.summary).style({ lineClamp: 3 })                        // 3 строки, затем "…"
UIText(article.summary).style({ lineClamp: 3, textOverflow: "clip" })  // 3 строки, жёсткий срез

textOverflow действует только вместе с lineClamp — без ограничения бокс размеряется под всю строку, так что ничего не переполняется. "clip" на вебе приблизителен: он режет по боксу line-height, а не по глифу, поэтому нижний выносной элемент последней строки может срезаться не так, как на iOS. "ellipsis" точен на обоих.

Note

фон экрана по умолчанию чёрный — всегда задавай color явно.

.style() у UIText не принимает свойств рамки/фонового-изображения — bgColor, отступы и поля работают, но декорированный текстовый чип — это UIRow/UIColumn вокруг UIText. Кастомные шрифты нужно зарегистрировать до открытия экрана — см. fonts.md.

UIImage

TypeScript
UIImage(src: ImageSource): UIImage

// ImageSource:
//   string          — удалённый URL, или asset() / импортированный файл проекта
//   FetchResponse   — скачанное тело, используемое напрямую как пиксели
//   File            — из openFilePicker()
//   SvgSource       — обёрнутый сырой SVG XML
//   Canvas          — 2D Canvas, запекается в текстуру при присвоении

img.src            // получить (разрешённый) источник / задать новый — обновляет отображаемое изображение

Специфичные для изображения стили:

TypeScript
objectFit:    "cover" | "contain" | "fill"   // как источник ложится в бокс
tintColor:    Color                          // только SVG-источники
borderRadius: number                         // px (только число на UIImage)
Note

tintColor применяется только к SVG-источникам и заменяет все цвета fill и stroke в SVG — это для монохромных иконок, не для многоцветной графики.

Note

голая строка относительного пути (UIImage("./photo.png")) не разрешается в собранный ассет — обычные строки работают только для https://… URL. Импортируй файл или используй asset('./photo.png').

Иконки: assetIcon("pack:name")

Для иконок предпочитай компайл-макрос assetIcon() ручному написанию SVG: он разрешает одну иконку из реестра иконок во время сборки и инлайнит её как источник изображения (та же форма, что возвращает SvgSource), поэтому она рендерится офлайн и одинаково на каждом хосте — без сети в момент рендера.

TypeScript
UIImage(assetIcon("lucide:bell")).style({ width: 24, height: 24, tintColor: "#8a8f98" })
UIImage(assetIcon("lucide:check", { color: colors.accent })).style({ width: 20, height: 20 })

Id "pack:name" должен быть строковым литералом (неизвестный id — ошибка компиляции; паки смотри на https://icon-registry.jt3.ru). Перекрашивай опцией { color } — hex-литерал запекается в SVG при компиляции, токен/выражение применяется как tint — или стилевым свойством tintColor.

Обрезка атласа: setSourceRect

TypeScript
img.setSourceRect(x: number, y: number, w: number, h: number): this   // пиксели текстуры

Рендерит только под-прямоугольник источника — примитив спрайт-листа. w/h прямоугольника становятся собственным размером элемента (один кадр, не весь атлас), и обрезанный кадр всегда заполняет бокс, переопределяя objectFit. Чейнится и безопасно вызывается до появления элемента на экране (начальная обрезка применяется при монтировании). Анимация в стиле спрайта — это смена прямоугольника на кадр:

TypeScript
const icon = UIImage(atlasUrl).style({ width: 64, height: 64 }).setSourceRect(0, 0, 128, 128)
let frame = 0
setInterval(() => { icon.setSourceRect((++frame % 8) * 128, 0, 128, 128) }, 100)

Ловушка: bgImage — не контент изображения

TypeScript
const img = UIImage("")            // ✗ пустой источник как заглушка
img.style.bgImage = url            // ✗ bgImage это декор контейнера, не контент
const img = UIImage(url)           // ✓ источник идёт в конструктор…
img.src = newUrl                   // ✓ …и меняется через .src

UIVideo

TypeScript
UIVideo(player: VideoPlayer): UIVideo

video.player       // только чтение — VideoPlayer, переданный при конструировании

Элемент — это лишь экранная поверхность; воспроизведение живёт целиком на VideoPlayer — создай его первым, управляй напрямую:

TypeScript
const player = new VideoPlayer(asset('./intro.mp4'))
const video = UIVideo(player).style({ width: "100%", height: 220, borderRadius: 12, objectFit: "cover" })
player.play()

Специфичный для видео стиль:

TypeScript
objectFit: "cover" | "contain" | "fill"

UIVideo также принимает рисуемые стили (border*, bgColor, …) для обрамления. Плеер — нативный ресурс — вызови dispose(), когда видео ушло навсегда, и останови воспроизведение в onClose экрана (см. Соглашения — Жизненный цикл).

Смотрите также