form support more inputs (#1554)

This commit is contained in:
Thomas Brandého 2023-08-10 18:54:04 +02:00 committed by GitHub
parent 2c61077796
commit cd47815a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 155 additions and 19 deletions

View File

@ -383,12 +383,26 @@ export const preventDefault = (event) => {
* @returns The value.
*/
export const getRefValue = (ref) => {
if (!ref || !ref.current){
if (!ref || !ref.current) {
return;
}
if (ref.current.type == "checkbox") {
return ref.current.checked;
} else {
return ref.current.value;
//querySelector(":checked") is needed to get value from radio_group
return ref.current.value || (ref.current.querySelector(':checked') && ref.current.querySelector(':checked').value);
}
}
/**
* Get the values from a ref array.
* @param refs The refs to get the values from.
* @returns The values array.
*/
export const getRefValues = (refs) => {
if (!refs) {
return;
}
// getAttribute is used by RangeSlider because it doesn't assign value
return refs.map((ref) => ref.current.value || ref.current.getAttribute("aria-valuenow"));
}

View File

@ -28,6 +28,7 @@ DEFAULT_IMPORTS: imports.ImportDict = {
ImportVar(tag="preventDefault"),
ImportVar(tag="refs"),
ImportVar(tag="getRefValue"),
ImportVar(tag="getRefValues"),
ImportVar(tag="getAllLocalStorageItems"),
},
"": {ImportVar(tag="focus-visible/dist/focus-visible")},

View File

@ -22,12 +22,18 @@ class Form(ChakraComponent):
A dict mapping the event trigger to the var that is passed to the handler.
"""
# Send all the input refs to the handler.
return {
"on_submit": {
ref[4:]: Var.create(f"getRefValue({ref})", is_local=False)
for ref in self.get_refs()
}
}
form_refs = {}
for ref in self.get_refs():
# when ref start with refs_ it's an array of refs, so we need different method
# to collect data
if ref.startswith("refs_"):
form_refs[ref[5:-3]] = Var.create(
f"getRefValues({ref[:-3]})", is_local=False
)
else:
form_refs[ref[4:]] = Var.create(f"getRefValue({ref})", is_local=False)
return {"on_submit": form_refs}
class FormControl(ChakraComponent):

View File

@ -88,8 +88,9 @@ class NumberInput(ChakraComponent):
The component.
"""
if len(children) == 0:
_id = props.pop("id", None)
children = [
NumberInputField.create(),
NumberInputField.create(id=_id) if _id else NumberInputField.create(),
NumberInputStepper.create(
NumberIncrementStepper.create(),
NumberDecrementStepper.create(),

View File

@ -1,10 +1,12 @@
"""A pin input component."""
from typing import Dict
from typing import Dict, Optional
from reflex.components.component import Component
from reflex.components.layout import Foreach
from reflex.components.libs.chakra import ChakraComponent
from reflex.event import EVENT_ARG
from reflex.utils import format
from reflex.vars import Var
@ -66,6 +68,26 @@ class PinInput(ChakraComponent):
"on_complete": EVENT_ARG,
}
def get_ref(self):
"""Return a reference because we actually attached the ref to the PinInputFields.
Returns:
None.
"""
return None
def _get_hooks(self) -> Optional[str]:
"""Override the base get_hooks to handle array refs.
Returns:
The overrided hooks.
"""
if self.id:
ref = format.format_array_ref(self.id, None)
if ref:
return f"const {ref} = Array.from({{length:{self.length}}}, () => useRef(null));"
return super()._get_hooks()
@classmethod
def create(cls, *children, **props) -> Component:
"""Create a pin input component.
@ -81,7 +103,21 @@ class PinInput(ChakraComponent):
The pin input component.
"""
if not children and "length" in props:
children = [PinInputField()] * props["length"]
_id = props.get("id", None)
length = props["length"]
if _id:
children = [
Foreach.create(
list(range(length)), # type: ignore
lambda ref, i: PinInputField.create(
key=i,
id=_id,
index=i,
),
)
]
else:
children = [PinInputField()] * length
return super().create(*children, **props)
@ -89,3 +125,19 @@ class PinInputField(ChakraComponent):
"""The text field that user types in - must be a direct child of PinInput."""
tag = "PinInputField"
# the position of the PinInputField inside the PinInput.
# Default to None because it is assigned by PinInput when created.
index: Optional[Var[int]] = None
def _get_hooks(self) -> Optional[str]:
return None
def get_ref(self):
"""Get the array ref for the pin input.
Returns:
The array ref.
"""
if self.id:
return format.format_array_ref(self.id, self.index)

View File

@ -1,10 +1,11 @@
"""A range slider component."""
from typing import Dict, List
from typing import Dict, List, Optional
from reflex.components.component import Component
from reflex.components.libs.chakra import ChakraComponent
from reflex.event import EVENT_ARG
from reflex.utils import format
from reflex.vars import Var
@ -55,6 +56,26 @@ class RangeSlider(ChakraComponent):
"on_change_start": EVENT_ARG,
}
def get_ref(self):
"""Get the ref of the component.
Returns:
The ref of the component.
"""
return None
def _get_hooks(self) -> Optional[str]:
"""Override the base get_hooks to handle array refs.
Returns:
The overrided hooks.
"""
if self.id:
ref = format.format_array_ref(self.id, None)
if ref:
return f"const {ref} = Array.from({{length:2}}, () => useRef(null));"
return super()._get_hooks()
@classmethod
def create(cls, *children, **props) -> Component:
"""Create a RangeSlider component.
@ -69,13 +90,23 @@ class RangeSlider(ChakraComponent):
The RangeSlider component.
"""
if len(children) == 0:
children = [
RangeSliderTrack.create(
RangeSliderFilledTrack.create(),
),
RangeSliderThumb.create(index=0),
RangeSliderThumb.create(index=1),
]
_id = props.get("id", None)
if _id:
children = [
RangeSliderTrack.create(
RangeSliderFilledTrack.create(),
),
RangeSliderThumb.create(index=0, id=_id),
RangeSliderThumb.create(index=1, id=_id),
]
else:
children = [
RangeSliderTrack.create(
RangeSliderFilledTrack.create(),
),
RangeSliderThumb.create(index=0),
RangeSliderThumb.create(index=1),
]
return super().create(*children, **props)
@ -98,3 +129,16 @@ class RangeSliderThumb(ChakraComponent):
# The position of the thumb.
index: Var[int]
def _get_hooks(self) -> Optional[str]:
# hook is None because RangeSlider is handling it.
return None
def get_ref(self):
"""Get an array ref for the range slider thumb.
Returns:
The array ref.
"""
if self.id:
return format.format_array_ref(self.id, self.index)

View File

@ -434,6 +434,24 @@ def format_ref(ref: str) -> str:
return f"ref_{clean_ref}"
def format_array_ref(refs: str, idx) -> str:
"""Format a ref accessed by array.
Args:
refs : The ref array to access.
idx : The index of the ref in the array.
Returns:
The formatted ref.
"""
clean_ref = re.sub(r"[^\w]+", "_", refs)
if idx:
idx.is_local = True
return f"refs_{clean_ref}[{idx}]"
else:
return f"refs_{clean_ref}"
def format_dict(prop: ComponentStyle) -> str:
"""Format a dict with vars potentially as values.