LibWeb: Add textarea placeholder

This commit is contained in:
Bastiaan van der Plaat 2023-12-21 18:13:16 +01:00 committed by Andreas Kling
parent 091faf1aae
commit 52397d01bd
Notes: sideshowbarker 2024-07-17 01:28:15 +09:00
6 changed files with 85 additions and 33 deletions

View file

@ -47,6 +47,8 @@ void HTMLTextAreaElement::initialize(JS::Realm& realm)
void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_placeholder_element);
visitor.visit(m_placeholder_text_node);
visitor.visit(m_inner_text_element);
visitor.visit(m_text_node);
}
@ -86,6 +88,8 @@ void HTMLTextAreaElement::reset_algorithm()
m_dirty = false;
// and set the raw value of element to its child text content.
m_raw_value = child_text_content();
update_placeholder_visibility();
}
void HTMLTextAreaElement::form_associated_element_was_inserted()
@ -181,9 +185,22 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed()
return;
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
set_shadow_root(shadow_root);
auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
MUST(shadow_root->append_child(element));
m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
m_placeholder_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::Placeholder);
MUST(element->append_child(*m_placeholder_element));
m_placeholder_text_node = heap().allocate<DOM::Text>(realm(), document(), String {});
m_placeholder_text_node->set_data(get_attribute(HTML::AttributeNames::placeholder).value_or(String {}));
m_placeholder_text_node->set_editable_text_node_owner(Badge<HTMLTextAreaElement> {}, *this);
MUST(m_placeholder_element->append_child(*m_placeholder_text_node));
m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
MUST(element->append_child(*m_inner_text_element));
m_text_node = heap().allocate<DOM::Text>(realm(), document(), String {});
m_text_node->set_always_editable(true);
@ -191,11 +208,23 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed()
// NOTE: If `children_changed()` was called before now, `m_raw_value` will hold the text content.
// Otherwise, it will get filled in whenever that does get called.
m_text_node->set_text_content(m_raw_value);
MUST(m_inner_text_element->append_child(*m_text_node));
MUST(element->append_child(*m_inner_text_element));
MUST(shadow_root->append_child(element));
set_shadow_root(shadow_root);
update_placeholder_visibility();
}
void HTMLTextAreaElement::update_placeholder_visibility()
{
if (!m_placeholder_element)
return;
if (!m_text_node)
return;
auto placeholder_text = get_attribute(AttributeNames::placeholder);
if (placeholder_text.has_value() && m_text_node->data().is_empty()) {
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv));
} else {
MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv));
}
}
// https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:children-changed-steps
@ -207,6 +236,16 @@ void HTMLTextAreaElement::children_changed()
m_raw_value = child_text_content();
if (m_text_node)
m_text_node->set_text_content(m_raw_value);
update_placeholder_visibility();
}
}
void HTMLTextAreaElement::attribute_changed(FlyString const& name, Optional<String> const& value)
{
HTMLElement::attribute_changed(name, value);
if (name == HTML::AttributeNames::placeholder) {
if (m_placeholder_text_node)
m_placeholder_text_node->set_data(value.value_or(String {}));
}
}
@ -214,6 +253,8 @@ void HTMLTextAreaElement::did_edit_text_node(Badge<Web::HTML::BrowsingContext>)
{
// A textarea element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the raw value.
m_dirty = true;
update_placeholder_visibility();
}
}