Provide high-level API for most components (#451)

This commit is contained in:
Thomas Brandého 2023-02-06 01:02:21 +01:00 committed by GitHub
parent d709ab9e03
commit 0b2f1369a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 586 additions and 24 deletions

View File

@ -19,6 +19,24 @@ class List(ChakraComponent):
# Shorthand prop for listStyleType
style_type: Var[str]
@classmethod
def create(cls, *children, items=None, **props) -> Component:
"""Create a list component.
Args:
children: The children of the component.
items: A list of items to add to the list.
props: The properties of the component.
Returns:
The list component.
"""
if len(children) == 0:
children = []
for item in items or []:
children.append(ListItem.create(*item))
return super().create(*children, **props)
class ListItem(ChakraComponent):
"""A single list item."""
@ -26,13 +44,13 @@ class ListItem(ChakraComponent):
tag = "ListItem"
class OrderedList(ChakraComponent):
class OrderedList(List):
"""An ordered list component with numbers."""
tag = "OrderedList"
class UnorderedList(ChakraComponent):
class UnorderedList(List):
"""An unordered list component with bullets."""
tag = "UnorderedList"

View File

@ -1,5 +1,6 @@
"""Statistics components."""
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -9,6 +10,42 @@ class Stat(ChakraComponent):
tag = "Stat"
@classmethod
def create(
cls, *children, label=None, number=0, help_text=None, arrow_type=None, **props
) -> Component:
"""Create a stat component.
Args:
children: The children of the component.
label: A label for the stat component.
number: The value of the stat component.
help_text: A text added to the stat component.
arrow_type: The type of the arrow ("increase", "decrease", None)
props: The properties of the component.
Returns:
The stat component.
"""
if len(children) == 0:
children = []
if label:
children.append(StatLabel.create(label))
children.append(StatNumber.create(number))
if help_text:
if arrow_type:
children.append(
StatHelpText.create(
help_text, StatArrow.create(type_=arrow_type)
)
)
else:
children.append(StatHelpText.create(help_text))
return super().create(*children, **props)
class StatLabel(ChakraComponent):
"""A stat label component."""

View File

@ -1,6 +1,7 @@
"""Table components."""
from typing import List
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -23,30 +24,130 @@ class Table(ChakraComponent):
# The placement of the table caption.
placement: Var[str]
@classmethod
def create(
cls, *children, caption=None, headers=None, rows=None, footers=None, **props
) -> Component:
"""Create a table component.
class Thead(Table):
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:
children.append(TableCaption.create(caption))
if headers:
children.append(Thead.create(headers=headers))
if rows:
children.append(Tbody.create(rows=rows))
if footers:
children.append(Tfoot.create(footers=footers))
return super().create(*children, **props)
class Thead(ChakraComponent):
"""A table header component."""
tag = "Thead"
@classmethod
def create(cls, *children, headers=None, **props) -> Component:
"""Create a table header component.
class Tbody(Table):
Args:
children: The children of the component.
props: The properties of the component.
headers (list, optional): List of headers. Defaults to None.
Returns:
The table header component.
"""
if len(children) == 0:
children = [Tr.create(cell_type="header", cells=headers)]
return super().create(*children, **props)
class Tbody(ChakraComponent):
"""A table body component."""
tag = "Tbody"
@classmethod
def create(cls, *children, rows=None, **props) -> Component:
"""Create a table body component.
class Tfoot(Table):
Args:
children: The children of the component.
props: The properties of the component.
rows (list[list], optional): The rows of the table body. Defaults to None.
Returns:
Component: _description_
"""
if len(children) == 0:
children = [Tr.create(cell_type="data", cells=row) for row in rows or []]
return super().create(*children, **props)
class Tfoot(ChakraComponent):
"""A table footer component."""
tag = "Tfoot"
@classmethod
def create(cls, *children, footers=None, **props) -> Component:
"""Create a table footer component.
class Tr(Table):
Args:
children: The children of the component.
props: The properties of the component.
footers (list, optional): List of footers. Defaults to None.
Returns:
The table footer component.
"""
if len(children) == 0:
children = [Tr.create(cell_type="header", cells=footers)]
return super().create(*children, **props)
class Tr(ChakraComponent):
"""A table row component."""
tag = "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.
props: The properties of the component.
cell_type (str): the type of cells in this table row. "header" or "data". Defaults to None.
cells (list, optional): The cells value to add in the table row. Defaults to None.
Returns:
The table row component
"""
types = {"header": Th, "data": Td}
cell_cls = types.get(cell_type)
if len(children) == 0 and cell_cls:
children = [cell_cls.create(cell) for cell in cells or []]
return super().create(*children, **props)
class Th(ChakraComponent):
"""A table header cell component."""

View File

@ -1,6 +1,7 @@
"""Container to stack elements with spacing."""
from typing import List, Optional, Union
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -26,6 +27,39 @@ class Accordion(ChakraComponent):
# If true, height animation and transitions will be disabled.
reduce_motion: Var[bool]
@classmethod
def create(cls, *children, items=None, icon_pos="right", **props) -> Component:
"""Create an accordion component.
Args:
children: The children of the component.
items: The items of the accordion component: list of tuples (label,panel)
icon_pos: The position of the arrow icon of the accordion. "right", "left" or None
props: The properties of the component.
Returns:
The accordion component
"""
if len(children) == 0:
children = []
if not items:
items = []
for label, panel in items:
if icon_pos == "right":
button = AccordionButton.create(label, AccordionIcon.create())
elif icon_pos == "left":
button = AccordionButton.create(AccordionIcon.create(), label)
else:
button = AccordionButton.create(label)
children.append(
AccordionItem.create(
button,
AccordionPanel.create(panel),
)
)
return super().create(*children, **props)
class AccordionItem(ChakraComponent):
"""A single accordion item."""

View File

@ -1,5 +1,6 @@
"""Tab components."""
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -33,6 +34,29 @@ class Tabs(ChakraComponent):
# "line" | "enclosed" | "enclosed-colored" | "soft-rounded" | "solid-rounded" | "unstyled"
variant: Var[str]
@classmethod
def create(cls, *children, items=None, **props) -> Component:
"""Create a tab component.
Args:
children: The children of the component.
items: The items for the tabs component, a list of tuple (label, panel)
props: The properties of the component.
Returns:
The tab component
"""
if len(children) == 0:
tabs = []
panels = []
if not items:
items = []
for label, panel in items:
tabs.append(Tab.create(label))
panels.append(TabPanel.create(panel))
children = [TabList.create(*tabs), TabPanels.create(*panels)]
return super().create(*children, **props)
class Tab(ChakraComponent):
"""An element that serves as a label for one of the tab panels and can be activated to display that panel.."""

View File

@ -1,5 +1,6 @@
"""Alert components."""
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -15,6 +16,35 @@ class Alert(ChakraComponent):
# "subtle" | "left-accent" | "top-accent" | "solid"
variant: Var[str]
@classmethod
def create(
cls, *children, icon=True, title="Alert title", desc=None, **props
) -> Component:
"""Create an alert component.
Args:
children: The children of the component.
icon (bool): The icon of the alert.
title (str): The title of the alert.
desc (str): The description of the alert
props: The properties of the component.
Returns:
The alert component.
"""
if len(children) == 0:
contents = []
if icon:
contents.append(AlertIcon.create())
contents.append(AlertTitle.create(title))
if desc:
contents.append(AlertDescription.create(desc))
return super().create(*children, **props)
class AlertIcon(ChakraComponent):
"""An icon displayed in the alert."""

View File

@ -1,5 +1,6 @@
"""Form components."""
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -24,6 +25,50 @@ class FormControl(ChakraComponent):
# The label text used to inform users as to what information is requested for a text field.
label: Var[str]
@classmethod
def create(
cls,
*children,
label=None,
input=None,
help_text=None,
error_message=None,
**props
) -> Component:
"""Create a form control component.
Args:
children: The children of the form control.
label: The label of the form control.
input: The input of the form control.
help_text: The help text of the form control.
error_message: The error message of the form control.
props: The properties of the form control.
Raises:
AttributeError: raise an error if missing required kwargs.
Returns:
The form control component.
"""
if len(children) == 0:
children = []
if label:
children.append(FormLabel.create(*label))
if not input:
raise AttributeError("input keyword argument is required")
children.append(input)
if help_text:
children.append(FormHelperText.create(*help_text))
if error_message:
children.append(FormErrorMessage.create(*error_message))
return super().create(*children, **props)
class FormHelperText(ChakraComponent):
"""A form helper text component."""

View File

@ -68,7 +68,7 @@ class RangeSlider(ChakraComponent):
Returns:
The RangeSlider component.
"""
if not children:
if len(children) == 0:
children = [
RangeSliderTrack.create(
RangeSliderFilledTrack.create(),

View File

@ -68,7 +68,7 @@ class Slider(ChakraComponent):
Returns:
The slider component.
"""
if not children:
if len(children) == 0:
children = [
SliderTrack.create(
SliderFilledTrack.create(),

View File

@ -1,5 +1,6 @@
"""Container to stack elements with spacing."""
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -30,6 +31,25 @@ class Wrap(ChakraComponent):
# The vertical spacing between the items.
spacing_y: Var[str]
@classmethod
def create(cls, *children, items=None, **props) -> Component:
"""Return a wrap component.
Args:
children: The children of the component.
items (list): The items of the wrap component.
props: The properties of the component.
Returns:
The wrap component.
"""
if len(children) == 0:
children = []
for item in items or []:
children.append(WrapItem.create(*item))
return super().create(*children, **props)
class WrapItem(ChakraComponent):
"""Item of the Wrap component."""

View File

@ -1,5 +1,6 @@
"""Breadcrumb components."""
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -15,6 +16,28 @@ class Breadcrumb(ChakraComponent):
# The left and right margin applied to the separator
separator_margin: Var[str]
@classmethod
def create(cls, *children, items=None, **props) -> Component:
"""Create a breadcrumb component.
If the kw-args `items` is provided and is a list, they will be added as children.
Args:
children: The children of the component.
items (list): The items of the breadcrumb: (label, link)
props: The properties of the component.
Returns:
The breadcrumb component.
"""
if len(children) == 0:
children = []
for label, link in items or []:
children.append(
BreadcrumbItem.create(BreadcrumbLink.create(label, href=link))
)
return super().create(*children, **props)
class BreadcrumbItem(ChakraComponent):
"""Individual breadcrumb element containing a link and a divider."""

View File

@ -2,6 +2,8 @@
from typing import Set
from pynecone.components.component import Component
from pynecone.components.media.icon import Icon
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -64,6 +66,57 @@ class AlertDialog(ChakraComponent):
"on_overlay_click",
}
@classmethod
def create(
cls, *children, header=None, body=None, footer=None, close_button=None, **props
) -> Component:
"""Create an alert dialog component.
Args:
children: The children of the alert dialog component.
header: The header of the alert dialog.
body: The body of the alert dialog.
footer: The footer of the alert dialog.
close_button: The close button of the alert dialog.
props: The properties of the alert dialog component.
Raises:
AttributeError: if there is a conflict between the props used.
Returns:
The alert dialog component.
"""
if len(children) == 0:
contents = []
if header:
contents.append(AlertDialogHeader.create(header))
if body:
contents.append(AlertDialogBody.create(body))
if footer:
contents.append(AlertDialogFooter.create(footer))
# add AlertDialogCloseButton if either a prop for one was passed, or if on_close callback is present
if props.get("on_close"):
# get user defined close button or use default one
if not close_button:
close_button = Icon.create(tag="CloseIcon")
contents.append(AlertDialogCloseButton.create(close_button))
elif close_button:
raise AttributeError(
"Close button can not be used if on_close event handler is not defined"
)
children = [
AlertDialogOverlay.create(
AlertDialogContent.create(*contents),
)
]
return super().create(*children, **props)
class AlertDialogBody(ChakraComponent):
"""Should contain the description announced by screen readers."""

View File

@ -1,8 +1,10 @@
"""Container to stack elements with spacing."""
from typing import Set
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.components.media.icon import Icon
from pynecone.var import Var
@ -70,6 +72,56 @@ class Drawer(ChakraComponent):
"on_overlay_click",
}
@classmethod
def create(
cls, *children, header=None, body=None, footer=None, close_button=None, **props
) -> Component:
"""Create a drawer component.
Args:
children: The children of the drawer component.
header: The header of the drawer.
body: The body of the drawer.
footer: The footer of the drawer.
close_button: The close button of the drawer.
props: The properties of the drawer component.
Raises:
AttributeError: error that occurs if conflicting props are passed
Returns:
The drawer component.
"""
if len(children) == 0:
contents = []
if header:
contents.append(DrawerHeader.create(header))
if body:
contents.append(DrawerBody.create(body))
if footer:
contents.append(DrawerFooter.create(footer))
if props.get("on_close"):
# use default close button if undefined
if not close_button:
close_button = Icon.create(tag="CloseIcon")
contents.append(DrawerCloseButton.create(close_button))
elif close_button:
raise AttributeError(
"Close button can not be used if on_close event handler is not defined"
)
children = [
DrawerOverlay.create(
DrawerContent.create(*contents),
)
]
return super().create(*children, **props)
class DrawerBody(ChakraComponent):
"""Drawer body."""
@ -89,19 +141,19 @@ class DrawerFooter(ChakraComponent):
tag = "DrawerFooter"
class DrawerOverlay(Drawer):
class DrawerOverlay(ChakraComponent):
"""Drawer overlay."""
tag = "DrawerOverlay"
class DrawerContent(Drawer):
class DrawerContent(ChakraComponent):
"""Drawer content."""
tag = "DrawerContent"
class DrawerCloseButton(Drawer):
class DrawerCloseButton(ChakraComponent):
"""Drawer close button."""
tag = "DrawerCloseButton"

View File

@ -1,6 +1,7 @@
"""Menu components."""
from typing import Set
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -68,6 +69,25 @@ class Menu(ChakraComponent):
"""
return super().get_triggers() | {"on_close", "on_open"}
@classmethod
def create(cls, *children, items=None, **props) -> Component:
"""Create a menu component.
Args:
children: The children of the component.
items (list): The item of the menu.
props: The properties of the component.
Returns:
The menu component.
"""
if len(children) == 0:
button = MenuButton.create(*props.pop("button"))
if not items:
items = []
children = [button, MenuList.create(*items)]
return super().create(*children, **props)
class MenuButton(ChakraComponent):
"""The trigger for the menu list. Must be a direct child of Menu."""
@ -87,7 +107,7 @@ class MenuList(ChakraComponent):
tag = "MenuList"
class MenuItem(Menu):
class MenuItem(ChakraComponent):
"""The trigger that handles menu selection. Must be a direct child of a MenuList."""
tag = "MenuItem"
@ -108,7 +128,7 @@ class MenuItem(Menu):
is_focusable: Var[bool]
class MenuItemOption(Menu):
class MenuItemOption(ChakraComponent):
"""The checkable menu item, to be used with MenuOptionGroup."""
tag = "MenuItemOption"
@ -138,13 +158,13 @@ class MenuItemOption(Menu):
value: Var[str]
class MenuGroup(Menu):
class MenuGroup(ChakraComponent):
"""A wrapper to group related menu items."""
tag = "MenuGroup"
class MenuOptionGroup(Menu):
class MenuOptionGroup(ChakraComponent):
"""A wrapper for checkable menu items (radio and checkbox)."""
tag = "MenuOptionGroup"
@ -156,7 +176,7 @@ class MenuOptionGroup(Menu):
value: Var[str]
class MenuDivider(Menu):
class MenuDivider(ChakraComponent):
"""A visual separator for menu items and groups."""
tag = "MenuDivider"

View File

@ -1,8 +1,10 @@
"""Modal components."""
from typing import Set
from pynecone.components.component import Component
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.components.media import Icon
from pynecone.var import Var
@ -64,8 +66,62 @@ class Modal(ChakraComponent):
"on_overlay_click",
}
@classmethod
def create(
cls, *children, header=None, body=None, footer=None, close_button=None, **props
) -> Component:
"""Create a modal component.
class ModalOverlay(Modal):
Args:
children: The children of the component.
header: The header of the modal.
body: The body of the modal.
footer: The footer of the modal.
close_button: The close button of the modal.
props: The properties of the component.
Raises:
AttributeError: error that occurs if conflicting props are passed
Returns:
The modal component.
"""
if len(children) == 0:
contents = []
# add header if present in props
if header:
contents.append(ModalHeader.create(header))
# add ModalBody if present in props
if body:
contents.append(ModalBody.create(body))
# add ModalFooter if present in props
if footer:
contents.append(ModalFooter.create(footer))
# add ModalCloseButton if either a prop for one was passed, or if
if props.get("on_close"):
# get user defined close button or use default one
if not close_button:
close_button = Icon.create(tag="CloseIcon")
contents.append(ModalCloseButton.create(close_button))
elif close_button:
raise AttributeError(
"Close button can not be used if on_close event handler is not defined"
)
children = [
ModalOverlay.create(
ModalContent.create(*contents),
)
]
return super().create(*children, **props)
class ModalOverlay(ChakraComponent):
"""The dimmed overlay behind the modal dialog."""
tag = "ModalOverlay"
@ -83,7 +139,7 @@ class ModalFooter(ChakraComponent):
tag = "ModalFooter"
class ModalContent(Modal):
class ModalContent(ChakraComponent):
"""The container for the modal dialog's content."""
tag = "ModalContent"
@ -95,7 +151,7 @@ class ModalBody(ChakraComponent):
tag = "ModalBody"
class ModalCloseButton(Modal):
class ModalCloseButton(ChakraComponent):
"""The button that closes the modal."""
tag = "ModalCloseButton"

View File

@ -1,6 +1,8 @@
"""Popover components."""
from typing import Set
from pynecone.components.component import Component
from pynecone.components.forms.button import Button
from pynecone.components.libs.chakra import ChakraComponent
from pynecone.var import Var
@ -83,8 +85,55 @@ class Popover(ChakraComponent):
"""
return super().get_triggers() | {"on_close", "on_open"}
@classmethod
def create(
cls,
*children,
trigger=None,
header=None,
body=None,
footer=None,
use_close_button=False,
**props
) -> Component:
"""Create a popover component.
class PopoverContent(Popover):
Args:
children: The children of the component.
trigger: The trigger that opens the popover.
header: The header of the popover.
body: The body of the popover.
footer: The footer of the popover.
use_close_button: Whether to add a close button on the popover.
props: The properties of the component.
Returns:
The popover component.
"""
if len(children) == 0:
contents = []
trigger = PopoverTrigger.create(trigger)
# add header if present in props
if header:
contents.append(PopoverHeader.create(header))
if body:
contents.append(PopoverBody.create(body))
if footer:
contents.append(PopoverFooter.create(footer))
if use_close_button:
contents.append(PopoverCloseButton.create())
children = [trigger, PopoverContent.create(*contents)]
return super().create(*children, **props)
class PopoverContent(ChakraComponent):
"""The popover itself."""
tag = "PopoverContent"
@ -108,25 +157,25 @@ class PopoverBody(ChakraComponent):
tag = "PopoverBody"
class PopoverArrow(Popover):
class PopoverArrow(ChakraComponent):
"""A visual arrow that points to the reference (or trigger)."""
tag = "PopoverArrow"
class PopoverCloseButton(Popover):
class PopoverCloseButton(ChakraComponent):
"""A button to close the popover."""
tag = "PopoverCloseButton"
class PopoverAnchor(Popover):
class PopoverAnchor(ChakraComponent):
"""Used to wrap the position-reference element."""
tag = "PopoverAnchor"
class PopoverTrigger(Popover):
class PopoverTrigger(ChakraComponent):
"""Used to wrap the reference (or trigger) element."""
tag = "PopoverTrigger"