Questa pagina è stata tradotta automaticamente e può contenere errori. Leggi l'originale in inglese

Elementi

Dock

Un’area dock è uno spazio di lavoro che l’utente organizza: pannelli in gruppi di schede, gruppi affiancati o impilati, e dock sui bordi sinistro, destro e inferiore che si aprono, si chiudono e si ridimensionano. Una scheda trascinata su un altro gruppo vi si unisce, trascinata sul bordo di un gruppo lo divide, e ogni pannello conserva ciò che contiene mentre si sposta. DockArea gestisce il comportamento: il trascinamento e il rilascio delle schede, che avvengono in modo nativo fotogramma per fotogramma, il ridimensionamento di divisioni e dock, l’apertura e la chiusura dei dock, lo zoom e il blocco. L’aspetto è dell’app: le schede, le barre delle schede, le maniglie, l’indicatore di rilascio e il contenuto di ogni pannello sono ciò che l’app renderizza e stila, e l’area non disegna nulla di suo.

el-dock.tsx
el-dock.tsxLive
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())
        })
}

DockArea e i valori che accetta provengono da nsh/base, il crate nsh-base in Rust (nsh_base::dock_area(), i valori in nsh_base::dock). In Rust, dock_area() lo crea, con un setter per ogni prop, un listener per ogni evento e i metodi di stile tipizzati; DockLayout descrive un layout tramite le chiavi dei pannelli (h_split(), v_split(), tabs(), .child(layout, size), .panel(key)).

#Il layout

Il layout è un valore, DockAreaLayout: il centro e un dock su ciascuno dei bordi sinistro, destro e inferiore, ognuno opzionale. Ogni regione è un albero con due tipi di nodo:

NodoContieneCampi
{ type: "split" }Altri nodi, affiancati (axis: "horizontal") o impilati ("vertical")children e sizes: la lunghezza di ciascun figlio lungo l’asse in punti, null per dividere lo spazio restante
{ type: "tabs" }Pannelli, per chiave, uno visibile alla voltapanels (le chiavi, nell’ordine delle schede), activeIndex (quello visibile, predefinito 0)

Un pannello vive sempre in un gruppo di schede: una regione con un solo pannello è un gruppo di schede con una sola scheda. La chiave di un pannello è la stringa che l’app usa per identificarlo ("files", l’id di un documento), e una chiave compare una sola volta in tutto il layout. Un dock (DockRegion) è il suo albero (layout), la sua dimensione trasversale rispetto al bordo (size, predefinito 200, minimo 100), il fatto che sia aperto (open, predefinito true) e il fatto che trascinarne il bordo sotto la dimensione minima lo chiuda (collapsible, predefinito true).

Ogni nodo ha un id. Un layout scritto dall’app può omettere gli id: l’area li assegna, e ogni layout che segnala li include. Passare di nuovo un layout segnalato mantiene l’identità di ogni gruppo, con cui i metodi del ref identificano i gruppi. L’area inoltre riordina ciò che riceve: un gruppo o una divisione vuoti vengono eliminati, una divisione con un solo figlio diventa quel figlio, una divisione dentro una divisione con lo stesso asse si unisce al genitore, activeIndex rimane nell’intervallo valido, una chiave elencata due volte mantiene la prima posizione. I layout che segnala hanno sempre quell’unica forma canonica.

Il layout appartiene all’app (layout): ogni modifica fatta dall’utente (una scheda selezionata, trascinata o chiusa, una divisione o un dock ridimensionati, un dock aperto o chiuso) viene segnalata come l’intero nuovo layout (onLayoutChange), e l’area mostra il layout che l’app le restituisce. L’app modifica il layout passando un nuovo valore: un layout salvato ripristinato, un comando che riorganizza lo spazio di lavoro. Un’area senza layout mantiene il proprio layout, a partire da defaultLayout, e segnala comunque ogni modifica.

#Il contenuto dei pannelli

Ciò che un pannello mostra è dell’app: renderPanel lo renderizza a partire dalla chiave del pannello (panel.id). Ogni pannello di un gruppo viene posizionato e solo quello visualizzato viene mostrato, quindi cambiare scheda conserva il contenuto di ciascun pannello; e quando un pannello si sposta (in un altro gruppo, in un dock, in uno zoom e ritorno), lo stesso contenuto si sposta con lui: i signal che ha creato, il testo di un input, una posizione di scorrimento, la pagina di una web view restano tutti. L’area renderizza un pannello una sola volta, quando compare per la prima volta, e ne rilascia il contenuto quando il pannello esce dal 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 imposta ciò che ogni pannello consente, per chiave; un pannello non elencato consente tutto:

CampoPredefinitoDescrizione
idLa chiave del pannello.
closabletruePuò essere chiuso.
zoomabletrueIl suo gruppo può andare in zoom mentre è visualizzato.
visibletrueViene mostrato. Un pannello nascosto mantiene il proprio posto e vi ritorna; un gruppo o una divisione i cui pannelli sono tutti nascosti non occupa spazio.

#Schede

Ogni gruppo ha una barra delle schede con una scheda per ogni pannello visibile, nell’ordine di panels. renderTab renderizza ciò che una scheda mostra (il titolo, un’icona, un pulsante di chiusura dell’app); senza di esso una scheda mostra la chiave del pannello. L’area la rende una scheda a tutti gli effetti: una pressione la seleziona, un trascinamento sposta il suo pannello, gli screen reader la annunciano come scheda con il suo stato di selezione, e le classi selected: di tabClass stilano quella visualizzata. Il valore di una scheda indica ciò che serve all’app per disegnarla:

CampoTipoDescrizione
panelstringLa chiave del pannello.
groupnumberL’id del gruppo di schede in cui si trova.
indexnumberLa sua posizione tra le schede visibili del gruppo.
activebooleanÈ la scheda visualizzata del gruppo.
closablebooleanL’area la chiuderebbe: è chiudibile, l’area non è bloccata e il suo gruppo può cederla.
zoomedbooleanIl suo gruppo è in 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} />}
/>

#Trascinamento e rilascio

Una scheda trascinata su un gruppo mostra dove atterrerebbe, e il rilascio la colloca lì:

Rilasciata suIl pannello
Una barra delle schedeSi unisce a quel gruppo, tra le schede accanto a cui viene rilasciato
Il centro del contenuto di un gruppoSi unisce a quel gruppo
Un bordo del contenuto di un gruppo (il 35% esterno su ogni lato)Divide quel gruppo: un nuovo gruppo tutto suo su quel lato

Durante il passaggio del trascinamento, l’area mostra l’indicatore di rilascio sulla parte del gruppo che il pannello occuperebbe (tutto il contenuto per unirsi, metà per dividere): una parte che l’app stila (dropIndicatorClass), con un contenuto proprio se l’app ne renderizza uno (renderDropIndicator, a cui vengono passati group, il side che separa o la barra delle schede index). Si sposta verso ogni nuova posizione in modo nativo, con un’animazione di transizione e una dissolvenza in entrata su un altro gruppo mentre quello precedente svanisce (immediatamente quando l’utente chiede meno movimento). Anche ciò che segue il puntatore o il dito è dell’app (renderDragPreview). Un rilascio è una singola modifica del layout, segnalata una sola volta; un rilascio che non cambierebbe nulla non cambia nulla.

Un’area bloccata, un’area in zoom e l’ultimo pannello di un gruppo quando accanto al gruppo non c’è nulla non permettono alcun trascinamento.

#Dock

Un dock si apre e si chiude senza perdere i suoi pannelli né la sua dimensione: chiuderlo ne conserva l’albero, e aprirlo mostra gli stessi gruppi con la stessa dimensione. Il bordo tra un dock e il centro è una maniglia che l’utente trascina per ridimensionare il dock; la sua dimensione resta di almeno 100 punti e il centro mantiene almeno 100 punti, e un dock comprimibile rilasciato sotto i 50 punti si chiude e conserva la propria dimensione per quando verrà riaperto. L’area si adatta sempre ai propri limiti: un dock viene mostrato con il suo size entro lo spazio che l’area gli lascia, e quando nemmeno questo basta, i dock si riducono insieme al resto e le dimensioni minime delle divisioni cedono. renderDock disegna il chrome proprio del dock prima del suo layout (una striscia del titolo, un pulsante di riduzione), ricevendo placement, open, size e collapsible; dockClass ne stila il riquadro.

Apri, chiudi e ridimensiona i dock dall’app tramite il ref (toggleDock, setDockOpen, setDockSize), che funzionano in entrambe le presentazioni, oppure scrivendo open e size del dock nel layout che passi (open appartiene alla presentazione completa). onDockOpenChange segnala ogni volta che un dock viene mostrato o nascosto, misurato rispetto al open del layout (un’area che è compatta fin dall’inizio segnala come nascosto ogni dock aperto nel layout), così un interruttore che parte dal open del layout e segue onDockOpenChange mostra sempre lo stato del 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

Un gruppo in zoom riempie l’intera area finché non viene riportato a dimensione normale o esce dal layout (area.toggleZoom(group), e area.toggleZoom() per annullare lo zoom), e onZoomChange segnala il pannello che visualizza, oppure null. Lo zoom appartiene al gruppo, quindi resta in zoom quando il pannello visualizzato si chiude e subentra il successivo. Un pannello con zoomable: false impedisce al proprio gruppo di andare in zoom mentre è visualizzato.

#Blocco

locked mantiene la disposizione così com’è: nessun trascinamento di schede, nessun rilascio, nessuna chiusura. La selezione delle schede, il ridimensionamento e l’apertura e chiusura dei dock continuano a funzionare, quindi uno spazio di lavoro bloccato resta comunque uno spazio di lavoro utilizzabile.

#Metodi

ref={area} fornisce i metodi dell’area (DockAreaRef), per i comandi, le scorciatoie e i menu dell’app. Ogni modifica viene segnalata con onLayoutChange come quelle dell’utente. Un metodo a cui viene passato un pannello, un gruppo, un indice di scheda o un dock che il layout non ha lascia il layout com’è e registra un avviso con il nome del metodo e il valore.

MetodoDescrizione
layout()Promise<DockAreaLayout>: il layout mostrato.
addPanel(panel, placement?, size?)Aggiunge un pannello a una regione ("center", il valore predefinito, "left", "right", "bottom"), nel suo primo gruppo di schede, visualizzato; un dock mancante viene creato, largo size in trasversale. Un pannello che l’area già contiene viene spostato lì.
removePanel(panel)Rimuove un pannello, qualunque cosa dicano le sue opzioni.
closePanel(panel)Chiude un pannello come farebbe la sua scheda: a meno che l’area non sia bloccata, il pannello non sia chiudibile, o sia l’ultimo pannello di un gruppo senza nulla accanto.
movePanel(panel, target)Sposta un pannello in target: { group, side?, index?, activate?, size? }, nel gruppo di schede group (in index) o in un nuovo gruppo sul suo side (size punti lungo la nuova divisione).
splitAt(group, panel, side)Colloca un pannello in un nuovo gruppo di schede su un lato ("left", "right", "top", "bottom") di un contenitore.
selectPanel(panel)Visualizza un pannello nel suo gruppo.
selectTab(group, index)Visualizza una scheda di un gruppo.
toggleDock(placement), setDockOpen(placement, open)Apre o chiude un dock.
setDockSize(placement, size)Imposta la dimensione di un dock trasversalmente al suo bordo (almeno 100).
toggleZoom(group?)Porta un gruppo in zoom, o lo riporta a dimensione normale se è già in zoom; senza group, annulla lo zoom del gruppo in zoom.
zoomedGroup()Promise<number | null>: l’id del gruppo in zoom.
groupOf(panel)Promise<number | null>: l’id del gruppo di schede che contiene un pannello.

In Rust, component_ref::<DockArea>() e .component_ref(&r) sull’area forniscono un ref il cui r.api::<DockAreaApi>() ha gli stessi metodi in snake case.

#Salvataggio e ripristino

Un layout segnalato è semplice dato, quindi salvarlo significa JSON.stringify(layout) nello storage, e ripristinarlo significa passare il valore analizzato come layout. onLayoutChange segnala ogni modifica, compresi un ridimensionamento e una selezione di scheda, quindi scrivi nello storage solo quando le modifiche si sono assestate (un breve timer riavviato a ogni segnalazione).

In Rust, DockAreaLayout::to_state e from_state leggono e scrivono DockAreaState, il formato di file di layout che usa anche il dock di gpui-base.

#Telefoni e finestre strette

Il dock si adatta alla propria larghezza. Un’area larga 600 punti o più (in una finestra su desktop, sul web, su un tablet, su un iPad a schermo intero, su un foldable aperto) è l’intero spazio di lavoro: divisioni, gruppi e dock affiancati, come nelle app multi-pannello dei tablet. Con un dito su un telefono o un tablet, il trascinamento di una scheda inizia con una pressione prolungata, come nel drag and drop di iPadOS, così una barra delle schede continua a scorrere con uno swipe, e le maniglie hanno un’area di tocco più ampia con lo stesso aspetto.

Un’area più stretta di 600 punti (su un telefono, su un iPad in Slide Over, in una finestra di desktop o browser stretta, o in una colonna stretta di una più larga) presenta lo stesso layout per un solo schermo, come le split view dell’iPad comprimono le loro colonne e il supporting pane di Material Design diventa un bottom sheet:

  • Il centro mostra un gruppo alla volta, a dimensione piena. La sua barra delle schede elenca ogni pannello visibile del centro, e selezionare un pannello di un altro gruppo rende quel gruppo quello mostrato.
  • I dock sinistro e destro sono drawer sopra il centro a partire dal loro bordo, larghi quanto il dock, e il dock inferiore è un sheet alto quanto il dock. Ne viene mostrato uno alla volta, quando l’app lo presenta (toggleDock, setDockOpen, che chiudono l’altro); scorre dentro e fuori, e una pressione sul suo sfondo, Escape o indietro lo chiudono. L’ingresso nella presentazione compatta non ne mostra alcuno.
  • Una scheda trascinata (un dito dopo una pressione prolungata, un puntatore subito) si sposta all’interno della barra delle schede per riordinarla.

Ogni pannello resta posizionato in entrambe le presentazioni, quindi il passaggio ne conserva il contenuto. Il drawer o lo sheet mostrato è proprio della presentazione compatta: il open del layout appartiene alla presentazione completa, e presentare o chiudere un drawer non segnala alcun onLayoutChange, quindi un’area che torna ad allargarsi mostra i dock esattamente com’erano. onDockOpenChange segnala entrambi: un dock il cui open del layout cambia nella presentazione completa, un drawer o uno sheet mostrato o chiuso in quella compatta, e ogni dock il cui stato di visualizzazione cambia quando la presentazione cambia. In renderDock, open indica se il dock è mostrato nella presentazione corrente. Anche il layout resta lo stesso valore, quindi un’area che attraversa i 600 punti (un foldable che si apre, un iPad che entra in Split View, una finestra o una colonna ridimensionata) mantiene ogni pannello dov’era, e l’app salva un solo layout. La larghezza dell’area decide una volta che è stata disposta (prima di allora vale la larghezza della finestra), e compact sceglie la presentazione al posto della larghezza. I drawer, lo sheet e lo sfondo sono parti che l’app stila (drawerClass, sheetClass, backdropClass), come il resto dell’area.

<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

PropRustTipoPredefinitoDescrizione
layout.layout(..)DockAreaLayoutnessunoIl layout mostrato (controllato): restituisci ciò che riporta onLayoutChange.
defaultLayout.default_layout(..)DockAreaLayoutun centro vuotoIl layout iniziale, quando layout non è impostato; da quel momento l’area mantiene il proprio.
locked.locked(..)booleanfalseNessun trascinamento, rilascio o chiusura di tab.
compact.compact(..)booleanin base alla larghezzaLa presentazione per telefono (true) o quella completa (false); se non è impostata, compatta finché l’area è più stretta di 600 punti.
panels.panels(..)DockPanelOptions[][]Ciò che ogni pannello consente, per chiave: closable, zoomable, visible.
renderPanel.render_panel(|panel| ..)(panel: DockPanel) => JSX.ElementnessunoIl contenuto di un pannello, in base alla sua chiave (panel.id).
renderTab.render_tab(|tab| ..)(tab: DockTab) => JSX.Elementla chiaveCiò che mostra una tab.
renderDropIndicator.render_drop_indicator(|drop| ..)(drop: DockDropIndicator) => JSX.ElementnessunoCiò che c’è dentro l’indicatore di rilascio: group, side, index.
renderDock.render_dock(|dock| ..)(dock: DockInfo) => JSX.ElementnessunoIl chrome proprio di un dock, prima del suo layout: placement, open (mostrato nella presentazione corrente), size, collapsible.
renderEmpty.render_empty(|placement| ..)(placement: DockPlacement) => JSX.ElementnessunoCiò che mostra un centro vuoto.
renderDragPreview.render_drag_preview(|tab| ..)(tab: DockTab) => JSX.ElementnessunoCiò che segue il puntatore o il dito mentre si trascina una tab.
groupClass, groupStyle.group_style(|s| ..)string, StyleProp<ViewStyle>nessunoIl riquadro di ogni gruppo di tab.
tabBarClass, tabBarStyle.tab_bar_style(|s| ..)string, StyleProp<ViewStyle>nessunoOgni barra delle tab (flex-row per una riga).
tabClass, tabStyle.tab_style(|s| ..)string, StyleProp<ViewStyle>nessunoOgni tab; selected: per quella visualizzata.
contentClass, contentStyle.content_style(|s| ..)string, StyleProp<ViewStyle>nessunoIl riquadro del contenuto di ogni gruppo, dove compaiono i suoi pannelli.
dropIndicatorClass, dropIndicatorStyle.drop_indicator_style(|s| ..)string, StyleProp<ViewStyle>nessunoL’indicatore di rilascio.
handleClass, handleStyle.handle_style(|s| ..)string, StyleProp<ViewStyle>nessunoL’indicatore di ogni maniglia, tra i gruppi di uno split e al bordo di ogni dock; group-hover: e group-active: seguono la maniglia. Misura 1 punto in larghezza rispetto alla maniglia e ne percorre l’intera lunghezza, a meno che non sia specificato diversamente, quindi bg-border è una linea sottilissima in entrambi i casi; w-[3px] / h-[3px] la ispessiscono.
handleThumbClass, handleThumbStyle.handle_thumb_class(..), .handle_thumb_style(|s| ..)string, StyleProp<ViewStyle>nessunoIl thumb di ogni maniglia: una presa centrata sulla linea, disegnata in verticale come su una linea verticale e ruotata di un quarto di giro su una orizzontale, così una sola classe serve entrambi gli assi. Resta la stessa vista mentre la maniglia è in hover, premuta e trascinata, quindi le classi group-hover: e group-active: ne gestiscono la transizione (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>nessunoIl riquadro di ogni dock.
drawerClass, drawerStyle.drawer_style(|s| ..)string, StyleProp<ViewStyle>nessunoCompatto: i drawer dei dock laterali.
sheetClass, sheetStyle.sheet_style(|s| ..)string, StyleProp<ViewStyle>nessunoCompatto: lo sheet del dock inferiore.
backdropClass, backdropStyle.backdrop_style(|s| ..)string, StyleProp<ViewStyle>nessunoCompatto: lo sfondo dietro un drawer o uno sheet aperto, la cui pressione lo chiude.
handleAccessibilityLabel.handle_accessibility_label(..)stringnessunoCiò che gli screen reader annunciano per ogni maniglia, tra i gruppi di uno split e al bordo di ogni dock.
ref.component_ref(&r) (component_ref::<DockArea>())DockAreaRefI metodi dell’area.
class, stylemetodi di stilestring, StyleProp<ViewStyle>nessunoL’area stessa: la sua dimensione nel genitore (flex-1), il suo sfondo. Resta entro i propri limiti e non ricava alcuna dimensione minima dal contenuto.

#Eventi

EventoRustDescrizione
onLayoutChange.on_layout_change(|layout: &DockAreaLayout| ..)Il layout è cambiato (un trascinamento, una selezione, un ridimensionamento, un dock aperto o chiuso, un metodo): { layout }, l’intero nuovo layout, con tutti i suoi id.
onPanelActiveChange.on_panel_active_change(|e: &DockAreaEvent| ..) (PanelActiveChange(panel, active)){ panel, active }: un pannello è diventato, o ha smesso di essere, la tab visualizzata del suo gruppo (un dock chiuso non ne visualizza nessuna). Una volta per ogni cambiamento; a un pannello che se ne va non viene comunicato false: onPanelRemove è la sua fine.
onPanelRemove.on_panel_remove(|panel: &PanelId| ..){ panel }: un pannello ha lasciato l’area definitivamente: chiuso, rimosso o assente da un nuovo layout. Un pannello che si sposta non lo riceve mai.
onZoomChange.on_zoom_change(|panel: &Option<PanelId>| ..)Un gruppo è stato ingrandito o ripristinato: { panel }, il pannello che visualizza, oppure null.
onDockOpenChange.on_dock_open_change(|e: &DockAreaEvent| ..) (DockOpenChange(placement, open)){ placement, open }: un dock è stato mostrato o nascosto: il open del suo layout è cambiato nella presentazione completa, il suo drawer o sheet è stato mostrato o chiuso in quella compatta, oppure la presentazione è cambiata.

#Parti

ParteDentroStilizzata conStati
L’areaclass, style
Un gruppo di tabuna regionegroupClass, groupStyle
Una barra delle tabun gruppotabBarClass, tabBarStyle
Una tabuna barra delle tabtabClass, tabStyle; renderTabselected:, active:, hover:
Il contenuto di un gruppoun gruppocontentClass, contentStyle; renderPanel
L’indicatore di una manigliatra i gruppi di uno split, al bordo di un dockhandleClass, handleStylegroup-hover:, group-active:
Il thumb di una manigliacentrato sulla linea di una manigliahandleThumbClass, handleThumbStylegroup-hover:, group-active: (premuto fino alla fine del trascinamento)
Un dockun bordodockClass, dockStyle; renderDock
L’indicatore di rilasciosopra un gruppo durante un trascinamentodropIndicatorClass, dropIndicatorStyle; renderDropIndicator
L’anteprima di trascinamentosotto il puntatore o il ditorenderDragPreview
Il centro vuotoil centrorenderEmpty
Un drawer, lo sheet, lo sfondosopra il centro, compattodrawerClass, sheetClass, backdropClass (e i loro stili)

#Accessibilità

  • Ogni tab è una tab con il proprio stato di selezione per gli screen reader, come lo è Tab, e attivarla la seleziona. La sua etichetta è il testo disegnato da renderTab.
  • Ogni maniglia, tra i gruppi di uno split e al bordo di ogni dock, è un elemento regolabile con etichetta handleAccessibilityLabel. Le maniglie tra i gruppi di uno split sono quelle di Resizable: uno screen reader le sposta di 10 punti alla volta, annunciati con onLayoutChange.
  • Nella presentazione compatta, Escape e indietro chiudono il drawer o lo sheet mostrato.

Vedi Accessibilità per etichette, ruoli e annunci in generale.

#Note sulle piattaforme

  • Un puntatore (un mouse, un trackpad, una penna sospesa sullo schermo) trascina una tab dopo essersi spostato di 4 punti con il pulsante premuto, su ogni piattaforma: un iPad con trackpad e una finestra stretta del browser usata con il mouse si comportano come un desktop. Su macOS, Windows, Linux e GPUI ogni pressione conta come quella di un puntatore, compreso il dito su un laptop con touchscreen.
  • Un dito su iOS, iPadOS e Android trascina una tab dopo una pressione prolungata, come fa il drag and drop di iPadOS, quindi uno swipe sulla barra delle tab la fa scorrere.
  • Desktop e web: le maniglie mostrano il cursore di ridimensionamento del loro asse.
  • Telefoni e aree strette: la presentazione compatta, con drawer e uno sheet inferiore.
  • Ogni piattaforma disegna ciò che l’app stilizza: tab, barre delle tab, maniglie, indicatore di rilascio, dock, drawer e sheet.