-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RFC: html: add RadioButton and RadioGroup
Signed-off-by: Florian Scherf <mail@florianscherf.de>
- Loading branch information
Showing
4 changed files
with
141 additions
and
4 deletions.
There are no files selected for viewing
This file contains 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 file contains 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 file contains 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 file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
from lona.html.nodes.forms.inputs import TextInput | ||
from lona.html.node import Node | ||
|
||
|
||
class RadioButton(TextInput): | ||
INPUT_ATTRIBUTE_NAME = 'checked' | ||
|
||
ATTRIBUTES = { | ||
'type': 'radio', | ||
'name': 'radio', | ||
} | ||
|
||
def __init__( | ||
self, | ||
*args, | ||
value='', | ||
bubble_up=True, | ||
checked=False, | ||
render_value=True, | ||
**kwargs, | ||
): | ||
|
||
self.render_value = render_value | ||
|
||
super().__init__(*args, bubble_up=bubble_up, **kwargs) | ||
|
||
self.value = value | ||
self.checked = checked | ||
|
||
def _render_value(self, value): | ||
return str(value) | ||
|
||
# value | ||
@property | ||
def value(self): | ||
return self._value | ||
|
||
@value.setter | ||
def value(self, new_value): | ||
with self.lock: | ||
if self.render_value: | ||
self.attributes['value'] = self._render_value(new_value) | ||
|
||
self._value = new_value | ||
|
||
# name | ||
@property | ||
def name(self): | ||
return self.attributes['name'] | ||
|
||
@name.setter | ||
def name(self, new_value): | ||
self.attributes['name'] = new_value | ||
|
||
# checked | ||
@property | ||
def checked(self): | ||
return 'checked' in self.attributes | ||
|
||
@checked.setter | ||
def checked(self, new_value): | ||
if new_value: | ||
self.attributes['checked'] = '' | ||
|
||
else: | ||
del self.attributes['checked'] | ||
|
||
|
||
class RadioGroup(Node): | ||
TAG_NAME = 'form' | ||
|
||
def get_radio_buttons(self): | ||
return self.query_selector_all('input[type=radio]') | ||
|
||
def get_checked_radio_button(self): | ||
with self.lock: | ||
for radio_button in self.get_radio_buttons(): | ||
if radio_button.checked: | ||
return radio_button | ||
|
||
def handle_input_event(self, input_event): | ||
|
||
# check if incoming event was fired by a radio button and bubble it | ||
# up if not | ||
if input_event.name != 'change': | ||
return super().handle_input_event(input_event) | ||
|
||
if (not input_event.node or | ||
input_event.node.tag_name != 'input' or | ||
input_event.node.attributes.get('type', '') != 'radio'): | ||
|
||
return super().handle_input_event(input_event) | ||
|
||
# uncheck all radio buttons in the same radio group that are unchecked | ||
# on the client | ||
with self.lock: | ||
name = input_event.node.attributes.get('name', '') | ||
|
||
for radio_button in self.get_radio_buttons(): | ||
if radio_button is input_event.node: | ||
continue | ||
|
||
if radio_button.attributes.get('name', '') != name: | ||
continue | ||
|
||
# The browser unchecks all previously checked radio buttons | ||
# in the same radio group autamatically. So we don't need | ||
# to send a patch to the original issuer of the change event. | ||
if 'checked' in radio_button.attributes: | ||
radio_button.attributes.__delitem__( | ||
'checked', | ||
issuer=(input_event.connection, input_event.window_id), | ||
) | ||
|
||
# patch input_event so `input_event.node.value` and `input_event.data` | ||
# yield the actual value of the radio group | ||
input_event.node = self | ||
input_event.data = self.value | ||
|
||
return super().handle_input_event(input_event) | ||
|
||
# value | ||
@property | ||
def value(self): | ||
checked_radio_button = self.get_checked_radio_button() | ||
|
||
if not checked_radio_button: | ||
return | ||
|
||
return checked_radio_button.value | ||
|
||
@value.setter | ||
def value(self, new_value): | ||
with self.lock: | ||
for radio_button in self.get_radio_buttons(): | ||
radio_button.checked = radio_button.value == new_value |