एलिमेंट
Switch
Switch किसी सेटिंग को चालू या बंद करता है, और बदलाव तुरंत लागू हो जाता है। व्यवहार उसी का है: प्रेस, कीबोर्ड से सक्रियण, disabled स्थिति, और स्क्रीन रीडर के लिए switch role व उसका मान। रूप ऐप का है: Switch के अपने कोई रंग, आकार, motion या label नहीं होते। यह एक track से बना है जिसके अंदर एक thumb होता है, फिर उसके children (एक label)। ऐप हर भाग को स्टाइल करता है, checked: और disabled: classes के साथ जो switch का अनुसरण करती हैं।
import { mount, signal, Text, View } from "nsh";
import { Switch } from "nsh/base";
import { desktop, focusRing, hoverFill, Log, Panel, size, Split, ui } from "./palette";
// The parts: geometry and motion in classes (`checked:` moves the thumb,
// `disabled:` dims the row, both following the switch), colors from the
// palette by the value the app owns. A pointer gets the compact size; the
// pointer's hover and the keyboard's focus ring come from the palette, and
// the thumb and track stay still with reduced motion.
const row = `flex-row-reverse items-center justify-between gap-3 ${desktop ? "px-3 py-2" : "px-4 py-3"} active:opacity-70 disabled:opacity-40`;
const track = `${desktop ? "w-9" : "w-12"} rounded-full p-0.5 transition-colors motion-reduce:transition-none`;
const thumb = desktop ? "size-4 rounded-full transition-transform motion-reduce:transition-none checked:translate-x-4" : "size-5.5 rounded-full transition-transform motion-reduce:transition-none checked:translate-x-5";
function Setting(props: { label: string; detail: string; checked: boolean; disabled?: boolean; onChange: (e: { checked: boolean }) => void }) {
return (
<Switch
checked={props.checked}
disabled={props.disabled}
onChange={props.onChange}
class={row}
hoverStyle={props.disabled ? undefined : hoverFill()}
focusStyle={focusRing()}
trackClass={track}
trackStyle={props.checked ? ui().accentFill : ui().inset}
thumbClass={thumb}
thumbStyle={props.checked ? { backgroundColor: ui().onAccent } : ui().raised}
>
<View style={{ flex: 1, gap: 2 }}>
<Text style={{ fontSize: size.label, fontWeight: "500", color: ui().text }}>{props.label}</Text>
<Text style={{ fontSize: size.detail, color: ui().muted }}>{props.detail}</Text>
</View>
</Switch>
);
}
function App() {
const wifi = signal(true);
const bluetooth = signal(false);
const ask = signal(true);
const events = signal<string[]>([]);
const change = (name: string, value: { set: (v: boolean) => void }) => ({ checked }: { checked: boolean }) => {
value.set(checked);
events.set([`${name} → ${checked}`, ...events()].slice(0, 3));
};
const line = () => <View style={{ height: 1, marginLeft: desktop ? 12 : 16, backgroundColor: ui().line }} />;
return (
<View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
<Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>switch</Text>
<Text style={{ color: ui().muted }}>
{desktop ? "Click a row, or Tab to a switch and press Space" : "Tap a row: the label toggles it too"}
</Text>
<Split
gap={14}
inspector={
<>
<Panel title="checked · disabled">
<Text style={{ fontSize: 13, color: ui().text }}>
Wi-Fi {wifi() ? "on" : "off"} · ask {ask() ? "on" : "off"}
{wifi() ? "" : " (disabled)"} · Bluetooth {bluetooth() ? "on" : "off"}
</Text>
</Panel>
<Log title="onChange({ checked })" lines={events()} minHeight={92} />
</>
}
>
<View style={{ ...ui().card, borderRadius: 16, overflow: "hidden" }}>
<Setting label="Wi-Fi" detail={wifi() ? "Connected to Studio" : "Off"} checked={wifi()} onChange={change("Wi-Fi", wifi)} />
{line()}
<Setting
label="Ask to join networks"
detail={wifi() ? "Known networks join on their own" : "Turn on Wi-Fi first"}
checked={ask()}
disabled={!wifi()}
onChange={change("Ask to join", ask)}
/>
{line()}
<Setting label="Bluetooth" detail={bluetooth() ? "Discoverable as “Desk”" : "Off"} checked={bluetooth()} onChange={change("Bluetooth", bluetooth)} />
</View>
</Split>
</View>
);
}
mount(App);use nsh_base::{switch, theme};
use nsh_core::prelude::*;
fn wifi() -> impl IntoElement {
let on = signal(false);
switch()
.checked(on)
.on_change(move |next: &bool| on.set(*next))
.flex_row()
.items_center()
.gap_3()
.track_style(|s| {
s.w(px(44.0))
.p(px(2.0))
.rounded_full()
.bg(move || theme().muted)
.transition_colors()
.checked(|s| s.bg(move || theme().primary))
})
.thumb_style(|s| {
s.size(px(20.0))
.rounded_full()
.bg(move || theme().background)
.shadow_sm()
.transition_transform()
.checked(|s| s.translate_x(px(20.0)))
})
.child(text("Wi-Fi"))
}import { mount, signal, Text } from "nsh";
import { Switch } from "nsh/base";
function App() {
const on = signal(false);
return (
<Switch
checked={on()}
onChange={({ checked }) => on.set(checked)}
class="flex-row items-center gap-3"
trackClass="w-11 rounded-full bg-muted p-0.5 transition-colors checked:bg-primary"
thumbClass="size-5 rounded-full bg-background shadow-sm transition-transform checked:translate-x-5"
>
<Text>Wi-Fi</Text>
</Switch>
);
}
mount(App);Switch nsh/base से आता है, जो Rust में nsh-base crate है। मान ऐप का है: प्रेस onChange के साथ अगला मान रिपोर्ट करता है, और switch वही checked दिखाता है जो ऐप वापस देता है। Rust में switch() इसे बनाता है: हर prop के लिए एक setter (.checked(..), .track_style(..)), हर event के लिए एक listener (.on_change(..)), switch के लिए typed style methods, और उसके label के लिए .child(..)। किसी भाग की style में state methods (.checked(..), .disabled_style(..)) होते हैं जो switch का अनुसरण करते हैं।
#Props
| Prop | Rust | प्रकार | डिफ़ॉल्ट | विवरण |
|---|---|---|---|---|
checked | .checked(..) | boolean | false | चालू या बंद। प्रेस onChange के साथ अगला मान रिपोर्ट करता है; switch वही मान दिखाता है जो ऐप वापस देता है। |
disabled | .disabled(..) | boolean | false | कोई प्रेस नहीं और प्रेस का कोई फ़ीडबैक नहीं; disabled: styles switch और उसके भागों पर लागू होती हैं, और स्क्रीन रीडर बताते हैं कि यह dimmed है। |
checkedStyle | .checked_style(|s| ..) | StyleProp<ViewStyle> | कोई नहीं | checked होने पर और disabled न होने पर switch की style, checked: classes के रूप में। |
accessibilityLabel | .accessibility_label(..) | string | कोई नहीं | स्क्रीन रीडर क्या बोलते हैं: switch क्या चालू करता है, जब उसके children में इसे बताने वाला कोई टेक्स्ट न हो। |
trackClass, trackStyle | .track_style(|s| ..) | string, StyleProp<ViewStyle> | कोई नहीं | track, यानी वह भाग जिस पर thumb चलता है। |
thumbClass, thumbStyle | .thumb_style(|s| ..) | string, StyleProp<ViewStyle> | कोई नहीं | thumb, जो track के अंदर होता है। |
hitSlop | .hit_slop(..) | number | Partial<Edges> | कोई नहीं | अपनी सीमाओं के कितना बाहर तक प्रेस अब भी गिने जाते हैं, points में: सभी तरफ़ के लिए एक संख्या, या नामित तरफ़ें। जैसा view के hitSlop में होता है। |
children | .child(..) | JSX.Element | कोई नहीं | track के बाद की सामग्री: एक label, जिसे स्क्रीन रीडर बोलते हैं। |
class, style | style methods | string, StyleProp<ViewStyle> | कोई नहीं | स्वयं switch: track और label के आसपास की row। |
activeStyle, hoverStyle, focusStyle, disabledStyle | .active(|s| ..), .hover(..), .focus(..), .disabled_style(..) | StyleProp<ViewStyle> | कोई नहीं | pressed, hovered, focused या disabled होने पर switch की style, <state>: classes के रूप में। |
#Events
| Event | Rust | विवरण |
|---|---|---|
onChange | .on_change(|checked: &bool| ..) | switch को दबाया गया: tap, click, फ़ोकस होने पर Space या Return, या स्क्रीन रीडर का सक्रियण। यह वह मान checked के रूप में रिपोर्ट करता है जो switch माँगता है, यानी उसके मौजूदा checked का उल्टा। |
Disabled switch कुछ रिपोर्ट नहीं करता।
#भाग
| भाग | अंदर | स्टाइल का तरीका | States |
|---|---|---|---|
| Switch | class, style | checked:, disabled:, active:, hover:, focus: (या checkedStyle, disabledStyle, …) | |
| Track | switch, सबसे पहले | trackClass, trackStyle | checked:, disabled: switch का अनुसरण करते हैं |
| Thumb | track | thumbClass, thumbStyle | checked:, disabled: switch का अनुसरण करते हैं |
| Label | switch, track के बाद | अपने elements (children) | group-checked: और अन्य group-*: variants, switch के class पर group के साथ |
Thumb अपनी checked: styles से चलता है: translation (checked:translate-x-5), या margin या track का alignment (track पर checked:justify-end)। transition-* class बदलाव को native रूप से animate करती है। label को track से पहले रखने के लिए row को उल्टा करें: class="flex-row-reverse justify-between"।
#Disabled
Disabled switch कोई प्रेस नहीं लेता, और स्क्रीन रीडर उसे dimmed बताते हैं। उसकी disabled: classes और उसके track व thumb की classes लागू होती हैं, उनकी checked: classes के ऊपर:
<Switch
checked={on()}
disabled={!online()}
onChange={({ checked }) => on.set(checked)}
class="flex-row items-center gap-3 disabled:opacity-50"
trackClass="w-11 rounded-full bg-muted p-0.5 checked:bg-primary disabled:bg-muted"
thumbClass="size-5 rounded-full bg-background checked:translate-x-5"
>
<Text>Sync over cellular</Text>
</Switch>
#hitSlop के साथ छोटा लक्ष्य
जिस switch के बगल में कोई label नहीं है, वह उँगली से छोटा हो सकता है। hitSlop उसके पास पड़ने वाले प्रेस को भी गिनता है, हर तरफ़ से points में, उसके layout या रूप को बदले बिना: सभी तरफ़ के लिए एक संख्या, या नामित तरफ़ें ({ top: 8, bottom: 8 }), जैसे view का hitSlop इन्हें लेता है।
<Switch
checked={on()}
hitSlop={{ top: 8, bottom: 8 }}
accessibilityLabel="Notifications"
onChange={({ checked }) => on.set(checked)}
trackClass="w-9 rounded-full bg-muted p-0.5 checked:bg-primary"
thumbClass="size-4 rounded-full bg-background checked:translate-x-4"
/>
#एक्सेसिबिलिटी
- स्क्रीन रीडर के लिए switch एक ही element है, जिसका role
"switch"है और जिसका मान (चालू या बंद) बताया जाता है; track और thumb उसके भाग हैं। उसका label उसके children का टेक्स्ट है; अगर कोई टेक्स्ट नहीं है तोaccessibilityLabelदें। disabledको वैसे ही बोला जाता है जैसे प्लेटफ़ॉर्म बोलता है (VoiceOver, TalkBack, UI Automation, ब्राउज़र का accessibility tree)।- macOS और web पर switch कीबोर्ड फ़ोकस का एक पड़ाव है: Tab से उस तक पहुँचा जाता है, Space और Return उसे टॉगल करते हैं, और
focus:styles दिखाती हैं कि फ़ोकस कहाँ है।
labels, roles और announcements के बारे में सामान्य जानकारी के लिए एक्सेसिबिलिटी देखें।
#प्लेटफ़ॉर्म नोट्स
- iOS, Android और Mac Catalyst: प्रेस एक tap है जो switch पर ही खत्म होता है, उसके label सहित।
- डेस्कटॉप (macOS, Windows, Linux, GPUI) और web: प्रेस एक click है। macOS और web पर, Space और Return भी उस switch को टॉगल करते हैं जिस पर कीबोर्ड फ़ोकस है।
- हर प्लेटफ़ॉर्म वही बनाता है जो ऐप स्टाइल करता है; thumb वैसे ही चलता है जैसा ऐप की styles और transitions कहती हैं।