dara-core 1.16.22__py3-none-any.whl → 1.17.0__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.
- dara/core/base_definitions.py +9 -0
- dara/core/definitions.py +45 -12
- dara/core/interactivity/__init__.py +2 -0
- dara/core/interactivity/actions.py +1 -9
- dara/core/interactivity/condition.py +8 -0
- dara/core/interactivity/plain_variable.py +0 -14
- dara/core/interactivity/switch_variable.py +416 -0
- dara/core/internal/dependency_resolution.py +138 -1
- dara/core/main.py +3 -0
- dara/core/umd/dara.core.umd.js +442 -255
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/METADATA +10 -10
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/RECORD +15 -14
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/LICENSE +0 -0
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/WHEEL +0 -0
- {dara_core-1.16.22.dist-info → dara_core-1.17.0.dist-info}/entry_points.txt +0 -0
dara/core/base_definitions.py
CHANGED
|
@@ -428,6 +428,15 @@ class PendingTask(BaseTask):
|
|
|
428
428
|
"""
|
|
429
429
|
self.subscribers -= 1
|
|
430
430
|
|
|
431
|
+
async def value(self):
|
|
432
|
+
"""
|
|
433
|
+
Wait for the task to complete and return the result
|
|
434
|
+
"""
|
|
435
|
+
await self.event.wait()
|
|
436
|
+
if self.error:
|
|
437
|
+
raise self.error
|
|
438
|
+
return self.result
|
|
439
|
+
|
|
431
440
|
|
|
432
441
|
class PendingValue:
|
|
433
442
|
"""
|
dara/core/definitions.py
CHANGED
|
@@ -70,6 +70,11 @@ DEFAULT_ERROR_TITLE = 'Unexpected error occurred'
|
|
|
70
70
|
DEFAULT_ERROR_DESCRIPTION = 'Try again or contact the application owner'
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
def _kebab_to_camel(string: str):
|
|
74
|
+
chunks = string.split('-')
|
|
75
|
+
return chunks[0] + ''.join([chunk[0].upper() + chunk[1:].lower() for chunk in chunks[1:]])
|
|
76
|
+
|
|
77
|
+
|
|
73
78
|
class ErrorHandlingConfig(BaseModel):
|
|
74
79
|
title: str = DEFAULT_ERROR_TITLE
|
|
75
80
|
"""Title to display in the error boundary"""
|
|
@@ -77,8 +82,27 @@ class ErrorHandlingConfig(BaseModel):
|
|
|
77
82
|
description: str = DEFAULT_ERROR_DESCRIPTION
|
|
78
83
|
"""Description to display in the error boundary"""
|
|
79
84
|
|
|
80
|
-
raw_css: Optional[
|
|
81
|
-
"""
|
|
85
|
+
raw_css: Optional[Any] = None
|
|
86
|
+
"""
|
|
87
|
+
Raw styling to apply to the displayed error boundary.
|
|
88
|
+
Accepts a CSSProperties, dict, str, or NonDataVariable.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
@field_validator('raw_css', mode='before')
|
|
92
|
+
@classmethod
|
|
93
|
+
def validate_raw_css(cls, value):
|
|
94
|
+
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
95
|
+
|
|
96
|
+
if isinstance(value, str):
|
|
97
|
+
return str
|
|
98
|
+
if isinstance(value, dict):
|
|
99
|
+
return {_kebab_to_camel(k): v for k, v in value.items()}
|
|
100
|
+
if isinstance(value, NonDataVariable):
|
|
101
|
+
return value
|
|
102
|
+
if isinstance(value, CSSProperties):
|
|
103
|
+
return value
|
|
104
|
+
|
|
105
|
+
raise ValueError(f'raw_css must be a CSSProperties, dict, str, or NonDataVariable, got {type(value)}')
|
|
82
106
|
|
|
83
107
|
def model_dump(self, *args, **kwargs):
|
|
84
108
|
result = super().model_dump(*args, **kwargs)
|
|
@@ -120,14 +144,15 @@ class ComponentInstance(BaseModel):
|
|
|
120
144
|
required_routes: ClassVar[List[ApiRoute]] = []
|
|
121
145
|
"""List of routes the component depends on. Will be implicitly added to the app if this component is used"""
|
|
122
146
|
|
|
123
|
-
raw_css: Optional[
|
|
147
|
+
raw_css: Optional[Any] = None
|
|
124
148
|
"""
|
|
125
149
|
Raw styling to apply to the component.
|
|
126
|
-
Can be an dict/CSSProperties instance representing the `styles` tag,
|
|
150
|
+
Can be an dict/CSSProperties instance representing the `styles` tag, a string injected directly into the CSS of the wrapping component,
|
|
151
|
+
or a NonDataVariable resoling to either of the above.
|
|
127
152
|
|
|
128
153
|
```python
|
|
129
154
|
|
|
130
|
-
from dara.core import CSSProperties
|
|
155
|
+
from dara.core import CSSProperties, Variable
|
|
131
156
|
|
|
132
157
|
# `style` - use the class for autocompletion/typesense
|
|
133
158
|
Stack(..., raw_css=CSSProperties(maxWidth='100px'))
|
|
@@ -140,6 +165,9 @@ class ComponentInstance(BaseModel):
|
|
|
140
165
|
max-width: 100px;
|
|
141
166
|
\"\"\")
|
|
142
167
|
|
|
168
|
+
css_var = Variable('color: red;')
|
|
169
|
+
Stack(..., raw_css=css_var)
|
|
170
|
+
|
|
143
171
|
```
|
|
144
172
|
"""
|
|
145
173
|
|
|
@@ -173,17 +201,23 @@ class ComponentInstance(BaseModel):
|
|
|
173
201
|
|
|
174
202
|
@field_validator('raw_css', mode='before')
|
|
175
203
|
@classmethod
|
|
176
|
-
def parse_css(cls, css: Optional[
|
|
204
|
+
def parse_css(cls, css: Optional[Any]):
|
|
205
|
+
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
206
|
+
|
|
177
207
|
# If it's a plain dict, change kebab case to camel case
|
|
178
208
|
if isinstance(css, dict):
|
|
209
|
+
return {_kebab_to_camel(k): v for k, v in css.items()}
|
|
210
|
+
|
|
211
|
+
if isinstance(css, NonDataVariable):
|
|
212
|
+
return css
|
|
179
213
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return chunks[0] + ''.join([chunk[0].upper() + chunk[1:].lower() for chunk in chunks[1:]])
|
|
214
|
+
if isinstance(css, CSSProperties):
|
|
215
|
+
return css
|
|
183
216
|
|
|
184
|
-
|
|
217
|
+
if isinstance(css, str):
|
|
218
|
+
return css
|
|
185
219
|
|
|
186
|
-
|
|
220
|
+
raise ValueError(f'raw_css must be a CSSProperties, dict, str, or NonDataVariable, got {type(css)}')
|
|
187
221
|
|
|
188
222
|
@classmethod
|
|
189
223
|
def isinstance(cls, obj: Any) -> bool:
|
|
@@ -399,7 +433,6 @@ class BaseFallback(StyledComponentInstance):
|
|
|
399
433
|
|
|
400
434
|
ComponentInstance.model_rebuild()
|
|
401
435
|
|
|
402
|
-
|
|
403
436
|
ComponentInstanceType = Union[ComponentInstance, Callable[..., ComponentInstance]]
|
|
404
437
|
|
|
405
438
|
|
|
@@ -42,6 +42,7 @@ from dara.core.interactivity.derived_data_variable import DerivedDataVariable
|
|
|
42
42
|
from dara.core.interactivity.derived_variable import DerivedVariable
|
|
43
43
|
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
44
44
|
from dara.core.interactivity.plain_variable import Variable
|
|
45
|
+
from dara.core.interactivity.switch_variable import SwitchVariable
|
|
45
46
|
from dara.core.interactivity.url_variable import UrlVariable
|
|
46
47
|
|
|
47
48
|
__all__ = [
|
|
@@ -52,6 +53,7 @@ __all__ = [
|
|
|
52
53
|
'DataVariable',
|
|
53
54
|
'NonDataVariable',
|
|
54
55
|
'Variable',
|
|
56
|
+
'SwitchVariable',
|
|
55
57
|
'DerivedVariable',
|
|
56
58
|
'DerivedDataVariable',
|
|
57
59
|
'UrlVariable',
|
|
@@ -1247,15 +1247,7 @@ class ActionCtx:
|
|
|
1247
1247
|
|
|
1248
1248
|
task = Task(func=func, args=args, kwargs=kwargs, on_progress=on_progress)
|
|
1249
1249
|
pending_task = await task_mgr.run_task(task)
|
|
1250
|
-
|
|
1251
|
-
# Run until completion
|
|
1252
|
-
await pending_task.event.wait()
|
|
1253
|
-
|
|
1254
|
-
# Raise exception if there was one
|
|
1255
|
-
if pending_task.error:
|
|
1256
|
-
raise pending_task.error
|
|
1257
|
-
|
|
1258
|
-
return pending_task.result
|
|
1250
|
+
return await pending_task.value()
|
|
1259
1251
|
|
|
1260
1252
|
async def execute_action(self, action: ActionImpl):
|
|
1261
1253
|
"""
|
|
@@ -20,6 +20,8 @@ from __future__ import annotations
|
|
|
20
20
|
from enum import Enum
|
|
21
21
|
from typing import TYPE_CHECKING, ClassVar, Union
|
|
22
22
|
|
|
23
|
+
from pydantic import SerializerFunctionWrapHandler, model_serializer
|
|
24
|
+
|
|
23
25
|
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
24
26
|
|
|
25
27
|
# Type-only imports
|
|
@@ -46,3 +48,9 @@ class Condition(BaseModel):
|
|
|
46
48
|
variable: AnyVariable
|
|
47
49
|
|
|
48
50
|
Operator: ClassVar[OperatorType] = Operator
|
|
51
|
+
|
|
52
|
+
@model_serializer(mode='wrap')
|
|
53
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
54
|
+
parent_dict = nxt(self)
|
|
55
|
+
|
|
56
|
+
return {**parent_dict, '__typename': 'Condition'}
|
|
@@ -37,20 +37,6 @@ from dara.core.internal.utils import call_async
|
|
|
37
37
|
from dara.core.logging import dev_logger
|
|
38
38
|
from dara.core.persistence import PersistenceStore
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
def _is_subclass_safe(value: type, base: type) -> bool:
|
|
42
|
-
"""
|
|
43
|
-
Check if a class is a subclass of another class. Returns False if the value is not a class.
|
|
44
|
-
|
|
45
|
-
:param value: the class to check
|
|
46
|
-
:param base: the class to check against
|
|
47
|
-
"""
|
|
48
|
-
try:
|
|
49
|
-
return issubclass(value, base)
|
|
50
|
-
except TypeError:
|
|
51
|
-
return False
|
|
52
|
-
|
|
53
|
-
|
|
54
40
|
VARIABLE_INIT_OVERRIDE = ContextVar[Optional[Callable[[dict], dict]]]('VARIABLE_INIT_OVERRIDE', default=None)
|
|
55
41
|
|
|
56
42
|
VariableType = TypeVar('VariableType')
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2023 Impulse Innovations Limited
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import Any, Dict, Optional, Union
|
|
21
|
+
|
|
22
|
+
from pydantic import SerializerFunctionWrapHandler, field_validator, model_serializer
|
|
23
|
+
|
|
24
|
+
from dara.core.interactivity.condition import Condition
|
|
25
|
+
from dara.core.interactivity.non_data_variable import NonDataVariable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SwitchVariable(NonDataVariable):
|
|
29
|
+
"""
|
|
30
|
+
A SwitchVariable represents a conditional value that switches between
|
|
31
|
+
different values based on a condition or variable value.
|
|
32
|
+
|
|
33
|
+
SwitchVariable provides a clean way to create conditional logic in your
|
|
34
|
+
application by mapping input values or conditions to output values. It
|
|
35
|
+
supports boolean conditions, value mappings, and default fallbacks.
|
|
36
|
+
|
|
37
|
+
There are three main patterns for creating SwitchVariables:
|
|
38
|
+
|
|
39
|
+
1. **Boolean Conditions**: Switch between two values based on a true/false condition
|
|
40
|
+
2. **Value Mapping**: Map specific input values to corresponding output values
|
|
41
|
+
3. **Mixed Usage**: Combine conditions and mappings with default fallbacks
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
Basic boolean switching:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from dara.core import ConfigurationBuilder, Variable, SwitchVariable
|
|
48
|
+
from dara.components import Stack, Text, Button, Card
|
|
49
|
+
|
|
50
|
+
config = ConfigurationBuilder()
|
|
51
|
+
is_admin = Variable(default=False)
|
|
52
|
+
|
|
53
|
+
# Show different UI based on admin status
|
|
54
|
+
ui_mode = SwitchVariable.when(
|
|
55
|
+
condition=is_admin,
|
|
56
|
+
true_value='Admin Panel',
|
|
57
|
+
false_value='User Panel'
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Complete component usage
|
|
61
|
+
page_content = Card(
|
|
62
|
+
Stack(
|
|
63
|
+
Text('Current Mode:'),
|
|
64
|
+
Text(text=ui_mode),
|
|
65
|
+
Button('Toggle Admin', onclick=is_admin.toggle())
|
|
66
|
+
),
|
|
67
|
+
title='Admin Panel Demo'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
config.add_page('Admin Demo', content=page_content)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Value mapping with defaults:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from dara.core import ConfigurationBuilder, Variable, SwitchVariable
|
|
77
|
+
from dara.components import Stack, Text, Select, Item, Card
|
|
78
|
+
|
|
79
|
+
config = ConfigurationBuilder()
|
|
80
|
+
user_role = Variable(default='guest')
|
|
81
|
+
|
|
82
|
+
# Map user roles to permission levels
|
|
83
|
+
permissions = SwitchVariable.match(
|
|
84
|
+
value=user_role,
|
|
85
|
+
mapping={
|
|
86
|
+
'admin': 'Full Access',
|
|
87
|
+
'editor': 'Write Access',
|
|
88
|
+
'viewer': 'Read Access'
|
|
89
|
+
},
|
|
90
|
+
default='No Access' # for unknown roles
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Complete component usage
|
|
94
|
+
page_content = Card(
|
|
95
|
+
Stack(
|
|
96
|
+
Text('Select Role:'),
|
|
97
|
+
Select(
|
|
98
|
+
value=user_role,
|
|
99
|
+
items=[
|
|
100
|
+
Item(label='Guest', value='guest'),
|
|
101
|
+
Item(label='Viewer', value='viewer'),
|
|
102
|
+
Item(label='Editor', value='editor'),
|
|
103
|
+
Item(label='Admin', value='admin')
|
|
104
|
+
]
|
|
105
|
+
),
|
|
106
|
+
Text('Permissions:'),
|
|
107
|
+
Text(text=permissions)
|
|
108
|
+
),
|
|
109
|
+
title='Role Permissions'
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
config.add_page('Permissions Demo', content=page_content)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Complex conditions:
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from dara.core import Variable, SwitchVariable
|
|
119
|
+
from dara.components import Stack, Text, Input, Card
|
|
120
|
+
|
|
121
|
+
score = Variable(default=85)
|
|
122
|
+
|
|
123
|
+
# Grade based on score ranges
|
|
124
|
+
grade = SwitchVariable.when(
|
|
125
|
+
condition=score >= 90,
|
|
126
|
+
true_value='A Grade',
|
|
127
|
+
false_value='B Grade'
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Complete component usage
|
|
131
|
+
grade_component = Card(
|
|
132
|
+
Stack(
|
|
133
|
+
Text('Enter Score:'),
|
|
134
|
+
Input(value=score, type='number'),
|
|
135
|
+
Text('Grade (>=90 = A, <90 = B):'),
|
|
136
|
+
Text(text=grade)
|
|
137
|
+
),
|
|
138
|
+
title='Grade Calculator'
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Switching on computed conditions:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from dara.core import Variable, SwitchVariable
|
|
146
|
+
from dara.components import Stack, Text, Input, Card
|
|
147
|
+
|
|
148
|
+
temperature = Variable(default=20)
|
|
149
|
+
|
|
150
|
+
# Weather advice based on temperature
|
|
151
|
+
advice = SwitchVariable.when(
|
|
152
|
+
condition=temperature > 25,
|
|
153
|
+
true_value='Wear light clothes - it\'s warm!',
|
|
154
|
+
false_value='Wear warm clothes - it\'s cool!'
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Complete component usage
|
|
158
|
+
weather_app = Card(
|
|
159
|
+
Stack(
|
|
160
|
+
Text('Temperature (°C):'),
|
|
161
|
+
Input(value=temperature, type='number'),
|
|
162
|
+
Text('Advice:'),
|
|
163
|
+
Text(text=advice)
|
|
164
|
+
),
|
|
165
|
+
title='Weather Advisor'
|
|
166
|
+
)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Using with other variables:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from dara.core import Variable, SwitchVariable
|
|
173
|
+
from dara.components import Stack, Text, Select, Item, Card
|
|
174
|
+
|
|
175
|
+
user_preference = Variable(default='auto')
|
|
176
|
+
mapping_variable = Variable({
|
|
177
|
+
'auto': 'System Theme',
|
|
178
|
+
'light': 'Light Theme',
|
|
179
|
+
'dark': 'Dark Theme'
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
# Theme selection with user preference override
|
|
183
|
+
active_theme = SwitchVariable.match(
|
|
184
|
+
value=user_preference,
|
|
185
|
+
mapping=mapping_variable,
|
|
186
|
+
default='Default Theme'
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Complete component usage
|
|
190
|
+
theme_selector = Card(
|
|
191
|
+
Stack(
|
|
192
|
+
Text('Theme Preference:'),
|
|
193
|
+
Select(
|
|
194
|
+
value=user_preference,
|
|
195
|
+
items=[
|
|
196
|
+
Item(label='Auto', value='auto'),
|
|
197
|
+
Item(label='Light', value='light'),
|
|
198
|
+
Item(label='Dark', value='dark')
|
|
199
|
+
]
|
|
200
|
+
),
|
|
201
|
+
Text('Active Theme:'),
|
|
202
|
+
Text(text=active_theme)
|
|
203
|
+
),
|
|
204
|
+
title='Theme Selector'
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Note:
|
|
209
|
+
- Value can be a condition, raw value, or variable
|
|
210
|
+
- Value map can be a variable or a dict
|
|
211
|
+
- Default can be a variable or a raw value
|
|
212
|
+
- The switch evaluation happens reactively when underlying variables change
|
|
213
|
+
- Default values are only used in mapping scenarios when the switch value
|
|
214
|
+
doesn't match any key in the mapping
|
|
215
|
+
|
|
216
|
+
Key Serialization:
|
|
217
|
+
When using mappings with SwitchVariable, be aware that JavaScript object keys
|
|
218
|
+
are always strings. The system automatically converts lookup keys to strings:
|
|
219
|
+
- Python: {True: 'admin', False: 'user'}
|
|
220
|
+
- JavaScript: {"true": "admin", "false": "user"}
|
|
221
|
+
- Boolean values are converted to lowercase strings ("true"/"false")
|
|
222
|
+
- Other values use standard string conversion to match JavaScript's String() behavior
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
value: Optional[Union[Condition, NonDataVariable, Any]] = None
|
|
226
|
+
# must be typed as any, otherwise pydantic is trying to instantiate the variables incorrectly
|
|
227
|
+
value_map: Optional[Any] = None
|
|
228
|
+
default: Optional[Any] = None
|
|
229
|
+
|
|
230
|
+
def __init__(
|
|
231
|
+
self,
|
|
232
|
+
value: Union[Condition, NonDataVariable, Any],
|
|
233
|
+
value_map: Dict[Any, Any] | NonDataVariable,
|
|
234
|
+
default: Optional[Any] = None,
|
|
235
|
+
uid: Optional[str] = None,
|
|
236
|
+
):
|
|
237
|
+
"""
|
|
238
|
+
Create a SwitchVariable with a mapping of values.
|
|
239
|
+
|
|
240
|
+
:param value: Variable, condition, or value to switch on
|
|
241
|
+
:param value_map: Dict mapping switch values to return values
|
|
242
|
+
:param default: Default value when switch value not in mapping
|
|
243
|
+
:param uid: Unique identifier for this variable
|
|
244
|
+
"""
|
|
245
|
+
super().__init__(
|
|
246
|
+
uid=uid,
|
|
247
|
+
value=value,
|
|
248
|
+
value_map=value_map,
|
|
249
|
+
default=default,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
@field_validator('value_map')
|
|
253
|
+
@classmethod
|
|
254
|
+
def validate_value_map(cls, v):
|
|
255
|
+
"""
|
|
256
|
+
Validate that value_map is either a dict or a NonDataVariable.
|
|
257
|
+
|
|
258
|
+
:param v: The value to validate
|
|
259
|
+
:return: The validated value
|
|
260
|
+
:raises ValueError: If value_map is not a dict or NonDataVariable
|
|
261
|
+
"""
|
|
262
|
+
if v is None:
|
|
263
|
+
return v
|
|
264
|
+
if isinstance(v, dict):
|
|
265
|
+
return v
|
|
266
|
+
if isinstance(v, NonDataVariable):
|
|
267
|
+
return v
|
|
268
|
+
raise ValueError(f'value_map must be a dict or NonDataVariable, got {type(v)}')
|
|
269
|
+
|
|
270
|
+
@classmethod
|
|
271
|
+
def when(
|
|
272
|
+
cls,
|
|
273
|
+
condition: Union[Condition, NonDataVariable, Any],
|
|
274
|
+
true_value: Union[Any, NonDataVariable],
|
|
275
|
+
false_value: Union[Any, NonDataVariable],
|
|
276
|
+
uid: Optional[str] = None,
|
|
277
|
+
) -> 'SwitchVariable':
|
|
278
|
+
"""
|
|
279
|
+
Create a SwitchVariable for boolean conditions.
|
|
280
|
+
|
|
281
|
+
This is the most common pattern for simple if/else logic. The condition
|
|
282
|
+
is evaluated and returns true_value if truthy, false_value otherwise.
|
|
283
|
+
|
|
284
|
+
:param condition: Condition to evaluate (Variable, Condition object, or any value)
|
|
285
|
+
:param true_value: Value to return when condition evaluates to True
|
|
286
|
+
:param false_value: Value to return when condition evaluates to False
|
|
287
|
+
:param uid: Unique identifier for this variable
|
|
288
|
+
:return: SwitchVariable configured for boolean switching
|
|
289
|
+
|
|
290
|
+
Example with variable condition:
|
|
291
|
+
```python
|
|
292
|
+
from dara.core import Variable, SwitchVariable
|
|
293
|
+
from dara.components import Stack, Text, Button
|
|
294
|
+
|
|
295
|
+
is_loading = Variable(default=True)
|
|
296
|
+
|
|
297
|
+
# Show spinner while loading, content when done
|
|
298
|
+
display = SwitchVariable.when(
|
|
299
|
+
condition=is_loading,
|
|
300
|
+
true_value='Loading...',
|
|
301
|
+
false_value='Content loaded!'
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
# Use in a complete component
|
|
305
|
+
page_content = Stack(
|
|
306
|
+
Text(text=display),
|
|
307
|
+
Button(
|
|
308
|
+
text='Toggle Loading',
|
|
309
|
+
onclick=is_loading.toggle()
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Example with a condition object:
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
from dara.core import Variable, SwitchVariable
|
|
318
|
+
from dara.components import Stack, Text, Input, Card
|
|
319
|
+
|
|
320
|
+
# Temperature-based clothing advice
|
|
321
|
+
temperature = Variable(default=20)
|
|
322
|
+
advice = SwitchVariable.when(
|
|
323
|
+
condition=temperature > 25,
|
|
324
|
+
true_value='Wear light clothes - it\'s warm!',
|
|
325
|
+
false_value='Wear warm clothes - it\'s cool!'
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Complete weather advice component
|
|
329
|
+
weather_component = Card(
|
|
330
|
+
Stack(
|
|
331
|
+
Text('Enter temperature (°C):'),
|
|
332
|
+
Input(value=temperature, type='number'),
|
|
333
|
+
Text('Advice:'),
|
|
334
|
+
Text(text=advice)
|
|
335
|
+
),
|
|
336
|
+
title='Weather Advice'
|
|
337
|
+
)
|
|
338
|
+
```
|
|
339
|
+
"""
|
|
340
|
+
return cls(
|
|
341
|
+
value=condition,
|
|
342
|
+
value_map={True: true_value, False: false_value},
|
|
343
|
+
uid=uid,
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
@classmethod
|
|
347
|
+
def match(
|
|
348
|
+
cls,
|
|
349
|
+
value: Union[NonDataVariable, Any],
|
|
350
|
+
mapping: Union[Dict[Any, Any], NonDataVariable],
|
|
351
|
+
default: Optional[Union[Any, NonDataVariable]] = None,
|
|
352
|
+
uid: Optional[str] = None,
|
|
353
|
+
) -> 'SwitchVariable':
|
|
354
|
+
"""
|
|
355
|
+
Create a SwitchVariable with a custom mapping.
|
|
356
|
+
|
|
357
|
+
This pattern is useful when you have multiple specific values to map to
|
|
358
|
+
different outputs, similar to a switch statement in other languages.
|
|
359
|
+
|
|
360
|
+
:param value: Variable or value to switch on
|
|
361
|
+
:param mapping: Dict mapping switch values to return values
|
|
362
|
+
:param default: Default value when switch value not found in mapping
|
|
363
|
+
:param uid: Unique identifier for this variable
|
|
364
|
+
:return: SwitchVariable configured with the provided mapping
|
|
365
|
+
|
|
366
|
+
Example:
|
|
367
|
+
```python
|
|
368
|
+
from dara.core import Variable, SwitchVariable
|
|
369
|
+
from dara.components import Stack, Text, Select, Item
|
|
370
|
+
|
|
371
|
+
status = Variable(default='pending')
|
|
372
|
+
|
|
373
|
+
# Map status codes to user-friendly messages
|
|
374
|
+
message = SwitchVariable.match(
|
|
375
|
+
value=status,
|
|
376
|
+
mapping={
|
|
377
|
+
'pending': 'Please wait...',
|
|
378
|
+
'success': 'Operation completed!',
|
|
379
|
+
'error': 'Something went wrong',
|
|
380
|
+
'cancelled': 'Operation was cancelled'
|
|
381
|
+
},
|
|
382
|
+
default='Unknown status'
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Use in a complete component
|
|
386
|
+
page_content = Stack(
|
|
387
|
+
Select(
|
|
388
|
+
value=status,
|
|
389
|
+
items=[
|
|
390
|
+
Item(label='Pending', value='pending'),
|
|
391
|
+
Item(label='Success', value='success'),
|
|
392
|
+
Item(label='Error', value='error'),
|
|
393
|
+
Item(label='Cancelled', value='cancelled')
|
|
394
|
+
]
|
|
395
|
+
),
|
|
396
|
+
Text(text=message)
|
|
397
|
+
)
|
|
398
|
+
```
|
|
399
|
+
"""
|
|
400
|
+
return cls(
|
|
401
|
+
value=value,
|
|
402
|
+
value_map=mapping,
|
|
403
|
+
default=default,
|
|
404
|
+
uid=uid,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
@model_serializer(mode='wrap')
|
|
408
|
+
def ser_model(self, nxt: SerializerFunctionWrapHandler) -> dict:
|
|
409
|
+
"""
|
|
410
|
+
Serialize the SwitchVariable model with additional metadata.
|
|
411
|
+
|
|
412
|
+
:param nxt: The next serializer function in the chain
|
|
413
|
+
:return: Serialized dictionary with __typename and uid fields
|
|
414
|
+
"""
|
|
415
|
+
parent_dict = nxt(self)
|
|
416
|
+
return {**parent_dict, '__typename': 'SwitchVariable', 'uid': str(parent_dict['uid'])}
|