Elementos
Dock
Uma área de docks é um espaço de trabalho que o usuário organiza: painéis em grupos de abas, grupos divididos lado a lado ou empilhados, e docks nas bordas esquerda, direita e inferior que abrem, fecham e redimensionam. Uma aba arrastada para outro grupo entra nele, arrastada para a borda de um grupo o divide, e cada painel mantém o que contém ao ser movido. DockArea é responsável pelo comportamento: arrastar e soltar abas, que roda de forma nativa quadro a quadro, redimensionar divisões e docks, abrir e fechar docks, zoom e o bloqueio. A aparência é do app: as abas, as barras de abas, as alças, o indicador de soltura e o conteúdo de cada painel são o que o app renderiza e estiliza, e a área não desenha nada por conta própria.
import { mount, signal, Text, View } from "nsh";
import { Button, DockArea, type DockAreaLayout, type DockAreaRef } from "nsh/base";
import { button, buttonText, desktop, focusRing, Log, Panel, Split, ui, wide } from "./palette";
// The panels' keys and what each shows. The layout lists the keys; the app
// renders a panel's content from its key, and the content moves with it.
const titles: Record<string, string> = {
files: "Files",
readme: "README.md",
main: "main.rs",
preview: "Preview",
terminal: "Terminal",
problems: "Problems",
};
const lines: Record<string, string[]> = {
files: ["src/", " main.rs", " lib.rs", "README.md", "Cargo.toml"],
readme: ["# Hopper", "", "A small app with a dockable workspace."],
main: ["fn main() {", " hopper::run();", "}"],
preview: ["Hopper", "A small app with a dockable workspace."],
terminal: ["$ cargo run", " Compiling hopper v0.1.0", " Finished dev in 1.92s"],
problems: ["No problems"],
};
const initial: DockAreaLayout = {
center: {
type: "split",
axis: "horizontal",
children: [
{ type: "tabs", panels: ["readme", "main"], activeIndex: 1 },
{ type: "tabs", panels: ["preview"] },
],
sizes: [null, 260],
},
left: { layout: { type: "tabs", panels: ["files"] }, size: 180 },
bottom: { layout: { type: "tabs", panels: ["terminal", "problems"] }, size: 130 },
};
function App() {
const layout = signal<DockAreaLayout>(initial);
const locked = signal(false);
// What shows now, in either presentation: the layout's docks when the area
// is wide, the drawer or sheet presented when it is narrow. It starts from
// the layout and follows onDockOpenChange.
const shown = signal<Record<string, boolean>>({ left: true, bottom: true });
const events = signal<string[]>([]);
const log = (line: string) => events.set([line, ...events()].slice(0, 4));
let area!: DockAreaRef;
const toolbarButton = (label: string, onPress: () => void, on?: () => boolean) => (
<Button onPress={onPress} selected={on?.() ?? false} {...button("secondary", { size: "sm", on })}>
<Text style={buttonText("secondary", { on })}>{label}</Text>
</Button>
);
return (
<View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
<Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>dock</Text>
<Text style={{ color: ui().muted }}>
{desktop
? "Drag a tab onto another group, or to a group's edge to split it"
: "Tap Files or Terminal to open a dock; hold a tab to reorder it"}
</Text>
<Split
grow
width={720}
inspector={
<>
<Panel title="docks">
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
{toolbarButton("Files", () => area.toggleDock("left"), () => shown().left)}
{toolbarButton("Terminal", () => area.toggleDock("bottom"), () => shown().bottom)}
{toolbarButton("Lock", () => locked.set(!locked()), locked)}
{toolbarButton("Reset", () => layout.set(initial))}
</View>
</Panel>
<Log title="events" lines={events()} minHeight={96} />
</>
}
>
<View style={{ height: wide() ? 420 : 460, ...ui().card, borderRadius: 14, overflow: "hidden" }}>
<DockArea
ref={area}
class="flex-1"
layout={layout()}
onLayoutChange={({ layout: next }) => {
layout.set(next);
log("onLayoutChange");
}}
onDockOpenChange={({ placement, open }) => {
shown.set({ ...shown(), [placement]: open });
log(`${placement} ${open ? "shown" : "hidden"}`);
}}
onPanelActiveChange={({ panel, active }) => active && log(`${titles[panel]} shown`)}
locked={locked()}
// The demo's two layouts: the full workspace beside the inspector in a
// wide window, the phone presentation in one column. Without `compact`
// the area decides by its own width (under 600 points: compact).
compact={!wide()}
groupClass="flex-1"
tabBarClass="flex-row border-b border-border"
tabClass="group px-3 py-1.5 selected:bg-muted"
contentClass="flex-1"
handleClass="bg-border group-hover:bg-muted-foreground group-active:bg-primary"
handleAccessibilityLabel="Resize"
dockClass="bg-muted/40"
dropIndicatorClass="rounded-(--radius) border-2 border-primary bg-primary/10"
drawerClass="bg-background border-r border-border"
sheetClass="rounded-t-(--radius-lg) bg-background"
backdropClass="bg-black/30"
renderTab={(tab) => (
// One view inside the tab: a row holding the title and the close button.
<View class="flex-row items-center gap-1.5">
<Text class="text-[13px] text-muted-foreground group-selected:text-foreground">{titles[tab.panel]}</Text>
{tab.closable && desktop ? (
<Button
onPress={() => area.closePanel(tab.panel)}
accessibilityLabel={`Close ${titles[tab.panel]}`}
class="rounded-sm px-1 hover:bg-muted active:opacity-70"
focusStyle={focusRing()}
>
<Text class="text-xs text-muted-foreground">×</Text>
</Button>
) : null}
</View>
)}
renderDragPreview={(tab) => (
<View class="rounded-(--radius) border border-border bg-background px-3 py-1.5 shadow-md">
<Text class="text-[13px] text-foreground">{titles[tab.panel]}</Text>
</View>
)}
renderPanel={(panel) => (
<View class="flex-1 gap-1 p-3">
{(lines[panel.id] ?? []).map((line) => (
<Text class="font-mono text-xs text-foreground">{line || " "}</Text>
))}
</View>
)}
renderEmpty={() => (
<View class="flex-1 items-center justify-center">
<Text class="text-sm text-muted-foreground">Every panel is closed</Text>
</View>
)}
/>
</View>
</Split>
</View>
);
}
mount(App);use nsh_base::{
dock::{DockAreaLayout, DockLayout, DockPanel, DockPlacement, DockRegion, DockTab},
dock_area, theme,
};
use nsh_core::prelude::*;
fn workspace() -> impl IntoElement {
let layout = signal(
DockAreaLayout::new(
DockLayout::h_split()
.child(DockLayout::tabs().panel("readme").panel("main"), None)
.child(DockLayout::tabs().panel("preview"), Some(px(320.0))),
)
.with_dock(DockPlacement::Left, DockRegion::new(DockLayout::tabs().panel("files")))
.with_dock(DockPlacement::Bottom, DockRegion::new(DockLayout::tabs().panel("terminal"))),
);
dock_area()
.layout(move || layout.get())
.on_layout_change(move |next: &DockAreaLayout| layout.set(next.clone()))
.handle_style(|s| s.bg(move || theme().border()))
.tab_bar_style(|s| s.flex_row().border_b_1().border_color(move || theme().border()))
.tab_style(|s| s.px_3().py_1().selected(|s| s.bg(move || theme().muted())))
.render_tab(|tab: &DockTab| text(tab.panel.to_string()).text_sm().text_color(move || theme().foreground()))
.render_panel(|panel: &DockPanel| {
text(format!("{} content", panel.id)).p_4().text_color(move || theme().foreground())
})
}import { mount, signal, Text } from "nsh";
import { DockArea, type DockAreaLayout } from "nsh/base";
const titles: Record<string, string> = {
files: "Files", readme: "README.md", main: "main.rs", preview: "Preview", terminal: "Terminal",
};
function App() {
const layout = signal<DockAreaLayout>({
center: {
type: "split",
axis: "horizontal",
children: [
{ type: "tabs", panels: ["readme", "main"] },
{ type: "tabs", panels: ["preview"] },
],
sizes: [null, 320],
},
left: { layout: { type: "tabs", panels: ["files"] }, size: 240 },
bottom: { layout: { type: "tabs", panels: ["terminal"] }, size: 180 },
});
return (
<DockArea
class="size-full bg-background"
layout={layout()}
onLayoutChange={(e) => layout.set(e.layout)}
handleClass="bg-border group-active:bg-primary"
tabBarClass="flex-row border-b border-border"
tabClass="px-3 py-1.5 selected:bg-muted"
renderTab={(tab) => <Text class="text-sm text-foreground">{titles[tab.panel]}</Text>}
renderPanel={(panel) => <Text class="p-4 text-foreground">{titles[panel.id]} content</Text>}
/>
);
}
mount(App);DockArea e os valores que ele recebe vêm de nsh/base, o crate nsh-base em Rust
(nsh_base::dock_area(), os valores em nsh_base::dock). Em Rust, dock_area() o cria, com um setter por
prop, um listener por evento e os métodos de estilo tipados; DockLayout descreve um layout pelas chaves dos painéis
(h_split(), v_split(), tabs(), .child(layout, size), .panel(key)).
#O layout
O layout é um valor, DockAreaLayout: o centro e um dock em cada uma das bordas esquerda, direita e inferior, cada um
opcional. Cada região é uma árvore com dois tipos de nó:
| Nó | Contém | Campos |
|---|---|---|
{ type: "split" } |
Outros nós, lado a lado (axis: "horizontal") ou empilhados ("vertical") |
children e sizes: o comprimento de cada filho ao longo do eixo, em pontos, null para dividir o restante |
{ type: "tabs" } |
Painéis, por chave, um exibido por vez | panels (as chaves, na ordem das abas), activeIndex (a exibida, padrão 0) |
Um painel sempre vive em um grupo de abas: uma região com um painel é um grupo de abas de um só. A chave de um painel é a string do app para ele
("files", o id de um documento), e uma chave aparece uma única vez em todo o layout. Um dock (DockRegion) é sua árvore
(layout), seu tamanho transversal à borda (size, padrão 200, no mínimo 100), se está aberto (open, padrão true)
e se arrastar sua borda abaixo do menor tamanho o fecha (collapsible, padrão true).
Todo nó tem um id. Um layout que o app escreve pode omitir ids: a área os atribui, e todo layout que ela reporta
os traz. Devolver um layout reportado preserva a identidade de cada grupo, pela qual os métodos do ref nomeiam os grupos. A
área também organiza o que recebe: um grupo ou divisão vazio é removido, uma divisão de um só filho vira esse filho, uma divisão
dentro de uma divisão do mesmo eixo é incorporada ao pai, activeIndex permanece dentro do intervalo, e uma chave listada duas vezes mantém sua primeira
posição. Os layouts que ela reporta sempre estão nessa forma canônica.
O layout é do app (layout): toda mudança feita pelo usuário (uma aba selecionada, arrastada ou fechada, uma divisão ou dock
redimensionado, um dock aberto ou fechado) é reportada como o novo layout completo (onLayoutChange), e a área exibe o
layout que o app devolve. O app altera o layout passando um novo valor: um layout salvo restaurado, um
comando que reorganiza o espaço de trabalho. Uma área sem layout mantém o seu próprio, partindo de defaultLayout, e
continua reportando todas as mudanças.
#Conteúdo dos painéis
O que um painel exibe é do app: renderPanel o renderiza a partir da chave do painel (panel.id). Todos os painéis de um grupo são
posicionados e apenas o exibido é mostrado, então trocar de aba preserva o conteúdo de cada painel; e quando um painel se move (para
outro grupo, para um dock, para um zoom e de volta), o mesmo conteúdo se move com ele: os signals que criou, o texto de um input, a posição de rolagem, a página de uma web view, tudo permanece. A área renderiza um painel uma vez, quando ele aparece pela primeira vez, e descarta o
conteúdo quando o painel deixa o layout (onPanelRemove).
<DockArea
layout={layout()}
onLayoutChange={(e) => layout.set(e.layout)}
renderPanel={(panel) => (panel.id === "terminal" ? <Terminal /> : <Editor file={panel.id} />)}
onPanelActiveChange={({ panel, active }) => active && setTitle(panel)}
onPanelRemove={({ panel }) => closeFile(panel)}
/>
panels define o que cada painel permite, por chave; um painel não listado permite tudo:
| Campo | Padrão | Descrição |
|---|---|---|
id |
A chave do painel. | |
closable |
true |
Pode ser fechado. |
zoomable |
true |
Seu grupo pode receber zoom enquanto ele está exibido. |
visible |
true |
Ele é mostrado. Um painel oculto mantém seu lugar e volta para ele; um grupo ou divisão cujos painéis estão todos ocultos não ocupa espaço. |
#Abas
Cada grupo tem uma barra de abas com uma aba por painel visível, na ordem de seu panels. renderTab renderiza o que uma aba
mostra (seu título, um ícone, um botão de fechar do app); sem ele, uma aba mostra a chave do painel. A área a transforma na
aba: um toque a seleciona, um arrasto move seu painel, leitores de tela a anunciam como uma aba com seu estado de seleção, e as
classes selected: de tabClass estilizam a exibida. O valor de uma aba informa o que o app precisa para desenhá-la:
| Campo | Tipo | Descrição |
|---|---|---|
panel |
string |
A chave do painel. |
group |
number |
O id do grupo de abas em que ela está. |
index |
number |
Sua posição entre as abas visíveis do grupo. |
active |
boolean |
É a aba exibida do grupo. |
closable |
boolean |
A área a fecharia: ela é fechável, a área não está bloqueada e seu grupo pode abrir mão dela. |
zoomed |
boolean |
Seu grupo está com zoom. |
let area!: DockAreaRef;
<DockArea
ref={area}
layout={layout()}
onLayoutChange={(e) => layout.set(e.layout)}
renderTab={(tab) => (
<View class="flex-row items-center gap-2">
<Text class="text-sm">{titles[tab.panel]}</Text>
<If when={tab.closable}>
<Button onPress={() => area.closePanel(tab.panel)} accessibilityLabel="Close" class="rounded-sm px-1 hover:bg-muted">
<Text class="text-xs">×</Text>
</Button>
</If>
</View>
)}
renderPanel={(panel) => <Editor file={panel.id} />}
/>
#Arrastar e soltar
Uma aba arrastada sobre um grupo mostra onde ela pousaria, e a soltura a coloca ali:
| Solta sobre | O painel |
|---|---|
| Uma barra de abas | Entra nesse grupo, entre as abas ao lado das quais é solto |
| O meio do conteúdo de um grupo | Entra nesse grupo |
| Uma borda do conteúdo de um grupo (os 35% externos de cada lado) | Divide esse grupo: um novo grupo só dele nesse lado |
Enquanto um arrasto paira, a área mostra o indicador de soltura sobre a parte do grupo que o painel ocuparia (todo o
conteúdo para entrar, metade dele para dividir): uma parte que o
app estiliza (dropIndicatorClass), com conteúdo próprio se o app renderizar algum (renderDropIndicator, que recebe o
group, o side que ele separa ou a barra de abas index). Ele se move para cada novo lugar de forma nativa, suavizando a transição e surgindo gradualmente em outro grupo enquanto o último desaparece (imediatamente quando o usuário pede menos movimento). O que acompanha o ponteiro ou o dedo também é do app
(renderDragPreview). Uma soltura é uma única mudança do layout, reportada uma vez; uma soltura que não mudaria nada não muda nada.
Uma área bloqueada, uma com zoom e o último painel de um grupo quando nada está ao lado do grupo não permitem arrastar.
#Docks
Um dock abre e fecha sem perder seus painéis nem seu tamanho: fechá-lo mantém sua árvore, e abri-lo mostra os
mesmos grupos no mesmo tamanho. A borda entre um dock e o centro é uma alça que o usuário arrasta para redimensionar o dock;
ele permanece com pelo menos 100 pontos e o centro mantém pelo menos 100 pontos, e um dock recolhível solto abaixo de 50 pontos
se fecha e guarda seu tamanho para quando abrir. A área sempre cabe em seus limites: um dock é exibido em seu size dentro do que
a área lhe deixa, e quando nem isso cabe, os docks encolhem junto com o restante e os tamanhos mínimos das divisões
cedem. renderDock desenha o chrome próprio de um dock antes de seu layout (uma faixa de título, um botão de recolher), recebendo seu
placement, open, size e collapsible; dockClass estiliza seu quadro.
Abra, feche e redimensione docks a partir do app pelo ref (toggleDock, setDockOpen, setDockSize), que funcionam
nas duas apresentações, ou escrevendo open e size do dock no layout que ele passa (open é o da
apresentação completa). onDockOpenChange reporta
cada vez que um dock é mostrado ou ocultado, medido em relação ao open do layout (uma área que é compacta desde o início reporta
cada dock aberto no layout como oculto), de modo que um alternador que parte do open do layout e segue onDockOpenChange sempre
mostra o estado do dock:
const terminalOpen = signal(true);
<DockArea
ref={area}
layout={layout()}
onLayoutChange={(e) => layout.set(e.layout)}
onDockOpenChange={({ placement, open }) => placement === "bottom" && terminalOpen.set(open)}
renderPanel={(panel) => <Editor file={panel.id} />}
/>
<Button selected={terminalOpen()} onPress={() => area.toggleDock("bottom")} class="px-3 py-1.5 selected:bg-muted">
<Text>Terminal</Text>
</Button>
#Zoom
Um grupo com zoom preenche toda a área até que o zoom seja desfeito ou ele deixe o layout (area.toggleZoom(group), e area.toggleZoom() para desfazer o
zoom), e onZoomChange reporta o painel que ele exibe, ou null. O zoom pertence ao grupo, então ele continua com zoom
quando seu painel exibido é fechado e o próximo assume. Um painel com zoomable: false impede que seu grupo receba
zoom enquanto ele está exibido.
#Bloqueio
locked mantém a organização como está: sem arrastar abas, sem soltar, sem fechar. Selecionar abas, redimensionar e abrir e
fechar docks continuam funcionando, então um espaço de trabalho bloqueado ainda é um espaço de trabalho utilizável.
#Métodos
ref={area} fornece os métodos da área (DockAreaRef), para comandos, atalhos e menus do app. Cada edição é
reportada com onLayoutChange como as do usuário. Um método que recebe um painel, um grupo, um índice de aba ou um dock que o layout
não tem deixa o layout como está e registra um aviso com o nome do método e o valor.
| Método | Descrição |
|---|---|
layout() |
Promise<DockAreaLayout>: o layout exibido. |
addPanel(panel, placement?, size?) |
Adiciona um painel a uma região ("center", o padrão, "left", "right", "bottom"), em seu primeiro grupo de abas, exibido; um dock ausente é criado, com size de largura. Um painel que a área já contém é movido para lá. |
removePanel(panel) |
Remove um painel, independentemente do que suas opções dizem. |
closePanel(panel) |
Fecha um painel como sua aba faria: a menos que a área esteja bloqueada, o painel não seja fechável ou seja o último painel de um grupo sem nada ao lado. |
movePanel(panel, target) |
Move um painel para target: { group, side?, index?, activate?, size? }, para o grupo de abas group (em index) ou para um novo grupo em seu side (size pontos ao longo da nova divisão). |
splitAt(group, panel, side) |
Coloca um painel em um novo grupo de abas em um lado ("left", "right", "top", "bottom") de um contêiner. |
selectPanel(panel) |
Exibe um painel em seu grupo. |
selectTab(group, index) |
Exibe uma aba de um grupo. |
toggleDock(placement), setDockOpen(placement, open) |
Abre ou fecha um dock. |
setDockSize(placement, size) |
Define o tamanho de um dock transversal à sua borda (no mínimo 100). |
toggleZoom(group?) |
Aplica zoom em um grupo, ou o desfaz quando ele já está com zoom; sem group, desfaz o zoom do grupo com zoom. |
zoomedGroup() |
Promise<number | null>: o id do grupo com zoom. |
groupOf(panel) |
Promise<number | null>: o id do grupo de abas que contém um painel. |
Em Rust, component_ref::<DockArea>() e .component_ref(&r) na área fornecem um ref cujo r.api::<DockAreaApi>() tem
os mesmos métodos em snake case.
#Salvar e restaurar
Um layout reportado é um dado simples, então salvá-lo é JSON.stringify(layout) em storage,
e restaurá-lo é passar o valor analisado como layout. onLayoutChange reporta toda mudança, inclusive um redimensionamento e uma seleção de
aba, então grave no storage quando as mudanças se acalmarem (um temporizador curto reiniciado a cada relatório).
Em Rust, DockAreaLayout::to_state e from_state leem e gravam DockAreaState, o formato de arquivo de layout que
o dock de gpui-base também usa.
#Celulares e janelas estreitas
O dock se adapta à sua própria largura. Uma área com 600 pontos de largura ou mais (em uma janela no desktop, na web, em um tablet, em um iPad em tela cheia, em um dobrável aberto) é o espaço de trabalho completo: divisões, grupos e docks lado a lado, como nos apps multipainel de um tablet. Com o dedo em um celular ou tablet, o arrasto de uma aba começa com um toque longo, como no arrastar e soltar do iPadOS, de modo que uma barra de abas ainda role com um deslize, e as alças ganham uma área de toque maior com a mesma aparência.
Uma área com menos de 600 pontos (em um celular, em um iPad no Slide Over, em uma janela estreita de desktop ou navegador, ou em uma coluna estreita de uma área larga) apresenta o mesmo layout para uma tela, como as split views do iPad recolhem suas colunas e o painel de apoio do Material Design vira uma bottom sheet:
- O centro mostra um grupo por vez, em tamanho total. Sua barra de abas lista todos os painéis visíveis do centro, e selecionar um painel de outro grupo faz desse grupo o exibido.
- Os docks esquerdo e direito são gavetas sobre o centro a partir de sua borda, tão largas quanto o dock, e o dock inferior é uma
sheet tão alta quanto o dock. Um é mostrado por vez, quando o app o apresenta (
toggleDock,setDockOpen, que dispensam o outro); ele desliza para dentro e para fora, e um toque em seu backdrop, Escape ou voltar o dispensam. Entrar na apresentação compacta não mostra nenhum. - Uma aba arrastada (um dedo após um toque longo, um ponteiro imediatamente) se move dentro da barra de abas para reordená-la.
Todo painel continua posicionado nas duas apresentações, então a troca preserva seu conteúdo. A gaveta ou sheet exibida é
própria da apresentação compacta: o open do layout pertence à apresentação completa, e apresentar ou dispensar uma
gaveta não reporta onLayoutChange, de modo que uma área que volta a se alargar mostra os docks exatamente como estavam.
onDockOpenChange reporta ambos: um dock cujo open do layout muda na apresentação completa, uma gaveta ou sheet mostrada
ou dispensada na compacta, e cada dock cujo estado exibido muda quando a apresentação é trocada. Em
renderDock, open indica se o dock é mostrado na apresentação atual. O layout também continua sendo o mesmo valor,
então uma área que cruza 600 pontos (um dobrável abrindo, um iPad entrando no Split View, uma janela ou uma coluna redimensionada)
mantém cada painel onde estava, e o app salva um único layout. A largura da área decide depois que ela é posicionada (a
largura da janela a substitui antes disso), e compact escolhe a apresentação em vez da largura. As
gavetas, a sheet e o backdrop são partes que o app estiliza (drawerClass, sheetClass, backdropClass), como o
restante da área.
<DockArea
layout={layout()}
onLayoutChange={(e) => layout.set(e.layout)}
drawerClass="bg-background border-r border-border"
sheetClass="rounded-t-(--radius-lg) bg-background"
backdropClass="bg-black/40"
renderTab={(tab) => <Text>{titles[tab.panel]}</Text>}
renderPanel={(panel) => <Editor file={panel.id} />}
/>
#Props
| Prop | Rust | Tipo | Padrão | Descrição |
|---|---|---|---|---|
layout |
.layout(..) |
DockAreaLayout |
nenhum | O layout exibido (controlado): passe de volta o que onLayoutChange informa. |
defaultLayout |
.default_layout(..) |
DockAreaLayout |
um centro vazio | O layout inicial, quando layout não está definido; a partir daí a área mantém o seu próprio. |
locked |
.locked(..) |
boolean |
false |
Nenhuma aba é arrastada, solta ou fechada. |
compact |
.compact(..) |
boolean |
por largura | A apresentação de celular (true) ou a completa (false); sem ela, compacta enquanto a área for mais estreita que 600 pontos. |
panels |
.panels(..) |
DockPanelOptions[] |
[] |
O que cada painel permite, por chave: closable, zoomable, visible. |
renderPanel |
.render_panel(|panel| ..) |
(panel: DockPanel) => JSX.Element |
nenhum | O conteúdo de um painel, pela sua chave (panel.id). |
renderTab |
.render_tab(|tab| ..) |
(tab: DockTab) => JSX.Element |
a chave | O que uma aba exibe. |
renderDropIndicator |
.render_drop_indicator(|drop| ..) |
(drop: DockDropIndicator) => JSX.Element |
nenhum | O que há dentro do indicador de soltura: group, side, index. |
renderDock |
.render_dock(|dock| ..) |
(dock: DockInfo) => JSX.Element |
nenhum | O chrome próprio de um dock, antes do seu layout: placement, open (exibido na apresentação atual), size, collapsible. |
renderEmpty |
.render_empty(|placement| ..) |
(placement: DockPlacement) => JSX.Element |
nenhum | O que um centro vazio exibe. |
renderDragPreview |
.render_drag_preview(|tab| ..) |
(tab: DockTab) => JSX.Element |
nenhum | O que acompanha o ponteiro ou o dedo enquanto uma aba é arrastada. |
groupClass, groupStyle |
.group_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | O quadro de cada grupo de abas. |
tabBarClass, tabBarStyle |
.tab_bar_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | Cada barra de abas (flex-row para uma linha). |
tabClass, tabStyle |
.tab_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | Cada aba; selected: para a exibida. |
contentClass, contentStyle |
.content_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | O quadro de conteúdo de cada grupo, onde seus painéis são exibidos. |
dropIndicatorClass, dropIndicatorStyle |
.drop_indicator_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | O indicador de soltura. |
handleClass, handleStyle |
.handle_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | O indicador de cada alça, entre os grupos de uma divisão e na borda de cada dock; group-hover: e group-active: acompanham a alça. Ele tem 1 ponto de espessura na alça e percorre todo o seu comprimento, a menos que se diga o contrário, então bg-border é uma linha fina em ambos os casos; w-[3px] / h-[3px] o engrossam. |
handleThumbClass, handleThumbStyle |
.handle_thumb_class(..), .handle_thumb_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | O pegador de cada alça: uma empunhadura centralizada na linha, desenhada na vertical como em uma linha vertical e girada um quarto de volta em uma horizontal, de modo que uma única classe serve aos dois eixos. Ele continua sendo a mesma view enquanto a alça recebe hover, é pressionada e arrastada, então as classes group-hover: e group-active: fazem a transição dele (h-11 w-[3px] rounded-full bg-muted-foreground opacity-0 scale-y-0 transition group-hover:opacity-40 group-hover:scale-y-[0.45] group-active:opacity-90 group-active:scale-y-100). |
dockClass, dockStyle |
.dock_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | O quadro de cada dock. |
drawerClass, drawerStyle |
.drawer_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | Compacto: as gavetas dos docks laterais. |
sheetClass, sheetStyle |
.sheet_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | Compacto: a folha do dock inferior. |
backdropClass, backdropStyle |
.backdrop_style(|s| ..) |
string, StyleProp<ViewStyle> |
nenhum | Compacto: o fundo atrás de uma gaveta ou folha aberta, cujo pressionamento a fecha. |
handleAccessibilityLabel |
.handle_accessibility_label(..) |
string |
nenhum | O que os leitores de tela dizem para cada alça, entre os grupos de uma divisão e na borda de cada dock. |
ref |
.component_ref(&r) (component_ref::<DockArea>()) |
DockAreaRef |
Os métodos da área. | |
class, style |
métodos de estilo | string, StyleProp<ViewStyle> |
nenhum | A própria área: seu tamanho no elemento pai (flex-1), seu fundo. Ela permanece dentro dos seus limites e não assume nenhum tamanho mínimo a partir do seu conteúdo. |
#Eventos
| Evento | Rust | Descrição |
|---|---|---|
onLayoutChange |
.on_layout_change(|layout: &DockAreaLayout| ..) |
O layout mudou (um arrasto, uma seleção, um redimensionamento, um dock aberto ou fechado, um método): { layout }, o novo layout completo, com todos os ids. |
onPanelActiveChange |
.on_panel_active_change(|e: &DockAreaEvent| ..) (PanelActiveChange(panel, active)) |
{ panel, active }: um painel passou a ser, ou deixou de ser, a aba exibida do seu grupo (um dock fechado não exibe nenhuma). Uma vez por mudança; um painel que sai não recebe false: onPanelRemove é o seu fim. |
onPanelRemove |
.on_panel_remove(|panel: &PanelId| ..) |
{ panel }: um painel saiu da área definitivamente: fechado, removido ou ausente de um novo layout. Um painel que apenas muda de lugar nunca o recebe. |
onZoomChange |
.on_zoom_change(|panel: &Option<PanelId>| ..) |
Um grupo foi ampliado ou restaurado: { panel }, o painel que ele exibe, ou null. |
onDockOpenChange |
.on_dock_open_change(|e: &DockAreaEvent| ..) (DockOpenChange(placement, open)) |
{ placement, open }: um dock foi exibido ou ocultado: o open do seu layout mudou na apresentação completa, sua gaveta ou folha foi exibida ou dispensada na compacta, ou a apresentação mudou. |
#Partes
| Parte | Dentro de | Estilizada com | Estados |
|---|---|---|---|
| A área | class, style |
||
| Um grupo de abas | uma região | groupClass, groupStyle |
|
| Uma barra de abas | um grupo | tabBarClass, tabBarStyle |
|
| Uma aba | uma barra de abas | tabClass, tabStyle; renderTab |
selected:, active:, hover: |
| O conteúdo de um grupo | um grupo | contentClass, contentStyle; renderPanel |
|
| O indicador de uma alça | entre os grupos de uma divisão, na borda de um dock | handleClass, handleStyle |
group-hover:, group-active: |
| O pegador de uma alça | centralizado na linha de uma alça | handleThumbClass, handleThumbStyle |
group-hover:, group-active: (pressionado até o fim do arrasto) |
| Um dock | uma borda | dockClass, dockStyle; renderDock |
|
| O indicador de soltura | sobre um grupo durante um arrasto | dropIndicatorClass, dropIndicatorStyle; renderDropIndicator |
|
| A prévia do arrasto | sob o ponteiro ou o dedo | renderDragPreview |
|
| O centro vazio | o centro | renderEmpty |
|
| Uma gaveta, a folha, o fundo | sobre o centro, no modo compacto | drawerClass, sheetClass, backdropClass (e seus estilos) |
#Acessibilidade
- Cada aba é uma aba com seu estado de selecionada para os leitores de tela, como
Tabé, e ativá-la a seleciona. Seu rótulo é o texto querenderTabdesenha. - Toda alça, entre os grupos de uma divisão e na borda de cada dock, é um elemento ajustável com o rótulo
handleAccessibilityLabel. As alças entre os grupos de uma divisão são as de Resizable: um leitor de tela as move 10 pontos por vez, informados comonLayoutChange. - Na apresentação compacta, Escape e voltar dispensam a gaveta ou a folha exibida.
Veja Acessibilidade para rótulos, papéis e anúncios em geral.
#Notas de plataforma
- Um ponteiro (um mouse, um trackpad, uma caneta pairando sobre a tela) arrasta uma aba depois de se mover 4 pontos com o botão pressionado, em todas as plataformas: um iPad com trackpad e uma janela estreita de navegador controlada por um arrasto de mouse funcionam como um desktop. No macOS, Windows, Linux e GPUI todo toque conta como de um ponteiro, inclusive o de um dedo na tela sensível ao toque de um laptop.
- Um dedo no iOS, iPadOS e Android arrasta uma aba após um toque longo, como o arrastar e soltar do iPadOS, então um deslize sobre a barra de abas a rola.
- Desktops e a web: as alças mostram o cursor de redimensionamento do seu eixo.
- Celulares e áreas estreitas: a apresentação compacta, com gavetas e uma folha inferior.
- Todas as plataformas desenham o que o app estiliza: abas, barras de abas, alças, o indicador de soltura, docks, gavetas e a folha.