flet 0.70.0.dev5774__py3-none-any.whl → 0.70.0.dev5835__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.

Potentially problematic release.


This version of flet might be problematic. Click here for more details.

Files changed (96) hide show
  1. flet/__init__.py +32 -4
  2. flet/components/__init__.py +0 -0
  3. flet/components/component.py +346 -0
  4. flet/components/component_decorator.py +24 -0
  5. flet/components/component_owned.py +22 -0
  6. flet/components/hooks/__init__.py +0 -0
  7. flet/components/hooks/hook.py +12 -0
  8. flet/components/hooks/use_callback.py +28 -0
  9. flet/components/hooks/use_context.py +91 -0
  10. flet/components/hooks/use_effect.py +104 -0
  11. flet/components/hooks/use_memo.py +52 -0
  12. flet/components/hooks/use_state.py +58 -0
  13. flet/components/memo.py +34 -0
  14. flet/components/observable.py +269 -0
  15. flet/components/public_utils.py +10 -0
  16. flet/components/utils.py +85 -0
  17. flet/controls/base_control.py +34 -10
  18. flet/controls/base_page.py +44 -40
  19. flet/controls/context.py +22 -1
  20. flet/controls/control.py +12 -6
  21. flet/controls/control_event.py +19 -2
  22. flet/controls/core/animated_switcher.py +3 -2
  23. flet/controls/core/autofill_group.py +6 -2
  24. flet/controls/core/column.py +5 -0
  25. flet/controls/core/dismissible.py +12 -10
  26. flet/controls/core/drag_target.py +20 -10
  27. flet/controls/core/draggable.py +9 -9
  28. flet/controls/core/icon.py +16 -12
  29. flet/controls/core/interactive_viewer.py +24 -23
  30. flet/controls/core/pagelet.py +3 -2
  31. flet/controls/core/reorderable_draggable.py +3 -2
  32. flet/controls/core/row.py +5 -0
  33. flet/controls/core/safe_area.py +3 -2
  34. flet/controls/core/text_span.py +5 -3
  35. flet/controls/core/view.py +6 -6
  36. flet/controls/core/window_drag_area.py +3 -2
  37. flet/controls/cupertino/cupertino_action_sheet.py +10 -5
  38. flet/controls/cupertino/cupertino_action_sheet_action.py +3 -4
  39. flet/controls/cupertino/cupertino_activity_indicator.py +4 -3
  40. flet/controls/cupertino/cupertino_alert_dialog.py +6 -3
  41. flet/controls/cupertino/cupertino_button.py +6 -5
  42. flet/controls/cupertino/cupertino_context_menu.py +8 -4
  43. flet/controls/cupertino/cupertino_context_menu_action.py +3 -4
  44. flet/controls/cupertino/cupertino_date_picker.py +44 -28
  45. flet/controls/cupertino/cupertino_dialog_action.py +3 -4
  46. flet/controls/cupertino/cupertino_list_tile.py +3 -4
  47. flet/controls/cupertino/cupertino_navigation_bar.py +6 -5
  48. flet/controls/cupertino/cupertino_picker.py +14 -10
  49. flet/controls/cupertino/cupertino_segmented_button.py +6 -5
  50. flet/controls/cupertino/cupertino_slider.py +16 -12
  51. flet/controls/cupertino/cupertino_sliding_segmented_button.py +6 -5
  52. flet/controls/cupertino/cupertino_timer_picker.py +38 -31
  53. flet/controls/id_counter.py +24 -0
  54. flet/controls/material/alert_dialog.py +6 -5
  55. flet/controls/material/app_bar.py +17 -14
  56. flet/controls/material/banner.py +13 -11
  57. flet/controls/material/bottom_app_bar.py +5 -4
  58. flet/controls/material/bottom_sheet.py +5 -4
  59. flet/controls/material/button.py +12 -4
  60. flet/controls/material/chip.py +13 -12
  61. flet/controls/material/circle_avatar.py +17 -13
  62. flet/controls/material/datatable.py +48 -41
  63. flet/controls/material/divider.py +30 -14
  64. flet/controls/material/dropdown.py +5 -3
  65. flet/controls/material/expansion_tile.py +11 -22
  66. flet/controls/material/floating_action_button.py +32 -23
  67. flet/controls/material/icon_button.py +7 -3
  68. flet/controls/material/navigation_rail.py +14 -11
  69. flet/controls/material/outlined_button.py +7 -3
  70. flet/controls/material/progress_bar.py +18 -10
  71. flet/controls/material/radio_group.py +5 -1
  72. flet/controls/material/range_slider.py +13 -13
  73. flet/controls/material/segmented_button.py +21 -17
  74. flet/controls/material/selection_area.py +3 -2
  75. flet/controls/material/slider.py +16 -12
  76. flet/controls/material/snack_bar.py +18 -10
  77. flet/controls/material/switch.py +6 -5
  78. flet/controls/material/tabs.py +18 -14
  79. flet/controls/material/textfield.py +32 -15
  80. flet/controls/material/vertical_divider.py +20 -12
  81. flet/controls/object_patch.py +434 -197
  82. flet/controls/page.py +205 -85
  83. flet/controls/services/haptic_feedback.py +0 -3
  84. flet/controls/services/shake_detector.py +0 -3
  85. flet/messaging/flet_socket_server.py +13 -6
  86. flet/messaging/session.py +103 -10
  87. flet/{controls/session_storage.py → messaging/session_store.py} +2 -2
  88. flet/version.py +1 -1
  89. {flet-0.70.0.dev5774.dist-info → flet-0.70.0.dev5835.dist-info}/METADATA +5 -5
  90. {flet-0.70.0.dev5774.dist-info → flet-0.70.0.dev5835.dist-info}/RECORD +93 -80
  91. flet/controls/cache.py +0 -87
  92. flet/controls/control_id.py +0 -22
  93. flet/controls/core/state_view.py +0 -60
  94. {flet-0.70.0.dev5774.dist-info → flet-0.70.0.dev5835.dist-info}/WHEEL +0 -0
  95. {flet-0.70.0.dev5774.dist-info → flet-0.70.0.dev5835.dist-info}/entry_points.txt +0 -0
  96. {flet-0.70.0.dev5774.dist-info → flet-0.70.0.dev5835.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
7
7
 
8
8
  from flet.controls.context import _context_page, context
9
9
  from flet.controls.control_event import ControlEvent, get_event_field_type
10
- from flet.controls.control_id import ControlId
10
+ from flet.controls.id_counter import ControlId
11
11
  from flet.controls.keys import KeyValue
12
12
  from flet.controls.ref import Ref
13
13
  from flet.utils.from_dict import from_dict
@@ -119,7 +119,6 @@ class BaseControl:
119
119
  """
120
120
  Arbitrary data of any type.
121
121
  """
122
-
123
122
  key: Optional[KeyValue] = None
124
123
 
125
124
  ref: InitVar[Optional[Ref["BaseControl"]]] = None
@@ -173,18 +172,21 @@ class BaseControl:
173
172
  return parent_ref() if parent_ref else None
174
173
 
175
174
  @property
176
- def page(self) -> Optional[Union["Page", "BasePage"]]:
175
+ def page(self) -> Union["Page", "BasePage"]:
177
176
  """
178
177
  The page to which this control belongs to.
179
178
  """
180
- from .page import BasePage, Page
179
+ from .page import Page
181
180
 
182
181
  parent = self
183
182
  while parent:
184
- if isinstance(parent, (Page, BasePage)):
183
+ if isinstance(parent, (Page)):
185
184
  return parent
186
185
  parent = parent.parent
187
- return None
186
+ raise RuntimeError(
187
+ f"{self.__class__.__qualname__}({self._i}) "
188
+ "Control must be added to the page first"
189
+ )
188
190
 
189
191
  def is_isolated(self):
190
192
  return hasattr(self, "_isolated") and self._isolated
@@ -209,15 +211,25 @@ class BaseControl:
209
211
  """
210
212
  pass
211
213
 
214
+ def _before_update_safe(self):
215
+ frozen = getattr(self, "_frozen", None)
216
+ if frozen is not None:
217
+ del self._frozen
218
+
219
+ self.before_update()
220
+
221
+ if frozen is not None:
222
+ self._frozen = frozen
223
+
212
224
  def before_event(self, e: ControlEvent):
213
225
  return True
214
226
 
215
227
  def did_mount(self):
216
- controls_log.debug(f"{self._c}({self._i}).did_mount")
228
+ controls_log.debug(f"{self}.did_mount()")
217
229
  pass
218
230
 
219
231
  def will_unmount(self):
220
- controls_log.debug(f"{self._c}({self._i}).will_unmount")
232
+ controls_log.debug(f"{self}.will_unmount()")
221
233
  pass
222
234
 
223
235
  # public methods
@@ -239,7 +251,7 @@ class BaseControl:
239
251
  f"{self.__class__.__qualname__} Control must be added to the page first"
240
252
  )
241
253
 
242
- return await self.page.get_session().invoke_method(
254
+ return await self.page.session.invoke_method(
243
255
  self._i, method_name, arguments, timeout
244
256
  )
245
257
 
@@ -271,11 +283,13 @@ class BaseControl:
271
283
  _context_page.set(self.page)
272
284
  context.reset_auto_update()
273
285
 
286
+ controls_log.debug(f"Trigger event {self}.{field_name} {e}")
287
+
274
288
  assert self.page, (
275
289
  "Control must be added to a page before triggering events. "
276
290
  "Use page.add(control) or add it to a parent control that's on a page."
277
291
  )
278
- session = self.page.get_session()
292
+ session = self.page.session
279
293
 
280
294
  # Handle async and sync event handlers accordingly
281
295
  event_handler = getattr(self, field_name)
@@ -310,3 +324,13 @@ class BaseControl:
310
324
  event_handler(e)
311
325
 
312
326
  await session.after_event(session.index.get(self._i))
327
+
328
+ def _migrate_state(self, other: "BaseControl"):
329
+ if not isinstance(other, BaseControl):
330
+ return
331
+ self._i = other._i
332
+ if self.data is None:
333
+ self.data = other.data
334
+
335
+ def __str__(self):
336
+ return f"{self._c}({self._i} - {id(self)})"
@@ -159,33 +159,32 @@ class BasePage(AdaptiveControl):
159
159
  reported by the framework.
160
160
  """
161
161
 
162
- width: Optional[Number] = None
162
+ title: Optional[str] = None
163
+ """
164
+ Page or window title.
163
165
  """
164
- Page width in logical pixels.
165
166
 
166
- Note:
167
- - This property is read-only.
168
- - To get or set the full window height including window chrome (e.g.,
169
- title bar and borders) when running a Flet app on desktop,
170
- use the [`width`][flet.Window.] property of
171
- [`Page.window`][flet.] instead.
167
+ enable_screenshots: bool = False
168
+ """
169
+ Enable taking screenshots of the entire page with `take_screenshot` method.
172
170
  """
173
171
 
174
- height: Optional[Number] = None
172
+ on_resize: Optional[EventHandler["PageResizeEvent"]] = None
175
173
  """
176
- Page height in logical pixels.
174
+ Called when a user resizes a browser or native OS window containing Flet app, for
175
+ example:
177
176
 
178
- Note:
179
- - This property is read-only.
180
- - To get or set the full window height including window chrome (e.g.,
181
- title bar and borders) when running a Flet app on desktop,
182
- use the [`height`][flet.Window.] property of
183
- [`Page.window`][flet.] instead.
177
+ ```python
178
+ def page_resize(e):
179
+ print("New page size:", page.window.width, page.window_height)
180
+
181
+ page.on_resize = page_resize
182
+ ```
184
183
  """
185
184
 
186
- title: Optional[str] = None
185
+ on_media_change: Optional[EventHandler[PageMediaData]] = None
187
186
  """
188
- Page or window title.
187
+ Called when `media` has changed.
189
188
  """
190
189
 
191
190
  media: PageMediaData = field(
@@ -197,30 +196,35 @@ class BasePage(AdaptiveControl):
197
196
  )
198
197
  )
199
198
  """
200
- Represents the environmental metrics of a page or window.
201
- """
199
+ The current environmental metrics of the page or window.
202
200
 
203
- enable_screenshots: bool = False
204
- """
205
- Enable taking screenshots of the entire page with `take_screenshot` method.
201
+ This data is updated whenever the platform window or layout changes,
202
+ such as when rotating a device, resizing a browser window, or adjusting
203
+ system UI elements like the keyboard or safe areas.
206
204
  """
207
205
 
208
- on_resize: Optional[EventHandler["PageResizeEvent"]] = None
206
+ width: Optional[Number] = None
209
207
  """
210
- Called when a user resizes a browser or native OS window containing Flet app, for
211
- example:
212
-
213
- ```python
214
- def page_resize(e):
215
- print("New page size:", page.window.width, page.window_height)
208
+ Page width in logical pixels.
216
209
 
217
- page.on_resize = page_resize
218
- ```
210
+ Note:
211
+ - This property is read-only.
212
+ - To get or set the full window height including window chrome (e.g.,
213
+ title bar and borders) when running a Flet app on desktop,
214
+ use the [`width`][flet.Window.width] property of
215
+ [`Page.window`][flet.Page.window] instead.
219
216
  """
220
217
 
221
- on_media_change: Optional[EventHandler[PageMediaData]] = None
218
+ height: Optional[Number] = None
222
219
  """
223
- Called when `media` has changed.
220
+ Page height in logical pixels.
221
+
222
+ Note:
223
+ - This property is read-only.
224
+ - To get or set the full window height including window chrome (e.g.,
225
+ title bar and borders) when running a Flet app on desktop,
226
+ use the [`height`][flet.Window.height] property of
227
+ [`Page.window`][flet.Page.window] instead.
224
228
  """
225
229
 
226
230
  _overlay: "Overlay" = field(default_factory=lambda: Overlay())
@@ -492,29 +496,29 @@ class BasePage(AdaptiveControl):
492
496
 
493
497
  # horizontal_alignment
494
498
  @property
495
- def horizontal_alignment(self) -> Optional[CrossAxisAlignment]:
499
+ def horizontal_alignment(self) -> CrossAxisAlignment:
496
500
  return self.__default_view().horizontal_alignment
497
501
 
498
502
  @horizontal_alignment.setter
499
- def horizontal_alignment(self, value: Optional[CrossAxisAlignment]):
503
+ def horizontal_alignment(self, value: CrossAxisAlignment):
500
504
  self.__default_view().horizontal_alignment = value
501
505
 
502
506
  # vertical_alignment
503
507
  @property
504
- def vertical_alignment(self) -> Optional[MainAxisAlignment]:
508
+ def vertical_alignment(self) -> MainAxisAlignment:
505
509
  return self.__default_view().vertical_alignment
506
510
 
507
511
  @vertical_alignment.setter
508
- def vertical_alignment(self, value: Optional[MainAxisAlignment]):
512
+ def vertical_alignment(self, value: MainAxisAlignment):
509
513
  self.__default_view().vertical_alignment = value
510
514
 
511
515
  # spacing
512
516
  @property
513
- def spacing(self) -> Optional[Number]:
517
+ def spacing(self) -> Number:
514
518
  return self.__default_view().spacing
515
519
 
516
520
  @spacing.setter
517
- def spacing(self, value: Optional[Number]):
521
+ def spacing(self, value: Number):
518
522
  self.__default_view().spacing = value
519
523
 
520
524
  # padding
flet/controls/context.py CHANGED
@@ -13,6 +13,9 @@ class Context:
13
13
  Context instance is accessed via [`flet.context`][flet.context].
14
14
  """
15
15
 
16
+ def __init__(self) -> None:
17
+ self.__components_mode = False
18
+
16
19
  @property
17
20
  def page(self) -> "Page":
18
21
  """
@@ -95,6 +98,21 @@ class Context:
95
98
  """
96
99
  _update_behavior_context_var.get()._auto_update_enabled = False
97
100
 
101
+ def enable_components_mode(self):
102
+ """
103
+ Enables components mode in the current context.
104
+ """
105
+ self.__components_mode = True
106
+
107
+ def is_components_mode(self) -> bool:
108
+ """
109
+ Returns whether the current context is in components mode.
110
+
111
+ Returns:
112
+ `True` if in components mode, `False` otherwise.
113
+ """
114
+ return self.__components_mode
115
+
98
116
  def auto_update_enabled(self) -> bool:
99
117
  """
100
118
  Returns whether auto-update is enabled in the current context.
@@ -102,7 +120,10 @@ class Context:
102
120
  Returns:
103
121
  `True` if auto-update is enabled, `False` otherwise.
104
122
  """
105
- return _update_behavior_context_var.get()._auto_update_enabled
123
+ return (
124
+ not self.__components_mode
125
+ and _update_behavior_context_var.get()._auto_update_enabled
126
+ )
106
127
 
107
128
  def reset_auto_update(self):
108
129
  """
flet/controls/control.py CHANGED
@@ -15,6 +15,10 @@ class Control(BaseControl):
15
15
  Base class for controls.
16
16
 
17
17
  Not meant to be used directly.
18
+
19
+ Raises:
20
+ ValueError: If [`opacity`][(c).] is not between `0.0` and `1.0` inclusive.
21
+ ValueError: If [`expand`][(c).] is not None and not of type `bool` or `int`.
18
22
  """
19
23
 
20
24
  expand: Optional[Union[bool, int]] = None
@@ -132,12 +136,14 @@ class Control(BaseControl):
132
136
 
133
137
  def before_update(self):
134
138
  super().before_update()
135
- assert 0.0 <= self.opacity <= 1.0, (
136
- f"opacity must be between 0.0 and 1.0 inclusive, got {self.opacity}"
137
- )
138
- assert self.expand is None or isinstance(self.expand, (bool, int)), (
139
- f"expand must be of type bool or int, got {type(self.expand)}"
140
- )
139
+ if not (0.0 <= self.opacity <= 1.0):
140
+ raise ValueError(
141
+ f"opacity must be between 0.0 and 1.0 inclusive, got {self.opacity}"
142
+ )
143
+ if self.expand is not None and not isinstance(self.expand, (bool, int)):
144
+ raise ValueError(
145
+ f"expand must be of type bool or int, got {type(self.expand)}"
146
+ )
141
147
 
142
148
  def clean(self) -> None:
143
149
  raise Exception("Deprecated!")
@@ -32,14 +32,17 @@ __all__ = [
32
32
 
33
33
  def get_event_field_type(control: Any, field_name: str):
34
34
  frame = inspect.currentframe().f_back
35
- globalns = sys.modules[control.__class__.__module__].__dict__
36
35
  localns = frame.f_globals.copy()
37
36
  localns.update(frame.f_locals)
38
37
 
39
38
  merged_annotations = {}
39
+ annotation_modules = {}
40
40
 
41
41
  for cls in control.__class__.__mro__:
42
42
  annotations = getattr(cls, "__annotations__", {})
43
+ module = sys.modules.get(cls.__module__)
44
+ module_dict = module.__dict__ if module else {}
45
+
43
46
  for name, annotation in annotations.items():
44
47
  if get_origin(annotation) is InitVar or str(annotation).startswith(
45
48
  "dataclasses.InitVar"
@@ -47,12 +50,25 @@ def get_event_field_type(control: Any, field_name: str):
47
50
  continue # Skip InitVar
48
51
  if name not in merged_annotations:
49
52
  merged_annotations[name] = annotation
53
+ annotation_modules[name] = module_dict
50
54
 
51
55
  if field_name not in merged_annotations:
52
56
  return None
53
57
 
54
58
  annotation = merged_annotations[field_name]
55
59
 
60
+ globalns = {}
61
+ current_module = sys.modules.get(control.__class__.__module__)
62
+ if current_module:
63
+ globalns.update(current_module.__dict__)
64
+
65
+ owner_module_dict = annotation_modules.get(field_name)
66
+ if owner_module_dict:
67
+ for key, value in owner_module_dict.items():
68
+ globalns.setdefault(key, value)
69
+
70
+ globalns.setdefault("__builtins__", __builtins__)
71
+
56
72
  try:
57
73
  # Resolve forward refs manually
58
74
  if isinstance(annotation, ForwardRef):
@@ -80,7 +96,8 @@ class Event(Generic[EventControlType]):
80
96
  control: EventControlType = field(repr=False)
81
97
 
82
98
  @property
83
- def page(self) -> Optional[Union["Page", "BasePage"]]:
99
+ def page(self) -> Union["Page", "BasePage"]:
100
+ assert self.control.page
84
101
  return self.control.page
85
102
 
86
103
  @property
@@ -22,7 +22,7 @@ class AnimatedSwitcher(LayoutControl):
22
22
  Used to switch between controls with an animation.
23
23
 
24
24
  Raises:
25
- AssertionError: The [`content`][(c).] must be provided and visible.
25
+ ValueError: If [`content`][(c).] is not visible.
26
26
  """
27
27
 
28
28
  content: Control
@@ -60,4 +60,5 @@ class AnimatedSwitcher(LayoutControl):
60
60
 
61
61
  def before_update(self):
62
62
  super().before_update()
63
- assert self.content.visible, "content must be visible"
63
+ if not self.content.visible:
64
+ raise ValueError("content must be visible")
@@ -3,7 +3,7 @@ from enum import Enum
3
3
  from flet.controls.base_control import control
4
4
  from flet.controls.control import Control
5
5
 
6
- __all__ = ["AutofillGroup", "AutofillHint", "AutofillGroupDisposeAction"]
6
+ __all__ = ["AutofillGroup", "AutofillGroupDisposeAction", "AutofillHint"]
7
7
 
8
8
 
9
9
  class AutofillHint(Enum):
@@ -84,6 +84,9 @@ class AutofillGroupDisposeAction(Enum):
84
84
  class AutofillGroup(Control):
85
85
  """
86
86
  Used to group autofill controls together.
87
+
88
+ Raises:
89
+ ValueError: If [`content`][(c).] is not visible.
87
90
  """
88
91
 
89
92
  content: Control
@@ -99,4 +102,5 @@ class AutofillGroup(Control):
99
102
 
100
103
  def before_update(self):
101
104
  super().before_update()
102
- assert self.content.visible, "content must be visible"
105
+ if not self.content.visible:
106
+ raise ValueError("content must be visible")
@@ -65,6 +65,11 @@ class Column(LayoutControl, ScrollableControl, AdaptiveControl):
65
65
  is `True`.
66
66
  """
67
67
 
68
+ intrinsic_width: bool = False
69
+ """
70
+ If `True`, the Column will be as wide as the widest child control.
71
+ """
72
+
68
73
  def init(self):
69
74
  super().init()
70
75
  self._internals["host_expanded"] = True
@@ -46,9 +46,9 @@ class Dismissible(LayoutControl, AdaptiveControl):
46
46
  over the specified [`resize_duration`][(c).].
47
47
 
48
48
  Raises:
49
- AssertionError: If the [`content`][(c).] is not visible.
50
- AssertionError: If the [`secondary_background`][(c).] is specified but the
51
- [`background`][(c).] is not specified/visible.
49
+ ValueError: If the [`content`][(c).] is not visible.
50
+ ValueError: If the [`secondary_background`][(c).] is provided and visible
51
+ but the [`background`][(c).] is not provided and visible.
52
52
  """
53
53
 
54
54
  content: Control
@@ -155,13 +155,15 @@ class Dismissible(LayoutControl, AdaptiveControl):
155
155
 
156
156
  def before_update(self):
157
157
  super().before_update()
158
- assert self.content.visible, "content must be visible"
159
- assert not (
160
- self.secondary_background and self.secondary_background.visible
161
- ) or (self.background and self.background.visible), (
162
- "secondary_background can only be specified if background is also "
163
- "specified/visible"
164
- )
158
+ if not self.content.visible:
159
+ raise ValueError("content must be visible")
160
+ if (self.secondary_background and self.secondary_background.visible) and not (
161
+ self.background and self.background.visible
162
+ ):
163
+ raise ValueError(
164
+ "secondary_background can only be specified if background is also "
165
+ "specified/visible"
166
+ )
165
167
 
166
168
  async def confirm_dismiss(self, dismiss: bool):
167
169
  await self._invoke_method("confirm_dismiss", {"dismiss": dismiss})
@@ -1,9 +1,10 @@
1
- from dataclasses import dataclass
2
- from typing import Optional
1
+ from dataclasses import dataclass, field
2
+ from typing import Optional, cast
3
3
 
4
4
  from flet.controls.base_control import control
5
5
  from flet.controls.control import Control
6
6
  from flet.controls.control_event import Event, EventHandler
7
+ from flet.controls.core.draggable import Draggable
7
8
  from flet.controls.transform import Offset
8
9
 
9
10
  __all__ = [
@@ -15,14 +16,22 @@ __all__ = [
15
16
 
16
17
 
17
18
  @dataclass
18
- class DragWillAcceptEvent(Event["DragTarget"]):
19
+ class DragEventBase(Event["DragTarget"]):
20
+ src_id: Optional[int]
21
+ src: Draggable = field(init=False)
22
+
23
+ def __post_init__(self):
24
+ if self.src_id is not None:
25
+ self.src = cast(Draggable, self.page.get_control(self.src_id))
26
+
27
+
28
+ @dataclass
29
+ class DragWillAcceptEvent(DragEventBase):
19
30
  accept: bool
20
- src_id: int
21
31
 
22
32
 
23
33
  @dataclass
24
- class DragTargetEvent(Event["DragTarget"]):
25
- src_id: int
34
+ class DragTargetEvent(DragEventBase):
26
35
  x: float
27
36
  y: float
28
37
 
@@ -32,8 +41,8 @@ class DragTargetEvent(Event["DragTarget"]):
32
41
 
33
42
 
34
43
  @dataclass
35
- class DragTargetLeaveEvent(Event["DragTarget"]):
36
- src_id: Optional[int]
44
+ class DragTargetLeaveEvent(DragEventBase):
45
+ pass
37
46
 
38
47
 
39
48
  @control("DragTarget")
@@ -50,7 +59,7 @@ class DragTarget(Control):
50
59
  asked to accept the `Draggable`'s data.
51
60
 
52
61
  Raises:
53
- AssertionError: If [`content`][(c).] is not visible.
62
+ ValueError: If [`content`][(c).] is not visible.
54
63
  """
55
64
 
56
65
  content: Control
@@ -92,4 +101,5 @@ class DragTarget(Control):
92
101
 
93
102
  def before_update(self):
94
103
  super().before_update()
95
- assert self.content.visible, "content must be visible"
104
+ if not self.content.visible:
105
+ raise ValueError("content must be visible")
@@ -19,8 +19,8 @@ class Draggable(Control):
19
19
  given the opportunity to complete drag-and-drop flow.
20
20
 
21
21
  Raises:
22
- AssertionError: If [`content`][(c).] is not visible.
23
- AssertionError: If [`max_simultaneous_drags`][(c).] is set to a negative value.
22
+ ValueError: If [`content`][(c).] is not visible.
23
+ ValueError: If [`max_simultaneous_drags`][(c).] is set to a negative value.
24
24
  """
25
25
 
26
26
  content: Control
@@ -101,10 +101,10 @@ class Draggable(Control):
101
101
 
102
102
  def before_update(self):
103
103
  super().before_update()
104
- assert self.content.visible, "content must be visible"
105
- assert self.max_simultaneous_drags is None or (
106
- self.max_simultaneous_drags >= 0
107
- ), (
108
- f"max_simultaneous_drags must be greater than or equal to 0, "
109
- f"got {self.max_simultaneous_drags}"
110
- )
104
+ if not self.content.visible:
105
+ raise ValueError("content must be visible")
106
+ if self.max_simultaneous_drags is not None and self.max_simultaneous_drags < 0:
107
+ raise ValueError(
108
+ f"max_simultaneous_drags must be greater than or equal to 0, "
109
+ f"got {self.max_simultaneous_drags}"
110
+ )
@@ -17,9 +17,9 @@ class Icon(LayoutControl):
17
17
  parameters such as stroke weight, fill level, and shadows.
18
18
 
19
19
  Raises:
20
- AssertionError: If [`fill`][(c).] is less than `0.0` or greater than `1.0`.
21
- AssertionError: If [`weight`][(c).] is less than or equal to `0.0`.
22
- AssertionError: If [`optical_size`][(c).] is less than or equal to `0.0`.
20
+ ValueError: If [`fill`][(c).] is less than `0.0` or greater than `1.0`.
21
+ ValueError: If [`weight`][(c).] is less than or equal to `0.0`.
22
+ ValueError: If [`optical_size`][(c).] is less than or equal to `0.0`.
23
23
  """
24
24
 
25
25
  icon: IconData
@@ -110,12 +110,16 @@ class Icon(LayoutControl):
110
110
 
111
111
  def before_update(self):
112
112
  super().before_update()
113
- assert self.fill is None or (0.0 <= self.fill <= 1.0), (
114
- f"fill must be between 0.0 and 1.0 inclusive, got {self.fill}"
115
- )
116
- assert self.weight is None or (self.weight > 0.0), (
117
- f"weight must be strictly greater than 0.0, got {self.weight}"
118
- )
119
- assert self.optical_size is None or (self.optical_size > 0.0), (
120
- f"optical_size must be strictly greater than 0.0, got {self.optical_size}"
121
- )
113
+ if self.fill is not None and not (0.0 <= self.fill <= 1.0):
114
+ raise ValueError(
115
+ f"fill must be between 0.0 and 1.0 inclusive, got {self.fill}"
116
+ )
117
+ if self.weight is not None and self.weight <= 0.0:
118
+ raise ValueError(
119
+ f"weight must be strictly greater than 0.0, got {self.weight}"
120
+ )
121
+ if self.optical_size is not None and self.optical_size <= 0.0:
122
+ raise ValueError(
123
+ f"optical_size must be strictly greater than 0.0, "
124
+ f"got {self.optical_size}"
125
+ )
@@ -23,11 +23,11 @@ class InteractiveViewer(LayoutControl):
23
23
  Allows you to pan, zoom, and rotate its [`content`][(c).].
24
24
 
25
25
  Raises:
26
- AssertionError: If [`content`][(c).] is not visible.
27
- AssertionError: If [`min_scale`][(c).] is not greater than `0`.
28
- AssertionError: If [`max_scale`][(c).] is not greater than `0`.
29
- AssertionError: If [`max_scale`][(c).] is less than `min_scale`.
30
- AssertionError: If [`interaction_end_friction_coefficient`][(c).] is not
26
+ ValueError: If [`content`][(c).] is not visible.
27
+ ValueError: If [`min_scale`][(c).] is not greater than `0`.
28
+ ValueError: If [`max_scale`][(c).] is not greater than `0`.
29
+ ValueError: If [`max_scale`][(c).] is less than `min_scale`.
30
+ ValueError: If [`interaction_end_friction_coefficient`][(c).] is not
31
31
  greater than `0`.
32
32
  """
33
33
 
@@ -130,24 +130,25 @@ class InteractiveViewer(LayoutControl):
130
130
 
131
131
  def before_update(self):
132
132
  super().before_update()
133
- assert self.content.visible, "content must be visible"
134
- assert self.min_scale > 0, (
135
- f"min_scale must be greater than 0, got {self.min_scale}"
136
- )
137
- assert self.max_scale > 0, (
138
- f"max_scale must be greater than 0, got {self.max_scale}"
139
- )
140
- assert self.max_scale >= self.min_scale, (
141
- "max_scale must be greater than or equal to min_scale, "
142
- f"got max_scale={self.max_scale}, min_scale={self.min_scale}"
143
- )
144
- assert (
145
- self.interaction_end_friction_coefficient is None
146
- or self.interaction_end_friction_coefficient > 0
147
- ), (
148
- "interaction_end_friction_coefficient must be greater than 0, "
149
- f"got {self.interaction_end_friction_coefficient}"
150
- )
133
+ if not self.content.visible:
134
+ raise ValueError("content must be visible")
135
+ if self.min_scale <= 0:
136
+ raise ValueError(f"min_scale must be greater than 0, got {self.min_scale}")
137
+ if self.max_scale <= 0:
138
+ raise ValueError(f"max_scale must be greater than 0, got {self.max_scale}")
139
+ if self.max_scale < self.min_scale:
140
+ raise ValueError(
141
+ "max_scale must be greater than or equal to min_scale, "
142
+ f"got max_scale={self.max_scale}, min_scale={self.min_scale}"
143
+ )
144
+ if (
145
+ self.interaction_end_friction_coefficient is not None
146
+ and self.interaction_end_friction_coefficient <= 0
147
+ ):
148
+ raise ValueError(
149
+ "interaction_end_friction_coefficient must be greater than 0, "
150
+ f"got {self.interaction_end_friction_coefficient}"
151
+ )
151
152
 
152
153
  async def reset(self, animation_duration: Optional[DurationValue] = None):
153
154
  await self._invoke_method(