singlestage/components/field/
field.rs

1use crate::FieldContext;
2use leptos::{context::Provider, prelude::*};
3
4/// The core wrapper for a single field. Provides orientation control, invalid state styling, and
5/// spacing.
6#[component]
7pub fn Field(
8    children: Children,
9
10    /// Sets the display orientation.
11    ///
12    /// Accepted values: "vertical" | "horizontal" | "responsive". Defaults to "vertical".
13    #[prop(optional, into)]
14    orientation: MaybeProp<String>,
15    /// Sets the display variant of the `Field`.
16    ///
17    /// Accepted values: "button".
18    #[prop(optional, into)]
19    variant: MaybeProp<String>,
20
21    // GLOBAL ATTRIBUTES
22    //
23    /// A space separated list of keys to focus this element. The first key available on the user's
24    /// keyboard layout is used.
25    #[prop(optional, into)]
26    accesskey: MaybeProp<String>,
27    /// Sets whether the input value should be capitalized and how. If a parent `<form>` has
28    /// `autocapitalize` rules set, it will override any rules set here.
29    ///
30    /// Accepted values: "none" or "off" | "sentences" or "on" | "words" | "characters".
31    #[prop(optional, into)]
32    autocapitalize: MaybeProp<String>,
33    /// Grabs focus once the page has finished loading. Only one element on the page can be focused
34    /// at a time.
35    #[prop(optional, into)]
36    autofocus: MaybeProp<bool>,
37    /// Apply classes to the element.
38    #[prop(optional, into)]
39    class: MaybeProp<String>,
40    /// Allows client-side editing of the element by the user.
41    ///
42    /// Accepted values: "true" | "false" | "plaintext-only"
43    #[prop(optional, into)]
44    contenteditable: MaybeProp<String>,
45    /// Indicate directionality of the element's text.
46    ///
47    /// Accepted values: "ltr" | "rtl" | "auto"
48    #[prop(optional, into)]
49    dir: MaybeProp<String>,
50    /// Toggle whether the element can be dragged.
51    #[prop(optional, into)]
52    draggable: MaybeProp<bool>,
53    /// Modifies the appearance of the enter key on virtual keyboards.
54    #[prop(optional, into)]
55    enterkeyhint: MaybeProp<String>,
56    /// Expose elements in the shadow DOM to be manipulated by the DOM.
57    #[prop(optional, into)]
58    exportparts: MaybeProp<String>,
59    /// Controls hidden status of the element.
60    #[prop(optional, into)]
61    hidden: MaybeProp<String>,
62    /// Set the id of this element.
63    #[prop(optional, into)]
64    id: MaybeProp<String>,
65    /// Toggle if the browser reacts to input events from this element.
66    #[prop(optional, into)]
67    inert: MaybeProp<bool>,
68    /// Hints to the browser of what type of virtual keyboard to display when editing this element
69    /// or its children.
70    #[prop(optional, into)]
71    inputmode: MaybeProp<String>,
72    /// Used to render a standard element as a custom element.
73    #[prop(optional, into)]
74    is: MaybeProp<String>,
75    /// Unique global identifier of an item.
76    #[prop(optional, into)]
77    itemid: MaybeProp<String>,
78    /// Used to add properties to an item.
79    #[prop(optional, into)]
80    itemprop: MaybeProp<String>,
81    /// Used to associate an item with a related non-parent element that's using `itemscope`.
82    #[prop(optional, into)]
83    itemref: MaybeProp<String>,
84    /// Used to declare that children elements are related to a particular item.
85    #[prop(optional, into)]
86    itemscope: MaybeProp<String>,
87    /// URL of data used to define `itemprops`.
88    #[prop(optional, into)]
89    itemtype: MaybeProp<String>,
90    /// Defines the language of an element.
91    #[prop(optional, into)]
92    lang: MaybeProp<String>,
93    /// Cryptographic "number used once".
94    #[prop(optional, into)]
95    nonce: MaybeProp<String>,
96    /// List of the part names of the element.
97    #[prop(optional, into)]
98    part: MaybeProp<String>,
99    /// Designate an element as a popover element.
100    #[prop(optional, into)]
101    popover: MaybeProp<String>,
102    /// Assigns a slot to an element.
103    #[prop(optional, into)]
104    slot: MaybeProp<String>,
105    /// Toggle spellcheck for this input.
106    ///
107    /// Accepted values: "default" | "true" | "false".
108    #[prop(optional, into)]
109    spellcheck: MaybeProp<String>,
110    /// Define CSS to be applied to the element.
111    #[prop(optional, into)]
112    style: MaybeProp<String>,
113    /// Controls how an element behaves when a user navigates using the tab key.
114    #[prop(optional, into)]
115    tabindex: MaybeProp<usize>,
116    /// Describes the content of the element to screen readers.
117    #[prop(optional, into)]
118    title: MaybeProp<String>,
119    /// Defines localization behavior for the element.
120    #[prop(optional, into)]
121    translate: MaybeProp<String>,
122) -> impl IntoView {
123    let context = FieldContext {
124        description_id: RwSignal::new(String::default()),
125        input_id: RwSignal::new(String::default()),
126        label_id: RwSignal::new(String::default()),
127    };
128
129    let global_attrs_1 = view! {
130        <{..}
131            accesskey=move || accesskey.get()
132            autocapitalize=move || autocapitalize.get()
133            autofocus=move || autofocus.get()
134            contenteditable=move || contenteditable.get()
135            dir=move || dir.get()
136            draggable=move || draggable.get()
137            enterkeyhint=move || enterkeyhint.get()
138            exportparts=move || exportparts.get()
139            hidden=move || hidden.get()
140            id=move || id.get()
141            inert=move || inert.get()
142            inputmode=move || inputmode.get()
143            is=move || is.get()
144            itemid=move || itemid.get()
145        />
146    };
147
148    let global_attrs_2 = view! {
149        <{..}
150            itemprop=move || itemprop.get()
151            itemref=move || itemref.get()
152            itemscope=move || itemscope.get()
153            itemtype=move || itemtype.get()
154            lang=move || lang.get()
155            nonce=move || nonce.get()
156            part=move || part.get()
157            popover=move || popover.get()
158            slot=move || slot.get()
159            spellcheck=move || spellcheck.get()
160            style=move || style.get()
161            tabindex=move || tabindex.get()
162            title=move || title.get()
163            translate=move || translate.get()
164        />
165    };
166
167    view! {
168        <div
169            class=move || {
170                format!(
171                    "singlestage-field{}{}{}",
172                    match orientation.get().unwrap_or_default().as_str() {
173                        "horizontal" => " singlestage-field-horizontal",
174                        "responsive" => " singlestage-field-responsive",
175                        _ => " singlestage-field-vertical",
176                    },
177                    match variant.get().unwrap_or_default().as_str() {
178                        "button" => " singlestage-field-button",
179                        _ => "",
180                    },
181                    class.get().unwrap_or_default(),
182                )
183            }
184            role="group"
185
186            {..global_attrs_1}
187            {..global_attrs_2}
188        >
189            <Provider value=context>{children()}</Provider>
190        </div>
191    }
192}