इस पेज का अनुवाद मशीन द्वारा किया गया है और इसमें त्रुटियाँ हो सकती हैं। मूल अंग्रेज़ी पढ़ें

एलिमेंट

Toggle

टॉगल एक ऐसा बटन है जो चालू या बंद रहता है: टेक्स्ट टूलबार में बोल्ड, पसंदीदा का तारा, व्यू का कोई विकल्प। Toggle ऐसे एक टॉगल का व्यवहार संभालता है: प्रेस, कीबोर्ड से सक्रियण, disabled स्थिति, और pressed स्टेट के साथ बटन रोल, जिसे स्क्रीन रीडर टॉगल बटन के रूप में बोलते हैं। कई टॉगल के चारों ओर ToggleGroup एक टूलबार बनाता है: स्क्रीन रीडर इसे एक टूलबार के रूप में बोलते हैं, कीबोर्ड एक ही स्टॉप में इस तक पहुँचता है और ऐरो कीज़ से इसके टॉगल के बीच घूमता है। रूप-रंग ऐप का अपना होता है: टॉगल के अपने कोई रंग, आकार, आइकन या लेबल नहीं होते। इसके children ऐप के अपने आइकन या लेबल होते हैं, और टॉगल पर aria-pressed: क्लास, या उसके अंदर की चीज़ों पर group-aria-pressed: क्लास, उसकी स्टेट के अनुसार लागू होती हैं।

el-toggle.tsx
el-toggle.tsxLive
import { Canvas, mount, signal, Text, View } from "nsh";
import { Toggle, ToggleGroup } from "nsh/base";
import { desktop, focusRing, hoverFill, Log, Split, tint, ui } from "./palette";

type Mark = "bold" | "italic" | "underline";
type Align = "left" | "center" | "right";

const marks: { id: Mark; label: string; glyph: string }[] = [
  { id: "bold", label: "Bold", glyph: "B" },
  { id: "italic", label: "Italic", glyph: "I" },
  { id: "underline", label: "Underline", glyph: "U" },
];
const aligns: { id: Align; label: string }[] = [
  { id: "left", label: "Align left" },
  { id: "center", label: "Align center" },
  { id: "right", label: "Align right" },
];

/** Three lines of a paragraph, set as `align` sets them. */
function Lines(props: { align: Align; color: string }) {
  return (
    <Canvas
      style={{ pointerEvents: "none", width: 16, height: 16 }}
      draw={(cx) => {
        cx.strokeStyle = props.color;
        cx.lineWidth = 1.6;
        cx.lineCap = "round";
        [12, 7, 10].forEach((w, i) => {
          const x = props.align === "center" ? 8 - w / 2 : props.align === "right" ? 14 - w : 2;
          cx.beginPath();
          cx.moveTo(x, 4 + i * 4);
          cx.lineTo(x + w, 4 + i * 4);
          cx.stroke();
        });
      }}
    />
  );
}

function App() {
  const on = signal<Mark[]>(["bold"]);
  const align = signal<Align>("left");
  const starred = signal(false);
  const events = signal<string[]>([]);
  const log = (line: string) => events.set([line, ...events()].slice(0, 3));
  const has = (m: Mark) => on().includes(m);
  // A toolbar toggle: bare while off, raised while pressed (`pressedStyle`).
  const look = (pressed: boolean) => ({
    style: { width: desktop ? 28 : 34, height: desktop ? 28 : 34, borderRadius: 7, alignItems: "center", justifyContent: "center" } as const,
    class: "transition-colors active:opacity-70 motion-reduce:transition-none",
    pressedStyle: ui().raised,
    hoverStyle: pressed ? undefined : hoverFill(),
    focusStyle: focusRing(),
  });
  return (
    <View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
      <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>toggle</Text>
      <Text style={{ color: ui().muted }}>
        {desktop ? "Click a toggle, or Tab to the toolbar and move with the arrow keys" : "Tap a toggle to turn it on or off"}
      </Text>

      <Split gap={14} inspector={<Log title={`onChange · ${on().join(", ") || "none"}, ${align()}`} lines={events()} minHeight={76} />}>
        {/* One toolbar: the marks any number at once, the alignment exactly one. */}
        <ToggleGroup style={{ flexDirection: "row", alignItems: "center", alignSelf: "flex-start", gap: 2, padding: 3, borderRadius: 10, ...ui().inset }}>
          <Each of={marks}>
            {(m) => (
              <Toggle
                pressed={has(m().id)}
                accessibilityLabel={m().label}
                onChange={({ pressed }) => {
                  on.set(pressed ? [...on(), m().id] : on().filter((x) => x !== m().id));
                  log(`${m().label}: ${pressed}`);
                }}
                {...look(has(m().id))}
              >
                <Text
                  style={{
                    fontSize: 14,
                    color: has(m().id) ? ui().text : ui().muted,
                    fontWeight: m().id === "bold" ? "800" : "500",
                    fontStyle: m().id === "italic" ? "italic" : "normal",
                    textDecorationLine: m().id === "underline" ? "underline" : "none",
                  }}
                >
                  {m().glyph}
                </Text>
              </Toggle>
            )}
          </Each>
          <View style={{ width: 1, height: 16, marginHorizontal: 4, backgroundColor: ui().line }} />
          <Each of={aligns}>
            {(a) => (
              <Toggle
                pressed={align() === a().id}
                accessibilityLabel={a().label}
                onChange={({ pressed }) => {
                  // Exactly one: the pressed alignment stays pressed.
                  if (pressed) align.set(a().id);
                  log(`${a().label}: ${pressed}`);
                }}
                {...look(align() === a().id)}
              >
                <Lines align={a().id} color={align() === a().id ? ui().text : ui().muted} />
              </Toggle>
            )}
          </Each>
        </ToggleGroup>

        {/* The text the toolbar formats: the app reads its own values. */}
        <View style={{ ...ui().card, borderRadius: 14, padding: 14 }}>
          <Text
            style={{
              fontSize: 15,
              lineHeight: 22,
              color: ui().text,
              fontWeight: has("bold") ? "700" : "400",
              fontStyle: has("italic") ? "italic" : "normal",
              textDecorationLine: has("underline") ? "underline" : "none",
              textAlign: align(),
            }}
          >
            Every toggle reports the value it asks for; the app passes it back.
          </Text>
        </View>

        {/* A toggle on its own, with a label: `aria-pressed:` tints it. */}
        <Toggle
          pressed={starred()}
          onChange={({ pressed }) => {
            starred.set(pressed);
            log(`Star: ${pressed}`);
          }}
          class="transition-colors active:opacity-70 motion-reduce:transition-none"
          style={{ flexDirection: "row", alignItems: "center", alignSelf: "flex-start", gap: 6, height: desktop ? 28 : 34, paddingHorizontal: 12, borderRadius: 8, borderWidth: 1, borderColor: ui().line }}
          pressedStyle={{ backgroundColor: tint(ui().accent, 0.16), borderColor: tint(ui().accent, 0.4) }}
          hoverStyle={starred() ? undefined : hoverFill()}
          focusStyle={focusRing()}
        >
          <Text style={{ fontSize: 14, color: starred() ? ui().accent : ui().muted }}>{starred() ? "★" : "☆"}</Text>
          <Text style={{ fontSize: 13, fontWeight: "500", color: starred() ? ui().accent : ui().text }}>{starred() ? "Starred" : "Star"}</Text>
        </Toggle>
      </Split>
    </View>
  );
}

mount(App);
use nsh_base::{theme, toggle, toggle_group};
use nsh_core::prelude::*;

fn formatting() -> impl IntoElement {
    let bold = signal(true);
    let italic = signal(false);
    let item = move |label: &'static str, on: Signal<bool>| {
        toggle()
            .pressed(on)
            .accessibility_label(label)
            .on_change(move |pressed: &bool| on.set(*pressed))
            .size_8()
            .items_center()
            .justify_center()
            .rounded(move || theme().radius)
            .pressed_style(|s| s.bg(move || theme().accent))
            .child(text(&label[..1]).text_sm().text_color(move || {
                if on.get() { theme().accent_foreground } else { theme().muted_foreground }
            }))
    };
    toggle_group()
        .flex_row()
        .self_start()
        .gap_1()
        .child(item("Bold", bold))
        .child(item("Italic", italic))
}

Toggle और ToggleGroup nsh/base से आते हैं, जो Rust में nsh-base क्रेट है। वैल्यू ऐप की होती है: हर टॉगल को बताया जाता है कि वह pressed है या नहीं, प्रेस onChange के साथ वही वैल्यू रिपोर्ट करता है जो वह चाहता है, और टॉगल वही pressed दिखाता है जो ऐप वापस देता है। ग्रुप भी कोई वैल्यू नहीं रखता, इसलिए कितने टॉगल pressed हो सकते हैं, यह भी ऐप तय करता है: कितने भी (बोल्ड, इटैलिक और अंडरलाइन), या ठीक एक (अलाइनमेंट, व्यू स्विचर), जहाँ ऐप माँगे गए टॉगल को press करता है, बाकी को छोड़ देता है और pressed टॉगल को pressed ही रखता है। Rust में toggle() और toggle_group() इन्हें बनाते हैं, जिनमें हर prop के लिए एक सेटर (.pressed(..)), हर इवेंट के लिए एक लिसनर (.on_change(..)), टाइप्ड स्टाइल मेथड और उनकी सामग्री के लिए .child(..) होता है।

#Props

#Toggle

PropRustTypeDefaultDescription
pressed.pressed(..)booleanfalseचालू या बंद: स्क्रीन रीडर इसे pressed बोलते हैं, और aria-pressed: स्टाइल लागू होती हैं। प्रेस onChange के साथ दूसरी वैल्यू रिपोर्ट करता है; टॉगल वही वैल्यू दिखाता है जो ऐप वापस देता है।
disabled.disabled(..)booleanfalseकोई प्रेस नहीं और प्रेस का कोई फ़ीडबैक नहीं; disabled: स्टाइल लागू होती हैं, और स्क्रीन रीडर इसे dimmed बोलते हैं। disabled ToggleGroup में मौजूद टॉगल भी disabled होता है।
pressedStyle.pressed_style(|s| ..)StyleProp<ViewStyle>कोई नहींpressed और not disabled होने पर टॉगल की स्टाइल, aria-pressed: क्लास के रूप में।
accessibilityLabel.accessibility_label(..)stringकोई नहींस्क्रीन रीडर क्या बोलते हैं: टॉगल क्या चालू करता है, जब उसके children में इसे बताने वाला टेक्स्ट न हो (जैसे आइकन)।
hitSlop.hit_slop(..)number | Partial<Edges>कोई नहींअपनी सीमाओं से बाहर कितनी दूर तक प्रेस अब भी गिने जाते हैं, points में: हर तरफ़ के लिए एक संख्या, या नामित तरफ़ें। जैसे किसी view का hitSlop।
children.child(..)JSX.Elementकोई नहींऐप का आइकन या लेबल, जिसे स्क्रीन रीडर बोलते हैं।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींटॉगल स्वयं: उसकी सामग्री के चारों ओर की सतह।
activeStyle, hoverStyle, focusStyle, disabledStyle.active(|s| ..), .hover(..), .focus(..), .disabled_style(..)StyleProp<ViewStyle>कोई नहींप्रेस जारी रहने, hover, focus या disabled होने पर टॉगल की स्टाइल, <state>: क्लास के रूप में।

#ToggleGroup

PropRustTypeDefaultDescription
disabled.disabled(..)booleanfalseग्रुप का हर टॉगल disabled हो जाता है, चाहे उसका अपना disabled कुछ भी हो।
children.child(..)JSX.Elementकोई नहींग्रुप के टॉगल, और उनके बीच ऐप जो भी लेआउट करे (जैसे सेपरेटर)।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींग्रुप का लेआउट और रूप: डिफ़ॉल्ट रूप से कॉलम, पंक्ति के लिए flex-row।

#Events

EventRustDescription
onChange (Toggle).on_change(|pressed: &bool| ..)टॉगल को press किया गया: टैप, क्लिक, कीबोर्ड फ़ोकस होने पर Space या Return, या स्क्रीन रीडर का सक्रियण। { pressed } वह वैल्यू है जो टॉगल चाहता है, यानी pressed का उलटा। यह कुछ भी बदलने से पहले चलता है: टॉगल वही pressed दिखाता है जो ऐप वापस देता है।

ToggleGroup की ऐरो की केवल कीबोर्ड फ़ोकस को ले जाती है: यह कुछ press नहीं करती। disabled टॉगल कुछ रिपोर्ट नहीं करता।

#Parts

PartInsideStyled withStates
ग्रुपToggleGroup के class, style
टॉगलग्रुप, या कहीं भीToggle के class, stylearia-pressed:, disabled:, active:, hover:, focus: (या pressedStyle, disabledStyle, …)
आइकन या लेबलटॉगलअपने स्वयं के एलिमेंट (children)group-aria-pressed:, group-disabled: और टॉगल के class पर group के साथ अन्य group-*: वेरिएंट

aria-pressed: का मतलब है टॉगल का चालू होना; active: का मतलब है प्रेस जारी होना, चालू हो या बंद। pressed टॉगल को उसकी अपनी सतह से दिखाएँ: फ़िल, उठी हुई पिल, या गहरा आइकन (Design guides देखें)।

#Segmented

जिस ग्रुप के टॉगल जुड़े हुए हों और एक समय में एक ही pressed हो, वह segmented control (व्यू स्विचर) है: ऐप एक वैल्यू रखता है और उस वैल्यू वाले टॉगल को press करता है। जुड़ा हुआ रूप ऐप की क्लास से बनता है:

<ToggleGroup class="flex-row self-start">
  <Each of={["grid", "list"]}>
    {(value) => (
      <Toggle
        pressed={view() === value()}
        accessibilityLabel={`${value()} view`}
        onChange={({ pressed }) => pressed && view.set(value())}
        class="-ml-px h-8 px-3 border border-border first:ml-0 first:rounded-l-(--radius) last:rounded-r-(--radius) aria-pressed:bg-accent"
      >
        <Text class="text-sm">{value()}</Text>
      </Toggle>
    )}
  </Each>
</ToggleGroup>

#Disabled

disabled टॉगल कोई प्रेस नहीं लेता, और स्क्रीन रीडर उसे dimmed बोलते हैं। उसकी disabled: क्लास उसकी aria-pressed: क्लास के ऊपर लागू होती हैं, और उसके अंदर की group-disabled: क्लास भी उसके अनुसार चलती हैं। ToggleGroup पर disabled उसके हर टॉगल को disabled कर देता है, यानी ऐप द्वारा लॉक किया गया टूलबार:

<ToggleGroup disabled={readonly()} class="flex-row gap-1">
  <Toggle pressed={bold()} onChange={({ pressed }) => bold.set(pressed)} class="size-8 disabled:opacity-50">
    <Text>B</Text>
  </Toggle>
</ToggleGroup>

#hitSlop के साथ छोटा टारगेट

आइकन टॉगल उँगली से छोटा हो सकता है। hitSlop उसके पास पड़ने वाले प्रेस को गिनता है, हर तरफ़ से points में, उसके लेआउट या रूप को बदले बिना: हर तरफ़ के लिए एक संख्या, या नामित तरफ़ें ({ top: 8, bottom: 8 }), ठीक वैसे जैसे view का hitSlop उन्हें लेता है।

<Toggle
  pressed={favorite()}
  hitSlop={12}
  accessibilityLabel="Favorite"
  onChange={({ pressed }) => favorite.set(pressed)}
  class="size-5 items-center justify-center"
>
  <Text>★</Text>
</Toggle>

#Accessibility

  • स्क्रीन रीडर के लिए टॉगल एक ही एलिमेंट है, जिसका रोल "button" है और साथ में उसकी pressed स्टेट: यानी टॉगल बटन। उसका लेबल उसके children का टेक्स्ट है, या accessibilityLabel।
  • ToggleGroup का रोल "toolbar" है: स्क्रीन रीडर उसके अंदर के टॉगल को एक टूलबार के रूप में सुनते हैं, और हर टॉगल फिर भी अपना अलग एलिमेंट रहता है।
  • disabled को वैसे ही बोला जाता है जैसे प्लेटफ़ॉर्म उसे बोलता है (VoiceOver, TalkBack, UI Automation, ब्राउज़र का accessibility tree)।
  • macOS और वेब पर ग्रुप कीबोर्ड फ़ोकस का एक स्टॉप है, और ऐरो कीज़ उसके टॉगल के बीच घूमती हैं (नीचे देखें)।

लेबल, रोल और घोषणाओं के बारे में सामान्य जानकारी के लिए एक्सेसिबिलिटी देखें।

#Keyboard

macOS और वेब पर, जहाँ Tab प्रेस लेने वाले views तक पहुँचता है, ToggleGroup WAI-ARIA toolbar pattern का पालन करता है:

KeyWhat it does
Tab, Shift+Tabग्रुप में पहले pressed टॉगल पर प्रवेश करता है (यदि कोई pressed नहीं है तो पहले ऐसे टॉगल पर जो disabled नहीं है), और एक ही चरण में उससे बाहर निकल जाता है।
Right, Downफ़ोकस को अगले टॉगल पर ले जाता है, अंतिम से पहले पर।
Left, Upफ़ोकस को पिछले टॉगल पर ले जाता है, पहले से अंतिम पर।
Home, Endफ़ोकस को पहले या अंतिम टॉगल पर ले जाता है।
Space, Returnफ़ोकस वाले टॉगल को press करता है।

ऐरो कीज़ disabled टॉगल को छोड़ देती हैं और कुछ press नहीं करतीं: टॉगल केवल तभी बदलता है जब उसे press किया जाए। ग्रुप का लेआउट जो भी हो, जो ऐप की क्लास है, ऐरो के दोनों जोड़े फ़ोकस को ले जाते हैं। right-to-left लेआउट में Left और Right की भूमिकाएँ आपस में बदल जाती हैं: Left अगले टॉगल पर और Right पिछले पर ले जाता है; Up, Down, Home और End अपनी भूमिका बनाए रखते हैं। focus: स्टाइल दिखाती हैं कि फ़ोकस कहाँ है। ToggleGroup के बाहर का Toggle अपने आप में एक अलग स्टॉप है।

#Platform notes

  • iOS, Android और Mac Catalyst: प्रेस एक ऐसा टैप है जो टॉगल पर समाप्त होता है, उसकी सामग्री सहित।
  • डेस्कटॉप (macOS, Windows, Linux, GPUI) और वेब: प्रेस एक क्लिक है। macOS और वेब पर, Space और Return भी कीबोर्ड फ़ोकस वाले टॉगल को press करते हैं, और ऐरो कीज़, Home और End उसे ले जाती हैं (कीबोर्ड)।
  • हर प्लेटफ़ॉर्म वही बनाता है जो ऐप स्टाइल करता है: टॉगल, pressed टॉगल की सतह और ग्रुप का ट्रैक।