305 lines
9.1 KiB
Python
305 lines
9.1 KiB
Python
"""Table components."""
|
|
from typing import List, Optional, Tuple
|
|
|
|
from reflex.components.chakra import ChakraComponent
|
|
from reflex.components.component import Component
|
|
from reflex.components.core.foreach import Foreach
|
|
from reflex.utils import types
|
|
from reflex.vars import Var
|
|
|
|
|
|
class Table(ChakraComponent):
|
|
"""A table component."""
|
|
|
|
tag: str = "Table"
|
|
|
|
# The color scheme of the table
|
|
color_scheme: Optional[Var[str]] = None
|
|
|
|
# The variant of the table style to use
|
|
variant: Optional[Var[str]] = None
|
|
|
|
# The size of the table
|
|
size: Optional[Var[str]] = None
|
|
|
|
# The placement of the table caption.
|
|
placement: Optional[Var[str]] = None
|
|
|
|
@classmethod
|
|
def create(
|
|
cls, *children, caption=None, headers=None, rows=None, footers=None, **props
|
|
) -> Component:
|
|
"""Create a table component.
|
|
|
|
Args:
|
|
*children: The children of the component.
|
|
caption: The caption of the table component.
|
|
headers: The headers of the table component.
|
|
rows: The rows of the table component.
|
|
footers: The footers of the table component.
|
|
**props: The properties of the component.
|
|
|
|
Returns:
|
|
The table component.
|
|
"""
|
|
if len(children) == 0:
|
|
children = []
|
|
|
|
if caption is not None:
|
|
children.append(TableCaption.create(caption))
|
|
|
|
if headers is not None:
|
|
children.append(Thead.create(headers=headers))
|
|
|
|
if rows is not None:
|
|
children.append(Tbody.create(rows=rows))
|
|
|
|
if footers is not None:
|
|
children.append(Tfoot.create(footers=footers))
|
|
return super().create(*children, **props)
|
|
|
|
|
|
class Thead(ChakraComponent):
|
|
"""A table header component."""
|
|
|
|
tag: str = "Thead"
|
|
|
|
# invalid children components
|
|
_invalid_children: List[str] = ["Tbody", "Thead", "Tfoot"]
|
|
|
|
@classmethod
|
|
def create(cls, *children, headers=None, **props) -> Component:
|
|
"""Create a table header component.
|
|
|
|
Args:
|
|
*children: The children of the component.
|
|
headers (list, optional): List of headers. Defaults to None.
|
|
**props: The properties of the component.
|
|
|
|
Returns:
|
|
The table header component.
|
|
|
|
"""
|
|
if len(children) == 0:
|
|
cls.validate_headers(headers)
|
|
|
|
children = [Tr.create(cell_type="header", cells=headers)]
|
|
return super().create(*children, **props)
|
|
|
|
@staticmethod
|
|
def validate_headers(headers):
|
|
"""Type checking for table headers.
|
|
|
|
Args:
|
|
headers: The table headers.
|
|
|
|
Raises:
|
|
TypeError: If headers are not of type list or type tuple.
|
|
|
|
"""
|
|
allowed_types = (list, tuple)
|
|
if (
|
|
(
|
|
isinstance(headers, Var)
|
|
and not types.check_type_in_allowed_types(
|
|
headers._var_type, allowed_types
|
|
)
|
|
)
|
|
or not isinstance(headers, Var)
|
|
and not types.check_type_in_allowed_types(type(headers), allowed_types)
|
|
):
|
|
raise TypeError("table headers should be a list or tuple")
|
|
|
|
|
|
class Tbody(ChakraComponent):
|
|
"""A table body component."""
|
|
|
|
tag: str = "Tbody"
|
|
|
|
# invalid children components
|
|
_invalid_children: List[str] = ["Tbody", "Thead", "Tfoot", "Td", "Th"]
|
|
|
|
@classmethod
|
|
def create(cls, *children, rows=None, **props) -> Component:
|
|
"""Create a table body component.
|
|
|
|
Args:
|
|
*children: The children of the component.
|
|
rows (list[list], optional): The rows of the table body. Defaults to None.
|
|
**props: The properties of the component.
|
|
|
|
Returns:
|
|
Component: The table body component
|
|
"""
|
|
if len(children) == 0:
|
|
cls.validate_rows(rows) if rows is not None else None
|
|
|
|
if isinstance(rows, Var):
|
|
children = [
|
|
Foreach.create(
|
|
rows, lambda row: Tr.create(cell_type="data", cells=row)
|
|
)
|
|
]
|
|
else:
|
|
children = [
|
|
Tr.create(cell_type="data", cells=row) for row in rows or []
|
|
]
|
|
return super().create(*children, **props)
|
|
|
|
@staticmethod
|
|
def validate_rows(rows):
|
|
"""Type checking for table rows.
|
|
|
|
Args:
|
|
rows: Table rows.
|
|
|
|
Raises:
|
|
TypeError: If rows are not lists or tuples containing inner lists or tuples.
|
|
"""
|
|
allowed_subclasses = (List, Tuple)
|
|
if isinstance(rows, Var):
|
|
outer_type = rows._var_type
|
|
inner_type = (
|
|
outer_type.__args__[0] if hasattr(outer_type, "__args__") else None
|
|
)
|
|
|
|
# check that the outer container and inner container types are lists or tuples.
|
|
if not (
|
|
types._issubclass(types.get_base_class(outer_type), allowed_subclasses)
|
|
and (
|
|
inner_type is None
|
|
or types._issubclass(
|
|
types.get_base_class(inner_type), allowed_subclasses
|
|
)
|
|
)
|
|
):
|
|
raise TypeError(
|
|
f"table rows should be a list or tuple containing inner lists or tuples. Got {outer_type} instead"
|
|
)
|
|
elif not (
|
|
types._issubclass(type(rows), allowed_subclasses)
|
|
and (not rows or types._issubclass(type(rows[0]), allowed_subclasses))
|
|
):
|
|
raise TypeError(
|
|
"table rows should be a list or tuple containing inner lists or tuples."
|
|
)
|
|
|
|
|
|
class Tfoot(ChakraComponent):
|
|
"""A table footer component."""
|
|
|
|
tag: str = "Tfoot"
|
|
|
|
# invalid children components
|
|
_invalid_children: List[str] = ["Tbody", "Thead", "Td", "Th", "Tfoot"]
|
|
|
|
@classmethod
|
|
def create(cls, *children, footers=None, **props) -> Component:
|
|
"""Create a table footer component.
|
|
|
|
Args:
|
|
*children: The children of the component.
|
|
footers (list, optional): List of footers. Defaults to None.
|
|
**props: The properties of the component.
|
|
|
|
Returns:
|
|
The table footer component.
|
|
"""
|
|
if len(children) == 0:
|
|
cls.validate_footers(footers)
|
|
children = [Tr.create(cell_type="header", cells=footers)]
|
|
return super().create(*children, **props)
|
|
|
|
@staticmethod
|
|
def validate_footers(footers):
|
|
"""Type checking for table footers.
|
|
|
|
Args:
|
|
footers: Table rows.
|
|
|
|
Raises:
|
|
TypeError: If footers are not of type list.
|
|
"""
|
|
allowed_types = (list, tuple)
|
|
if (
|
|
(
|
|
isinstance(footers, Var)
|
|
and not types.check_type_in_allowed_types(
|
|
footers._var_type, allowed_types
|
|
)
|
|
)
|
|
or not isinstance(footers, Var)
|
|
and not types.check_type_in_allowed_types(type(footers), allowed_types)
|
|
):
|
|
raise TypeError("table headers should be a list or tuple")
|
|
|
|
|
|
class Tr(ChakraComponent):
|
|
"""A table row component."""
|
|
|
|
tag: str = "Tr"
|
|
|
|
# invalid children components
|
|
_invalid_children: List[str] = ["Tbody", "Thead", "Tfoot", "Tr"]
|
|
|
|
@classmethod
|
|
def create(cls, *children, cell_type: str = "", cells=None, **props) -> Component:
|
|
"""Create a table row component.
|
|
|
|
Args:
|
|
*children: The children of the component.
|
|
cell_type: the type of cells in this table row. "header" or "data". Defaults to None.
|
|
cells: The cells value to add in the table row. Defaults to None.
|
|
**props: The properties of the component.
|
|
|
|
Returns:
|
|
The table row component
|
|
"""
|
|
types = {"header": Th, "data": Td}
|
|
cell_cls = types.get(cell_type)
|
|
if len(children) == 0 and cell_cls:
|
|
if isinstance(cells, Var):
|
|
children = [Foreach.create(cells, cell_cls.create)]
|
|
else:
|
|
children = [cell_cls.create(cell) for cell in cells or []]
|
|
return super().create(*children, **props)
|
|
|
|
|
|
class Th(ChakraComponent):
|
|
"""A table header cell component."""
|
|
|
|
tag: str = "Th"
|
|
|
|
# invalid children components
|
|
_invalid_children: List[str] = ["Tbody", "Thead", "Tr", "Td", "Th"]
|
|
|
|
# Aligns the cell content to the right.
|
|
is_numeric: Optional[Var[bool]] = None
|
|
|
|
|
|
class Td(ChakraComponent):
|
|
"""A table data cell component."""
|
|
|
|
tag: str = "Td"
|
|
|
|
# invalid children components
|
|
_invalid_children: List[str] = ["Tbody", "Thead"]
|
|
|
|
# Aligns the cell content to the right.
|
|
is_numeric: Optional[Var[bool]] = None
|
|
|
|
|
|
class TableCaption(ChakraComponent):
|
|
"""A table caption component."""
|
|
|
|
tag: str = "TableCaption"
|
|
|
|
# The placement of the table caption. This sets the `caption-side` CSS attribute.
|
|
placement: Optional[Var[str]] = None
|
|
|
|
|
|
class TableContainer(ChakraComponent):
|
|
"""The table container component renders a div that wraps the table component."""
|
|
|
|
tag: str = "TableContainer"
|