Skip to main content

leptos_forms_rs/components/
markdown_input.rs

1use crate::core::types::FieldValue;
2use leptos::prelude::*;
3
4/// Markdown input component with live preview
5#[component]
6pub fn MarkdownInput(
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    /// Whether to show preview
35    #[prop(optional)]
36    show_preview: Option<bool>,
37    /// Whether to show toolbar
38    #[prop(optional)]
39    show_toolbar: Option<bool>,
40    /// Minimum height in pixels
41    #[prop(optional)]
42    min_height: Option<u32>,
43    /// Maximum height in pixels
44    #[prop(optional)]
45    max_height: Option<u32>,
46) -> impl IntoView {
47    let _show_preview = show_preview.unwrap_or(true);
48    let show_toolbar = show_toolbar.unwrap_or(true);
49    let min_height = min_height.unwrap_or(200);
50    let max_height = max_height.unwrap_or(600);
51
52    let current_value = move || match value.get() {
53        FieldValue::String(s) => s,
54        _ => String::new(),
55    };
56
57    let toolbar_view = move || {
58        if show_toolbar {
59            Some(view! {
60                <div class="markdown-toolbar">
61                    <button type="button" class="toolbar-btn">"H1"</button>
62                    <button type="button" class="toolbar-btn">"H2"</button>
63                    <button type="button" class="toolbar-btn">"H3"</button>
64                    <div class="toolbar-separator"></div>
65                    <button type="button" class="toolbar-btn">"B"</button>
66                    <button type="button" class="toolbar-btn">"I"</button>
67                    <div class="toolbar-separator"></div>
68                    <button type="button" class="toolbar-btn">"•"</button>
69                    <button type="button" class="toolbar-btn">"1."</button>
70                    <div class="toolbar-separator"></div>
71                    <button type="button" class="toolbar-btn">"🔗"</button>
72                    <button type="button" class="toolbar-btn">"🖼️"</button>
73                    <button type="button" class="toolbar-btn">"```"</button>
74                    <button type="button" class="toolbar-btn">">"</button>
75                    <div class="toolbar-separator"></div>
76                    <button type="button" class="toolbar-btn">"👁️"</button>
77                    <button type="button" class="toolbar-btn">"⫴"</button>
78                </div>
79            })
80        } else {
81            None
82        }
83    };
84
85    let error_view = move || {
86        error.as_ref().map(|error_msg| {
87            view! {
88                <div class="error-message">
89                    {error_msg.clone()}
90                </div>
91            }
92        })
93    };
94
95    view! {
96        <div class={format!("markdown-input {}", class.unwrap_or_default())}>
97            {toolbar_view}
98
99            <div class="markdown-editor-container">
100                <textarea
101                    name={name.clone()}
102                    placeholder={placeholder}
103                    required={required}
104                    disabled={disabled}
105                    class={move || {
106                        let mut classes = vec!["markdown-textarea"];
107                        if has_error.unwrap_or(false) {
108                            classes.push("error");
109                        }
110                        if disabled.unwrap_or(false) {
111                            classes.push("disabled");
112                        }
113                        classes.join(" ")
114                    }}
115                    style={format!("min-height: {}px; max-height: {}px;", min_height, max_height)}
116                >{current_value}</textarea>
117            </div>
118
119            {error_view}
120        </div>
121    }
122}