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

एलिमेंट

Overlay

Overlay अपने children को उस root view में सबसे ऊपर दिखाता है जिसमें वह है: window, या उसके आसपास का dialog। इसके parent का आकार, overflow: hidden और scroll views इसे clip नहीं करते। बाकी सब बातों में यह वहीं रहता है जहाँ आपने इसे लिखा है: यह अपने आसपास का context पढ़ता है, इसके handlers इसके component के होते हैं, और यह अपने parent के साथ हट जाता है। Popovers, menus, tooltips और sheets इसी पर बने हैं; nsh/base में ये तैयार मिलते हैं (Popover, Menu, Tooltip, Sheet), और अपना खुद का बनाने के लिए आप overlay का उपयोग करते हैं।

el-overlay.tsx
el-overlay.tsxLive
import { mount, Overlay, signal, Text, View, type ViewRef } from "nsh";
import { button, buttonText, desktop, Inspector, Panel, Segmented, Stage, ui, wide } from "./palette";

type Placement = "top" | "bottom" | "left" | "right";
type Align = "start" | "center" | "end";

function App() {
  const open = signal(false);
  const placement = signal<Placement>("bottom");
  const align = signal<Align>("center");
  const position = signal("—");
  const closed = signal("—");
  let anchor: ViewRef | undefined;

  const placements = () => (
    <Segmented
      label="placement"
      options={["top", "bottom", "left", "right"] as Placement[]}
      value={placement()}
      onChange={(v) => (placement.set(v), open.set(true))}
    />
  );
  const aligns = () => (
    <Segmented label="align" options={["start", "center", "end"] as Align[]} value={align()} onChange={(v) => (align.set(v), open.set(true))} />
  );
  const subject = () => (
    <>
      <View
        style={{
          width: 220,
          height: 72,
          overflow: "hidden",
          borderRadius: 14,
          borderWidth: 2,
          borderStyle: "dashed",
          borderColor: ui().faint,
          alignItems: "center",
          justifyContent: "center",
        }}
      >
        <View
          ref={anchor}
          onPress={() => open.set(!open())}
          {...button("accent", { align: "center" })}
        >
          <Text style={buttonText("accent")}>{open() ? "Close" : "Open"}</Text>
        </View>
        <If when={open()}>
          <Overlay
            anchor={anchor}
            placement={placement()}
            align={align()}
            offset={8}
            margin={12}
            onPosition={(e) =>
              position.set(
                `${e.placement} at ${Math.round(e.bounds.origin.x)}, ${Math.round(e.bounds.origin.y)} · ${Math.round(e.bounds.size.width)}×${Math.round(e.bounds.size.height)} · room ${Math.round(e.available.width)}×${Math.round(e.available.height)}`,
              )
            }
            onPressOutside={(e) => {
              closed.set(`onPressOutside at ${Math.round(e.pagePosition.x)}, ${Math.round(e.pagePosition.y)}`);
              open.set(false);
            }}
            onCancel={() => {
              closed.set("onCancel");
              open.set(false);
            }}
            style={{
              ...ui().card,
              borderRadius: 12,
              padding: 14,
              gap: 4,
              // Narrow beside the button, so it fits on a phone's side.
              maxWidth: !wide() && (placement() === "left" || placement() === "right") ? 130 : 220,
            }}
          >
            <Text style={{ fontSize: 15, fontWeight: "600", color: ui().text }}>On top of everything</Text>
            <Text style={{ fontSize: 13, color: ui().muted }}>
              Its box clips the button, not this overlay. Pick a side {wide() ? "on the right" : "above"}.
            </Text>
          </Overlay>
        </If>
      </View>
      <Text style={{ marginTop: 6, fontSize: 12, color: ui().faint }}>The dashed box has overflow: hidden</Text>
    </>
  );

  return (
    <View style={{ flex: 1, backgroundColor: ui().page, padding: 20, paddingTop: 24, gap: 14 }}>
      <Text style={{ fontSize: 22, fontWeight: "700", color: ui().text }}>overlay</Text>
      <Text style={{ color: ui().muted }}>
        {desktop ? "Click the button, then click outside or press Escape" : "Tap the button, then tap outside"}
      </Text>

      {() =>
        wide() ? (
          <View style={{ flex: 1, flexDirection: "row", gap: 16 }}>
            <Stage>
              <View style={{ alignItems: "center" }}>{subject()}</View>
            </Stage>
            <Inspector>
              {placements()}
              {aligns()}
              <Panel title="onPosition">
                <Text style={{ fontSize: 13, color: ui().text, fontVariant: ["tabular-nums"] }}>{position()}</Text>
              </Panel>
              <Panel title="closed by" grow>
                <Text style={{ fontSize: 13, color: closed() === "—" ? ui().text : ui().accent }}>{closed()}</Text>
              </Panel>
            </Inspector>
          </View>
        ) : (
          <>
            <View style={{ flexDirection: "row", gap: 12 }}>
              <View style={{ flex: 3 }}>{placements()}</View>
              <View style={{ flex: 2 }}>{aligns()}</View>
            </View>

            <View style={{ flex: 1, minHeight: 160, alignItems: "center", justifyContent: "center" }}>{subject()}</View>

            <View style={{ ...ui().inset, borderRadius: 12, padding: 12, gap: 6 }}>
              <Text style={{ fontSize: 12, color: ui().muted }}>
                onPosition <Text style={{ color: ui().text }}>{position()}</Text>
              </Text>
              <Text style={{ fontSize: 12, color: ui().muted }}>
                closed by <Text style={{ color: closed() === "—" ? ui().text : ui().accent }}>{closed()}</Text>
              </Text>
            </View>
          </>
        )
      }
    </View>
  );
}

mount(App);
use nsh_core::prelude::*;

fn more_button() -> impl IntoElement {
    let open = signal(false);
    let anchor = element_ref();
    view()
        .child(
            view()
                .element_ref(&anchor)
                .on_press(move || open.update(|o| *o = !*o))
                .px_3()
                .py_2()
                .child(text("More")),
        )
        .child(when(move || open.get(), move || {
            overlay()
                .anchor(&anchor)
                .placement(Placement::Bottom)
                .align(Align::Start)
                .offset(px(6.0))
                .margin(px(8.0))
                .on_press_outside(move |_: &Point<Points>| open.set(false))
                .on_cancel(move || open.set(false))
                .rounded_lg()
                .bg(white())
                .p_3()
                .shadow_lg()
                .child(text("Shown above everything"))
        }))
}

TSX में Overlay nsh से आता है और Rust में overlay() nsh_core::prelude से। यह तब तक दिखता है जब तक यह view में है: इसे खोलने और बंद करने के लिए इसे If (Rust: when(..)) में रखें। बाद में दिखाए गए overlays पहले वालों के ऊपर होते हैं, और किसी दूसरे के अंदर का overlay उसके ऊपर होता है।

#Placement

anchor के बिना, overlay अपने root view के एक position: fixed box की तरह लेआउट होता है: इसके top, left, inset, size और margins पूरी window (या dialog) के सापेक्ष होते हैं। class="inset-0" इसे पूरा ढक देता है, जो backdrop या पूरी window की layer बनता है; class="bottom-0 left-0 right-0" नीचे की ओर एक bar है।

anchor के साथ, NSH इसे anchor के पास रखता है:

  • anchor एक view (ref), root view के coordinates में एक Point ({ x, y }), touch का pageX / pageY (pointer जहाँ है वहाँ menu), या वहीं एक Bounds ({ origin, size }) हो सकता है: किसी input का caret (onCaretMove का pageBounds), जहाँ टाइप किए जा रहे टेक्स्ट के सुझाव खुलते हैं।
  • placement वह side है जिस पर यह जाता है: "top", "bottom" (डिफ़ॉल्ट), "left" या "right"। जब यह वहाँ फिट नहीं होता और दूसरी side पर फिट होता है, तो यह वहाँ चला जाता है; जब यह किसी पर भी फिट नहीं होता, तो यह उस side पर जाता है जहाँ ज़्यादा जगह हो।
  • align इसे उस side के साथ संरेखित करता है: "start", "center" (डिफ़ॉल्ट) या "end"। anchor के ऊपर और नीचे, start और end लेआउट की दिशा के अनुसार चलते हैं: start बायाँ किनारा है, या right-to-left लेआउट में दायाँ किनारा।
  • offset anchor और overlay के बीच की दूरी है; margin वह दूरी है जो यह safe area के किनारों से बनाए रखता है, और जिसके अंदर यह रहता है: यह फिट होने के लिए अपनी side के साथ खिसक जाता है।

Anchored overlay अपने anchor का अनुसरण करता है: जब anchor scroll होता है, हिलता है या उसका आकार बदलता है, तो overlay उसी frame में उसके साथ चलता है जिसमें NSH उसे लेआउट करता है। onPosition बताता है कि यह कहाँ गया, ताकि कोई arrow anchor की ओर इशारा कर सके या लंबा menu अपनी ऊँचाई सीमित कर सके:

<Overlay
  anchor={anchor}
  onPosition={(e) => {
    side.set(e.placement); // "top" when it flipped
    maxHeight.set(e.available.height);
  }}
/>

#Dismissing

Overlay वह रिपोर्ट करता है जो आमतौर पर popup को बंद करता है, और निर्णय आप पर छोड़ देता है:

  • onPressOutside तब fire होता है जब overlay, उसके anchor और उसके अंदर के overlays के बाहर कोई उँगली या mouse button नीचे जाता है। press उसके नीचे वाली चीज़ तक पहुँचता रहता है।
  • onCancel Escape, Android के back button और iOS पर VoiceOver के escape gesture पर fire होता है, केवल उस सबसे ऊपरी overlay के लिए जो इसे सुनता है। यह इन्हें pages और BackHandler से पहले ले लेता है।

popup के पीछे की screen को block करने के लिए, उसके नीचे class="inset-0" के साथ एक दूसरा overlay रखें और एक onPress जो दोनों को बंद करे: Sheet और Popover का phone presentation इसी तरह काम करते हैं।

#Props

PropRustप्रकारडिफ़ॉल्टविवरण
anchor.anchor(&element_ref), .anchor_point(point), .anchor_bounds(bounds)ViewRef | Point | Bounds | nullकोई नहींवह view, point या box जिसके सापेक्ष इसे रखा जाता है। इसके बिना यह अपने root view का एक fixed box होता है।
placement.placement(..)"top" | "bottom" | "left" | "right""bottom"anchor की वह side जिस पर यह जाता है।
align.align(..)"start" | "center" | "end""center"उस side के साथ यह anchor से कैसे संरेखित होता है।
offset.offset(px(..))number0anchor और overlay के बीच की दूरी।
margin.margin(px(..))number0safe area के किनारों से यह जो दूरी बनाए रखता है।
children.child(..)JSX.Elementकोई नहींयह क्या दिखाता है।
class, stylestyle methodsstring, StyleProp<ViewStyle>कोई नहींइसका रूप और, anchor के बिना, इसकी जगह।

यह view के common props भी लेता है।

#Events

EventRustविवरण
onPressOutside.on_press_outside(|point| ..)एक press इसके, इसके anchor के और इसके अंदर के overlays के बाहर नीचे गया (pagePosition, एक Point)।
onCancel.on_cancel(|| ..)Escape, Android का back button या VoiceOver का escape gesture, जब यह सुनने वाला सबसे ऊपरी overlay हो।
onPosition.on_position(|position| ..)NSH ने इसे रखा: placement (वह side जिस पर यह गया), root view में bounds, और available (एक Size: उस side पर उपलब्ध जगह)।

#Platform notes

  • हर platform overlay को window (या dialog) के root view के एक native view के रूप में दिखाता है, जो आसपास के views की तरह ही draw और hit-test होता है: UIKit, Android views, AppKit, GTK, Win32, GPUI और DOM।
  • Dialog के अंदर का overlay उसी dialog में, उसकी सामग्री के ऊपर दिखता है।
  • Overlays अपनी window में ही रहते हैं: जो menu window के किनारे से आगे जाता, वह खिसककर वापस उसके अंदर आ जाता है।