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

एलिमेंट

Accordion

Accordion ऐसे सेक्शन को एक के नीचे एक रखता है जो अपने शीर्षकों के नीचे खुलते और बंद होते हैं: FAQ के प्रश्न, सेटिंग पेज के समूह, चेकआउट के चरण। इसके पाँच भाग हैं, जिनमें से हर भाग व्यवहार का एक हिस्सा संभालता है और रूप-रंग का कोई नहीं:

el-accordion.tsx
el-accordion.tsxLive
import { mount, signal, Text, View } from "nsh";
import { Accordion, AccordionHeader, AccordionItem, AccordionPanel, AccordionTrigger } from "nsh/base";
import { desktop, focusRing, hoverFill, Icon, Log, Panel, Segmented, size, Split, ui } from "./palette";

// An FAQ: each item a heading whose trigger (a button that says whether it
// is expanded) opens its answer. The app owns which items are open
// (`value`) and sets what `onChange` reports; one at a time, or several
// with `multiple`. The panels spring open and shut (`reveal`) and the
// items below move with them; the chevron turns with `group-aria-expanded:`.
// The pointer's hover and the keyboard's focus ring come from the palette;
// the reveal and the chevron stay still with reduced motion.
const pad = desktop ? "px-3" : "px-4";

const FAQ: { value: string; question: string; answer: string; disabled?: boolean }[] = [
  { value: "shipping", question: "How long does shipping take?", answer: "Orders leave the warehouse within a day and arrive in two to five working days." },
  { value: "returns", question: "Can I return an item?", answer: "Within 30 days, unused and in its box: print the label from your order and drop it off." },
  { value: "warranty", question: "What does the warranty cover?", answer: "Two years against defects in materials and workmanship, parts and labour included." },
  { value: "gift", question: "Can I send it as a gift?", answer: "Gift wrapping opens with the holiday season.", disabled: true },
];

const MODES = ["one at a time", "several"] as const;

function App() {
  const open = signal<string[]>(["shipping"]);
  const mode = signal<(typeof MODES)[number]>("one at a time");
  const events = signal<string[]>([]);
  return (
    <View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
      <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>accordion</Text>
      <Text style={{ color: ui().muted }}>{desktop ? "Click a question, or Tab to it and press Space" : "Tap a question to read its answer"}</Text>

      <Split
        gap={14}
        inspector={
          <>
            <Panel title="multiple">
              <Segmented
                options={MODES}
                value={mode()}
                onChange={(value) => {
                  mode.set(value);
                  // One at a time keeps the item opened last.
                  if (value === "one at a time") open.set(open().slice(-1));
                }}
              />
            </Panel>
            <Panel title="value">
              <Text style={{ fontSize: 13, color: ui().text }}>{open().length ? open().join(", ") : "none open"}</Text>
            </Panel>
            <Log title="onChange({ value })" lines={events()} minHeight={92} />
          </>
        }
      >
        <Accordion
          value={open()}
          multiple={mode() === "several"}
          onChange={({ value }) => {
            open.set(value);
            events.set([`[${value.join(", ")}]`, ...events()].slice(0, 4));
          }}
          style={{ ...ui().card, borderRadius: 16, overflow: "hidden" }}
        >
          {FAQ.map((item, i) => (
            <AccordionItem value={item.value} disabled={item.disabled}>
              <View style={{ height: i === 0 ? 0 : 1, marginLeft: desktop ? 12 : 16, backgroundColor: ui().line }} />
              <AccordionHeader>
                <AccordionTrigger
                  hoverStyle={item.disabled ? undefined : hoverFill()}
                  focusStyle={focusRing()}
                  class={`group flex-row items-center gap-3 ${pad} active:opacity-70 disabled:opacity-40`}
                  style={{ minHeight: size.row + 8 }}
                >
                  <Text style={{ flex: 1, fontSize: size.label, fontWeight: "500", color: ui().text }}>{item.question}</Text>
                  <View class="transition-transform duration-200 motion-reduce:transition-none group-aria-expanded:rotate-180">
                    <Icon name="chevron-down" size={14} tint={ui().muted} />
                  </View>
                </AccordionTrigger>
              </AccordionHeader>
              <AccordionPanel reveal accessibilityLabel={item.question}>
                <View class={`${pad} pb-3`}>
                  <Text style={{ fontSize: size.label, lineHeight: size.label * 1.45, color: ui().muted }}>{item.answer}</Text>
                </View>
              </AccordionPanel>
            </AccordionItem>
          ))}
        </Accordion>
      </Split>
    </View>
  );
}

mount(App);
  • Accordion, स्टैक: यह ऐप के value को, यानी खुले आइटमों के मानों को, रखता है और अगला मान onChange से रिपोर्ट करता है। एक समय में एक आइटम खुलता है, या multiple के साथ कई।
  • AccordionItem, एक सेक्शन, जिसकी पहचान उसका value है। जब तक accordion का value उसे रखता है, वह खुला रहता है।
  • AccordionHeader, आइटम का शीर्षक, जो एक level है और जिसे स्क्रीन रीडर बोलकर बताते हैं।
  • AccordionTrigger, शीर्षक का वह बटन जो अपने आइटम को खोलता और बंद करता है, और बताता है कि वह खुला है या नहीं।
  • AccordionPanel, आइटम की सामग्री, जो आइटम के खुले रहने तक दिखती है; reveal के साथ इसकी ऊँचाई स्प्रिंग की तरह खुलती और बंद होती है।
use nsh_base::{accordion, accordion_header, accordion_item, accordion_panel, accordion_trigger, theme};
use nsh_core::prelude::*;

const FAQ: [(&str, &str, &str); 2] = [
    ("shipping", "When will my order ship?", "Orders placed before 2 pm leave the same day."),
    ("returns", "How do I return an item?", "Print the label from your order within 30 days."),
];

fn faq() -> impl IntoElement {
    let open = signal(vec![SharedString::from("shipping")]);
    let mut list = accordion()
        .value(open)
        .on_change(move |value: &Vec<SharedString>| open.set(value.clone()));
    for (value, question, answer) in FAQ {
        list = list.child(
            accordion_item()
                .value(value)
                .border_b_1()
                .border_color(move || theme().border)
                .child(
                    accordion_header().child(
                        accordion_trigger()
                            .flex_row()
                            .items_center()
                            .justify_between()
                            .py_3()
                            .child(text(question).font_weight(FontWeight::MEDIUM))
                            .child(text("⌄").transition_transform().rotate(move || {
                                let open = open.get().iter().any(|v| &**v == value);
                                if open { deg(180.0) } else { deg(0.0) }
                            })),
                    ),
                )
                .child(
                    accordion_panel()
                        .reveal(true)
                        .accessibility_label(question)
                        .child(view().pb_3().child(text(answer).text_color(move || theme().muted_foreground))),
                ),
        );
    }
    list
}

ये भाग nsh/base से आते हैं, जो Rust में nsh-base crate है। मान ऐप का होता है: ट्रिगर का प्रेस onChange के साथ अगला value रिपोर्ट करता है, और accordion उन आइटमों को खोलता है जिनके मान ऐप वापस देता है। हर भाग अपने आसपास के भाग को खुद ढूँढ लेता है, इसलिए ऐप उन्हें खुलकर व्यवस्थित कर सकता है: आइटमों के बीच डिवाइडर, शीर्षक में बैज, या टाइटल से पहले आइकन। Rust में accordion(), accordion_item(), accordion_header(), accordion_trigger() और accordion_panel() इन्हें बनाते हैं; हर prop के लिए एक सेटर होता है और भीतर की सामग्री के लिए .child(..) होता है।

#Props

#Accordion

PropRustटाइपडिफ़ॉल्टविवरण
value.value(..)string[][]खुले आइटमों के value। ये ऐप के होते हैं: ट्रिगर का प्रेस अगले मान onChange से रिपोर्ट करता है, और accordion वही आइटम दिखाता है जो ऐप वापस देता है।
multiple.multiple(..)booleanfalseकई आइटम एक साथ खुले रह सकते हैं: एक को खोलने पर बाकी खुले रहते हैं। इसके बिना, किसी आइटम को खोलने पर जो खुला था वह बंद हो जाता है।
disabled.disabled(..)booleanfalseहर आइटम disabled हो जाता है: कोई ट्रिगर प्रेस नहीं लेता, और स्क्रीन रीडर हर एक को dimmed बताते हैं।
children.child(..)JSX.Elementकोई नहींआइटम, और वह सब जो ऐप उनके बीच रखता है (डिवाइडर)।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींस्टैक का लेआउट और रूप: आइटमों के चारों ओर कार्ड, उनके बीच की दूरी।

#AccordionItem

PropRustटाइपडिफ़ॉल्टविवरण
value.value(..)stringआवश्यकजो accordion के value में आइटम की पहचान कराता है: ऐप की अपनी id, कभी उसका टाइटल नहीं, क्योंकि टाइटल भाषा के साथ बदलता है।
disabled.disabled(..)booleanfalseइसका ट्रिगर प्रेस नहीं लेता, और स्क्रीन रीडर इसे dimmed बताते हैं। यह वैसे ही खुला या बंद रहता है जैसा accordion का value कहता है।
children.child(..)JSX.Elementकोई नहींआइटम का हेडर और पैनल, और वह सब जो ऐप इसमें रखता है।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींसेक्शन: उसके नीचे डिवाइडर, उसके चारों ओर कार्ड।

#AccordionHeader

PropRustटाइपडिफ़ॉल्टविवरण
level.level(..)number3पेज के शीर्षकों में इस शीर्षक का स्तर, स्क्रीन रीडर के लिए (1 सबसे ऊपर)।
children.child(..)JSX.Elementकोई नहींआइटम का ट्रिगर, और वह सब जो हेडर में और हो (बैज, अपना कोई एक्शन)।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींशीर्षक का लेआउट।

#AccordionTrigger

PropRustटाइपडिफ़ॉल्टविवरण
expandedStyle.expanded_style(|s| ..)StyleProp<ViewStyle>कोई नहींआइटम के खुले रहने पर ट्रिगर की स्टाइल, aria-expanded: क्लासों के रूप में।
accessibilityLabel.accessibility_label(..)stringकोई नहींजो स्क्रीन रीडर बोलते हैं, जब children में ऐसा कोई टेक्स्ट न हो जो यह बताए।
children.child(..)JSX.Elementकोई नहींटाइटल और शेवरॉन; भीतर की group-aria-expanded: क्लासें ट्रिगर का अनुसरण करती हैं, इसके लिए ट्रिगर के class पर group लगाएँ।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींखुद ट्रिगर: वह पंक्ति जिसे उपयोगकर्ता दबाता है।
activeStyle, hoverStyle, focusStyle, disabledStyle.active(|s| ..), .hover(..), .focus(..), .disabled_style(..)StyleProp<ViewStyle>कोई नहींदबे होने, hover, फ़ोकस या disabled होने पर ट्रिगर की स्टाइल, <state>: क्लासों के रूप में।

#AccordionPanel

PropRustटाइपडिफ़ॉल्टविवरण
reveal.reveal(..)booleanfalseCollapsible की तरह रिवील के साथ खुलता और बंद होता है: पैनल अपनी सामग्री को क्लिप करता है और उसकी ऊँचाई शून्य से सामग्री की अपनी ऊँचाई तक और वापस स्प्रिंग होती है, क्रिटिकली डैम्प्ड (लगभग 265 ms, बिना ओवरशूट), और उसके बाद के आइटम साथ खिसकते हैं। reduced motion में यह तुरंत खुलता और बंद होता है।
keepMounted.keep_mounted(..)booleanfalseआइटम बंद होने पर सामग्री ट्री में बनी रहती है, नज़र से, कीबोर्ड फ़ोकस से और स्क्रीन रीडर से दूर: वीडियो अपनी जगह रखता है, वेब व्यू अपना पेज। इसके बिना, पैनल बंद होते समय (रिवील के उसे बंद कर देने के बाद) सामग्री ट्री से हट जाती है और जैसी थी वैसी ही लौट आती है।
accessibilityLabel.accessibility_label(..)stringकोई नहींस्क्रीन रीडर इस region को क्या कहते हैं: आम तौर पर आइटम का टाइटल, जैसा उसका हेडर बताता है।
children.child(..)JSX.Elementकोई नहींसामग्री।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींregion का लेआउट और रूप। reveal के साथ पैनल बंद होने पर भी लेआउट में रहता है, शून्य ऊँचाई का, इसलिए उसकी padding और borders उसकी सामग्री के view पर लगाएँ।

#Events

इवेंटRustविवरण
onChange({ value }) (Accordion).on_change(|value: &Vec<SharedString>| ..)किसी ट्रिगर को दबाया गया: टैप, क्लिक, कीबोर्ड फ़ोकस होने पर Space या Return, या स्क्रीन रीडर का एक्टिवेशन। value खुले आइटमों के वे मान हैं जैसे वे होते: खुले आइटम का मान उसमें से हट जाता है, बंद आइटम का मान अंत में जुड़ जाता है, या multiple के बिना, उसकी जगह ले लेता है। यह कुछ भी बदलने से पहले चलता है: इसका अनुसरण करने के लिए value सेट करें।

#Parts

भागभीतरस्टाइलस्टेट
स्टैकAccordion के class, style
एक आइटमस्टैकAccordionItem के class, styleऐप का मान
शीर्षकआइटमAccordionHeader के class, style
ट्रिगरशीर्षकAccordionTrigger के class, stylearia-expanded:, disabled:, active:, hover:, focus: (या expandedStyle, disabledStyle, …)
टाइटल और शेवरॉनट्रिगरउनके अपने एलिमेंट (children)group-aria-expanded:, group-disabled: ट्रिगर के class पर group के साथ
पैनलआइटम, शीर्षक के बादAccordionPanel के class, style
सामग्रीपैनलइसके अपने एलिमेंट (children)

reveal के बिना, बंद पैनल लेआउट से बाहर रहता है और उसकी सामग्री ट्री से बाहर। reveal के साथ, यह बदलाव उस commit का लेआउट एनिमेशन होता है जो value को बदलता है (LayoutAnimation.configureNext की तरह, नेटिव रूप से चलता है, इसलिए इसके चलते समय कोई JavaScript नहीं चलता): खुलता पैनल शून्य से अपनी सामग्री की ऊँचाई तक बढ़ता है, बंद होता पैनल वापस सिकुड़ता है, दोनों एक ही स्प्रिंग में, और उनके बाद के आइटम साथ खिसकते हैं। ऐप का एक ट्रांज़िशन भी इसके साथ चलता है: शेवरॉन घूमता है (transition-transform group-aria-expanded:rotate-180)।

#Disabled

disabled आइटम का ट्रिगर प्रेस नहीं लेता, और स्क्रीन रीडर उसे dimmed बताते हैं; ट्रिगर पर disabled: क्लासें और उसके भीतर group-disabled: उसका अनुसरण करती हैं। accordion का disabled हर आइटम को disabled कर देता है। disabled आइटम फिर भी वही दिखाता है जो ऐप का value कहता है: ऐप उसे खोल सकता है।

#Accessibility

  • स्टैक एक group है, हर हेडर अपने level का शीर्षक है, और हर ट्रिगर उसके भीतर एक बटन है जो बताता है कि उसका आइटम expanded है या नहीं। स्क्रीन रीडर एक शीर्षक से दूसरे शीर्षक पर जा सकते हैं।
  • खुला पैनल एक region है, जिसका नाम accessibilityLabel से तय होता है; बंद पैनल स्क्रीन रीडर से छिपा रहता है, रखी गई सामग्री समेत।
  • खुले आइटम का रूप ऐप तय करता है, और उसे केवल रंग पर निर्भर नहीं होना चाहिए: शेवरॉन को घुमाएँ, जैसा उदाहरण करते हैं।

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

#Keyboard

जहाँ प्रेस लेने वाले views कीबोर्ड फ़ोकस के पड़ाव होते हैं (macOS और वेब), वहाँ हर ट्रिगर एक पड़ाव है, पेज के क्रम में, और Space तथा Return उसे दबाते हैं। disabled ट्रिगर छोड़ दिए जाते हैं, और बंद पैनल की सामग्री भी। Tab ट्रिगर से उसके खुले पैनल के कंट्रोल पर और फिर अगले ट्रिगर पर ले जाता है।

#Platform notes

  • हर प्लेटफ़ॉर्म खुले आइटम का पैनल उसके शीर्षक के नीचे दिखाता है और बंद पैनल को लेआउट से बाहर रखता है (reveal के साथ शून्य ऊँचाई का)।
  • reveal के साथ, हर प्लेटफ़ॉर्म पैनलों की ऊँचाई अपने एनिमेटर पर स्प्रिंग करता है (iOS, Android, macOS, Windows, Linux, GPUI और वेब), और जब सिस्टम reduced motion माँगता है तब उन्हें तुरंत खोलता और बंद करता है।
  • हर प्लेटफ़ॉर्म का स्क्रीन रीडर शीर्षकों को पढ़ते समय शीर्षक और उसका स्तर सुनाता है, और उसके भीतर के ट्रिगर को expanded स्टेट वाले बटन के रूप में; iOS पर VoiceOver ट्रिगर को खुद पढ़ता है।
  • iOS, Android और Mac Catalyst: ट्रिगर का प्रेस एक टैप है। डेस्कटॉप (macOS, Windows, Linux, GPUI) और वेब: एक क्लिक, और macOS तथा वेब पर फ़ोकस किए गए ट्रिगर पर Space या Return।