from __future__ import annotations from enum import Enum from typing import Callable, Dict, Any, Optional, Union, List, Tuple from pyWebLayout.core.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 @property def params(self) -> Dict[str, Any]: """Get the button parameters""" return self._params 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