leptos_forms_rs/components/
code_input.rs1use crate::core::types::FieldValue;
2use leptos::prelude::*;
3
4#[component]
6pub fn CodeInput(
7 #[prop(into)]
9 name: String,
10 #[prop(into)]
12 value: Signal<FieldValue>,
13 #[prop(into)]
15 _on_change: Callback<FieldValue>,
16 #[prop(optional, into)]
18 placeholder: Option<String>,
19 #[prop(optional)]
21 required: Option<bool>,
22 #[prop(optional)]
24 disabled: Option<bool>,
25 #[prop(optional, into)]
27 class: Option<String>,
28 #[prop(optional, into)]
30 error: Option<String>,
31 #[prop(optional)]
33 has_error: Option<bool>,
34 #[prop(optional, into)]
36 _language: Option<String>,
37 #[prop(optional)]
39 show_line_numbers: Option<bool>,
40 #[prop(optional)]
42 show_language_selector: Option<bool>,
43 #[prop(optional)]
45 show_fullscreen: Option<bool>,
46 #[prop(optional)]
48 min_height: Option<u32>,
49 #[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}