Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Checkbox inputs #125

Merged
merged 11 commits into from
Sep 6, 2024
Merged

Checkbox inputs #125

merged 11 commits into from
Sep 6, 2024

Conversation

cfraz89
Copy link
Contributor

@cfraz89 cfraz89 commented Sep 1, 2024

Adds support for input type="checkbox", as requested in #94 . Makes checkboxes send oninput event when clicked. If event isn't bound (there's no data-dioxus-id), they will behave as an unmanaged input, and handle checked state internally. If it is, they will send an oninput event with a FormData argument.

Adds a 'form' example to demonstrate.

Caveats:

  • They should be drawing their colors using the css property 'accent-color', but I couldnt figure out how to look that up, so they're using just 'color' for now.
  • There's something up with the vertical alignment when rendering labels with the text 'inline'. Check the second checkbox in the example, or try removing the 'inline-block' from label styling in the css. In chrome and ff, the labels render the same vertically whether inline or inline-block.
  • The FormEvent has valid: false, as its calculated as 'true' if value is empty? However I've set value to the value of the toggled element. Not sure if this is correct
  • Checking a checkbox send 'onclick' and 'oninput' events. Not sure if it should send 'onchanged' too? Didnt see any mention of that event in dioxus docs.

image

Copy link
Collaborator

@nicoburns nicoburns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They should be drawing their colors using the css property 'accent-color', but I couldnt figure out how to look that up, so they're using just 'color' for now.

See comments on relevant code.

There's something up with the vertical alignment when rendering labels with the text 'inline'. Check the second checkbox in the example, or try removing the 'inline-block' from label styling in the css. In chrome and ff, the labels render the same vertically whether inline or inline-block.

Weird that the two checkboxes are rendering differently, but let's not worry too much about this for now.

The FormEvent has valid: false, as its calculated as 'true' if value is empty? However I've set value to the value of the toggled element. Not sure if this is correct

I believe it should be the literal on if the value is empty. Saw that in an MDN doc somewhere.

Checking a checkbox send 'onclick' and 'oninput' events. Not sure if it should send 'onchanged' too? Didnt see any mention of that event in dioxus docs.

I believe browsers send onchanged for checkboxes, so we probably should too.


Some nice to haves (non-blocking):

  • Implementing the disabled attribute, which would prevent clicks from taking effect and would also make the checkbox render with a greyed out look.
  • Making the space bar toggle the checkbox if it is currently focussed.

return compute_leaf_layout(
inputs,
&node.style,
|_known_size, _available_space| {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that the vertical alignment issue you are seeing is due to taffy/parley assuming the bottom of the node's border-box as it's baseline. Whereas for checkboxes it probably ought to ignore the margin and use the bottom of the actual checkbox as the baseline.

We will need to add support for inline-box baselines to parley to make that work properly, but a good first step would be to modify the first_baselines.y field of the taffy::LayoutOutput returned by compute_leaf_layout to set the baseline to the bottom of the checkbox (height of checkbox + top padding/border/margin).

Comment on lines +278 to +292
self.tree()
.into_iter()
.filter_map(|(_id, node)| {
let element_data = node.element_data()?;
if element_data.name.local != local_name!("input") {
return None;
}
let id = element_data.id.as_ref()?;
if *id == *target_element_dom_id {
Some(node)
} else {
None
}
})
.collect()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably ought to maintain a global ID map so this can be looked up much more quickly? I wouldn't block this PR on that, but perhaps an "iterate over nodes with a given id" method could be added to Document?

.and_then(|c| c.parse().ok())
.unwrap_or_default();

// TODO this should be coming from css accent-color, but I couldn't find how to retrieve it
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accent-color can be enabled here: https://github.com/servo/stylo/blob/main/style/properties/longhands/inherited_ui.mako.rs#L94 by changing engines="gecko" to engines="gecko servo".

Such a change could be submitted to the DioxusLabs fork of Stylo. We are currently using the enable-table-moz-center-style-adjust branch. So changes should be based on that branch (we should probably be more principled about using a blitz branch or similar).

Comment on lines +1101 to +1105
let checked: bool = self
.element
.attr(local_name!("checked"))
.and_then(|c| c.parse().ok())
.unwrap_or_default();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checked is a boolean attribute, so this should just check for the presence of the checked attribute in the array. If it is present then checked is true else false (even the empty string or "false" counts as true).

See: https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML

Comment on lines +310 to +331
pub fn toggle_checkbox(el: &mut ElementNodeData) {
let checked_attr_opt = el
.attrs
.iter_mut()
.find(|attr| attr.name.local == local_name!("checked"));

let checked_attr = if let Some(attr) = checked_attr_opt {
attr
} else {
let attr = Attribute {
name: QualName::new(None, ns!(html), local_name!("checked")),
value: String::from("false"),
};
el.attrs.push(attr);
el.attrs
.iter_mut()
.find(|attr| attr.name.local == local_name!("checked"))
.unwrap()
};
let checked = checked_attr.value.parse().unwrap_or(false);
checked_attr.value = (!checked).to_string();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to actually remove the attribute entirely when unchecking and add it when checking. I suggest using the empty string for the value to avoid an allocation.

self.set_focus_to(hit.node_id);
}
// Clicking labels triggers click, and possibly input event, of associated input
else if el.name.local == local_name!("label") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice that you've implemented label!

@nicoburns
Copy link
Collaborator

This PR seems to be breaking button layout on the google example.

main branch:

Screenshot 2024-09-05 at 15 03 03

This PR:

Screenshot 2024-09-05 at 15 03 31

@nicoburns nicoburns merged commit f3045d1 into DioxusLabs:main Sep 6, 2024
8 checks passed
@nicoburns nicoburns mentioned this pull request Sep 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants