LeCodesdocs

Контейнеры UI

Структурные элементы: UIColumn стекает детей вертикально, UIRow — горизонтально, UIScrollable делает область переполнения панируемой, UISpacer съедает свободное место. Для целого экрана, который прокручивается, предпочитай UIScreen.makeScrollable() оборачиванию всего в UIScrollable — см. screen-router.md.

Обзор

TypeScript
const screen = UIScreen([
  UIRow([
    UIText("Library").style({ fontSize: 24, fontWeight: 700, color: "white" }),
    UISpacer(),                                          // толкает счётчик к дальнему краю
    UIText("12 items").style({ color: "#888" }),
  ]).style({ px: 16, py: 12, alignItems: "center" }),

  UIScrollable([
    UIColumn([ UIText("First").style({ color: "white" }) ]).style({ p: 16 }),
    UIColumn([ UIText("Second").style({ color: "white" }) ]).style({ p: 16 }),
  ]).style({ flexGrow: 1, flexShrink: 1, gap: 8 })       // flexShrink: 1 иначе переполнится
    .onScroll(pos => console.log("scrolled to", pos)),
]).style({ bgColor: "black", pt: "safe-top" })

screen.open()

UIRow / UIColumn

TypeScript
UIColumn(): UIColumn
UIColumn(children: UINodeChild[]): UIColumn

UIRow(children: UINodeChild[]): UIRow          // без перегрузки без аргументов — используй UIRow([])

Единственная разница между ними — дефолтный flexDirection (column против row) — оба принимают полную поверхность стиля контейнера (UIContainerStyle = свойства элемента + рисуемого + раскладки контейнера, см. styling.md). Записи-дети, равные null / undefined / false, пропускаются — см. overview.md.

Избегай контейнеров-обёрток, существующих только для выравнивания чего-то — выравнивание это свойство контейнера:

TypeScript
UIRow([ UIColumn([]).style({ flexGrow: 1 }), label ])      // ✗ фантомный элемент-распорка
UIRow([ label ]).style({ justifyContent: "flex-end" })     // ✓

Управление детьми

Все контейнеры на этой странице делят один императивный API детей (работает до и после появления элемента на экране; без диффинга):

TypeScript
c.append(...nodes: UINodeChild[]): this
c.insert(index: number, ...nodes: UINodeChild[]): this   // индекс в c.children
c.remove(...nodes: UINode[]): this                        // удаляет по идентичности
c.setContent(children: UINodeChild[]): this               // заменить всё
c.children                                                 // readonly UINodeChild[]

setContent — примитив «ре-рендера» — построй свежий массив (напр. items.map(Row)) и подмени его. Для длинных или неограниченных данных используй UIVirtualizedList.

UIBox — устарел

TypeScript
UIBox(children | style | (style, children)): UIBox   // легаси — используй UIRow / UIColumn

Легаси-контейнер, чьё единственное отличие — дефолт justifyContent и alignItems в "center" (дети центрированы по обеим осям). Оставлен для старых проектов; новый код пиши с UIRow/UIColumn плюс явное выравнивание.

UIScrollable

Панируемая область переполнения внутри в остальном фиксированной раскладки — список под закреплённой шапкой, горизонтальная карусель. Если весь экран — один поток (статья, лента), сделай прокручиваемым сам экран: pull-to-refresh (onRefresh) есть только на UIScreen.makeScrollable().

TypeScript
UIScrollable(): UIScrollable
UIScrollable(children: UINodeChild[]): UIScrollable

Дополнительные стили поверх поверхности контейнера:

TypeScript
scrollDirection: "horizontal" | "vertical"      // по умолчанию vertical
showScrollbar:   boolean
overscrollMode:  "none" | "absorb" | "default"  // поведение края при протягивании за контент

События прокрутки (чейнятся, как все on*):

TypeScript
s.onScroll(cb: (scrollPosition: number) => void): UIScrollable   // логические px от стартового края
s.onScrollRelease(cb: () => void): UIScrollable                  // палец поднят
s.onOverscroll(cb: (delta: number) => void): UIScrollable        // протянут за край (px)
Note

scrollTo нет — позицию UIScrollable нельзя задать программно. Это текущее ограничение. Если нужна программная прокрутка (scrollTo / scrollToEnd / scrollToKey), используй UIVirtualizedList.

Note

flexShrink по умолчанию 0 везде, поэтому вертикальный UIScrollable в колонке переполняет экран вместо прокрутки, пока он — и любые оборачивающие контейнеры между ним и экраном — не получат flexShrink: 1.

Горизонтальная карусель:

TypeScript
UIScrollable(items.map(Card))
  .style({ scrollDirection: "horizontal", showScrollbar: false, gap: 12, px: 16 })

Перетаскиваемым детям внутри скроллера нужно claim их направление жеста, иначе прокрутка украдёт указатель — см. touch.md.

UISpacer

TypeScript
UISpacer(): UISpacer      // без детей; его поверхность стиля — только ElementStyle — без фона

Гибкое пустое место: по умолчанию flexGrow: 1, съедает свободное место вдоль главной оси родителя. Тянись к нему, только когда обычное выравнивание не выражает раскладку — один элемент, толкнутый к дальнему краю, пока остальные стоят:

TypeScript
UIRow([ title, UISpacer(), closeButton ])

Если все дети двигаются вместе, justifyContent ("space-between", "flex-end", …) делает ту же работу без лишнего элемента.

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