Skip to content

Commit

Permalink
RFC: html: add RadioButton and RadioGroup
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Scherf <mail@florianscherf.de>
  • Loading branch information
fscherf committed Jul 7, 2023
1 parent ff9ea53 commit 3d61e56
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 4 deletions.
4 changes: 2 additions & 2 deletions lona/client/_lona/client/input-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export class LonaInputEventHandler {
_get_value(node) {
var value = node.value;

// checkbox
if(node.getAttribute('type') == 'checkbox') {
// checkbox / radiobutton
if(node.type == 'checkbox' || node.type == 'radio') {
value = node.checked;

// select
Expand Down
4 changes: 2 additions & 2 deletions lona/client2/_lona/client2/input-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export class LonaInputEventHandler {
_get_value(node) {
var value = node.value;

// checkbox
if(node.getAttribute('type') == 'checkbox') {
// checkbox / radiobutton
if(node.type == 'checkbox' || node.type == 'radio') {
value = node.checked;

// select
Expand Down
1 change: 1 addition & 0 deletions lona/html/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
Base,
)
from lona.html.nodes.interactive_elements import Summary, Details, Dialog
from lona.html.nodes.forms.radio_button import RadioButton, RadioGroup
from lona.html.nodes.scripting import NoScript, Script, Canvas
from lona.html.nodes.forms.select2 import Select2, Option2
from lona.html.nodes.web_components import Template, Slot
Expand Down
136 changes: 136 additions & 0 deletions lona/html/nodes/forms/radio_button.py
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

0 comments on commit 3d61e56

Please sign in to comment.