Last active
December 8, 2024 09:54
-
-
Save qknight/6d2a06a00e03542087cfcbb3704fccb6 to your computer and use it in GitHub Desktop.
This features two textareas in a horizontal layout which adapt their height to the content and sync the height among both textareas. It supports downsizing as well.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This features two textareas in a horizontal layout which adapt their height to the content and | |
// sync the height among both textareas. It supports downsizing as well. | |
// It is also quite a hack but it works! | |
#[component] | |
pub fn DescriptionEdit( | |
description_source: RwSignal<String>, | |
description_destination: RwSignal<String>, | |
merge_ui_element_states: RwSignal<HashMap<&'static str, i32>>, | |
unique_name: &'static str, | |
) -> impl IntoView { | |
let are_equal = create_memo(move |_| description_source.get() == description_destination.get()); | |
// refactoring WARNING: do not touch this code below because this makes sure that | |
// the textareas both sync the same maxium height and there is virtually no other easier way | |
let adaptHeightRight = move |ev: &Event| { | |
if let Some(target) = ev | |
.target() | |
.and_then(|t| t.dyn_into::<HtmlTextAreaElement>().ok()) | |
{ | |
// left | |
let textarea_left = document() | |
.get_element_by_id("textarea-left") | |
.unwrap() | |
.dyn_into::<HtmlTextAreaElement>() | |
.unwrap(); | |
textarea_left.set_value(description_source.get().as_str()); | |
// this makes it too small, so we can read the scroll_height and adapt the height accordingly | |
textarea_left.set_attribute("style", "height: 80px;"); | |
let textarea_height_left = textarea_left.scroll_height() + 25; | |
// right | |
// this makes it too small, so we can read the scroll_height and adapt the height accordingly | |
target.set_attribute("style", "height: 80px;"); | |
let textarea_height_right = target.scroll_height() + 25; | |
// common | |
let common_height = if textarea_height_left >= textarea_height_right { | |
textarea_height_left | |
} else { | |
textarea_height_right | |
}; | |
target.set_attribute("style", format!("height: {}px", common_height).as_str()); | |
textarea_left.set_attribute("style", format!("height: {}px", common_height).as_str()); | |
} | |
}; | |
fn resize_textareas( | |
description_source: RwSignal<String>, | |
description_destination: RwSignal<String>, | |
) { | |
let textarea_left = document() | |
.get_element_by_id("textarea-left") | |
.unwrap() | |
.dyn_into::<HtmlTextAreaElement>() | |
.unwrap(); | |
textarea_left.set_value(description_source.get().as_str()); | |
textarea_left.set_attribute("style", "height: 80px;"); | |
let textarea_height_left = textarea_left.scroll_height() + 25; | |
let textarea_right = document() | |
.get_element_by_id("textarea-right") | |
.unwrap() | |
.dyn_into::<HtmlTextAreaElement>() | |
.unwrap(); | |
textarea_right.set_value(description_destination.get().as_str()); | |
textarea_right.set_attribute("style", "height: 80px;"); | |
let textarea_height_right = textarea_right.scroll_height() + 25; | |
let common_height = if textarea_height_left >= textarea_height_right { | |
textarea_height_left | |
} else { | |
textarea_height_right | |
}; | |
textarea_left.set_attribute("style", format!("height: {}px", common_height).as_str()); | |
textarea_right.set_attribute("style", format!("height: {}px", common_height).as_str()); | |
} | |
Effect::new(move |_| { | |
resize_textareas(description_source, description_destination); | |
}); | |
view! { | |
<div class="border border-gray-300 rounded-md p-1 w-full"> | |
<p class="text-xl font-bold mb-2">"Description"</p> | |
<div class="grid grid-cols-[1fr_auto_1fr] items-center gap-2 w-full"> | |
// Source description (read-only) | |
<textarea | |
type="text" | |
id="textarea-left" | |
class="border border-gray-300 focus:ring-indigo-600 border focus:ring-2 focus:ring-inset rounded-md px-3 py-2 bg-gray-100 w-full h-full" | |
readonly | |
/> | |
// Equality display | |
<div class="text-lg flex items-center justify-center"> | |
{move || { | |
if are_equal.get() { | |
view! { <EqualIcon on_click=move |_| {} /> }.into_view() | |
} else { | |
view! { | |
<CopyRightIcon on_click=move |_| { | |
description_destination.set(description_source.get()); | |
} /> | |
} | |
.into_view() | |
} | |
}} | |
</div> | |
// Destination description (editable) | |
<textarea | |
type="text" | |
id="textarea-right" | |
class="border border-gray-300 rounded-md px-3 focus:ring-2 focus:ring-inset py-2 w-full h-full" | |
on:input=move |ev| { | |
adaptHeightRight(&ev); | |
description_destination.set(event_target_value(&ev)) | |
} | |
/> | |
</div> | |
</div> | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment