dara-core 1.21.22__py3-none-any.whl → 1.21.24__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.
@@ -454,6 +454,29 @@ class RouterPath(BaseModel):
454
454
  A URL hash string, beginning with '#'.
455
455
  """
456
456
 
457
+ params: dict[str, ClientVariable | Any] | None = None # type: ignore # noqa: F821
458
+ """
459
+ Optional mapping of dynamic path params to their values.
460
+
461
+ ```python
462
+ from dara.core import RouterPath, Variable
463
+
464
+ foo_var = Variable('foo_value')
465
+
466
+ # Will resolve to '/foo_value/bar'
467
+ path = RouterPath(pathname='/:foo/:bar', params={'foo': foo_var, 'bar': 'bar'})
468
+
469
+ Link('Link to path', to=path)
470
+ ```
471
+ """
472
+
473
+ def __init__(self, **data):
474
+ # Resolve the circular dependency to use ClientVariable as the type of the params
475
+ from dara.core.interactivity.client_variable import ClientVariable # noqa: F401
476
+
477
+ self.model_rebuild()
478
+ super().__init__(**data)
479
+
457
480
 
458
481
  class NavigateOptions(BaseModel):
459
482
  """
dara/core/defaults.py CHANGED
@@ -21,6 +21,8 @@ from typing import TYPE_CHECKING, cast
21
21
 
22
22
  from dara.core.base_definitions import ActionDef
23
23
  from dara.core.interactivity.actions import (
24
+ CopyToClipboard,
25
+ CopyToClipboardDef,
24
26
  DownloadContentDef,
25
27
  DownloadVariable,
26
28
  DownloadVariableDef,
@@ -42,6 +44,8 @@ from dara.core.visual.components import (
42
44
  Fallback,
43
45
  For,
44
46
  ForDef,
47
+ Match,
48
+ MatchDef,
45
49
  Menu,
46
50
  MenuDef,
47
51
  PoweredByCausalens,
@@ -87,6 +91,7 @@ CORE_COMPONENTS: dict[str, ComponentTypeAnnotation] = {
87
91
  cast(str, Fallback.Default.py_component): DefaultFallbackDef,
88
92
  cast(str, Fallback.Row.py_component): RowFallbackDef,
89
93
  cast(str, Fallback.Custom.py_component): CustomFallbackDef,
94
+ Match.__name__: MatchDef,
90
95
  For.__name__: ForDef,
91
96
  Link.__name__: LinkDef,
92
97
  Outlet.__name__: OutletDef,
@@ -102,6 +107,7 @@ CORE_ACTIONS: dict[str, ActionDef] = {
102
107
  TriggerVariable.__name__: TriggerVariableDef,
103
108
  ResetVariables.__name__: ResetVariablesDef,
104
109
  DownloadVariable.__name__: DownloadVariableDef,
110
+ CopyToClipboard.__name__: CopyToClipboardDef,
105
111
  'DownloadContent': DownloadContentDef,
106
112
  Notify.__name__: NotifyDef,
107
113
  }
@@ -23,6 +23,7 @@ from pydantic import BaseModel
23
23
 
24
24
  from dara.core.interactivity.actions import (
25
25
  ActionCtx,
26
+ CopyToClipboard,
26
27
  DownloadContent,
27
28
  DownloadContentImpl,
28
29
  DownloadVariable,
@@ -43,6 +44,7 @@ from dara.core.interactivity.condition import Condition, Operator
43
44
  from dara.core.interactivity.data_variable import DataVariable
44
45
  from dara.core.interactivity.derived_data_variable import DerivedDataVariable
45
46
  from dara.core.interactivity.derived_variable import DerivedVariable
47
+ from dara.core.interactivity.loop_variable import LoopVariable
46
48
  from dara.core.interactivity.non_data_variable import NonDataVariable
47
49
  from dara.core.interactivity.plain_variable import Variable
48
50
  from dara.core.interactivity.server_variable import ServerVariable
@@ -64,9 +66,11 @@ __all__ = [
64
66
  'DerivedVariable',
65
67
  'DerivedDataVariable',
66
68
  'UrlVariable',
69
+ 'CopyToClipboard',
67
70
  'DownloadVariable',
68
71
  'DownloadContent',
69
72
  'DownloadContentImpl',
73
+ 'LoopVariable',
70
74
  'NavigateTo',
71
75
  'NavigateToImpl',
72
76
  'Notify',
@@ -62,6 +62,7 @@ from dara.core.internal.utils import run_user_handler
62
62
  if TYPE_CHECKING:
63
63
  from dara.core.interactivity import (
64
64
  AnyVariable,
65
+ ClientVariable,
65
66
  DerivedVariable,
66
67
  Variable,
67
68
  )
@@ -727,6 +728,41 @@ class DownloadVariable(ActionImpl):
727
728
  type: Literal['csv', 'xlsx', 'json'] = 'csv'
728
729
 
729
730
 
731
+ CopyToClipboardDef = ActionDef(name='CopyToClipboard', js_module='@darajs/core', py_module='dara.core')
732
+
733
+
734
+ class CopyToClipboard(ActionImpl):
735
+ """
736
+ CopyToClipboard action copies the provided value to the user's clipboard.
737
+
738
+ ```python
739
+
740
+ from dara.core import action, ConfigurationBuilder, CopyToClipboard
741
+ from dara.components import Stack, Button
742
+
743
+ config = ConfigurationBuilder()
744
+
745
+ def test_page():
746
+ return Stack(
747
+ Button(
748
+ 'Copy to clipboard',
749
+ onclick=CopyToClipboard(value='Hello, World!')
750
+ )
751
+ )
752
+
753
+
754
+ config.router.add_page(path='copy-to-clipboard', content=test_page)
755
+ """
756
+
757
+ value: str | ClientVariable
758
+
759
+ success_message: str | None = None
760
+ """Message to display when the value is copied to the clipboard"""
761
+
762
+ error_message: str | None = None
763
+ """Message to display when the value could not be copied to the clipboard"""
764
+
765
+
730
766
  @deprecated('Use @action instead')
731
767
  def SideEffect(
732
768
  function: Callable[[ComponentActionContext], Any],
@@ -826,6 +862,51 @@ class ActionCtx:
826
862
  )
827
863
  self._on_action = _on_action
828
864
 
865
+ async def copy_to_clipboard(
866
+ self,
867
+ value: str,
868
+ success_message: str | None = None,
869
+ error_message: str | None = None,
870
+ ):
871
+ """
872
+ Copy a given value to the user's clipboard.
873
+
874
+ Note that this could fail in some browsers if e.g. user has disabled clipboard access.
875
+ Dara will display an error notification in this case, the message can be customized with the `error_message` parameter.
876
+
877
+ ```python
878
+
879
+ from dara.core import action, ConfigurationBuilder, CopyToClipboard
880
+ from dara.components import Stack, Button
881
+
882
+ config = ConfigurationBuilder()
883
+
884
+ @action
885
+ async def copy_demo(ctx: action.Ctx, value: str):
886
+ await ctx.copy_to_clipboard(value)
887
+
888
+ def test_page():
889
+ return Stack(
890
+ Button(
891
+ 'Copy to clipboard',
892
+ onclick=copy_demo(value='Hello, World!')
893
+ )
894
+ )
895
+
896
+
897
+ config.router.add_page(path='copy-to-clipboard', content=test_page)
898
+ ```
899
+
900
+ :param value: the value to copy to the clipboard
901
+ :param success_message: the message to display when the value is copied to the clipboard
902
+ :param error_message: the message to display when the value could not be copied to the clipboard
903
+ """
904
+ return await CopyToClipboard(
905
+ value=value,
906
+ success_message=success_message,
907
+ error_message=error_message,
908
+ ).execute(self)
909
+
829
910
  @overload
830
911
  async def update(self, variable: ServerVariable, value: DataFrame | None): ...
831
912
 
@@ -2,6 +2,10 @@ from pydantic import Field, SerializerFunctionWrapHandler, model_serializer
2
2
 
3
3
  from .client_variable import ClientVariable
4
4
 
5
+ _INDEX = '__index'
6
+ _IS_LAST = '__is_last'
7
+ _IS_FIRST = '__is_first'
8
+
5
9
 
6
10
  class LoopVariable(ClientVariable):
7
11
  """
@@ -76,6 +80,27 @@ class LoopVariable(ClientVariable):
76
80
  def __getitem__(self, key: str):
77
81
  return self.get(key)
78
82
 
83
+ @property
84
+ def index(self):
85
+ """
86
+ Value of the index of the current item in the list.
87
+ """
88
+ return self.model_copy(update={'nested': [_INDEX]}, deep=True)
89
+
90
+ @property
91
+ def is_last(self):
92
+ """
93
+ Whether the current item is the last item in the list.
94
+ """
95
+ return self.model_copy(update={'nested': [_IS_LAST]}, deep=True)
96
+
97
+ @property
98
+ def is_first(self):
99
+ """
100
+ Whether the current item is the first item in the list.
101
+ """
102
+ return self.model_copy(update={'nested': [_IS_FIRST]}, deep=True)
103
+
79
104
  @property
80
105
  def list_item(self):
81
106
  raise RuntimeError('LoopVariable does not support list_item')
@@ -81,6 +81,44 @@ class _PathParamStore(PersistenceStore):
81
81
  pass
82
82
 
83
83
 
84
+ class _RouteMatchStore(PersistenceStore):
85
+ """
86
+ Internal store for route matches.
87
+ Should not be used directly, Dara will use this internally to keep a variable
88
+ in sync with the current route matches.
89
+ """
90
+
91
+ async def init(self, variable: 'Variable'):
92
+ # noop
93
+ pass
94
+
95
+
96
+ class RouteMatch(BaseModel):
97
+ """
98
+ Data structure representing a route match in the router
99
+ """
100
+
101
+ id: str
102
+ """
103
+ Route ID, as set when defining the route
104
+ """
105
+
106
+ pathname: str
107
+ """
108
+ Full pathname of the route
109
+ """
110
+
111
+ params: dict[str, Any]
112
+ """
113
+ Mapping of dynamic path params to their values
114
+ """
115
+
116
+ definition: 'BaseRoute'
117
+ """
118
+ Definition of the route
119
+ """
120
+
121
+
84
122
  class RouteData(BaseModel):
85
123
  """
86
124
  Data structure representing a route in the router
@@ -709,6 +747,12 @@ class Router(HasChildRoutes):
709
747
  building routes step by step, while the object API is more declarative and compact.
710
748
  """
711
749
 
750
+ route_matches: Variable[list[RouteMatch]] = Field(default_factory=lambda: Variable([], store=_RouteMatchStore()))
751
+ """
752
+ Variable containing current list of route matches.
753
+ Note that this will be updated by Dara automatically, so you should not modify it directly.
754
+ """
755
+
712
756
  def __init__(self, *children: BaseRoute, **kwargs):
713
757
  routes = list(children)
714
758
  if 'children' not in kwargs: