logicaffeine_web/ui/components/
editor.rs1use dioxus::prelude::*;
18
19const EDITOR_STYLE: &str = r#"
20.editor-container {
21 flex: 1;
22 display: flex;
23 flex-direction: column;
24 min-height: 200px;
25 padding: 16px;
26}
27
28.editor-wrapper {
29 flex: 1;
30 background: rgba(255, 255, 255, 0.08);
31 border: 1px solid rgba(255, 255, 255, 0.2);
32 border-radius: 12px;
33 overflow: hidden;
34}
35
36.editor-fallback {
37 width: 100%;
38 height: 100%;
39 min-height: 150px;
40 background: rgba(255, 255, 255, 0.08);
41 border: 1px solid rgba(255, 255, 255, 0.2);
42 border-radius: 12px;
43 padding: 16px;
44 font-size: 16px;
45 font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
46 color: #e8e8e8;
47 resize: none;
48 outline: none;
49 -webkit-overflow-scrolling: touch;
50}
51
52.editor-fallback:focus {
53 border-color: #667eea;
54 box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
55}
56
57.editor-fallback::placeholder {
58 color: #666;
59}
60
61/* Mobile optimizations */
62@media (max-width: 768px) {
63 .editor-container {
64 padding: 12px;
65 min-height: unset;
66 }
67
68 .editor-fallback {
69 /* 16px minimum prevents iOS zoom on focus */
70 font-size: 16px;
71 padding: 14px;
72 border-radius: 10px;
73 min-height: 120px;
74 /* Allow textarea to grow with content */
75 resize: vertical;
76 }
77
78 .editor-fallback:focus {
79 /* Slightly stronger focus for mobile visibility */
80 box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.25);
81 }
82
83 .editor-fallback::placeholder {
84 color: #777;
85 font-size: 15px;
86 }
87}
88
89/* Extra small screens */
90@media (max-width: 480px) {
91 .editor-container {
92 padding: 10px;
93 }
94
95 .editor-fallback {
96 padding: 12px;
97 font-size: 16px; /* Keep at 16px to prevent zoom */
98 min-height: 100px;
99 }
100}
101
102/* Landscape mobile - more horizontal space for input */
103@media (max-height: 500px) and (orientation: landscape) {
104 .editor-container {
105 padding: 8px;
106 }
107
108 .editor-fallback {
109 min-height: 80px;
110 padding: 10px 12px;
111 }
112}
113"#;
114
115#[component]
116pub fn Editor(
117 value: String,
118 on_change: EventHandler<String>,
119 placeholder: Option<String>,
120) -> Element {
121 let placeholder_text = placeholder.unwrap_or_else(|| "Type an English sentence...".to_string());
122
123 rsx! {
124 style { "{EDITOR_STYLE}" }
125
126 div { class: "editor-container",
127 textarea {
128 class: "editor-fallback",
129 placeholder: "{placeholder_text}",
130 value: "{value}",
131 oninput: move |evt| on_change.call(evt.value()),
132 }
133 }
134 }
135}
136
137#[component]
138pub fn LiveEditor(
139 on_change: EventHandler<String>,
140 placeholder: Option<String>,
141 #[props(default)] value: String,
142) -> Element {
143 let placeholder_text = placeholder.unwrap_or_else(|| "Type an English sentence...".to_string());
144
145 let handle_input = move |evt: Event<FormData>| {
146 let new_value = evt.value();
147 on_change.call(new_value);
148 };
149
150 rsx! {
151 style { "{EDITOR_STYLE}" }
152
153 div { class: "editor-container",
154 textarea {
155 class: "editor-fallback",
156 placeholder: "{placeholder_text}",
157 value: "{value}",
158 oninput: handle_input,
159 spellcheck: "false",
160 autocomplete: "off",
161 autocapitalize: "off",
162 }
163 }
164 }
165}