dara-components 1.8.5__py3-none-any.whl → 1.22.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. dara/components/__init__.py +18 -0
  2. dara/components/_assets/__init__.py +30 -0
  3. dara/components/_assets/auto_js/.gitkeep +0 -0
  4. dara/components/_assets/auto_js/dara.components.css +1494 -0
  5. dara/components/_assets/auto_js/dara.components.umd.js +182837 -0
  6. dara/components/_assets/common/bokeh-3.1.1.min.js +690 -0
  7. dara/components/_assets/common/bokeh-api-3.1.1.min.js +60 -0
  8. dara/components/_assets/common/bokeh-gl-3.1.1.min.js +67 -0
  9. dara/components/_assets/common/bokeh-mathjax-3.1.1.min.js +329 -0
  10. dara/components/_assets/common/bokeh-tables-3.1.1.min.js +132 -0
  11. dara/components/_assets/common/bokeh-widgets-3.1.1.min.js +129 -0
  12. dara/components/_assets/common/pixi-filters.min.js +17 -0
  13. dara/components/_assets/common/pixi.min.js +2214 -0
  14. dara/components/_assets/common/pixi_viewport.js +1 -0
  15. dara/components/_assets/common/plotly.min.js +8 -0
  16. dara/components/common/__init__.py +11 -2
  17. dara/components/common/accordion.py +20 -26
  18. dara/components/common/anchor.py +9 -10
  19. dara/components/common/base_component.py +23 -36
  20. dara/components/common/bullet_list.py +1 -3
  21. dara/components/common/button.py +35 -26
  22. dara/components/common/button_bar.py +25 -20
  23. dara/components/common/card.py +4 -5
  24. dara/components/common/carousel.py +9 -9
  25. dara/components/common/checkbox_group.py +26 -19
  26. dara/components/common/code.py +8 -5
  27. dara/components/common/component_select_list.py +9 -13
  28. dara/components/common/datepicker.py +16 -16
  29. dara/components/common/dropdown_menu.py +161 -0
  30. dara/components/common/dropzone.py +42 -33
  31. dara/components/common/form.py +5 -7
  32. dara/components/common/form_page.py +4 -6
  33. dara/components/common/grid.py +21 -18
  34. dara/components/common/heading.py +5 -4
  35. dara/components/common/icon.py +1 -3
  36. dara/components/common/if_cmp.py +23 -17
  37. dara/components/common/image.py +2 -2
  38. dara/components/common/input.py +9 -11
  39. dara/components/common/label.py +13 -14
  40. dara/components/common/markdown.py +3 -5
  41. dara/components/common/modal.py +2 -2
  42. dara/components/common/overlay.py +8 -14
  43. dara/components/common/paragraph.py +2 -2
  44. dara/components/common/progress_bar.py +6 -8
  45. dara/components/common/radio_group.py +38 -21
  46. dara/components/common/select.py +33 -30
  47. dara/components/common/slider.py +74 -29
  48. dara/components/common/spacer.py +4 -6
  49. dara/components/common/stack.py +7 -4
  50. dara/components/common/switch.py +6 -8
  51. dara/components/common/tabbed_card.py +8 -11
  52. dara/components/common/table.py +224 -73
  53. dara/components/common/text.py +7 -9
  54. dara/components/common/textarea.py +7 -7
  55. dara/components/common/time_utils.py +2 -5
  56. dara/components/common/tooltip.py +4 -6
  57. dara/components/common/utils.py +29 -35
  58. dara/components/graphs/__init__.py +1 -0
  59. dara/components/graphs/components/base_graph_component.py +34 -22
  60. dara/components/graphs/components/causal_graph_viewer.py +13 -15
  61. dara/components/graphs/components/edge_encoder.py +49 -26
  62. dara/components/graphs/components/node_hierarchy_builder.py +17 -16
  63. dara/components/graphs/definitions.py +27 -20
  64. dara/components/graphs/graph_layout.py +90 -53
  65. dara/components/plotting/__init__.py +2 -1
  66. dara/components/plotting/bokeh/bokeh.py +7 -10
  67. dara/components/plotting/bokeh/utils.py +5 -3
  68. dara/components/plotting/plotly/plotly.py +24 -19
  69. dara/components/plotting/plotly/themes.py +7 -5
  70. dara/components/smart/__init__.py +7 -1
  71. dara/components/smart/chat/chat.py +7 -8
  72. dara/components/smart/chat/config.py +1 -1
  73. dara/components/smart/chat/types.py +4 -6
  74. dara/components/smart/code_editor/code_editor.py +18 -4
  75. dara/components/smart/code_editor/util.py +11 -11
  76. dara/components/smart/data_slicer/__init__.py +4 -0
  77. dara/components/smart/data_slicer/data_slicer.py +14 -18
  78. dara/components/smart/data_slicer/data_slicer_modal.py +4 -6
  79. dara/components/smart/data_slicer/extension/data_slicer_filter.py +3 -4
  80. dara/components/smart/data_slicer/extension/filter_status_button.py +1 -3
  81. dara/components/smart/data_slicer/utils/core.py +23 -23
  82. dara/components/smart/data_slicer/utils/data_preview.py +1 -3
  83. dara/components/smart/data_slicer/utils/plotting.py +8 -6
  84. dara/components/smart/hierarchy.py +9 -10
  85. {dara_components-1.8.5.dist-info → dara_components-1.22.1.dist-info}/METADATA +7 -7
  86. dara_components-1.22.1.dist-info/RECORD +100 -0
  87. {dara_components-1.8.5.dist-info → dara_components-1.22.1.dist-info}/WHEEL +1 -1
  88. dara_components-1.22.1.dist-info/entry_points.txt +3 -0
  89. dara/components/umd/dara.components.umd.js +0 -396288
  90. dara/components/umd/style.css +0 -745
  91. dara_components-1.8.5.dist-info/RECORD +0 -86
  92. {dara_components-1.8.5.dist-info → dara_components-1.22.1.dist-info}/LICENSE +0 -0
@@ -16,10 +16,9 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  import re
19
- from typing import Union
20
19
 
21
20
  from dara.components.common.base_component import ContentComponent
22
- from dara.core.interactivity import NonDataVariable
21
+ from dara.core.interactivity import ClientVariable
23
22
 
24
23
 
25
24
  class Heading(ContentComponent):
@@ -69,12 +68,14 @@ class Heading(ContentComponent):
69
68
  :param level: The level of heading to display, defaults to 1
70
69
  """
71
70
 
72
- heading: Union[str, NonDataVariable]
71
+ heading: str | ClientVariable
73
72
  level: int = 1
74
73
 
75
74
  @property
76
75
  def anchor_name(self):
76
+ if isinstance(self.heading, ClientVariable):
77
+ raise ValueError('Heading anchor name cannot be accessed directly from a variable')
77
78
  return re.sub(r'\s+', '-', self.heading.lower())
78
79
 
79
- def __init__(self, heading: Union[str, NonDataVariable], **kwargs):
80
+ def __init__(self, heading: str | ClientVariable, **kwargs):
80
81
  super().__init__(heading=heading, **kwargs)
@@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Optional
19
-
20
18
  from dara.components.common.base_component import ContentComponent
21
19
 
22
20
 
@@ -65,4 +63,4 @@ class Icon(ContentComponent):
65
63
  """
66
64
 
67
65
  icon: str
68
- color: Optional[str] = None
66
+ color: str | None = None
@@ -15,22 +15,25 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import List, Optional, Union
18
+ from typing import ClassVar
19
19
 
20
- from pydantic import validator
20
+ from pydantic import field_validator
21
21
 
22
22
  from dara.components.common.base_component import ModifierComponent
23
- from dara.core.definitions import ComponentInstance, TemplateMarker
23
+ from dara.core.definitions import ComponentInstance
24
24
  from dara.core.interactivity import AnyVariable, Condition, Operator
25
25
 
26
26
 
27
- def cast_list(value: Union[ComponentInstance, List[ComponentInstance]]) -> List[ComponentInstance]:
27
+ def cast_list(value: ComponentInstance | list[ComponentInstance | None]) -> list[ComponentInstance]:
28
28
  """
29
29
  Cast the value to a list if it is not or return original list if it is.
30
30
 
31
31
  :param value: the value to cast
32
32
  """
33
- return value if isinstance(value, List) else [value]
33
+ return [v for v in value if v is not None] if isinstance(value, list) else [value]
34
+
35
+
36
+ ConditionType = type[Condition]
34
37
 
35
38
 
36
39
  class If(ModifierComponent):
@@ -44,16 +47,17 @@ class If(ModifierComponent):
44
47
  An If component is created like so, in this example it is comparing the values of two variables:
45
48
 
46
49
  ```python
47
-
48
50
  from dara.core import Variable
49
51
  from dara.components.common import If, Text
50
52
 
53
+ var_1 = Variable(True)
54
+ var_2 = Variable(False)
55
+
51
56
  If(
52
- condition= Variable(True) == Variable(False),
57
+ condition=var_1 == var_2,
53
58
  true_children=Text('Equal'),
54
59
  false_children=Text('Different')
55
60
  )
56
-
57
61
  ```
58
62
 
59
63
  :param condition: a condition object
@@ -62,12 +66,12 @@ class If(ModifierComponent):
62
66
  """
63
67
 
64
68
  condition: Condition
65
- true_children: List[ComponentInstance]
66
- false_children: List[ComponentInstance]
69
+ true_children: list[ComponentInstance]
70
+ false_children: list[ComponentInstance]
67
71
 
68
- Condition = Condition
72
+ Condition: ClassVar[ConditionType] = Condition
69
73
 
70
- @validator('condition')
74
+ @field_validator('condition')
71
75
  @classmethod
72
76
  def validate_condition(cls, value):
73
77
  if not isinstance(value, Condition):
@@ -81,15 +85,17 @@ class If(ModifierComponent):
81
85
 
82
86
  def __init__(
83
87
  self,
84
- condition: Union[Condition, AnyVariable, TemplateMarker], # type: ignore
85
- true_children: Union[ComponentInstance, List[ComponentInstance]],
86
- false_children: Optional[Union[ComponentInstance, List[ComponentInstance]]] = None,
88
+ condition: Condition | AnyVariable, # type: ignore
89
+ true_children: ComponentInstance | list[ComponentInstance | None],
90
+ false_children: ComponentInstance | list[ComponentInstance | None] | None = None,
87
91
  ):
88
92
  if false_children is None:
89
93
  false_children = []
90
- if isinstance(condition, (AnyVariable, TemplateMarker)):
94
+ if isinstance(condition, AnyVariable):
91
95
  condition = Condition(operator=Operator.TRUTHY, other=None, variable=condition)
92
96
 
93
97
  super().__init__(
94
- condition=condition, true_children=cast_list(true_children), false_children=cast_list(false_children)
98
+ condition=condition,
99
+ true_children=cast_list(true_children),
100
+ false_children=cast_list(false_children), # type: ignore
95
101
  )
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from pydantic import validator
18
+ from pydantic import field_validator
19
19
 
20
20
  from dara.components.common.base_component import ContentComponent
21
21
 
@@ -57,7 +57,7 @@ class Image(ContentComponent):
57
57
 
58
58
  src: str
59
59
 
60
- @validator('src')
60
+ @field_validator('src')
61
61
  @classmethod
62
62
  def validate_src(cls, value: str):
63
63
  if not value.startswith('/static/') and not value.startswith('http'):
@@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Optional
19
-
20
18
  from dara.components.common.base_component import FormComponent
21
19
  from dara.core.base_definitions import Action
22
20
  from dara.core.interactivity import Variable
@@ -32,23 +30,23 @@ class Input(FormComponent):
32
30
  An Input component is created via:
33
31
 
34
32
  ```python
35
-
36
33
  from dara.core import Variable
37
34
  from dara.components.common import Input
38
35
 
39
- Input(value=Variable('initial input value'))
36
+ value_var = Variable('initial input value')
40
37
 
38
+ Input(value=value_var)
41
39
  ```
42
40
 
43
41
  You could define a numerical input with the following:
44
42
 
45
43
  ```python
46
-
47
44
  from dara.core import Variable
48
45
  from dara.components.common import Input
49
46
 
50
- Input(value=Variable(0), type='number')
47
+ value_var = Variable(0)
51
48
 
49
+ Input(value=value_var, type='number')
52
50
  ```
53
51
 
54
52
  :param value: A Variable instance recording the component's state
@@ -58,8 +56,8 @@ class Input(FormComponent):
58
56
  :param id: the key to be used if this component is within a form
59
57
  """
60
58
 
61
- id: Optional[str] = None
62
- placeholder: Optional[str] = None
63
- type: Optional[str] = None
64
- onchange: Optional[Action] = None
65
- value: Optional[Variable[str]] = None
59
+ id: str | None = None
60
+ placeholder: str | None = None
61
+ type: str | None = None
62
+ onchange: Action | None = None
63
+ value: Variable | None = None
@@ -15,9 +15,7 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import List, Optional, Union
19
-
20
- from pydantic import validator
18
+ from pydantic import field_validator
21
19
 
22
20
  from dara.components.common.base_component import ContentComponent
23
21
  from dara.core.definitions import ComponentInstance
@@ -33,29 +31,31 @@ class Label(ContentComponent):
33
31
  Example of a simple Label component:
34
32
 
35
33
  ```python
36
-
34
+ from dara.core import Variable
37
35
  from dara.components.common import Label, Input
38
36
 
37
+ value_var = Variable()
38
+
39
39
  Label(
40
- Input(),
40
+ Input(value=value_var),
41
41
  value='My Input:',
42
42
  direction='horizontal',
43
43
  )
44
-
45
44
  ```
46
45
 
47
46
  For further customization you can also pass a ComponentInstance to the value param:
48
47
 
49
48
  ```python
49
+ from dara.core import Variable
50
+ from dara.components.common import Label, Input, Heading
50
51
 
51
- from dara.components.common import Label, Input, Text
52
+ value_var = Variable()
52
53
 
53
54
  Label(
54
- Input(),
55
+ Input(value=value_var),
55
56
  value=Heading('My Input:', level=2),
56
57
  direction='horizontal',
57
58
  )
58
-
59
59
  ```
60
60
 
61
61
  :param value: A label to be presented with the child component, accepts a string or ComponentInstance
@@ -63,14 +63,13 @@ class Label(ContentComponent):
63
63
  :param label_width: A optional string containing the width the label should take
64
64
  """
65
65
 
66
- value: Union[str, ComponentInstance]
66
+ value: str | ComponentInstance
67
67
  direction: Direction = Direction.VERTICAL
68
- label_width: Optional[str] = None
68
+ label_width: str | None = None
69
69
 
70
- @validator('children')
70
+ @field_validator('children')
71
71
  @classmethod
72
- def validate_only_one_child(cls, children: List[ComponentInstance]) -> List[ComponentInstance]:
73
-
72
+ def validate_only_one_child(cls, children: list[ComponentInstance]) -> list[ComponentInstance]:
74
73
  if len(children) > 1:
75
74
  raise TypeError(
76
75
  'More than one component was passed to the Label component. Label accepts only one child component'
@@ -15,10 +15,8 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Union
19
-
20
18
  from dara.components.common.base_component import BaseDashboardComponent
21
- from dara.core.interactivity import NonDataVariable
19
+ from dara.core.interactivity import ClientVariable
22
20
 
23
21
 
24
22
  class Markdown(BaseDashboardComponent):
@@ -42,8 +40,8 @@ class Markdown(BaseDashboardComponent):
42
40
  if the markdown is user-provided
43
41
  """
44
42
 
45
- markdown: Union[NonDataVariable, str]
43
+ markdown: ClientVariable | str
46
44
  html_raw: bool = False
47
45
 
48
- def __init__(self, markdown: Union[NonDataVariable, str], html_raw: bool = False, **kwargs):
46
+ def __init__(self, markdown: ClientVariable | str, html_raw: bool = False, **kwargs):
49
47
  super().__init__(markdown=markdown, html_raw=html_raw, **kwargs)
@@ -16,7 +16,7 @@ limitations under the License.
16
16
  """
17
17
 
18
18
  from dara.components.common.base_component import LayoutComponent
19
- from dara.core.interactivity import NonDataVariable
19
+ from dara.core.interactivity import ClientVariable
20
20
 
21
21
 
22
22
  class Modal(LayoutComponent):
@@ -47,4 +47,4 @@ class Modal(LayoutComponent):
47
47
  :param align: How to align the content of the modal, accepts any flexbox alignments
48
48
  """
49
49
 
50
- show: NonDataVariable
50
+ show: ClientVariable
@@ -15,12 +15,10 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Optional
19
-
20
- from pydantic import validator
18
+ from pydantic import field_validator
21
19
 
22
20
  from dara.components.common.base_component import LayoutComponent
23
- from dara.core.interactivity import NonDataVariable
21
+ from dara.core.interactivity import ClientVariable
24
22
 
25
23
 
26
24
  class Overlay(LayoutComponent):
@@ -43,20 +41,16 @@ class Overlay(LayoutComponent):
43
41
 
44
42
  :param show: Boolean Variable instance recording the state, if True it renders the overlay and its' children
45
43
  :param position: the position of the overlay; can be top-left, top-right, bottom-left, bottom-right
46
- :param padding: the padding around the overlay elements passed a string; can also be passed as individual side's padding in css shorthand format
47
- :param margin: the margin property to shift the overlay in any direction passed as a string; can also be passed as individual side's margin in css shorthand format
48
44
  """
49
45
 
50
- show: Optional[NonDataVariable] = None
51
- padding: Optional[str] = None
52
- margin: Optional[str] = None
46
+ show: ClientVariable | None = None
53
47
 
54
- @validator('position')
48
+ @field_validator('position')
55
49
  @classmethod
56
- def validate_position(cls, position) -> str:
50
+ def validate_position(cls, value) -> str:
57
51
  position_list = ['top-left', 'top-right', 'bottom-left', 'bottom-right']
58
- if position not in position_list:
52
+ if value not in position_list:
59
53
  raise ValueError(
60
- f'Invalid position: {position}, position should be one of the following values: {position_list}'
54
+ f'Invalid position: {value}, position should be one of the following values: {position_list}'
61
55
  )
62
- return position
56
+ return value
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from pydantic import validator
18
+ from pydantic import field_validator
19
19
 
20
20
  from dara.components.common.anchor import Anchor
21
21
  from dara.components.common.base_component import LayoutComponent, LayoutError
@@ -66,7 +66,7 @@ class Paragraph(LayoutComponent):
66
66
 
67
67
  """
68
68
 
69
- @validator('children')
69
+ @field_validator('children')
70
70
  @classmethod
71
71
  def validate_children(cls, children):
72
72
  for child in children:
@@ -15,12 +15,10 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Optional, Union
19
-
20
- from pydantic import validator
18
+ from pydantic import field_validator
21
19
 
22
20
  from dara.components.common.base_component import ContentComponent
23
- from dara.core.interactivity import NonDataVariable
21
+ from dara.core.interactivity import ClientVariable
24
22
 
25
23
 
26
24
  class ProgressBar(ContentComponent):
@@ -48,13 +46,13 @@ class ProgressBar(ContentComponent):
48
46
  :param color: Optional color property for the progress bar, this should be the hex value of the color.
49
47
  """
50
48
 
51
- progress: Union[int, NonDataVariable]
49
+ progress: int | ClientVariable
52
50
  small: bool = False
53
- color: Optional[str] = None
51
+ color: str | None = None
54
52
 
55
- @validator('progress')
53
+ @field_validator('progress')
56
54
  @classmethod
57
55
  def validate_progress(cls, progress):
58
- if not isinstance(progress, (int, NonDataVariable)):
56
+ if not isinstance(progress, (int, ClientVariable)):
59
57
  raise ValueError('Progress must be an int or Variable')
60
58
  return progress
@@ -15,17 +15,23 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Any, List, Optional, Union
18
+ from typing import Any
19
19
 
20
- from pydantic import validator
20
+ from pydantic import field_validator
21
+ from typing_extensions import TypedDict
21
22
 
22
23
  from dara.components.common.base_component import FormComponent
23
- from dara.components.common.utils import Item
24
24
  from dara.core.base_definitions import Action
25
- from dara.core.interactivity import NonDataVariable, UrlVariable, Variable
25
+ from dara.core.definitions import ComponentInstance
26
+ from dara.core.interactivity import ClientVariable, Variable
26
27
  from dara.core.visual.components.types import Direction
27
28
 
28
29
 
30
+ class RadioItem(TypedDict):
31
+ value: Any
32
+ label: str | ComponentInstance
33
+
34
+
29
35
  class RadioGroup(FormComponent):
30
36
  """
31
37
  ![RadioGroup](../../../../docs/packages/dara-components/common/assets/RadioGroup.png)
@@ -36,19 +42,32 @@ class RadioGroup(FormComponent):
36
42
  Simple RadioGroup component:
37
43
 
38
44
  ```python
45
+ from dara.core import Variable
46
+ from dara.components import RadioGroup, RadioItem, Text
39
47
 
40
- from dara.components.common import RadioGroup, Item
48
+ value_var_str = Variable('first')
41
49
 
42
50
  # you can pass items as a list of values
43
51
  RadioGroup(
44
52
  items=['first', 'second'],
45
- value=Variable('first'),
53
+ value=value_var_str,
54
+ )
55
+
56
+ value_var_num = Variable(1)
57
+
58
+ # or as an `RadioItem` list
59
+ RadioGroup(
60
+ items=[RadioItem(label='first',value=1), Item(label='second',value=2)],
61
+ value=value_var_num,
46
62
  )
47
63
 
48
- # or as an `Item` list
64
+ # or as an `RadioItem` list with arbitrary components
49
65
  RadioGroup(
50
- items=[Item(label='first',value=1), Item(label='second',value=2)],
51
- value=Variable('first'),
66
+ items=[
67
+ RadioItem(label=Text(text='first', color='red'), value=1),
68
+ RadioItem(label=Text(text='second', color='blue'), value=2)
69
+ ],
70
+ value=value_var_num,
52
71
  )
53
72
  ```
54
73
 
@@ -59,7 +78,7 @@ class RadioGroup(FormComponent):
59
78
 
60
79
  RadioGroup(
61
80
  items=['first', 'second'],
62
- value=Variable('first'),
81
+ value=value_var_str,
63
82
  direction='horizontal'
64
83
  )
65
84
  ```
@@ -72,20 +91,18 @@ class RadioGroup(FormComponent):
72
91
  :param id: the key to be used if this component is within a form
73
92
  """
74
93
 
75
- items: Union[List[Item], NonDataVariable]
76
- value: Optional[Union[Variable[Any], UrlVariable[Any]]] = None
77
- list_styling: Optional[bool] = False
78
- onchange: Optional[Action] = None
94
+ items: list[RadioItem] | list[str] | ClientVariable
95
+ value: Variable[Any] | None = None
96
+ list_styling: bool | None = False
97
+ onchange: Action | None = None
79
98
  direction: Direction = Direction.VERTICAL
80
- id: Optional[str] = None
99
+ id: str | None = None
81
100
 
82
- @validator('items', pre=True)
101
+ @field_validator('items', mode='before')
83
102
  @classmethod
84
- def validate_items(cls, items: Any) -> Union[List[Item], NonDataVariable]:
85
- if isinstance(items, NonDataVariable):
103
+ def validate_items(cls, items: Any) -> list[RadioItem] | ClientVariable:
104
+ if isinstance(items, ClientVariable):
86
105
  return items
87
- if not isinstance(items, list):
88
- raise ValueError('Items must be passed as a list to the RadioGroup component')
89
106
  if len(items) == 0:
90
107
  raise ValueError('Items list is empty, you must provide at least one item')
91
- return [Item.to_item(item) for item in items]
108
+ return [RadioItem(value=item, label=item) if isinstance(item, str) else item for item in items]
@@ -15,14 +15,15 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  """
17
17
 
18
- from typing import Any, Dict, List, Optional, Union
18
+ from typing import Any
19
19
 
20
- from pydantic import BaseModel, validator
20
+ from pydantic import ValidationInfo, field_validator
21
21
 
22
22
  from dara.components.common.base_component import FormComponent
23
23
  from dara.components.common.utils import Item
24
24
  from dara.core.base_definitions import Action
25
- from dara.core.interactivity import NonDataVariable, UrlVariable, Variable
25
+ from dara.core.base_definitions import DaraBaseModel as BaseModel
26
+ from dara.core.interactivity import ClientVariable, Variable
26
27
  from dara.core.logging import dev_logger
27
28
 
28
29
 
@@ -35,11 +36,11 @@ class ListSection(BaseModel):
35
36
  """
36
37
 
37
38
  label: str
38
- items: List[Union[Item, str, dict]]
39
+ items: list[Item | str | dict]
39
40
 
40
- @validator('items')
41
+ @field_validator('items')
41
42
  @classmethod
42
- def validate_items(cls, items: Any) -> List[Item]:
43
+ def validate_items(cls, items: Any) -> list[Item]:
43
44
  if len(items) == 0:
44
45
  raise ValueError('Items of ListSection was empty, you must provide at least one item to the component')
45
46
  return [Item.to_item(item) for item in items]
@@ -58,55 +59,59 @@ class Select(FormComponent):
58
59
  A Select component is created via:
59
60
 
60
61
  ```python
61
-
62
62
  from dara.core import Variable
63
63
  from dara.components.common import Select
64
64
 
65
+ selection_var = Variable('first')
66
+
65
67
  Select(
66
- value=Variable('first'),
68
+ value=selection_var,
67
69
  items=['first', 'second', 'third'],
68
70
  )
69
-
70
71
  ```
71
72
 
72
73
  A searchable Select component is created via:
73
74
 
74
75
  ```python
75
-
76
76
  from dara.core import Variable
77
77
  from dara.components.common import Select
78
78
 
79
+ selection_var = Variable('first')
80
+
79
81
  Select(
80
- value=Variable('first'),
82
+ value=selection_var,
81
83
  items=['first', 'second', 'third'],
82
84
  searchable=True,
83
85
  )
84
-
85
86
  ```
86
87
 
87
88
  A more complicated example with explicit item labels/values that allows multiple items to be selected at the same time:
88
89
 
89
90
  ```python
90
-
91
91
  from dara.core import Variable
92
92
  from dara.components.common import Select, Item
93
93
 
94
+ selection_var = Variable([1, 2])
95
+
94
96
  Select(
95
97
  items=[Item(label='first',value=1), Item(label='second',value=2)],
96
- value=Variable([1, 2]),
98
+ value=selection_var,
97
99
  multiselect=True
98
100
  )
99
-
100
101
  ```
101
102
 
102
- Example of using a seclect component to allow selections in a sectioned list:
103
+ Example of using a select component to allow selections in a sectioned list:
103
104
 
104
105
  ```python
106
+ from dara.core import Variable
105
107
  from dara.components.common import Select, ListSection
106
108
 
109
+ selection_var = Variable()
110
+
107
111
  Select(
108
112
  items=[ListSection(label='Section 1', items=['1 item 1', '1 item 2', '1 item 3']), ListSection(label='Section 2', items=['2 item 1', '2 item 2'])],
109
113
  searchable=True,
114
+ value=selection_var,
110
115
  )
111
116
  ```
112
117
 
@@ -120,21 +125,21 @@ class Select(FormComponent):
120
125
  :param value: A Variable instance recording the component's state
121
126
  """
122
127
 
123
- id: Optional[str] = None
128
+ id: str | None = None
124
129
  multiselect: bool = False
125
130
  searchable: bool = False
126
- items: Union[List[Union[Item, ListSection]], NonDataVariable]
131
+ items: list[Item | ListSection] | ClientVariable
127
132
  max_rows: int = 3
128
- onchange: Optional[Action] = None
129
- placeholder: Optional[str] = None
130
- value: Optional[Union[Variable[Any], UrlVariable[Any]]] = None
133
+ onchange: Action | None = None
134
+ placeholder: str | None = None
135
+ value: Variable[Any] | None = None
131
136
 
132
- @validator('items', pre=True)
137
+ @field_validator('items', mode='before')
133
138
  @classmethod
134
- def validate_items(cls, items: Any, values: Dict[str, Any]) -> Union[List[Item], NonDataVariable]:
135
- multiselect = values.get('multiselect')
136
- searchable = values.get('searchable')
137
- if isinstance(items, NonDataVariable):
139
+ def validate_items(cls, items: Any, info: ValidationInfo) -> list[Item | ListSection] | ClientVariable:
140
+ multiselect = info.data.get('multiselect')
141
+ searchable = info.data.get('searchable')
142
+ if isinstance(items, ClientVariable):
138
143
  return items
139
144
  if not isinstance(items, list):
140
145
  raise ValueError('Items must be passed as a list to the select component')
@@ -151,19 +156,17 @@ class Select(FormComponent):
151
156
  return [Item.to_item(item) for item in items]
152
157
 
153
158
 
154
- def _parse_item(item: Any, return_listsection: bool = False):
159
+ def _parse_item(item: Any, return_listsection: bool = False) -> Item | ListSection:
155
160
  """
156
161
  Converts items to Item objects for a SectionedList. Can return a ListSection for a dictionary if
157
162
  return_listsection is set to True.
158
163
  """
159
- if isinstance(item, list):
160
- return [_parse_item(subitem) for subitem in item]
161
164
  if isinstance(item, dict):
162
165
  if return_listsection and item.get('label') is not None and item.get('items') is not None:
163
166
  items = item.get('items')
164
167
  if not isinstance(items, list):
165
168
  raise ValueError(f"Dictionary 'items' value for SectionedList must be a list, got {items}")
166
- return ListSection(label=str(item.get('label')), items=_parse_item(items))
169
+ return ListSection(label=str(item.get('label')), items=[Item.to_item(subitem) for subitem in items])
167
170
  return Item.to_item(item)
168
171
  if isinstance(item, ListSection):
169
172
  return item