Elementi
Selezione del testo
Una View con selectable fa sì che i testi al suo interno si selezionino insieme, come accade in una pagina web: un’unica selezione va da un punto di un testo a un punto di un altro, attraverso view, elementi Text e TextView, in ordine di lettura. Rendi selezionabile la trascrizione di una chat e una selezione può iniziare in un messaggio e finire in quello successivo; Copia mette il testo selezionato negli appunti, con i testi affiancati uniti da una tabulazione e gli altri da un a capo. In Rust si imposta con view().selectable(true), in TSX con <View selectable>; se un singolo testo possa essere selezionato dipende dal suo stile userSelect.
import { mount, Scroll, signal, Text, View } from "nsh";
import { Input } from "nsh/base";
import { TextView } from "nsh/base";
import { platform } from "nsh";
import { field, ui } from "./palette";
const NOTES = "- Sync runs **in the background**\n- Conflicts merge *per field*";
function App() {
const pasted = signal("");
return (
<Scroll style={{ flex: 1, backgroundColor: ui().page }}>
<View style={{ width: "100%", maxWidth: 560, alignSelf: "center", padding: 20, paddingTop: 24, gap: 14 }}>
<Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>selectable view</Text>
<Text style={{ color: ui().muted, lineHeight: 20 }}>
{platform.desktop
? "Drag with the mouse from the title into the list: one selection runs across them. Double click a word, triple click a paragraph, Ctrl / Cmd+C copies."
: "Long press a word, then drag the handles across the texts: one selection runs across them. Copy is in the menu."}
</Text>
<View selectable style={{ ...ui().card, borderRadius: 16, padding: 16, gap: 10 }}>
<Text style={{ fontSize: 17, fontWeight: "600", color: ui().text }}>Release notes</Text>
<Text style={{ color: ui().text, lineHeight: 22 }}>Version 2.4 moves the app to offline sync.</Text>
<TextView markdown={NOTES} />
<View style={{ flexDirection: "row", gap: 8 }}>
<Text style={{ userSelect: "none", color: ui().faint }}>
Updated (userSelect none)
</Text>
<Text style={{ color: ui().text }}>October 21</Text>
</View>
</View>
<Text style={{ fontSize: 12, color: ui().muted }}>Paste what you copied: the label marked userSelect none stays out of it.</Text>
<Input
multiline
value={pasted()}
onChange={({ value }) => pasted.set(value)}
placeholder="Paste here"
{...field({ style: { height: 110 } })}
/>
<Text style={{ fontSize: 12, color: ui().muted, fontVariant: ["tabular-nums"] }}>
{pasted().length} characters, {pasted() ? pasted().split("\n").length : 0} lines
</Text>
</View>
</Scroll>
);
}
mount(App);use nsh_base::text_view;
use nsh_core::prelude::*;
fn release_notes(notes: &'static str) -> impl IntoElement {
view()
.selectable(true)
.gap_3()
.p_4()
.child(text("Release notes").font_weight(FontWeight::SEMIBOLD))
.child(text_view().markdown(notes))
.child(
view()
.flex_row()
.gap_2()
.child(text("Updated").select_none().text_color(rgb(0x737373)))
.child(text("October 21")),
)
}import { Text, View } from "nsh";
import { TextView } from "nsh/base";
function ReleaseNotes(props: { notes: string }) {
return (
<View selectable class="gap-3 p-4">
<Text class="font-semibold">Release notes</Text>
<TextView markdown={props.notes} />
<View class="flex-row gap-2">
<Text style={{ userSelect: "none" }} class="text-neutral-500">Updated</Text>
<Text>October 21</Text>
</View>
</View>
);
}Il layout e l’aspetto della view sono gli stili dell’app, e il suo contenuto sono i suoi figli. La selezione in sé appartiene alla piattaforma: la view non disegna nulla di proprio per essa.
#Una trascrizione di chat
Una trascrizione in un’unica view selezionabile si seleziona come una sola pagina: un trascinamento inizia in una risposta e termina in un’altra, e Copia prende il testo di ogni messaggio intermedio.
use nsh_base::text_view;
use nsh_core::prelude::*;
fn transcript(replies: Signal<Vec<SharedString>>) -> impl IntoElement {
scroll().flex_1().child(
view().selectable(true).p_4().gap_4().child(each(
replies,
|reply: &SharedString| reply.clone(),
|reply, _index| text_view().markdown(move || reply.get()),
)),
)
}import { Scroll, View } from "nsh";
import { TextView } from "nsh/base";
<Scroll class="flex-1">
<View selectable class="gap-4 p-4">
<Each of={replies}>{(reply) => <TextView markdown={reply()} />}</Each>
</View>
</Scroll>#Come si effettua una selezione
| Piattaforma | Come si effettua una selezione |
|---|---|
| macOS, Windows, Linux, GPUI | Trascina sui testi; un doppio clic seleziona una parola, un triplo clic un paragrafo, e un trascinamento successivo prosegue per parole o paragrafi. Shift+clic estende la selezione. Ctrl / Cmd+A seleziona tutto nella view, Ctrl / Cmd+C copia, Esc o una pressione all’esterno annulla la selezione. |
| iOS, Android | Una pressione prolungata seleziona una parola; le maniglie della piattaforma la estendono, e il suo menu di modifica offre Copia e Seleziona tutto. |
| Web | La selezione nativa del browser, con i suoi gesti, il suo menu e le sue scorciatoie. |
#Cosa seleziona
- Testi esclusi: un testo con
userSelect: "none"(.select_none()in Rust, la classeselect-none; un’etichetta, il titolo di un pulsante) viene ignorato dalla selezione e dai suoi gesti. - View annidate: una view selezionabile all’interno di un’altra fa parte di quella esterna, quindi un’unica selezione copre entrambe.
- TextView: una
TextViewconselectablesi seleziona come un insieme a sé: i suoi paragrafi, elementi di elenco, celle e blocchi di codice si selezionano insieme. All’interno di una view selezionabile si unisce alla selezione esterna. - Testi singoli: un testo con
userSelect: "text"che non si trova in nessuna view selezionabile si seleziona da solo, come descritto in Text. - L’aspetto è l’evidenziazione della piattaforma nel
selectionColordi ciascun testo (selection:text-*). - Liste: in una List virtualizzata, la selezione copre le righe montate; le righe riciclate fuori dalla vista escono dalla selezione.