286 lines
9.0 KiB
Python
286 lines
9.0 KiB
Python
"""Drawer components based on Radix primitives."""
|
|
|
|
# Based on Vaul: https://github.com/emilkowalski/vaul
|
|
# Style based on https://ui.shadcn.com/docs/components/drawer
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Literal, Optional, Union
|
|
|
|
from reflex.components.component import Component, ComponentNamespace
|
|
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
|
from reflex.components.radix.themes.base import Theme
|
|
from reflex.components.radix.themes.layout.flex import Flex
|
|
from reflex.constants.compiler import MemoizationMode
|
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
|
from reflex.vars.base import Var
|
|
|
|
|
|
class DrawerComponent(RadixPrimitiveComponent):
|
|
"""A Drawer component."""
|
|
|
|
library = "vaul"
|
|
|
|
lib_dependencies: list[str] = ["@radix-ui/react-dialog@^1.0.5"]
|
|
|
|
|
|
LiteralDirectionType = Literal["top", "bottom", "left", "right"]
|
|
|
|
|
|
class DrawerRoot(DrawerComponent):
|
|
"""The Root component of a Drawer, contains all parts of a drawer."""
|
|
|
|
tag = "Drawer.Root"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
# The open state of the drawer when it is initially rendered. Use when you do not need to control its open state.
|
|
default_open: Var[bool]
|
|
|
|
# Whether the drawer is open or not.
|
|
open: Var[bool]
|
|
|
|
# Fires when the drawer is opened or closed.
|
|
on_open_change: EventHandler[passthrough_event_spec(bool)]
|
|
|
|
# When `False`, it allows interaction with elements outside of the drawer without closing it. Defaults to `True`.
|
|
modal: Var[bool]
|
|
|
|
# Direction of the drawer. This adjusts the animations and the drag direction. Defaults to `"bottom"`
|
|
direction: Var[LiteralDirectionType]
|
|
|
|
# Gets triggered after the open or close animation ends, it receives an open argument with the open state of the drawer by the time the function was triggered.
|
|
on_animation_end: EventHandler[passthrough_event_spec(bool)]
|
|
|
|
# When `False`, dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the open prop, otherwise you won't be able to open/close the drawer.
|
|
dismissible: Var[bool]
|
|
|
|
# When `True`, dragging will only be possible by the handle.
|
|
handle_only: Var[bool]
|
|
|
|
# Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. Should go from least visible. Also Accept px values, which doesn't take screen height into account.
|
|
snap_points: Optional[list[Union[str, float]]]
|
|
|
|
# Index of a snapPoint from which the overlay fade should be applied. Defaults to the last snap point.
|
|
fade_from_index: Var[int]
|
|
|
|
# Duration for which the drawer is not draggable after scrolling content inside of the drawer. Defaults to 500ms
|
|
scroll_lock_timeout: Var[int]
|
|
|
|
# When `True`, it prevents scroll restoration. Defaults to `True`.
|
|
prevent_scroll_restoration: Var[bool]
|
|
|
|
# Enable background scaling, it requires container element with `vaul-drawer-wrapper` attribute to scale its background.
|
|
should_scale_background: Var[bool]
|
|
|
|
# Number between 0 and 1 that determines when the drawer should be closed.
|
|
close_threshold: Var[float]
|
|
|
|
|
|
class DrawerTrigger(DrawerComponent):
|
|
"""The button that opens the dialog."""
|
|
|
|
tag = "Drawer.Trigger"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
# Defaults to true, if the first child acts as the trigger.
|
|
as_child: Var[bool] = Var.create(True)
|
|
|
|
_memoization_mode = MemoizationMode(recursive=False)
|
|
|
|
@classmethod
|
|
def create(cls, *children: Any, **props: Any) -> Component:
|
|
"""Create a new DrawerTrigger instance.
|
|
|
|
Args:
|
|
*children: The children of the element.
|
|
**props: The properties of the element.
|
|
|
|
Returns:
|
|
The new DrawerTrigger instance.
|
|
"""
|
|
for child in children:
|
|
if "on_click" in getattr(child, "event_triggers", {}):
|
|
children = (Flex.create(*children),)
|
|
break
|
|
return super().create(*children, **props)
|
|
|
|
|
|
class DrawerPortal(DrawerComponent):
|
|
"""Portals your drawer into the body."""
|
|
|
|
tag = "Drawer.Portal"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
|
|
# Based on https://www.radix-ui.com/primitives/docs/components/dialog#content
|
|
class DrawerContent(DrawerComponent):
|
|
"""Content that should be rendered in the drawer."""
|
|
|
|
tag = "Drawer.Content"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
# Style set partially based on the source code at https://ui.shadcn.com/docs/components/drawer
|
|
def _get_style(self) -> dict:
|
|
"""Get the style for the component.
|
|
|
|
Returns:
|
|
The dictionary of the component style as value and the style notation as key.
|
|
"""
|
|
base_style = {
|
|
"left": "0",
|
|
"right": "0",
|
|
"bottom": "0",
|
|
"top": "0",
|
|
"position": "fixed",
|
|
"z_index": 50,
|
|
"display": "flex",
|
|
}
|
|
style = self.style or {}
|
|
base_style.update(style)
|
|
return {"css": base_style}
|
|
|
|
# Fired when the drawer content is opened.
|
|
on_open_auto_focus: EventHandler[no_args_event_spec]
|
|
|
|
# Fired when the drawer content is closed.
|
|
on_close_auto_focus: EventHandler[no_args_event_spec]
|
|
|
|
# Fired when the escape key is pressed.
|
|
on_escape_key_down: EventHandler[no_args_event_spec]
|
|
|
|
# Fired when the pointer is down outside the drawer content.
|
|
on_pointer_down_outside: EventHandler[no_args_event_spec]
|
|
|
|
# Fired when interacting outside the drawer content.
|
|
on_interact_outside: EventHandler[no_args_event_spec]
|
|
|
|
@classmethod
|
|
def create(cls, *children, **props):
|
|
"""Create a Drawer Content.
|
|
We wrap the Drawer content in an `rx.theme` to make radix themes definitions available to
|
|
rendered div in the DOM. This is because Vaul Drawer injects the Drawer overlay content in a sibling
|
|
div to the root div rendered by radix which contains styling definitions. Wrapping in `rx.theme`
|
|
makes the styling available to the overlay.
|
|
|
|
Args:
|
|
*children: The list of children to use.
|
|
**props: Additional properties to apply to the drawer content.
|
|
|
|
Returns:
|
|
The drawer content.
|
|
"""
|
|
comp = super().create(*children, **props)
|
|
|
|
return Theme.create(comp)
|
|
|
|
|
|
class DrawerOverlay(DrawerComponent):
|
|
"""A layer that covers the inert portion of the view when the dialog is open."""
|
|
|
|
tag = "Drawer.Overlay"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
# Style set based on the source code at https://ui.shadcn.com/docs/components/drawer
|
|
def _get_style(self) -> dict:
|
|
"""Get the style for the component.
|
|
|
|
Returns:
|
|
The dictionary of the component style as value and the style notation as key.
|
|
"""
|
|
base_style = {
|
|
"position": "fixed",
|
|
"left": "0",
|
|
"right": "0",
|
|
"bottom": "0",
|
|
"top": "0",
|
|
"z_index": 50,
|
|
"background": "rgba(0, 0, 0, 0.5)",
|
|
}
|
|
style = self.style or {}
|
|
base_style.update(style)
|
|
return {"css": base_style}
|
|
|
|
|
|
class DrawerClose(DrawerTrigger):
|
|
"""A button that closes the drawer."""
|
|
|
|
tag = "Drawer.Close"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
|
|
class DrawerTitle(DrawerComponent):
|
|
"""A title for the drawer."""
|
|
|
|
tag = "Drawer.Title"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
# Style set based on the source code at https://ui.shadcn.com/docs/components/drawer
|
|
def _get_style(self) -> dict:
|
|
"""Get the style for the component.
|
|
|
|
Returns:
|
|
The dictionary of the component style as value and the style notation as key.
|
|
"""
|
|
base_style = {
|
|
"font-size": "1.125rem",
|
|
"font-weight": "600",
|
|
"line-weight": "1",
|
|
"letter-spacing": "-0.05em",
|
|
}
|
|
style = self.style or {}
|
|
base_style.update(style)
|
|
return {"css": base_style}
|
|
|
|
|
|
class DrawerDescription(DrawerComponent):
|
|
"""A description for the drawer."""
|
|
|
|
tag = "Drawer.Description"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
# Style set based on the source code at https://ui.shadcn.com/docs/components/drawer
|
|
def _get_style(self) -> dict:
|
|
"""Get the style for the component.
|
|
|
|
Returns:
|
|
The dictionary of the component style as value and the style notation as key.
|
|
"""
|
|
base_style = {
|
|
"font-size": "0.875rem",
|
|
}
|
|
style = self.style or {}
|
|
base_style.update(style)
|
|
return {"css": base_style}
|
|
|
|
|
|
class DrawerHandle(DrawerComponent):
|
|
"""A description for the drawer."""
|
|
|
|
tag = "Drawer.Handle"
|
|
|
|
alias = "Vaul" + tag
|
|
|
|
|
|
class Drawer(ComponentNamespace):
|
|
"""A namespace for Drawer components."""
|
|
|
|
root = __call__ = staticmethod(DrawerRoot.create)
|
|
trigger = staticmethod(DrawerTrigger.create)
|
|
portal = staticmethod(DrawerPortal.create)
|
|
content = staticmethod(DrawerContent.create)
|
|
overlay = staticmethod(DrawerOverlay.create)
|
|
close = staticmethod(DrawerClose.create)
|
|
title = staticmethod(DrawerTitle.create)
|
|
description = staticmethod(DrawerDescription.create)
|
|
handle = staticmethod(DrawerHandle.create)
|
|
|
|
|
|
drawer = Drawer()
|