proc main(): void {
view := hive.ui.column([hive.ui.pad(24), hive.ui.gap(12)], [
hive.ui.text([hive.ui.size(hive.ui.TextSize.Title()), hive.ui.heading(1)], "Tour do Hive"),
hive.ui.row([hive.ui.gap(8)], [
hive.ui.input([hive.ui.placeholder("seu nome")], ""),
hive.ui.button([hive.ui.tone(hive.ui.Tone.Good())], "Dizer oi"),
]),
hive.ui.link([hive.ui.background(hive.ui.Tone.Good())], "/1", "Começar →"),
])
body := hive.ui.page("Hive", view) // um documento inteiro, servido diretamente
echo len(body)
}type Model {
posts: Str[dyn]
draft: Str
}
func view(model: Model): ui.View {
mut ui.View[dyn] kids = [
ui.input([ui.placeholder("diga algo")], model.draft),
ui.button([ui.disabled(model.draft == "")], "Publicar"),
]
for each post in model.posts {
append(kids, ui.text([], post))
}
return ui.column([ui.pad(24), ui.gap(8)], kids)
}
test "o botão fica desabilitado até algo ser digitado" {
empty := ui.html(view(Model([], "")))
assert indexOf(empty, "disabled") is Result.Ok(_)
typed := ui.html(view(Model([], "oi")))
assert indexOf(typed, "disabled") is Result.Error(_)
}
test "o corpo de um post não contrabandeia markup para a página" {
page := ui.html(view(Model(["<script>steal()</script>"], "")))
assert indexOf(page, "<script>") is Result.Error(_)
}hive.ui constrói uma interface como um valor: uma View é uma árvore de widgets, cada um recebendo primeiro seus atributos e depois o que mostra — exceto spacer() e none(), que não recebem nada, e spinner(attrs), que recebe só atributos.
row(attrs, views), column(attrs, views), e overlay(attrs, child) — um diálogo centralizado e escurecido, ou, com um align ou justify, um painel preso a uma borda que deixa os cliques passarem.text(attrs, content), image(attrs, src, alt), icon(attrs, Icon), link(attrs, href, label), table(attrs, rows) para uma Table, e canvas para desenhar, no seu próprio slide.button(attrs, label), input(attrs, value), textarea(attrs, value), checkbox(attrs, label, checked), select(attrs, options, chosen).Os atributos também são um conjunto fechado: gap, pad, width, height, grow, align, justify e scroll cuidam do layout — um width é limitado ao espaço que houver — size, heading, tone e background do estilo, e disabled, busy (que também desabilita), placeholder, hint e kind descrevem o estado. Um que não se aplica a um widget é ignorado. As enumerações por trás deles são conjuntos fechados alcançados como qualquer tipo da biblioteca: Align é Start, Center, End ou Stretch; Justify é Start, Center, End ou Between; TextSize é Title, Subtitle, Body ou Caption; Axis é Horizontal, Vertical ou Both; um InputKind é Text, Number, Date, Password, Search ou Range, um controle deslizante de 0 a 100; um Tone é Normal, Muted, Good, Warn ou Danger, ou uma cor exata por HEX e RGBA — um HEX malformado vira Normal e um canal RGBA é limitado à faixa; e um Icon é um de dezoito, então um nome que não seja um deles é erro de compilação.
html(view) renderiza uma árvore como um fragmento HTML e page(title, view) como um documento inteiro com a folha de estilo embutida, claro e escuro — o que um handler de httpServe pode responder diretamente, e o que toda tela deste tour, exceto os exemplos de código, é. Numa tela de toque, os controles crescem até a largura de um polegar e o texto de um campo até 16 pixels. Como uma view é um valor, uma interface pode ser testada sem navegador: um teste renderiza uma com html e lê a marcação, que é o lugar mais barato para conferir que um botão desabilitado está desabilitado e que o texto de outra pessoa não consegue contrabandear um script para a página.