Skip to main content

leptos_forms_rs/components/
code_input.rs

1use crate::core::types::FieldValue;
2use leptos::prelude::*;
3
4/// Code input component with syntax highlighting support
5#[component]
6pub fn CodeInput(
7    /// Field name for the input
8    #[prop(into)]
9    name: String,
10    /// Current value of the field
11    #[prop(into)]
12    value: Signal<FieldValue>,
13    /// Callback when the value changes
14    #[prop(into)]
15    _on_change: Callback<FieldValue>,
16    /// Placeholder text
17    #[prop(optional, into)]
18    placeholder: Option<String>,
19    /// Whether the field is required
20    #[prop(optional)]
21    required: Option<bool>,
22    /// Whether the field is disabled
23    #[prop(optional)]
24    disabled: Option<bool>,
25    /// CSS classes
26    #[prop(optional, into)]
27    class: Option<String>,
28    /// Error message to display
29    #[prop(optional, into)]
30    error: Option<String>,
31    /// Whether the field has an error
32    #[prop(optional)]
33    has_error: Option<bool>,
34    /// Programming language for syntax highlighting
35    #[prop(optional, into)]
36    _language: Option<String>,
37    /// Whether to show line numbers
38    #[prop(optional)]
39    show_line_numbers: Option<bool>,
40    /// Whether to show language selector
41    #[prop(optional)]
42    show_language_selector: Option<bool>,
43    /// Whether to show fullscreen toggle
44    #[prop(optional)]
45    show_fullscreen: Option<bool>,
46    /// Minimum height in pixels
47    #[prop(optional)]
48    min_height: Option<u32>,
49    /// Maximum height in pixels
50    #[prop(optional)]
51    max_height: Option<u32>,
52) -> impl IntoView {
53    let show_line_numbers = show_line_numbers.unwrap_or(true);
54    let show_language_selector = show_language_selector.unwrap_or(true);
55    let _show_fullscreen = show_fullscreen.unwrap_or(true);
56    let min_height = min_height.unwrap_or(200);
57    let max_height = max_height.unwrap_or(600);
58
59    let current_value = move || match value.get() {
60        FieldValue::String(s) => s,
61        _ => String::new(),
62    };
63
64    let supported_languages = vec![
65        ("rust", "Rust"),
66        ("javascript", "JavaScript"),
67        ("typescript", "TypeScript"),
68        ("python", "Python"),
69        ("java", "Java"),
70        ("cpp", "C++"),
71        ("c", "C"),
72        ("go", "Go"),
73        ("html", "HTML"),
74        ("css", "CSS"),
75        ("sql", "SQL"),
76        ("json", "JSON"),
77        ("yaml", "YAML"),
78        ("toml", "TOML"),
79        ("markdown", "Markdown"),
80    ];
81
82    let language_selector_view = move || {
83        if show_language_selector {
84            Some(view! {
85                <select class="language-selector">
86                    {supported_languages.iter().map(|(value, label)| {
87                        view! {
88                            <option value={*value}>{*label}</option>
89                        }
90                    }).collect::<Vec<_>>()}
91                </select>
92            })
93        } else {
94            None
95        }
96    };
97
98    let error_view = move || {
99        error.as_ref().map(|error_msg| {
100            view! {
101                <div class="error-message">
102                    {error_msg.clone()}
103                </div>
104            }
105        })
106    };
107
108    view! {
109        <div class={format!("code-input {}", class.unwrap_or_default())}>
110            <div class="code-toolbar">
111                {language_selector_view}
112
113                <div class="toolbar-actions">
114                    <button type="button" class="toolbar-btn">"📋"</button>
115                    <button type="button" class="toolbar-btn">"💾"</button>
116                    <button type="button" class="toolbar-btn">"📝"</button>
117                    <button type="button" class="toolbar-btn">"🔍"</button>
118                    <button type="button" class="toolbar-btn">"⚙️"</button>
119                    <button type="button" class="toolbar-btn">"⛶"</button>
120                </div>
121            </div>
122
123            <div class="code-editor-container">
124                <textarea
125                    name={name.clone()}
126                    placeholder={placeholder}
127                    required={required}
128                    disabled={disabled}
129                    class={move || {
130                        let mut classes = vec!["code-textarea"];
131                        if has_error.unwrap_or(false) {
132                            classes.push("error");
133                        }
134                        if disabled.unwrap_or(false) {
135                            classes.push("disabled");
136                        }
137                        if show_line_numbers {
138                            classes.push("with-line-numbers");
139                        }
140                        classes.join(" ")
141                    }}
142                    style={format!("min-height: {}px; max-height: {}px;", min_height, max_height)}
143                >{current_value}</textarea>
144            </div>
145
146            {error_view}
147        </div>
148    }
149}