# Copyright 2026 Daitum
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
UI element definitions for the UI Generator system.
This module provides the core building blocks for constructing user interfaces,
including base classes, containers, and interactive elements. All elements support
state management, data binding, and event handling.
Main Components
---------------
**Configuration Enums:**
Visual and behavioural configuration options for UI elements:
- ElementSize: Predefined size options (EXTRA_SMALL to THREE_EXTRA_LARGE)
- ActionType: Element behaviour (VALUE for data updates, EVENTS for triggers)
- TextVariant: Text presentation styles (SPAN for inline, PARAGRAPH for block)
- FontWeight: Text thickness (REGULAR, SEMI_BOLD, BOLD)
- BadgeVariant: Badge visual styles (DEFAULT, UPDATED)
- ContainerType: Layout models (FLEX, GRID)
- LayoutStyle: Card layout modes (LIST, GRID)
- GridOrder: Grid element arrangement (ROW, COLUMN)
- RowSpacing: Vertical spacing options (NONE, TIGHT, RELAXED)
- AlignContent: Alignment options (CENTER, SPACE_BETWEEN)
- LayoutDirection: List layout direction (ROW, COLUMN)
- OverflowStrategy: Overflow handling for list containers (CLIP, SCROLL, SUMMARISE)
**Base Classes:**
Foundation classes providing common functionality:
- BaseElement: Abstract base with state management (disabled, required, hidden, etc.)
- Element: Base for all UI elements with property mapping and sizing
- ElementContainer: Base for elements that contain child elements
**Container Elements:**
Layout components for grouping and organizing UI elements:
- Container: Flexible layout container with flex/grid display modes
- Card: Rich container with customizable layout, styling, and interaction
- Badge: Compact label for status or metadata display
- OverflowElement: Replacement element shown for overflowing items in a ListElement
**Interactive Elements:**
User input and interaction components:
- Button: Clickable button with text, icons, and event handling
- Checkbox: Standard checkbox with check/uncheck events
- IconCheckbox: Checkbox using custom icons for on/off states
- Slider: Toggle slider for boolean values or event triggers
- ReviewRating: Star rating or similar review component
**Display Elements:**
Components for presenting information:
- Text: Text display with styling, variants, and tooltips
- IconElement: Icon display with click handling and tooltips
- ListElement: Renders all rows of a table using a reusable element template
**Link Elements:**
Navigation components:
- ModelEditorLink: Hyperlink that opens the Daitum model editor
**Helper Functions:**
- get_boolean_variable(): Converts Field/Parameter/Calculation/bool to ModelVariable
- get_model_variable(): Converts model objects to ModelVariable descriptors
Element Hierarchy
-----------------
All UI elements inherit from BaseElement or Element::
BaseElement (ABC)
└── Element
├── ElementContainer
│ ├── Container
│ ├── Card
│ ├── Badge
│ └── OverflowElement
├── Button
├── Checkbox
├── IconElement
├── IconCheckbox
├── ListElement
├── ModelEditorLink
├── ReviewRating
├── Slider
└── Text
State Management
----------------
Elements support dynamic state configuration through BaseElement methods:
- add_conditional_disabled(): Make element non-interactive
- add_conditional_required(): Mark as required for form validation
- add_conditional_read_only(): Prevent editing
- add_conditional_error/warning/success/info(): Visual feedback states
- add_conditional_hidden(): Hide element (optionally reserving space)
Each state can be bound to a Field, Parameter, Calculation, or boolean value.
Data Binding
------------
Elements can bind to data sources using:
- Field: Direct table field reference
- Parameter: Named parameter value
- Calculation: Computed value
- ContextVariable: Runtime context variable
- TemplateBindingKey: Template-based dynamic binding
Event Handling
--------------
Interactive elements support ModelEvent binding for user actions:
- on_click: Button and icon click events
- on_check/on_uncheck: Checkbox state changes
- toggle_on/toggle_off: Slider state changes
Elements can operate in two action modes:
- ActionType.VALUE: Updates bound data source
- ActionType.EVENTS: Triggers events without updating values
Examples
--------
Creating a text element::
# Static text
label = Text(value="Hello World", font_weight=FontWeight.BOLD)
# Dynamic text bound to a field
user_name = Text(value=user_table.name_field, color="#333333")
Creating an interactive button::
save_button = Button(
text_value="Save",
background_color="#007bff",
on_click=save_event
)
Creating a checkbox with state management::
agree_checkbox = Checkbox(data_source=agreement_field)
agree_checkbox.add_conditional_required(True)
agree_checkbox.add_conditional_disabled(is_locked_field)
Creating a card container::
user_card = Card(
layout_style=LayoutStyle.GRID,
row_spacing=RowSpacing.RELAXED,
border_radius="8px"
)
user_card.add_element(Text(value="Name:"))
user_card.add_element(Text(value=user_table.name_field))
Creating elements with events::
# Checkbox that triggers events instead of updating values
notify_checkbox = Checkbox(data_source=notification_field)
notify_checkbox.set_on_event(enable_notifications_event)
notify_checkbox.set_off_event(disable_notifications_event)
Creating a list element::
# Render every row of employees_table using a card template.
# TemplateBindingKey declares a placeholder inside the template;
# add_template_field_mapping connects each placeholder to a real field.
name_key = TemplateBindingKey("name")
role_key = TemplateBindingKey("role")
template_card = Card(layout_style=LayoutStyle.LIST)
template_card.add_element(Text(value=name_key, font_weight=FontWeight.BOLD))
template_card.add_element(Text(value=role_key))
employee_list = ListElement(
data_source=employees_table,
template=template_card,
layout_direction=LayoutDirection.COLUMN,
)
employee_list.add_template_field_mapping(name_key, name_field)
employee_list.add_template_field_mapping(role_key, role_field)
Creating a list element with a summarise overflow::
overflow = OverflowElement()
overflow.add_element(Text(value="More items..."))
employee_list = ListElement(
data_source=employees_table,
template=template_card,
overflow_strategy=OverflowStrategy.SUMMARISE,
overflow_element=overflow, # Required when strategy is SUMMARISE
)
"""
from abc import ABC
from dataclasses import dataclass, field
from enum import Enum
from daitum_model import Calculation, DataType, Field, ObjectDataType, Parameter, Table
from daitum_model.named_values import NamedValue
from typeguard import typechecked
from daitum_ui._buildable import Buildable, json_type_info
from daitum_ui._data import (
ModelPermissionsCondition,
ModelVariable,
ModelVariableCondition,
ModelVariableType,
)
from daitum_ui.context_variable import ContextVariable
from daitum_ui.data import Condition
from daitum_ui.model_event import ModelEvent
from daitum_ui.styles import HorizontalAlignment, IconConfig
from daitum_ui.template_binding_key import TemplateBindingKey
from ._link_destination import ModelEditorLinkDestination
[docs]
class ElementSize(Enum):
"""
Represents predefined size options for UI elements.
"""
EXTRA_SMALL = "EXTRA_SMALL"
SMALL = "SMALL"
MEDIUM = "MEDIUM"
LARGE = "LARGE"
EXTRA_LARGE = "EXTRA_LARGE"
TWO_EXTRA_LARGE = "TWO_EXTRA_LARGE"
THREE_EXTRA_LARGE = "THREE_EXTRA_LARGE"
[docs]
class ActionType(Enum):
"""
Defines how an element should behave when interacted with.
Attributes:
VALUE:
Element updates a value.
EVENTS:
Element triggers associated events instead of—or in addition to—
updating a value.
"""
VALUE = "VALUE"
EVENTS = "EVENTS"
[docs]
class TextVariant(Enum):
"""
Defines how text should be semantically or visually presented.
Attributes:
SPAN:
Inline text (e.g., for labels or short phrases).
PARAGRAPH:
Block-level text intended to occupy its own paragraph.
"""
SPAN = "SPAN"
PARAGRAPH = "PARAGRAPH"
[docs]
class FontWeight(Enum):
"""
Represents the thickness or boldness of the text.
Attributes:
REGULAR:
Standard body-weight text.
SEMI_BOLD:
A slightly heavier weight, often used for emphasis.
BOLD:
Strong emphasis or headings.
"""
REGULAR = "REGULAR"
SEMI_BOLD = "SEMI_BOLD"
BOLD = "BOLD"
[docs]
class BadgeVariant(Enum):
"""
The display variant of the badge.
Attributes:
DEFAULT:
Standard visual appearance.
UPDATED:
Indicates a visually updated or emphasised state.
"""
DEFAULT = "DEFAULT"
UPDATED = "UPDATED"
[docs]
class ContainerType(Enum):
"""
The layout type for arranging child elements within a container.
Options:
FLEX:
Uses a flexible box layout model for responsive design.
GRID:
Uses a grid layout model for structured arrangement.
"""
FLEX = "flex"
GRID = "grid"
[docs]
class LayoutStyle(Enum):
"""
Defines the layout style used when rendering elements inside the card.
"""
LIST = "LIST"
GRID = "GRID"
[docs]
class GridOrder(Enum):
"""
Defines how elements are arranged in GRID layout mode.
ROW:
Every second element is placed in the second column.
COLUMN:
The first column is filled up to `row_count` items before placing
additional items in the second column.
"""
ROW = "ROW"
COLUMN = "COLUMN"
[docs]
class RowSpacing(Enum):
"""
Controls vertical spacing between elements within the card.
"""
NONE = "NONE"
TIGHT = "TIGHT"
RELAXED = "RELAXED"
[docs]
class AlignContent(Enum):
"""
Controls how elements are aligned within the card’s container.
"""
CENTER = "CENTER"
SPACE_BETWEEN = "SPACE_BETWEEN"
[docs]
class LayoutDirection(Enum):
"""
Defines the direction in which items are laid out.
ROW:
Items are laid out horizontally.
COLUMN:
Items are laid out vertically.
"""
ROW = "ROW"
COLUMN = "COLUMN"
[docs]
class OverflowStrategy(Enum):
"""
Defines how content that exceeds its container bounds should be handled.
Attributes:
CLIP: Truncates (clips) the overflowing content so that only the visible
portion within the container is shown.
SCROLL: Enables scrolling, allowing users to access overflowing content
via scroll interaction.
SUMMARISE: Condenses or summarises the overflowing content into a shorter
representation (e.g., "..." or aggregated view).
"""
CLIP = "CLIP"
SCROLL = "SCROLL"
SUMMARISE = "SUMMARISE"
[docs]
@dataclass
@typechecked
class ElementStates(Buildable):
"""Holds the conditional state flags for a UI element. Each flag is a list of
:class:`~daitum_ui.data.Condition` instances; the element enters that state when
any condition evaluates to true."""
is_disabled: list[Condition] | None = None
is_required: list[Condition] | None = None
is_read_only: list[Condition] | None = None
is_error: list[Condition] | None = None
is_warning: list[Condition] | None = None
is_success: list[Condition] | None = None
is_hidden: list[Condition] | None = None
is_info: list[Condition] | None = None
reserve_space: list[Condition] | None = None
[docs]
@typechecked
def get_boolean_variable(variable: Field | Parameter | Calculation | bool):
"""
Converts a boolean-like input into a ModelVariable representing a boolean
value within the model.
Parameters:
variable (Field | Parameter | Calculation | bool):
The source of the boolean value. Must resolve to DataType.BOOLEAN
when a Field, Parameter, or Calculation is provided.
Returns:
ModelVariable:
A model variable referencing the appropriate underlying boolean source.
Raises:
ValueError:
If the provided Field, Parameter, or Calculation does not represent
a boolean data type.
"""
if isinstance(variable, Field):
if variable.data_type != DataType.BOOLEAN:
raise ValueError(
f"Field {variable.id} is not a boolean field, cannot convert to " f"ModelVariable."
)
return ModelVariable(ModelVariableType.FIELD, variable.id)
if isinstance(variable, Parameter | Calculation):
if variable.to_data_type() != DataType.BOOLEAN:
raise ValueError(
f"Named value {variable.id} is not a boolean field, cannot convert to "
f"ModelVariable."
)
return ModelVariable(ModelVariableType.NAMED_VALUE, variable.id)
return ModelVariable(ModelVariableType.CONTEXT_VARIABLE, "TRUE" if variable else "FALSE")
[docs]
@typechecked
class BaseElement(ABC, Buildable):
"""
Base class for all UI elements that support state management.
This class provides helper methods for configuring an element's
interactive properties.
Attributes:
element_states (Optional[ElementStates]):
Holds state flags for the element. Created only when a state-setting method is invoked.
"""
def __init__(self):
self.element_states: ElementStates | None = None
[docs]
def add_conditional_disabled(self, is_disabled: Field | Parameter | Calculation | bool):
"""
Sets whether the element is disabled (non-interactive).
Args:
is_disabled (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should be disabled.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_disabled))
if self.element_states.is_disabled is None:
self.element_states.is_disabled = []
self.element_states.is_disabled.append(condition)
[docs]
def add_permission_disabled(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is disabled.
Args:
is_base_user (bool): If True, the element is disabled for base users
(non-advanced users). If False, the element is disabled for advanced users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_disabled is None:
self.element_states.is_disabled = []
self.element_states.is_disabled.append(condition)
[docs]
def add_conditional_required(self, is_required: Field | Parameter | Calculation | bool):
"""
Sets whether the element is required for valid form submission.
Args:
is_required (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element is required.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_required))
if self.element_states.is_required is None:
self.element_states.is_required = []
self.element_states.is_required.append(condition)
[docs]
def add_permission_required(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is required.
Args:
is_base_user (bool): If True, the element is required for base users
(non-advanced users). If False, the element is required for advanced users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_required is None:
self.element_states.is_required = []
self.element_states.is_required.append(condition)
[docs]
def add_conditional_read_only(self, is_read_only: Field | Parameter | Calculation | bool):
"""
Sets whether the element is read-only (not editable).
Args:
is_read_only (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should be read-only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_read_only))
if self.element_states.is_read_only is None:
self.element_states.is_read_only = []
self.element_states.is_read_only.append(condition)
[docs]
def add_permission_read_only(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is read-only.
Args:
is_base_user (bool): If True, the element is read-only for base users
(non-advanced users). If False, the element is read-only for advanced users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_read_only is None:
self.element_states.is_read_only = []
self.element_states.is_read_only.append(condition)
[docs]
def add_conditional_error(self, is_error: Field | Parameter | Calculation | bool):
"""
Sets whether the element is in an error state.
Args:
is_error (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should show an error state.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_error))
if self.element_states.is_error is None:
self.element_states.is_error = []
self.element_states.is_error.append(condition)
[docs]
def add_permission_error(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is in an error state.
Args:
is_base_user (bool): If True, the element shows an error state for base users
(non-advanced users). If False, the element shows an error state for advanced
users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_error is None:
self.element_states.is_error = []
self.element_states.is_error.append(condition)
[docs]
def add_conditional_warning(self, is_warning: Field | Parameter | Calculation | bool):
"""
Sets whether the element is in a warning state.
Args:
is_warning (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should show a warning state.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_warning))
if self.element_states.is_warning is None:
self.element_states.is_warning = []
self.element_states.is_warning.append(condition)
[docs]
def add_permission_warning(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is in a warning state.
Args:
is_base_user (bool): If True, the element shows a warning state for base users
(non-advanced users). If False, the element shows a warning state for advanced
users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_warning is None:
self.element_states.is_warning = []
self.element_states.is_warning.append(condition)
[docs]
def add_conditional_success(self, is_success: Field | Parameter | Calculation | bool):
"""
Sets whether the element is in a success state.
Args:
is_success (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should show a success state.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_success))
if self.element_states.is_success is None:
self.element_states.is_success = []
self.element_states.is_success.append(condition)
[docs]
def add_permission_success(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is in a success state.
Args:
is_base_user (bool): If True, the element shows a success state for base users
(non-advanced users). If False, the element shows a success state for advanced
users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_success is None:
self.element_states.is_success = []
self.element_states.is_success.append(condition)
[docs]
def add_conditional_hidden(self, is_hidden: Field | Parameter | Calculation | bool):
"""
Sets whether the element is hidden in the UI.
Args:
is_hidden (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should be hidden.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_hidden))
if self.element_states.is_hidden is None:
self.element_states.is_hidden = []
self.element_states.is_hidden.append(condition)
[docs]
def add_permission_hidden(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is hidden.
Args:
is_base_user (bool): If True, the element is hidden from base users
(non-advanced users). If False, the element is hidden from advanced users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_hidden is None:
self.element_states.is_hidden = []
self.element_states.is_hidden.append(condition)
[docs]
def add_conditional_info(self, is_info: Field | Parameter | Calculation | bool):
"""
Sets whether the element is in an info state.
Args:
is_info (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when the element should show an info state.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(is_info))
if self.element_states.is_info is None:
self.element_states.is_info = []
self.element_states.is_info.append(condition)
[docs]
def add_permission_info(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether the element is in an info state.
Args:
is_base_user (bool): If True, the element shows an info state for base users
(non-advanced users). If False, the element shows an info state for advanced
users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.is_info is None:
self.element_states.is_info = []
self.element_states.is_info.append(condition)
[docs]
def add_conditional_reserve_space(self, reserve_space: Field | Parameter | Calculation | bool):
"""
Sets whether space is reserved in the layout when the element is hidden.
Args:
reserve_space (Field | Parameter | Calculation | bool):
Condition that evaluates to ``True`` when space should be reserved.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelVariableCondition(model_variable=get_boolean_variable(reserve_space))
if self.element_states.reserve_space is None:
self.element_states.reserve_space = []
self.element_states.reserve_space.append(condition)
[docs]
def add_permission_reserve_space(self, is_base_user: bool):
"""
Adds a permission-based condition controlling whether space is reserved when the element
is hidden.
Args:
is_base_user (bool): If True, space is reserved for base users
(non-advanced users). If False, space is reserved for advanced users only.
"""
if self.element_states is None:
self.element_states = ElementStates()
condition = ModelPermissionsCondition(is_advanced_user=not is_base_user)
if self.element_states.reserve_space is None:
self.element_states.reserve_space = []
self.element_states.reserve_space.append(condition)
[docs]
@typechecked
class Element(BaseElement):
"""
Base class for all UI elements.
Attributes:
property_field_mapping:
Mapping between UI element properties and the underlying
model or data field names that supply their values.
hidden:
Indicates whether the element should be initially hidden.
size:
Optional hint for how large the UI element should appear.
"""
def __init__(self, hidden: bool | None = None, size: ElementSize | None = None):
super().__init__()
self.hidden = hidden
self.size = size
self.property_field_mapping: dict[str, str] = {}
[docs]
def add_property_field_mapping(
self, property_name: str, value: Field | Parameter | Calculation | TemplateBindingKey
) -> None:
"""
Adds a mapping from a UI property to a model field.
Args:
property_name (str): The UI element property key.
value (Field | Parameter | Calculation | TemplateBindingKey): The model/data name.
"""
self.property_field_mapping[property_name] = value.to_string()
[docs]
@dataclass
@typechecked
class ElementContainer(Element):
"""
A UI element that groups and contains one or more child UI elements.
Containers allow hierarchical UI layouts where multiple elements are
visually or logically grouped together.
Attributes:
elements:
The list of child UI elements contained within this container.
"""
elements: list[Element] = field(default_factory=list)
def __post_init__(self):
super().__init__()
[docs]
def add_element(self, element: Element) -> None:
"""
Adds a child UI element to the container.
Args:
element (Element):
The UI element to add as a child of this container.
"""
self.elements.append(element)
[docs]
@dataclass
@typechecked
@json_type_info("container")
class Container(ElementContainer):
"""
A layout container used to group UI elements together.
Attributes:
display:
CSS display property for this container, either flex or grid.
gap:
Spacing between child elements. Applies to both row and column gaps for flex-based
layouts.
horizontal_alignment:
Determines how child elements are aligned horizontally within the container.
"""
display: ContainerType = ContainerType.FLEX
gap: str | None = None
horizontal_alignment: HorizontalAlignment | None = None
[docs]
@dataclass
@typechecked
@json_type_info("card")
class Card(ElementContainer):
"""
A UI card element that displays its child elements in either a list layout
or a grid layout. Cards may have customizable styling, spacing, and
interaction behavior.
"""
# Layout configuration
layout_style: LayoutStyle | None = None
row_count: int | None = None
grid_ordering: GridOrder | None = None
column_ratio: str | None = None
row_spacing: RowSpacing | None = None
align_content: AlignContent | None = None
compact: bool | None = None
# Interaction
on_click: ModelEvent | None = None
on_click_key: TemplateBindingKey | None = None
# Styling
border_color: str | None = None
border_left: str | None = None
border_radius: str | None = None
background_color: str | None = None
left_accent: str | None = None
right_accent: str | None = None
hover_background_color: str | None = None
hover_border_color: str | None = None
column_background_color: str | None = None
padding: str | None = None
# Footer
footer: Element | None = None
[docs]
@dataclass
@typechecked
@json_type_info("badge")
class Badge(ElementContainer):
"""
A small visual label used to display short status or metadata.
Attributes:
variant:
Determines the visual style variant of the badge.
background_color:
CSS-compatible string specifying the badge's background color.
on_click:
Action to perform when the badge is clicked.
minimum_width:
Minimum width for the badge, expressed in CSS-compatible units.
"""
variant: BadgeVariant | None = None
background_color: str | None = None
on_click: ModelEvent | None = None
minimum_width: str | None = None
[docs]
@dataclass
@typechecked
@json_type_info("overflow")
class OverflowElement(ElementContainer):
"""
A container element that replaces overflowing items in a :class:`ListElement`
when its ``overflow_strategy`` is set to :attr:`OverflowStrategy.SUMMARISE`.
Instead of clipping or scrolling, the list renders this element once in place
of all items that did not fit within the container's bounds. Populate it with
any child elements to create a custom overflow summary — for example, a
:class:`Text` label such as "+5 more" or an icon paired with a count.
Example::
overflow = OverflowElement()
overflow.add_element(Text(value=overflow_count_field))
employee_list = ListElement(
data_source=employees_table,
template=template_card,
overflow_strategy=OverflowStrategy.SUMMARISE,
overflow_element=overflow, # Required when strategy is SUMMARISE
)
"""
_OVERFLOW_COUNT = "${overflowCount}"
@property
def overflow_count(self):
return self._OVERFLOW_COUNT
[docs]
@typechecked
def get_model_variable(
variable: Field | Parameter | Calculation | ContextVariable,
) -> ModelVariable | None:
"""
Converts a model-related object into a ModelVariable descriptor.
Parameters:
variable:
A Field, Parameter, Calculation, or ContextVariable instance
representing a model value used by UI elements.
Returns:
ModelVariable (Optional):
A standardized wrapper describing the variable type
(FIELD, NAMED_VALUE, or CONTEXT_VARIABLE) and its identifier.
Notes:
- Field → ModelVariableType.FIELD (uses field_id)
- Parameter or Calculation → ModelVariableType.NAMED_VALUE (uses named_value_id)
- ContextVariable → ModelVariableType.CONTEXT_VARIABLE (uses id)
"""
if isinstance(variable, Field):
return ModelVariable(ModelVariableType.FIELD, variable.id)
if isinstance(variable, Parameter | Calculation):
return ModelVariable(ModelVariableType.NAMED_VALUE, variable.id)
if isinstance(variable, ContextVariable):
return ModelVariable(ModelVariableType.CONTEXT_VARIABLE, variable.id)
return None
[docs]
@typechecked
@json_type_info("checkbox")
class Checkbox(Element):
"""
A standard checkbox UI element.
Attributes:
on_check:
Event triggered when the checkbox transitions to the checked state.
on_uncheck:
Event triggered when the checkbox transitions to the unchecked state.
checkbox_data_source_id:
Model variable that stores the current value of the checkbox.
on_click_key:
Optional interaction key used for analytics or routing click events.
action_type:
Determines the checkbox's behavior when interacted with, such as updating a value
or emitting a custom event.
"""
def __init__(self, data_source: Field | Parameter | Calculation | ContextVariable):
super().__init__()
self.checkbox_data_source_id = get_model_variable(data_source)
self.on_check: ModelEvent | None = None
self.on_uncheck: ModelEvent | None = None
self.action_type = ActionType.VALUE
self.on_click_key: TemplateBindingKey | None = None
[docs]
def set_on_event(self, event: ModelEvent) -> "Checkbox":
"""
Assigns an event to be fired when the checkbox transitions into the "check" state.
Parameters:
event (ModelEvent):
The event to trigger when the checkbox becomes checked.
Notes:
Setting an on-event automatically changes the action_type
to ActionType.EVENTS, indicating the checkbox now emits events rather
than updating a value.
"""
self.on_check = event
self.action_type = ActionType.EVENTS
return self
[docs]
def set_off_event(self, event: ModelEvent) -> "Checkbox":
"""
Assigns an event to be fired when the checkbox transitions into the "uncheck" state.
Parameters:
event (ModelEvent):
The event to trigger when the checkbox becomes unchecked.
Notes:
Setting an off-event automatically changes the action_type
to ActionType.EVENTS, indicating the checkbox now emits events rather
than updating a value.
"""
self.on_uncheck = event
self.action_type = ActionType.EVENTS
return self
[docs]
@dataclass
@typechecked
@json_type_info("icon")
class IconElement(Element):
"""
Represents an icon UI element.
Attributes:
icon_source:
The icon to display. The string uses the DaitumIcon format, which is a string of the
form "iconFamily.ICON".
color:
The CSS-compatible colour to render the icon.
on_click:
Event triggered when the icon is clicked.
tooltip:
Optional tooltip displayed when the user hovers over the icon.
"""
def __post_init__(self):
super().__init__()
icon_source: str | None = None
color: str | None = None
on_click: ModelEvent | None = None
tooltip: str | None = None
[docs]
@typechecked
@json_type_info("iconCheckbox")
class IconCheckbox(Element):
"""
A checkbox UI element that uses icons to represent checked and unchecked states.
Attributes:
on_check:
Event triggered when the checkbox transitions to the checked state.
on_uncheck:
Event triggered when the checkbox transitions to the unchecked state.
icon_checkbox_data_source_id:
Model variable storing the current checkbox value.
on_click_key:
Optional key used to route click-based interactions such as analytics.
action_type:
Specifies how the checkbox should behave when interacted with,
such as updating a value or emitting an event.
off_icon:
Icon displayed when the checkbox is unchecked.
on_icon:
Icon displayed when the checkbox is checked.
"""
def __init__(
self,
data_source: Field | Parameter | Calculation | ContextVariable,
off_icon: IconConfig | None = None,
on_icon: IconConfig | None = None,
):
super().__init__()
self.icon_checkbox_data_source_id = get_model_variable(data_source)
self.on_check: ModelEvent | None = None
self.on_uncheck: ModelEvent | None = None
self.action_type = ActionType.VALUE
self.on_click_key: TemplateBindingKey | None = None
self.off_icon = off_icon
self.on_icon = on_icon
[docs]
def set_on_event(self, event: ModelEvent) -> "IconCheckbox":
"""
Assigns an event to be fired when the icon checkbox transitions into the "on" state.
Parameters:
event (ModelEvent):
The event to trigger when the icon checkbox turns on.
Notes:
Setting an on-event automatically changes the action_type
to ActionType.EVENTS, indicating the checkbox now emits events rather
than updating a value.
"""
self.on_check = event
self.action_type = ActionType.EVENTS
return self
[docs]
def set_off_event(self, event: ModelEvent) -> "IconCheckbox":
"""
Assigns an event to be fired when the icon checkbox transitions into the "off" state.
Parameters:
event (ModelEvent):
The event to trigger when the icon checkbox turns off.
Notes:
Setting an off-event automatically changes the action_type
to ActionType.EVENTS, indicating the checkbox now emits events rather
than updating a value.
"""
self.on_uncheck = event
self.action_type = ActionType.EVENTS
return self
[docs]
@typechecked
@json_type_info("reviewRating")
class ReviewRating(Element):
"""
A UI element representing a review rating component, consisting of filled and empty icons.
It supports binding to a model variable and tracking rating state.
Attributes:
fill_icon:
Icon configuration used to display the "filled" (selected) rating value.
empty_icon:
Icon configuration used to display the "empty" (unselected) rating value.
review_rating_data_source_id:
Model variable storing the current rating value (e.g., 1–5 stars).
fill_color:
Optional color override for the filled icons.
"""
def __init__(
self,
data_source: Field | Parameter | Calculation | ContextVariable,
):
super().__init__()
self.review_rating_data_source_id = get_model_variable(data_source)
self.fill_icon: IconConfig | None = None
self.empty_icon: IconConfig | None = None
self.fill_color: str | None = None
[docs]
def set_fill_icon(self, icon: IconConfig) -> "ReviewRating":
"""Sets the icon used for filled (selected) rating values."""
self.fill_icon = icon
return self
[docs]
def set_empty_icon(self, icon: IconConfig) -> "ReviewRating":
"""Sets the icon used for empty (unselected) rating values."""
self.empty_icon = icon
return self
[docs]
def set_fill_color(self, color: str) -> "ReviewRating":
"""Sets the colour override for filled rating icons."""
self.fill_color = color
return self
[docs]
@typechecked
@json_type_info("slider")
class Slider(Element):
"""
A slider UI element used for selecting values or triggering events.
Attributes:
toggle_on:
Event triggered when the slider moves into an "on" state.
toggle_off:
Event triggered when the slider moves into an "off" state.
slider_data_source_id:
Reference to a model variable that stores the slider's current value.
on_click_key:
An optional interaction key used to associate click behaviour
(e.g., analytics or event routing).
action_type:
Determines whether the slider updates a value or emits events
when interacted with.
"""
def __init__(self, data_source: Field | Parameter | Calculation | ContextVariable):
super().__init__()
self.slider_data_source_id = get_model_variable(data_source)
self.toggle_on: ModelEvent | None = None
self.toggle_off: ModelEvent | None = None
self.action_type = ActionType.VALUE
self.on_click_key: TemplateBindingKey | None = None
[docs]
def set_on_event(self, event: ModelEvent) -> "Slider":
"""
Assigns an event to be fired when the slider transitions into the "on" state.
Parameters:
event (ModelEvent):
The event to trigger when the slider switches from off to on.
Notes:
Setting an on-event automatically changes the slider's action_type
to ActionType.EVENTS, indicating the slider now emits events rather
than updating a value.
"""
self.toggle_on = event
self.action_type = ActionType.EVENTS
return self
[docs]
def set_off_event(self, event: ModelEvent) -> "Slider":
"""
Assigns an event to be fired when the slider transitions into the "off" state.
Parameters:
event (ModelEvent):
The event to trigger when the slider switches from on to off.
Notes:
Setting an off-event automatically changes the slider's action_type
to ActionType.EVENTS, indicating the slider now emits events rather
than updating a value.
"""
self.toggle_off = event
self.action_type = ActionType.EVENTS
return self
[docs]
@typechecked
@json_type_info("text")
class Text(Element):
"""
A text UI element used for displaying static or dynamic textual content.
Attributes:
value:
The string content to be displayed.
color:
Optional CSS-compatible colour for the text.
font_weight:
The thickness of the displayed text.
variant:
The presentation style of the text (inline or paragraph).
show_tool_tip:
Whether the text should display a tooltip containing its full value. Useful when the
displayed text is too long and may be truncated, but the user still needs access to
the complete, non-truncated content.
"""
def __init__(
self,
value: str | Parameter | Calculation | Field | TemplateBindingKey,
color: str | None = None,
font_weight: FontWeight | None = None,
variant: TextVariant | None = None,
show_tool_tip: bool = False,
):
super().__init__()
if isinstance(value, str):
self.value = value
elif value:
self.value = f"${{{value.to_string()}}}"
self.color = color
self.font_weight = font_weight
self.variant = variant
self.show_tool_tip = show_tool_tip
[docs]
def set_color(self, color: str) -> "Text":
"""Sets the CSS colour for this text element."""
self.color = color
return self
[docs]
def set_font_weight(self, weight: FontWeight) -> "Text":
"""Sets the font weight for this text element."""
self.font_weight = weight
return self
[docs]
def set_variant(self, variant: TextVariant) -> "Text":
"""Sets the presentation variant (inline or paragraph) for this text element."""
self.variant = variant
return self
[docs]
@typechecked
@json_type_info("list")
class ListElement(Element):
"""
A display element that renders every row of a data table using a reusable
element template.
For each row in the source table, the template is instantiated and its
placeholder values are resolved via field bindings. Placeholders are
declared inside the template using :class:`TemplateBindingKey`; bindings
are registered with :meth:`add_template_field_mapping`, which maps each
placeholder key to the corresponding field in the source table.
Attributes:
data_source (ModelVariable | None):
Model variable referencing the data source. Set when a Field or
NamedValue is provided; ``None`` when a Table is provided directly.
source_table (str | None):
ID of the source table. Set when a Table is provided; ``None``
when a Field or NamedValue is provided instead.
template (Optional[Element]):
The element used as a template for each row.
field_bindings (dict[str, str]):
Resolved mapping from placeholder names to source table field IDs.
Populated by :meth:`add_template_field_mapping`; do not set directly.
layout_direction (LayoutDirection):
The direction in which rendered items are arranged.
``COLUMN`` stacks items vertically (default);
``ROW`` places them horizontally.
wrap (Optional[bool]):
When ``True``, items wrap onto new lines when the container width
is exceeded. When ``False`` or ``None``, items are clipped or
scrollable depending on the container.
overflow_strategy (OverflowStrategy):
Defines strategies for handling content that exceeds the bounds of
its container.
width (Optional[str]):
Defines width of the list element.
height (Optional[str]):
Defines height of the list element.
overflow_element (Optional[OverflowElement]):
Element rendered in place of overflowing items when
``overflow_strategy`` is :attr:`OverflowStrategy.SUMMARISE`. Required
when strategy is ``SUMMARISE``; may also be supplied via
:meth:`set_overflow_element`.
row_gap (Optional[str]):
Vertical spacing between rows.
column_gap (Optional[str]):
Horizontal spacing between columns.
"""
def __init__(
self,
data_source: Table | Field | NamedValue,
template: Element,
layout_direction: LayoutDirection = LayoutDirection.COLUMN,
overflow_strategy: OverflowStrategy = OverflowStrategy.SCROLL,
overflow_element: "OverflowElement | None" = None,
):
super().__init__()
if isinstance(data_source, NamedValue | Field):
datatype = data_source.to_data_type()
if not (isinstance(datatype, ObjectDataType) and datatype.is_array()):
raise ValueError(f"Data source must be an object-array type, got {datatype}")
self.data_source: ModelVariable | None = None
self.source_table: str | None = None
if isinstance(data_source, Field | NamedValue):
self.data_source = get_model_variable(data_source)
elif isinstance(data_source, Table):
self.source_table = data_source.id
if overflow_strategy == OverflowStrategy.SUMMARISE and overflow_element is None:
raise ValueError("overflow_element is required when overflow_strategy is SUMMARISE.")
self.template = template
self.field_bindings: dict[str, str] = {}
self.layout_direction = layout_direction
self.wrap: bool = False
self.overflow_strategy = overflow_strategy
self.height: str | None = None
self.width: str | None = None
self.overflow_element: OverflowElement | None = overflow_element
self.row_gap: str | None = None
self.column_gap: str | None = None
[docs]
def set_wrap(self, wrap: bool) -> "ListElement":
"""Sets whether items wrap onto multiple lines."""
self.wrap = wrap
return self
[docs]
def set_size(self, height: str | None = None, width: str | None = None) -> "ListElement":
"""Sets the height and/or width of the list element."""
self.height = height
self.width = width
return self
[docs]
def set_overflow_element(self, overflow_element: OverflowElement) -> "ListElement":
"""Sets the element rendered in place of overflowing items when strategy is SUMMARISE."""
if self.overflow_strategy != OverflowStrategy.SUMMARISE:
raise ValueError(
"overflow_element is only applicable when overflow_strategy is SUMMARISE."
)
self.overflow_element = overflow_element
return self
[docs]
def set_gap(self, gap: str | None = None, cross_axis_gap: str | None = None) -> "ListElement":
"""
Sets the gap between items in the list.
Args:
gap: Spacing along the main axis direction.
cross_axis_gap: Spacing along the cross axis direction.
"""
if gap is not None or cross_axis_gap is not None:
if self.layout_direction == LayoutDirection.ROW:
self.column_gap = gap
self.row_gap = cross_axis_gap
else:
self.row_gap = gap
self.column_gap = cross_axis_gap
return self
[docs]
def add_template_field_mapping(self, key: TemplateBindingKey, field: Field) -> None:
"""
Registers a binding between a template placeholder and a source table field.
Each call adds one entry to ``field_bindings``. The placeholder declared
by ``key`` inside the template will be substituted with the value of
``field`` for each rendered row.
Args:
key (TemplateBindingKey):
The placeholder key referenced inside the template via
:class:`TemplateBindingKey`. Must match exactly the key used
when constructing the template elements.
field (Field):
The source table field whose value replaces the placeholder
at render time.
Example::
name_key = TemplateBindingKey("name")
employee_list.add_template_field_mapping(name_key, name_field)
"""
self.field_bindings[key.to_string()] = field.to_string()
[docs]
@typechecked
@json_type_info("modelEditorLink")
class ModelEditorLink(Element):
"""
A hyperlink element that opens the Daitum model editor.
Renders as a clickable text link. The model and scenario to open can be
bound to ``Field``, ``Parameter``, ``Calculation``, or ``ContextVariable``
values so they are resolved at runtime.
Attributes:
text_value (str):
The visible label of the link. Accepts a static string or a
model-variable reference rendered as ``${...}``. Defaults to
``"Open Model"``.
destination (ModelEditorLinkDestination):
The model-editor destination built from the provided ``model_id``,
``scenario_id``, and ``open_new_tab`` arguments.
"""
def __init__(
self,
model_id: Field | Parameter | Calculation | ContextVariable,
scenario_id: Field | Parameter | Calculation | ContextVariable | None = None,
text_value: str | Parameter | Calculation | Field | TemplateBindingKey = "Open Model",
open_new_tab: bool = False,
):
super().__init__()
if isinstance(text_value, str):
self.text_value: str = text_value
else:
self.text_value = f"${{{text_value.to_string()}}}"
self.destination = ModelEditorLinkDestination(model_id, scenario_id, open_new_tab)