311 lines
9.1 KiB
Python

from __future__ import annotations
from enum import Enum
from typing import Callable, Dict, Any, Optional, Union, List, Tuple
from pyWebLayout.base import Interactable
class LinkType(Enum):
"""Enumeration of different types of links for classification purposes"""
INTERNAL = 1 # Links within the same document (e.g., chapter references, bookmarks)
EXTERNAL = 2 # Links to external resources (e.g., websites, other documents)
API = 3 # Links that trigger API calls (e.g., for settings management)
FUNCTION = 4 # Links that execute a specific function
class Link(Interactable):
"""
A link that can navigate to a location or execute a function.
Links can be used for navigation within a document, to external resources,
or to trigger API calls for functionality like settings management.
"""
def __init__(self,
location: str,
link_type: LinkType = LinkType.INTERNAL,
callback: Optional[Callable] = None,
params: Optional[Dict[str, Any]] = None,
title: Optional[str] = None):
"""
Initialize a link.
Args:
location: The target location or identifier for this link
link_type: The type of link (internal, external, API, function)
callback: Optional callback function to execute when the link is activated
params: Optional parameters to pass to the callback or API
title: Optional title/tooltip for the link
"""
super().__init__(callback)
self._location = location
self._link_type = link_type
self._params = params or {}
self._title = title
@property
def location(self) -> str:
"""Get the target location of this link"""
return self._location
@property
def link_type(self) -> LinkType:
"""Get the type of this link"""
return self._link_type
@property
def params(self) -> Dict[str, Any]:
"""Get the parameters for this link"""
return self._params
@property
def title(self) -> Optional[str]:
"""Get the title/tooltip for this link"""
return self._title
def execute(self) -> Any:
"""
Execute the link action based on its type.
For internal and external links, returns the location.
For API and function links, executes the callback with the provided parameters.
Returns:
The result of the link execution, which depends on the link type.
"""
if self._link_type in (LinkType.API, LinkType.FUNCTION) and self._callback:
return self._callback(self._location, **self._params)
else:
# For INTERNAL and EXTERNAL links, return the location
# The renderer/browser will handle the navigation
return self._location
class Button(Interactable):
"""
A button that can be clicked to execute an action.
Buttons are similar to function links but are rendered differently.
"""
def __init__(self,
label: str,
callback: Callable,
params: Optional[Dict[str, Any]] = None,
enabled: bool = True):
"""
Initialize a button.
Args:
label: The text label for the button
callback: The function to execute when the button is clicked
params: Optional parameters to pass to the callback
enabled: Whether the button is initially enabled
"""
super().__init__(callback)
self._label = label
self._params = params or {}
self._enabled = enabled
@property
def label(self) -> str:
"""Get the button label"""
return self._label
@label.setter
def label(self, label: str):
"""Set the button label"""
self._label = label
@property
def enabled(self) -> bool:
"""Check if the button is enabled"""
return self._enabled
@enabled.setter
def enabled(self, enabled: bool):
"""Enable or disable the button"""
self._enabled = enabled
def execute(self) -> Any:
"""
Execute the button's callback function if the button is enabled.
Returns:
The result of the callback function, or None if the button is disabled.
"""
if self._enabled and self._callback:
return self._callback(**self._params)
return None
class Form(Interactable):
"""
A form that can contain input fields and be submitted.
Forms can be used for user input and settings configuration.
"""
def __init__(self,
form_id: str,
action: Optional[str] = None,
callback: Optional[Callable] = None):
"""
Initialize a form.
Args:
form_id: The unique identifier for this form
action: The action URL or endpoint for form submission
callback: Optional callback function to execute on form submission
"""
super().__init__(callback)
self._form_id = form_id
self._action = action
self._fields: Dict[str, FormField] = {}
@property
def form_id(self) -> str:
"""Get the form ID"""
return self._form_id
@property
def action(self) -> Optional[str]:
"""Get the form action"""
return self._action
def add_field(self, field: FormField):
"""
Add a field to this form.
Args:
field: The FormField to add
"""
self._fields[field.name] = field
field.form = self
def get_field(self, name: str) -> Optional[FormField]:
"""
Get a field by name.
Args:
name: The name of the field to get
Returns:
The FormField with the specified name, or None if not found
"""
return self._fields.get(name)
def get_values(self) -> Dict[str, Any]:
"""
Get the current values of all fields in this form.
Returns:
A dictionary mapping field names to their current values
"""
return {name: field.value for name, field in self._fields.items()}
def execute(self) -> Any:
"""
Submit the form, executing the callback with the form values.
Returns:
The result of the callback function, or the form values if no callback is provided.
"""
values = self.get_values()
if self._callback:
return self._callback(self._form_id, values)
return values
class FormFieldType(Enum):
"""Enumeration of different types of form fields"""
TEXT = 1
PASSWORD = 2
CHECKBOX = 3
RADIO = 4
SELECT = 5
TEXTAREA = 6
NUMBER = 7
DATE = 8
TIME = 9
EMAIL = 10
URL = 11
COLOR = 12
RANGE = 13
HIDDEN = 14
class FormField:
"""
A field in a form that can accept user input.
"""
def __init__(self,
name: str,
field_type: FormFieldType,
label: Optional[str] = None,
value: Any = None,
required: bool = False,
options: Optional[List[Tuple[str, str]]] = None):
"""
Initialize a form field.
Args:
name: The name of this field
field_type: The type of this field
label: Optional label for this field
value: Initial value for this field
required: Whether this field is required
options: Options for select, radio, or checkbox fields (list of (value, label) tuples)
"""
self._name = name
self._field_type = field_type
self._label = label or name
self._value = value
self._required = required
self._options = options or []
self._form: Optional[Form] = None
@property
def name(self) -> str:
"""Get the field name"""
return self._name
@property
def field_type(self) -> FormFieldType:
"""Get the field type"""
return self._field_type
@property
def label(self) -> str:
"""Get the field label"""
return self._label
@property
def value(self) -> Any:
"""Get the current field value"""
return self._value
@value.setter
def value(self, value: Any):
"""Set the field value"""
self._value = value
@property
def required(self) -> bool:
"""Check if the field is required"""
return self._required
@property
def options(self) -> List[Tuple[str, str]]:
"""Get the field options"""
return self._options
@property
def form(self) -> Optional[Form]:
"""Get the form containing this field"""
return self._form
@form.setter
def form(self, form: Form):
"""Set the form containing this field"""
self._form = form