-
Notifications
You must be signed in to change notification settings - Fork 990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement accordion component #2262
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
b5f9b04
Implement accordion component
picklelo eb1e5b7
Fix literal annotation
picklelo 90a26e4
Use style prop instead of css
picklelo c213cfa
Move accordion to primitives folder
picklelo b8c2084
Add pyi file
picklelo 4cde6a4
Fix pyright
picklelo 4cb01e7
Add apply_theme method
picklelo 1c07d0b
Fix apply_theme
picklelo 5fe339f
Use apply_theme method
picklelo ff4f2c4
Fix lint
picklelo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"""Radix primitive components (https://www.radix-ui.com/primitives).""" | ||
|
||
from .accordion import accordion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,279 @@ | ||
"""Radix accordion components.""" | ||
|
||
from typing import Literal | ||
|
||
from reflex.components.component import Component | ||
from reflex.components.tags import Tag | ||
from reflex.style import Style | ||
from reflex.utils import format, imports | ||
from reflex.vars import Var | ||
|
||
LiteralAccordionType = Literal["single", "multiple"] | ||
LiteralAccordionDir = Literal["ltr", "rtl"] | ||
LiteralAccordionOrientation = Literal["vertical", "horizontal"] | ||
|
||
|
||
DEFAULT_ANIMATION_DURATION = 250 | ||
|
||
|
||
class AccordionComponent(Component): | ||
"""Base class for all @radix-ui/accordion components.""" | ||
|
||
library = "@radix-ui/react-accordion@^1.1.2" | ||
|
||
# Change the default rendered element for the one passed as a child. | ||
as_child: Var[bool] | ||
|
||
def _render(self) -> Tag: | ||
return ( | ||
super() | ||
._render() | ||
.add_props( | ||
**{ | ||
"class_name": format.to_title_case(self.tag or ""), | ||
} | ||
) | ||
) | ||
|
||
|
||
class AccordionRoot(AccordionComponent): | ||
"""An accordion component.""" | ||
|
||
tag = "Root" | ||
|
||
alias = "RadixAccordionRoot" | ||
|
||
# The type of accordion (single or multiple). | ||
type_: Var[LiteralAccordionType] | ||
|
||
# The value of the item to expand. | ||
value: Var[str] | ||
|
||
# The default value of the item to expand. | ||
default_value: Var[str] | ||
|
||
# Whether or not the accordion is collapsible. | ||
collapsible: Var[bool] | ||
|
||
# Whether or not the accordion is disabled. | ||
disabled: Var[bool] | ||
|
||
# The reading direction of the accordion when applicable. | ||
dir: Var[LiteralAccordionDir] | ||
|
||
# The orientation of the accordion. | ||
orientation: Var[LiteralAccordionOrientation] | ||
|
||
def _apply_theme(self, theme: Component): | ||
self.style = Style( | ||
{ | ||
"border_radius": "6px", | ||
"background_color": "var(--accent-6)", | ||
"box_shadow": "0 2px 10px var(--black-a4)", | ||
**self.style, | ||
} | ||
) | ||
|
||
|
||
class AccordionItem(AccordionComponent): | ||
"""An accordion component.""" | ||
|
||
tag = "Item" | ||
|
||
alias = "RadixAccordionItem" | ||
|
||
# A unique identifier for the item. | ||
value: Var[str] | ||
|
||
# When true, prevents the user from interacting with the item. | ||
disabled: Var[bool] | ||
|
||
def _apply_theme(self, theme: Component): | ||
self.style = Style( | ||
{ | ||
"overflow": "hidden", | ||
"margin_top": "1px", | ||
"&:first-child": { | ||
"margin_top": 0, | ||
"border_top_left_radius": "4px", | ||
"border_top_right_radius": "4px", | ||
}, | ||
"&:last-child": { | ||
"border_bottom_left_radius": "4px", | ||
"border_bottom_right_radius": "4px", | ||
}, | ||
"&:focus-within": { | ||
"position": "relative", | ||
"z_index": 1, | ||
"box_shadow": "0 0 0 2px var(--accent-7)", | ||
}, | ||
**self.style, | ||
} | ||
) | ||
|
||
|
||
class AccordionHeader(AccordionComponent): | ||
"""An accordion component.""" | ||
|
||
tag = "Header" | ||
|
||
alias = "RadixAccordionHeader" | ||
|
||
def _apply_theme(self, theme: Component): | ||
self.style = Style( | ||
{ | ||
"display": "flex", | ||
**self.style, | ||
} | ||
) | ||
|
||
|
||
class AccordionTrigger(AccordionComponent): | ||
"""An accordion component.""" | ||
|
||
tag = "Trigger" | ||
|
||
alias = "RadixAccordionTrigger" | ||
|
||
def _apply_theme(self, theme: Component): | ||
self.style = Style( | ||
{ | ||
"font_family": "inherit", | ||
"padding": "0 20px", | ||
"height": "45px", | ||
"flex": 1, | ||
"display": "flex", | ||
"align_items": "center", | ||
"justify_content": "space-between", | ||
"font_size": "15px", | ||
"line_height": 1, | ||
"color": "var(--accent-11)", | ||
"box_shadow": "0 1px 0 var(--accent-6)", | ||
"&:hover": { | ||
"background_color": "var(--gray-2)", | ||
}, | ||
"&[data-state='open'] > .AccordionChevron": { | ||
"transform": "rotate(180deg)", | ||
}, | ||
**self.style, | ||
} | ||
) | ||
|
||
|
||
class AccordionContent(AccordionComponent): | ||
"""An accordion component.""" | ||
|
||
tag = "Content" | ||
|
||
alias = "RadixAccordionContent" | ||
|
||
def _apply_theme(self, theme: Component): | ||
self.style = Style( | ||
{ | ||
"overflow": "hidden", | ||
"fontSize": "15px", | ||
"color": "var(--accent-11)", | ||
"backgroundColor": "var(--accent-2)", | ||
"padding": "15px, 20px", | ||
"&[data-state='open']": { | ||
"animation": Var.create( | ||
f"${{slideDown}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)", | ||
_var_is_string=True, | ||
), | ||
}, | ||
"&[data-state='closed']": { | ||
"animation": Var.create( | ||
f"${{slideUp}} {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)", | ||
_var_is_string=True, | ||
), | ||
}, | ||
**self.style, | ||
} | ||
) | ||
|
||
def _get_imports(self): | ||
return { | ||
**super()._get_imports(), | ||
"@emotion/react": [imports.ImportVar(tag="keyframes")], | ||
} | ||
|
||
def _get_custom_code(self) -> str: | ||
return """ | ||
const slideDown = keyframes` | ||
from { | ||
height: 0; | ||
} | ||
to { | ||
height: var(--radix-accordion-content-height); | ||
} | ||
` | ||
const slideUp = keyframes` | ||
from { | ||
height: var(--radix-accordion-content-height); | ||
} | ||
to { | ||
height: 0; | ||
} | ||
` | ||
""" | ||
|
||
|
||
# TODO: Remove this once the radix-icons PR is merged in. | ||
class ChevronDownIcon(Component): | ||
"""A chevron down icon.""" | ||
|
||
library = "@radix-ui/react-icons" | ||
|
||
tag = "ChevronDownIcon" | ||
|
||
def _apply_theme(self, theme: Component): | ||
self.style = Style( | ||
{ | ||
"color": "var(--accent-10)", | ||
"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms cubic-bezier(0.87, 0, 0.13, 1)", | ||
**self.style, | ||
} | ||
) | ||
|
||
|
||
accordion_root = AccordionRoot.create | ||
accordion_item = AccordionItem.create | ||
accordion_trigger = AccordionTrigger.create | ||
accordion_content = AccordionContent.create | ||
accordion_header = AccordionHeader.create | ||
chevron_down_icon = ChevronDownIcon.create | ||
|
||
|
||
def accordion(items: list[tuple[str, str]], **props) -> Component: | ||
"""High level API for the Radix accordion. | ||
|
||
#TODO: We need to handle taking in state here. This is just for a POC. | ||
|
||
|
||
Args: | ||
items: The items of the accordion component: list of tuples (label,panel) | ||
**props: The properties of the component. | ||
|
||
Returns: | ||
The accordion component. | ||
""" | ||
return accordion_root( | ||
*[ | ||
accordion_item( | ||
accordion_header( | ||
accordion_trigger( | ||
label, | ||
chevron_down_icon( | ||
class_name="AccordionChevron", | ||
), | ||
), | ||
), | ||
accordion_content( | ||
panel, | ||
), | ||
value=f"item-{i}", | ||
) | ||
for i, (label, panel) in enumerate(items) | ||
], | ||
**props, | ||
) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why, but in my app this is rendering as
It still seems to work, somehow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah not sure I added an extra blank line but doesn't seem to care