Coverage for pyWebLayout/abstract/functional.py: 98%
144 statements
« prev ^ index » next coverage.py v7.11.2, created at 2025-11-12 12:02 +0000
« prev ^ index » next coverage.py v7.11.2, created at 2025-11-12 12:02 +0000
1from __future__ import annotations
2from enum import Enum
3from typing import Callable, Dict, Any, Optional, List, Tuple
4from pyWebLayout.core.base import Interactable
7class LinkType(Enum):
8 """Enumeration of different types of links for classification purposes"""
9 INTERNAL = 1 # Links within the same document (e.g., chapter references, bookmarks)
10 EXTERNAL = 2 # Links to external resources (e.g., websites, other documents)
11 API = 3 # Links that trigger API calls (e.g., for settings management)
12 FUNCTION = 4 # Links that execute a specific function
15class Link(Interactable):
16 """
17 A link that can navigate to a location or execute a function.
18 Links can be used for navigation within a document, to external resources,
19 or to trigger API calls for functionality like settings management.
20 """
22 def __init__(self,
23 location: str,
24 link_type: LinkType = LinkType.INTERNAL,
25 callback: Optional[Callable] = None,
26 params: Optional[Dict[str, Any]] = None,
27 title: Optional[str] = None,
28 html_id: Optional[str] = None):
29 """
30 Initialize a link.
32 Args:
33 location: The target location or identifier for this link
34 link_type: The type of link (internal, external, API, function)
35 callback: Optional callback function to execute when the link is activated
36 params: Optional parameters to pass to the callback or API
37 title: Optional title/tooltip for the link
38 html_id: Optional HTML id attribute (from <a id="...">) for callback binding
39 """
40 super().__init__(callback)
41 self._location = location
42 self._link_type = link_type
43 self._params = params or {}
44 self._title = title
45 self._html_id = html_id
47 @property
48 def location(self) -> str:
49 """Get the target location of this link"""
50 return self._location
52 @property
53 def link_type(self) -> LinkType:
54 """Get the type of this link"""
55 return self._link_type
57 @property
58 def params(self) -> Dict[str, Any]:
59 """Get the parameters for this link"""
60 return self._params
62 @property
63 def title(self) -> Optional[str]:
64 """Get the title/tooltip for this link"""
65 return self._title
67 @property
68 def html_id(self) -> Optional[str]:
69 """Get the HTML id attribute for callback binding"""
70 return self._html_id
72 def execute(self, point=None) -> Any:
73 """
74 Execute the link action based on its type.
76 For internal and external links, returns the location.
77 For API and function links, executes the callback with the provided parameters.
79 Args:
80 point: Optional interaction point passed from the interact() method
82 Returns:
83 The result of the link execution, which depends on the link type.
84 """
85 if self._link_type in (LinkType.API, LinkType.FUNCTION) and self._callback:
86 return self._callback(self._location, point, **self._params)
87 else:
88 # For INTERNAL and EXTERNAL links, return the location
89 # The renderer/browser will handle the navigation
90 return self._location
93class Button(Interactable):
94 """
95 A button that can be clicked to execute an action.
96 Buttons are similar to function links but are rendered differently.
97 """
99 def __init__(self,
100 label: str,
101 callback: Callable,
102 params: Optional[Dict[str, Any]] = None,
103 enabled: bool = True,
104 html_id: Optional[str] = None):
105 """
106 Initialize a button.
108 Args:
109 label: The text label for the button
110 callback: The function to execute when the button is clicked
111 params: Optional parameters to pass to the callback
112 enabled: Whether the button is initially enabled
113 html_id: Optional HTML id attribute (from <button id="...">) for callback binding
114 """
115 super().__init__(callback)
116 self._label = label
117 self._params = params or {}
118 self._enabled = enabled
119 self._html_id = html_id
121 @property
122 def label(self) -> str:
123 """Get the button label"""
124 return self._label
126 @label.setter
127 def label(self, label: str):
128 """Set the button label"""
129 self._label = label
131 @property
132 def enabled(self) -> bool:
133 """Check if the button is enabled"""
134 return self._enabled
136 @enabled.setter
137 def enabled(self, enabled: bool):
138 """Enable or disable the button"""
139 self._enabled = enabled
141 @property
142 def params(self) -> Dict[str, Any]:
143 """Get the button parameters"""
144 return self._params
146 @property
147 def html_id(self) -> Optional[str]:
148 """Get the HTML id attribute for callback binding"""
149 return self._html_id
151 def execute(self, point=None) -> Any:
152 """
153 Execute the button's callback function if the button is enabled.
155 Args:
156 point: Optional interaction point passed from the interact() method
158 Returns:
159 The result of the callback function, or None if the button is disabled.
160 """
161 if self._enabled and self._callback:
162 return self._callback(point, **self._params)
163 return None
166class Form(Interactable):
167 """
168 A form that can contain input fields and be submitted.
169 Forms can be used for user input and settings configuration.
170 """
172 def __init__(self,
173 form_id: str,
174 action: Optional[str] = None,
175 callback: Optional[Callable] = None,
176 html_id: Optional[str] = None):
177 """
178 Initialize a form.
180 Args:
181 form_id: The unique identifier for this form
182 action: The action URL or endpoint for form submission
183 callback: Optional callback function to execute on form submission
184 html_id: Optional HTML id attribute (from <form id="...">) for callback binding
185 """
186 super().__init__(callback)
187 self._form_id = form_id
188 self._action = action
189 self._fields: Dict[str, FormField] = {}
190 self._html_id = html_id
192 @property
193 def form_id(self) -> str:
194 """Get the form ID"""
195 return self._form_id
197 @property
198 def action(self) -> Optional[str]:
199 """Get the form action"""
200 return self._action
202 @property
203 def html_id(self) -> Optional[str]:
204 """Get the HTML id attribute for callback binding"""
205 return self._html_id
207 def add_field(self, field: FormField):
208 """
209 Add a field to this form.
211 Args:
212 field: The FormField to add
213 """
214 self._fields[field.name] = field
215 field.form = self
217 def get_field(self, name: str) -> Optional[FormField]:
218 """
219 Get a field by name.
221 Args:
222 name: The name of the field to get
224 Returns:
225 The FormField with the specified name, or None if not found
226 """
227 return self._fields.get(name)
229 def get_values(self) -> Dict[str, Any]:
230 """
231 Get the current values of all fields in this form.
233 Returns:
234 A dictionary mapping field names to their current values
235 """
236 return {name: field.value for name, field in self._fields.items()}
238 def execute(self) -> Any:
239 """
240 Submit the form, executing the callback with the form values.
242 Returns:
243 The result of the callback function, or the form values if no callback is provided.
244 """
245 values = self.get_values()
247 if self._callback:
248 return self._callback(self._form_id, values)
250 return values
253class FormFieldType(Enum):
254 """Enumeration of different types of form fields"""
255 TEXT = 1
256 PASSWORD = 2
257 CHECKBOX = 3
258 RADIO = 4
259 SELECT = 5
260 TEXTAREA = 6
261 NUMBER = 7
262 DATE = 8
263 TIME = 9
264 EMAIL = 10
265 URL = 11
266 COLOR = 12
267 RANGE = 13
268 HIDDEN = 14
271class FormField:
272 """
273 A field in a form that can accept user input.
274 """
276 def __init__(self,
277 name: str,
278 field_type: FormFieldType,
279 label: Optional[str] = None,
280 value: Any = None,
281 required: bool = False,
282 options: Optional[List[Tuple[str, str]]] = None):
283 """
284 Initialize a form field.
286 Args:
287 name: The name of this field
288 field_type: The type of this field
289 label: Optional label for this field
290 value: Initial value for this field
291 required: Whether this field is required
292 options: Options for select, radio, or checkbox fields (list of (value, label) tuples)
293 """
294 self._name = name
295 self._field_type = field_type
296 self._label = label or name
297 self._value = value
298 self._required = required
299 self._options = options or []
300 self._form: Optional[Form] = None
302 @property
303 def name(self) -> str:
304 """Get the field name"""
305 return self._name
307 @property
308 def field_type(self) -> FormFieldType:
309 """Get the field type"""
310 return self._field_type
312 @property
313 def label(self) -> str:
314 """Get the field label"""
315 return self._label
317 @property
318 def value(self) -> Any:
319 """Get the current field value"""
320 return self._value
322 @value.setter
323 def value(self, value: Any):
324 """Set the field value"""
325 self._value = value
327 @property
328 def required(self) -> bool:
329 """Check if the field is required"""
330 return self._required
332 @property
333 def options(self) -> List[Tuple[str, str]]:
334 """Get the field options"""
335 return self._options
337 @property
338 def form(self) -> Optional[Form]:
339 """Get the form containing this field"""
340 return self._form
342 @form.setter
343 def form(self, form: Form):
344 """Set the form containing this field"""
345 self._form = form