diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 00000000000..ba81d364437 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,19 @@ +--- +name: Documentation +about: Report a problem with the docs at reflex.dev/docs +title: "" +labels: documentation +assignees: "" +--- + +**Page** +Path: + +**What's wrong?** +A clear description of the issue (typo, missing info, broken example, etc.). + +**Suggested fix (optional)** +If you have a suggestion for how to improve the page, share it here. + +**Screenshots (optional)** +If applicable, add screenshots to help explain. diff --git a/docs/app/assets/tailwind-theme.css b/docs/app/assets/tailwind-theme.css index fbf4e63e176..453a3fbcf74 100644 --- a/docs/app/assets/tailwind-theme.css +++ b/docs/app/assets/tailwind-theme.css @@ -1699,14 +1699,6 @@ /* padding: 0rem 0.125rem 0rem 0.125rem; */ } - /* Override Radix Code (rt-Code) accent coloring so inline code matches slate theme */ - code.rt-Code, - code.rt-Code.rt-variant-soft, - .rt-Code.rt-variant-soft { - color: var(--c-slate-11) !important; - background-color: var(--c-slate-3) !important; - } - .code-error-style { font-family: var(--font-jetbrains); font-size: 0.835rem; @@ -1774,6 +1766,13 @@ font-family: var(--font-jetbrains) !important; } + .code-block.counter-code-block { + padding: 0 !important; + border: 0 !important; + border-radius: 0 !important; + background: transparent !important; + } + .code-block>* { background: transparent !important; font-family: var(--font-jetbrains) !important; @@ -1812,7 +1811,7 @@ } .code-block button, - .code-block > button { + .code-block>button { border: none !important; border-width: 0 !important; background: transparent !important; @@ -1832,19 +1831,19 @@ } .code-block button::after, - .code-block > button::after { + .code-block>button::after { content: "Copy"; } .code-block:hover button, - .code-block:hover > button, + .code-block:hover>button, .code-block button:focus-visible { opacity: 1 !important; pointer-events: auto; } .code-block button:hover, - .code-block > button:hover { + .code-block>button:hover { background: var(--c-slate-3) !important; background-color: var(--c-slate-3) !important; } @@ -2177,4 +2176,4 @@ body { @apply isolate bg-slate-1 font-sans antialiased; } -} \ No newline at end of file +} diff --git a/docs/app/reflex_docs/pages/docs/__init__.py b/docs/app/reflex_docs/pages/docs/__init__.py index 0784414fe5a..6ac16a0b29d 100644 --- a/docs/app/reflex_docs/pages/docs/__init__.py +++ b/docs/app/reflex_docs/pages/docs/__init__.py @@ -4,6 +4,7 @@ from types import SimpleNamespace import reflex as rx +from reflex_components_core.core.cond import Cond from reflex_docgen.markdown import parse_document # External Components @@ -23,6 +24,10 @@ from .library import library from .recipes_overview import overview +SPECIAL_COMPONENT_DOCS = { + "rx.cond": Cond, +} + def to_title_case(text: str) -> str: return " ".join(word.capitalize() for word in text.split("_")) @@ -65,6 +70,9 @@ def get_components_from_frontmatter(filepath: str) -> list: return [] components = [] for comp_str in doc.frontmatter.components: + if component := SPECIAL_COMPONENT_DOCS.get(comp_str): + components.append((component, comp_str)) + continue component = eval(comp_str) if isinstance(component, type): components.append((component, comp_str)) @@ -239,7 +247,17 @@ def comp(_actual=actual_path, _virtual=virtual_doc): # Build doc_markdown_sources mapping for _virtual, _actual in all_docs.items(): - if _virtual.endswith("-style.md") or _virtual.endswith("-ll.md"): + if _virtual.endswith("-style.md"): + continue + if _virtual.endswith("-ll.md"): + # Register low-level docs at /-ll.md so the copy button can + # fetch them from the served URL. + _hl_virtual = _virtual.replace("-ll.md", ".md") + _hl_route = doc_route_from_path(_hl_virtual) + if not _check_whitelisted_path(_hl_route): + continue + _ll_route = _hl_route.rstrip("/") + "-ll" + doc_markdown_sources[_ll_route] = _actual continue _route = doc_route_from_path(_virtual) if not _check_whitelisted_path(_route): diff --git a/docs/app/reflex_docs/pages/docs/component.py b/docs/app/reflex_docs/pages/docs/component.py index 77be602c2a8..07884e9a51c 100644 --- a/docs/app/reflex_docs/pages/docs/component.py +++ b/docs/app/reflex_docs/pages/docs/component.py @@ -2,12 +2,14 @@ import hashlib import os +import re import textwrap from pathlib import Path from types import UnionType from typing import Literal, Union, _GenericAlias, get_args, get_origin import reflex as rx +import reflex_components_internal as ui from reflex.components.base.fragment import Fragment from reflex.components.component import Component from reflex.components.radix.primitives.base import RadixPrimitiveComponent @@ -23,7 +25,7 @@ render_docgen_document, render_markdown, ) -from reflex_docs.templates.docpage import docdemobox, docpage, h1_comp, h2_comp +from reflex_docs.templates.docpage import docpage, h1_comp, h2_comp def get_code_style(color: str): @@ -76,9 +78,48 @@ class PropDocsState(rx.State): "DrawerPortal", "DrawerContent", "DrawerClose", + "Container", + "Spacer", + "Skeleton", + "Section", + "Tooltip", ] +_PILL_BTN_CLASS = ( + "text-sm font-medium cursor-pointer rounded-md px-2.5 py-1 text-secondary-11 " + "border border-secondary-5 bg-secondary-1 hover:bg-secondary-3 transition-colors" +) +_PILL_BTN_ACTIVE_CLASS = ( + "text-sm font-medium cursor-pointer rounded-md px-2.5 py-1 text-secondary-12 " + "border border-secondary-8 bg-secondary-4" +) + + +def _pill_button(label: str, active, on_click) -> rx.Component: + return rx.el.button( + label, + type="button", + on_click=on_click, + class_name=rx.cond(active, _PILL_BTN_ACTIVE_CLASS, _PILL_BTN_CLASS), + ) + + +def _pill_row(values, var, setter) -> rx.Component: + return rx.box( + *[_pill_button(v, var == v, setter(v)) for v in values], + class_name="flex flex-wrap gap-1.5", + ) + + +def _bool_pills(var, setter) -> rx.Component: + return rx.box( + _pill_button("false", ~var, setter(False)), + _pill_button("true", var, setter(True)), + class_name="flex flex-wrap gap-1.5", + ) + + def render_select(prop: PropDocumentation, component: type[Component], prop_dict: dict): if ( not safe_issubclass(component, (RadixThemesComponent, RadixPrimitiveComponent)) @@ -99,26 +140,26 @@ def render_select(prop: PropDocumentation, component: type[Component], prop_dict "default_checked", ]: name = get_id(f"{component.__qualname__}_{prop.name}") - PropDocsState.add_var(name, bool, False) + default = prop.name == "loading" and component.__name__ == "Spinner" + PropDocsState.add_var(name, bool, default) var = getattr(PropDocsState, name) PropDocsState._create_setter(name, var) setter = getattr(PropDocsState, f"set_{name}") prop_dict[prop.name] = var - return rx.checkbox( - var, - on_change=setter, - ) + return _bool_pills(var, setter) except TypeError: pass - if not isinstance(type_, _GenericAlias) or ( - type_.__origin__ not in (Literal, Union) + type_origin = get_origin(type_) + if not isinstance(type_, (_GenericAlias, UnionType)) or ( + type_origin not in (Literal, Union, UnionType) ): return rx.fragment() # For the Union[Literal, Breakpoints] type - if type_.__origin__ is Union: + if type_origin in (Union, UnionType): if not all( - arg.__name__ in ["Literal", "Breakpoints"] for arg in type_.__args__ + getattr(arg, "__name__", "") in ["Literal", "Breakpoints"] + for arg in type_.__args__ ): return rx.fragment() else: @@ -137,17 +178,7 @@ def render_select(prop: PropDocumentation, component: type[Component], prop_dict PropDocsState._create_setter(name, var) setter = getattr(PropDocsState, f"set_{name}") prop_dict[prop.name] = var - return rx.select.root( - rx.select.trigger(class_name="w-32 font-small text-slate-11"), - rx.select.content( - rx.select.group(*[ - rx.select.item(item, value=item, class_name="font-small") - for item in literal_values - ]) - ), - value=var, - on_change=setter, - ) + return _pill_row(literal_values, var, setter) # Get the first non-empty option. non_empty_args = [a for a in type_.__args__ if str(a) != ""] option = non_empty_args[0] if non_empty_args else type_.__args__[0] @@ -159,67 +190,42 @@ def render_select(prop: PropDocumentation, component: type[Component], prop_dict prop_dict[prop.name] = var if prop.name == "color_scheme": - return rx.popover.root( - rx.popover.trigger( - rx.box( - rx.button( - rx.text(var, class_name="font-small"), - # Match the select.trigger svg icon - rx.html( - """""" - ), - color_scheme=var, - variant="surface", - class_name="w-32 justify-between", - ), - ), + return ui.popover( + trigger=rx.button( + rx.text(var, class_name="text-sm font-medium"), + ui.icon("ArrowDown01Icon"), + color_scheme=var, + variant="surface", + class_name="w-32 justify-between cursor-pointer", ), - rx.popover.content( - rx.grid( - *[ - rx.box( - rx.icon( - "check", - size=15, - display=rx.cond(var == color, "block", "none"), - class_name="text-gray-12 absolute top-1/2 left-1/2 translate-x-[-50%] translate-y-[-50%]", - ), - bg=f"var(--{color}-9)", - on_click=PropDocsState.setvar(f"{name}", color), - border=rx.cond( - var == color, "2px solid var(--gray-12)", "" - ), - class_name="relative shrink-0 rounded-md size-8 cursor-pointer", - ) - for color in list(map(str, type_.__args__)) - if color != "" - ], - columns="6", - spacing="3", - ), + content=rx.box( + *[ + rx.box( + rx.icon( + "check", + size=15, + display=rx.cond(var == color, "block", "none"), + class_name="text-gray-12 absolute top-1/2 left-1/2 translate-x-[-50%] translate-y-[-50%]", + ), + bg=f"var(--{color}-9)", + on_click=PropDocsState.setvar(f"{name}", color), + border=rx.cond( + var == color, + "2px solid var(--gray-12)", + "2px solid transparent", + ), + class_name="relative shrink-0 rounded-md size-8 cursor-pointer box-border", + ) + for color in list(map(str, type_.__args__)) + if color != "" + ], + class_name="grid grid-cols-[repeat(6,2rem)] gap-3 p-3", ), + align="start", + class_name="w-fit", ) - return rx.select.root( - rx.select.trigger(class_name="font-small w-32 text-slate-11"), - rx.select.content( - rx.select.group(*[ - rx.select.item( - item, - value=item, - class_name="font-small", - _hover=( - {"background": f"var(--{item}-9)"} - if prop.name == "color_scheme" - else None - ), - ) - for item in list(map(str, type_.__args__)) - if item != "" - ]), - ), - value=var, - on_change=setter, - ) + literal_values = [str(a) for a in type_.__args__ if str(a) != ""] + return _pill_row(literal_values, var, setter) def hovercard(trigger: rx.Component, content: rx.Component) -> rx.Component: @@ -262,10 +268,8 @@ def safe_issubclass(cls, class_or_tuple): def prop_docs( prop: PropDocumentation, - prop_dict: dict, component: type[Component], - is_interactive: bool, -) -> list[rx.Component]: +) -> tuple[list[rx.Component], bool]: """Generate the docs for a prop.""" # Get the type of the prop. type_ = prop.type @@ -335,59 +339,58 @@ def prop_docs( default_value = prop.default_value if prop.default_value is not None else "-" # Get the color of the prop. color = TYPE_COLORS.get(short_type_name, "gray") + + description = prop.description or "" + is_long_row = len(description) > 160 or ( + literal_values and len(literal_values) > 8 and prop.name not in common_types + ) + + cell_content_class = ( + ( + "cell-content max-h-[6.5em] overflow-hidden " + "[mask-image:linear-gradient(to_bottom,black_85%,transparent)] " + "[-webkit-mask-image:linear-gradient(to_bottom,black_85%,transparent)]" + ) + if is_long_row + else "cell-content" + ) + # Return the docs for the prop. return [ rx.table.cell( rx.box( rx.code(prop.name, class_name="code-style text-nowrap leading-normal"), - hovercard( - rx.icon( - tag="info", - size=15, - class_name="!text-slate-9 shrink-0", - ), - rx.text(prop.description, class_name="font-small text-slate-11"), - ), - class_name="flex flex-row items-center gap-2", + class_name=cell_content_class, ), - class_name="justify-start pl-4", + class_name="justify-start pl-4 align-top py-3", ), rx.table.cell( rx.box( - rx.cond( - (len(literal_values) > 0) & (prop.name not in common_types), - rx.code( - ( - " | ".join( - [f'"{v}"' for v in literal_values[:max_prop_values]] - + ["..."] - ) - if len(literal_values) > max_prop_values - else type_name - ), - style=get_code_style(color), - class_name="code-style text-nowrap leading-normal", - ), - rx.code( - type_name, - style=get_code_style(color), - class_name="code-style text-nowrap leading-normal", - ), - ), - rx.cond( - len(literal_values) > max_prop_values - and prop.name not in common_types, - hovercard( - rx.icon( - tag="circle-ellipsis", - size=15, - class_name="!text-slate-9 shrink-0", - ), - rx.text( - " | ".join([f'"{v}"' for v in literal_values]), - class_name="font-small text-slate-11", + rx.box( + rx.box( + *( + [ + rx.code( + f'"{v}"', + color_scheme=color, + variant="soft", + class_name="code-style leading-normal text-nowrap", + ) + for v in literal_values + ] + if literal_values and prop.name not in common_types + else [ + rx.code( + type_name, + color_scheme=color, + variant="soft", + class_name="code-style leading-normal whitespace-normal break-words", + ) + ] ), + class_name="flex flex-wrap gap-1", ), + class_name=cell_content_class, ), rx.cond( (origin == Union) @@ -410,9 +413,9 @@ def prop_docs( (prop.name == "color_scheme") | (prop.name == "accent_color"), color_scheme_hovercard(literal_values), ), - class_name="flex flex-row items-center gap-2", + class_name="flex flex-row items-start gap-2", ), - class_name="justify-start pl-4", + class_name="justify-start pl-4 align-top py-3", ), rx.table.cell( rx.box( @@ -427,23 +430,28 @@ def prop_docs( ), class_name="code-style leading-normal text-nowrap", ), - class_name="flex", + class_name=cell_content_class, ), - class_name="justify-start pl-4", + class_name="justify-start pl-4 align-top py-3", ), rx.table.cell( - render_select(prop, component, prop_dict), - class_name="justify-start pl-4", - ) - if is_interactive - else rx.fragment(), - ] + rx.box( + rx.text( + description, + class_name="font-small text-slate-11 whitespace-normal leading-snug break-words", + ), + class_name=cell_content_class, + ), + class_name="justify-start pl-4 align-top py-3 w-full", + ), + ], is_long_row def generate_props( props: tuple[PropDocumentation, ...], component: type[Component], previews: dict[str, str], + display_name: str | None = None, ) -> rx.Component: prop_list = list(props) if len(prop_list) == 0: @@ -454,36 +462,121 @@ def generate_props( ) table_header_class_name = ( - "font-small text-slate-12 text-normal w-auto justify-start pl-4 font-bold" + "text-xs text-secondary-11 w-auto justify-start pl-4 font-semibold capitalize" ) prop_dict = {} is_interactive = True - if not issubclass( - component, (RadixThemesComponent, RadixPrimitiveComponent) - ) or component.__name__ in [ - "Theme", - "ThemePanel", - "DrawerRoot", - "DrawerTrigger", - "DrawerOverlay", - "DrawerPortal", - "DrawerContent", - "DrawerClose", - ]: + if ( + not issubclass(component, (RadixThemesComponent, RadixPrimitiveComponent)) + or component.__name__ in EXCLUDED_COMPONENTS + ): is_interactive = False - body = rx.table.body( - *[ + styling_props = { + "variant", + "size", + "color_scheme", + "radius", + "high_contrast", + "loading", + "disabled", + "weight", + "align", + "justify", + "direction", + "orientation", + } + + # Props that a specific component visually ignores (e.g. CSS overrides + # or deprecated HTML attributes), so we hide them from the interactive + # controls to avoid confusion. + per_component_skip = { + "Center": {"align", "justify"}, + "TableRoot": {"align"}, + "TableCell": {"align"}, + "TableRowHeaderCell": {"align"}, + "TableColumnHeaderCell": {"align"}, + "AccordionRoot": {"orientation"}, + "SegmentedControlRoot": {"color_scheme"}, + } + skip_props = per_component_skip.get(component.__name__, set()) + + interactive_controls: list[tuple[PropDocumentation, rx.Component]] = [] + if is_interactive: + for prop in prop_list: + if prop.name.startswith("on_"): + continue + if prop.name not in styling_props or prop.name in skip_props: + continue + control = render_select(prop, component, prop_dict) + if not isinstance(control, Fragment): + interactive_controls.append((prop, control)) + + def _toggle_row() -> rx.Component: + return rx.el.tr( + rx.el.td( + rx.el.details( + rx.el.summary( + rx.el.span( + "Show more", + rx.icon( + "chevron-down", + size=12, + class_name="inline-block align-[-2px] ml-1", + ), + class_name="group-open/details:hidden", + ), + rx.el.span( + "Show less", + rx.icon( + "chevron-up", + size=12, + class_name="inline-block align-[-2px] ml-1", + ), + class_name="hidden group-open/details:inline", + ), + class_name=( + "block list-none cursor-pointer text-center text-xs " + "font-medium text-slate-11 hover:text-slate-12 py-2 " + "[&::-webkit-details-marker]:hidden [&::marker]:hidden" + ), + ), + class_name="group/details", + ), + col_span=4, + class_name=( + "!p-0 !border-t-0 [box-shadow:0_-1px_0_0_var(--gray-a4)_inset]" + ), + ), + class_name="api-toggle-row bg-slate-2", + ) + + data_row_class = ( + "[&:has(+_tr.api-toggle-row_details[open])_.cell-content]:!max-h-none " + "[&:has(+_tr.api-toggle-row_details[open])_.cell-content]:!overflow-visible " + "[&:has(+_tr.api-toggle-row_details[open])_.cell-content]:![mask-image:none] " + "[&:has(+_tr.api-toggle-row_details[open])_.cell-content]:![-webkit-mask-image:none] " + "[&>td]:!shadow-none" + ) + + rows: list[rx.Component] = [] + for prop in prop_list: + if prop.name.startswith("on_"): # ignore event trigger props + continue + cells, is_long_row = prop_docs(prop, component) + rows.append( rx.table.row( - *prop_docs(prop, prop_dict, component, is_interactive), align="center" + *cells, + align="center", + class_name=data_row_class if is_long_row else "", ) - for prop in prop_list - if not prop.name.startswith("on_") # ignore event trigger props - ], - class_name="bg-slate-2", - ) + ) + if is_long_row: + rows.append(_toggle_row()) + + body = rx.table.body(*rows, class_name="bg-slate-2") comp: rx.Component try: @@ -495,7 +588,7 @@ def generate_props( else: try: - comp = rx.vstack(component.create("Test", **prop_dict)) + comp = rx.vstack(component.create("Preview", **prop_dict)) except Exception: comp = rx.fragment() if "data" in component.__name__.lower(): @@ -507,12 +600,198 @@ def generate_props( print(f"Failed to create component {component.__name__}, error: {e}") comp = rx.fragment() - interactive_component = ( - docdemobox(comp) if not isinstance(comp, Fragment) else "", - ) + if not isinstance(comp, Fragment) and interactive_controls: + component_call = display_name or f"rx.{component.__name__.lower()}" + highlighted = { + "variant", + "size", + "color_scheme", + "radius", + "high_contrast", + "loading", + "disabled", + "weight", + "align", + "justify", + "direction", + "orientation", + } + bool_excluded = { + "open", + "checked", + "as_child", + "default_open", + "default_checked", + } + + def _is_bool_prop(p: PropDocumentation) -> bool: + try: + inner = get_args(p.type)[0] + return safe_issubclass(inner, bool) and p.name not in bool_excluded + except Exception: + return False + + line_class = "font-mono text-sm whitespace-pre" + kw_class = "text-violet-11" + str_class = "text-orange-11" + bool_class = "text-blue-11" + prop_name_class = "text-slate-12" + token_re = re.compile( + r'(rx\.[\w.]+)|("[^"]*")|(\b(?:True|False|None)\b)|(\b\d+(?:\.\d+)?\b)|(\w+)|(\s+)|(.)' + ) + + def _render_static_line(line: str) -> rx.Component: + if not line.strip(): + return rx.el.div(class_name=line_class + " min-h-[1em]") + children: list = [] + for m in token_re.finditer(line): + kw, s, b, n, ident, ws, other = m.groups() + if kw is not None: + children.append(rx.el.span(kw, class_name=kw_class)) + elif s is not None: + children.append(rx.el.span(s, class_name=str_class)) + elif b is not None: + children.append(rx.el.span(b, class_name=bool_class)) + elif n is not None: + children.append(rx.el.span(n, class_name=bool_class)) + elif ident is not None: + children.append(ident) + elif ws is not None: + children.append(ws) + else: + children.append(other) + return rx.el.div(*children, class_name=line_class) + + def _py_bool(var): + return rx.cond(var, "True", "False") + + def _render_dynamic_prop(indent: str, p, var) -> rx.Component: + if _is_bool_prop(p): + return rx.el.div( + indent, + rx.el.span(p.name, class_name=prop_name_class), + "=", + rx.el.span(_py_bool(var), class_name=bool_class), + ",", + class_name=line_class, + ) + return rx.el.div( + indent, + rx.el.span(p.name, class_name=prop_name_class), + "=", + rx.el.span('"', var, '"', class_name=str_class), + ",", + class_name=line_class, + ) + + preview_source = previews.get(component.__name__, "") + code_children: list[rx.Component] = [] + used_preview = False + + if preview_source: + src = preview_source.strip() + m = re.match(r"^lambda\s+\*\*props\s*:\s*", src) + if m: + src = src[m.end() :] + src = textwrap.dedent(src).rstrip() + for line in src.split("\n"): + if "**props" not in line: + code_children.append(_render_static_line(line)) + continue + # Split the line around the **props token. + match = re.search(r"\*\*props\s*,?\s*", line) + before = line[: match.start()] if match else line + after = line[match.end() :] if match else "" + base_indent = len(line) - len(line.lstrip()) + inline = bool(before.strip()) + # Compute prop-line indent: 4 deeper than the line's own + # indent when the **props was inline; otherwise reuse the + # line's indent (multi-line preview format). + prop_indent = " " * (base_indent + 4 if inline else base_indent) + if inline: + trimmed_before = before.rstrip() + if not trimmed_before.endswith(","): + trimmed_before += "," + code_children.append(_render_static_line(trimmed_before)) + for p, _ in interactive_controls: + if p.name not in highlighted: + continue + var = prop_dict.get(p.name) + if var is None: + continue + code_children.append(_render_dynamic_prop(prop_indent, p, var)) + trimmed_after = after.lstrip(", \t") + if inline and trimmed_after.strip(): + closing_indent = " " * base_indent + code_children.append( + _render_static_line(closing_indent + trimmed_after) + ) + used_preview = True + + if not used_preview: + code_children.append( + rx.el.div( + rx.el.span(component_call, class_name=kw_class), + "(", + class_name=line_class, + ) + ) + for p, _ in interactive_controls: + if p.name not in highlighted: + continue + var = prop_dict.get(p.name) + if var is None: + continue + code_children.append(_render_dynamic_prop(" ", p, var)) + code_children.append(rx.el.div(")", class_name=line_class)) + + interactive_component = rx.box( + rx.box( + comp, + class_name=( + "flex flex-col items-center justify-center p-6 flex-1 " + "bg-slate-2 border-b lg:border-b-0 lg:border-r " + "border-slate-4 min-w-0" + ), + ), + rx.box( + *code_children, + class_name="flex-1 p-4 bg-slate-1 min-w-0 overflow-x-auto", + ), + class_name=( + "flex flex-col lg:flex-row w-full rounded-xl border " + "border-slate-4 overflow-hidden" + ), + ) + else: + interactive_component = rx.fragment() + + controls_panel: rx.Component = rx.fragment() + if not isinstance(comp, Fragment) and interactive_controls: + controls_panel = rx.box( + *[ + rx.box( + rx.code( + prop.name, + class_name="code-style text-nowrap leading-normal text-slate-11", + ), + control, + class_name="grid grid-cols-[8rem_1fr] gap-4 items-start", + ) + for prop, control in interactive_controls + ], + class_name="flex flex-col gap-3 border border-secondary-4 rounded-md p-4 bg-secondary-1 mb-4 w-full", + ) + return rx.vstack( interactive_component, - rx.scroll_area( + controls_panel, + rx.heading( + "Props", + as_="h3", + class_name="font-large text-slate-12 mt-4 mb-2 text-left self-start", + ), + rx.box( rx.table.root( rx.el.style( """ @@ -524,32 +803,30 @@ def generate_props( rx.table.header( rx.table.row( rx.table.column_header_cell( - "Prop", - class_name=table_header_class_name, + "prop", + class_name=ui.cn(table_header_class_name, "w-[9rem]"), ), rx.table.column_header_cell( - "Type | Values", - class_name=table_header_class_name, + "type", + class_name=ui.cn(table_header_class_name, "w-[34rem]"), ), rx.table.column_header_cell( - "Default", - class_name=table_header_class_name, + "default", + class_name=ui.cn(table_header_class_name, "w-[4rem]"), ), rx.table.column_header_cell( - "Interactive", - class_name=table_header_class_name, - ) - if is_interactive - else rx.fragment(), + "description", + class_name=ui.cn(table_header_class_name, "w-[18rem]"), + ), ), - class_name="bg-slate-3", + class_name="bg-secondary-2", ), body, variant="surface", size="1", - class_name="px-0 w-full border border-slate-4", + class_name="px-0 border border-slate-4 w-full", ), - class_name="max-h-96 mb-4", + class_name="mb-4 w-full overflow-hidden", ), ) @@ -650,9 +927,6 @@ def component_docs( """Generates documentation for a given component.""" component = component_tuple[0] doc = generate_documentation(component) - props = generate_props(doc.props, component, previews) - triggers = generate_event_triggers(doc.event_handlers) - children = generate_valid_children(component) # Map for component display name overrides (e.g., for Python reserved keywords) component_display_name_map = { @@ -663,6 +937,10 @@ def component_docs( component_tuple[1], component_tuple[1] ) + props = generate_props(doc.props, component, previews, comp_display_name) + triggers = generate_event_triggers(doc.event_handlers) + children = generate_valid_children(component) + return rx.box( h2_comp(text=comp_display_name), rx.box( @@ -687,8 +965,8 @@ def multi_docs( component_docs(component_tuple, previews) for component_tuple in component_list[1:] ] - fname = path.strip("/") + ".md" - ll_doc_exists = os.path.exists(fname.replace(".md", "-ll.md")) + ll_actual_path = actual_path.replace(".md", "-ll.md") + ll_doc_exists = os.path.exists(ll_actual_path) active_class_name = "font-small bg-slate-2 p-2 text-slate-11 rounded-xl shadow-large w-28 cursor-default border border-slate-4 text-center" @@ -743,37 +1021,50 @@ def out(): toc = get_docgen_toc(actual_path) doc_content = Path(actual_path).read_text(encoding="utf-8") # Append API Reference headings for the component list - if component_list: + if components: toc.append((1, "API Reference")) for component_tuple in component_list[1:]: toc.append((2, component_tuple[1])) + api_ref_section = ( + [ + h1_comp(text="API Reference"), + rx.box(*components, class_name="flex flex-col"), + ] + if components + else [] + ) return (toc, doc_content), rx.box( links("hl", ll_doc_exists, path), render_docgen_document( virtual_filepath=virtual_path, actual_filepath=actual_path ), - h1_comp(text="API Reference"), - rx.box(*components, class_name="flex flex-col"), + *api_ref_section, class_name="flex flex-col w-full", ) @docpage(set_path=path + "low", t=title + " (Low Level)") def ll(): - ll_actual = fname.replace(".md", "-ll.md") ll_virtual = virtual_path.replace(".md", "-ll.md") - toc = get_docgen_toc(ll_actual) - doc_content = Path(ll_actual).read_text(encoding="utf-8") - if component_list: + toc = get_docgen_toc(ll_actual_path) + doc_content = Path(ll_actual_path).read_text(encoding="utf-8") + if components: toc.append((1, "API Reference")) for component_tuple in component_list[1:]: toc.append((2, component_tuple[1])) + api_ref_section = ( + [ + h1_comp(text="API Reference"), + rx.box(*components, class_name="flex flex-col"), + ] + if components + else [] + ) return (toc, doc_content), rx.box( links("ll", ll_doc_exists, path), render_docgen_document( - virtual_filepath=ll_virtual, actual_filepath=ll_actual + virtual_filepath=ll_virtual, actual_filepath=ll_actual_path ), - h1_comp(text="API Reference"), - rx.box(*components, class_name="flex flex-col"), + *api_ref_section, class_name="flex flex-col w-full", ) diff --git a/docs/app/reflex_docs/pages/docs/library.py b/docs/app/reflex_docs/pages/docs/library.py index a513527c634..e0991cb140b 100644 --- a/docs/app/reflex_docs/pages/docs/library.py +++ b/docs/app/reflex_docs/pages/docs/library.py @@ -5,6 +5,37 @@ from reflex_docs.templates.docpage import docpage, h1_comp, text_comp_2 +def get_display_name(name: str) -> str: + normalized = to_snake_case(name) + if normalized == "html": + return "HTML" + if normalized == "svg": + return "SVG" + return to_title_case(normalized, sep=" ") + + +HTML_COMPONENT_ORDER = { + "html": 0, + "text": 1, + "layout": 2, + "forms": 3, + "media": 4, + "tables": 5, + "svg": 6, +} + + +def get_components_for_category(category: str, components: list) -> list: + if to_snake_case(category) != "html": + return components + return sorted( + components, + key=lambda component: HTML_COMPONENT_ORDER.get( + to_snake_case(component[0]), len(HTML_COMPONENT_ORDER) + ), + ) + + def component_grid(): from reflex_docs.pages.docs import component_list, graphing_components from reflex_docs.templates.docpage.sidebar.sidebar_items import get_component_link @@ -17,7 +48,7 @@ def generate_gallery( rx.box( rx.link( rx.el.h1( - to_title_case(to_snake_case(category), sep=" "), + get_display_name(category), class_name="font-large text-slate-12", ), get_icon("new_tab", class_name="text-slate-11 [&>svg]:size-4"), @@ -28,7 +59,7 @@ def generate_gallery( rx.box( *[ rx.link( - to_title_case(to_snake_case(c[0]), sep=" "), + get_display_name(c[0]), href=get_component_link( category=category, clist=c, @@ -36,7 +67,9 @@ def generate_gallery( ), class_name="font-small text-slate-11 hover:!text-violet-9 transition-color w-fit", ) - for c in components[category] + for c in get_components_for_category( + category, components[category] + ) ], class_name="flex flex-col gap-2.5 px-4 py-2 border-t border-slate-5", ), diff --git a/docs/app/reflex_docs/pages/docs/source.py b/docs/app/reflex_docs/pages/docs/source.py index 3ceadda4036..fd15d5d82c0 100644 --- a/docs/app/reflex_docs/pages/docs/source.py +++ b/docs/app/reflex_docs/pages/docs/source.py @@ -25,66 +25,60 @@ def format_fields( fields: tuple[FieldDocumentation, ...], ) -> rx.Component: return ( - rx.scroll_area( - rx.table.root( - rx.table.header( - rx.table.row(*[ - rx.table.column_header_cell( - header, class_name=table_header_class_name - ) - for header in headers - ]) - ), - rx.table.body( - *[ - rx.table.row( - rx.table.cell( - format_field(field), - ), - rx.table.cell( - render_markdown(field.description or ""), - class_name="font-small text-slate-11", - ), - ) - for field in fields - ], - ), - ), - max_height="35em", - ), - ) - - -def format_methods(methods: tuple[MethodDocumentation, ...]) -> rx.Component: - return rx.scroll_area( rx.table.root( rx.table.header( - rx.table.row( - rx.table.column_header_cell("Signature"), - rx.table.column_header_cell("Description"), - ) + rx.table.row(*[ + rx.table.column_header_cell( + header, class_name=table_header_class_name + ) + for header in headers + ]) ), rx.table.body( *[ rx.table.row( rx.table.cell( - rx.code( - method.name + method.signature, - class_name="code-style", - ), - white_space="normal", + format_field(field), ), rx.table.cell( - method.description or "", - white_space="normal", - class_name="font-small text-slate-11 text-nowrap", + render_markdown(field.description or ""), + class_name="font-small text-slate-11", ), ) - for method in methods + for field in fields ], ), ), - max_height="35em", + ) + + +def format_methods(methods: tuple[MethodDocumentation, ...]) -> rx.Component: + return rx.table.root( + rx.table.header( + rx.table.row( + rx.table.column_header_cell("Signature"), + rx.table.column_header_cell("Description"), + ) + ), + rx.table.body( + *[ + rx.table.row( + rx.table.cell( + rx.code( + method.name + method.signature, + class_name="code-style", + ), + white_space="normal", + ), + rx.table.cell( + method.description or "", + white_space="normal", + class_name="font-small text-slate-11 text-nowrap", + ), + ) + for method in methods + ], + ), ) diff --git a/docs/app/reflex_docs/pages/docs_landing/views/hosting.py b/docs/app/reflex_docs/pages/docs_landing/views/hosting.py index a8aac6a0ee5..4c7bfba9d5d 100644 --- a/docs/app/reflex_docs/pages/docs_landing/views/hosting.py +++ b/docs/app/reflex_docs/pages/docs_landing/views/hosting.py @@ -21,7 +21,7 @@ def hosting_section() -> rx.Component: faded_borders(), link_item( "CloudServerIcon", - "How to Host", + "Deployment", "Step-by-step instructions to deploy your Reflex application to the cloud, including configuration and setup guides.", hosting_page.deploy_quick_start.path, ), diff --git a/docs/app/reflex_docs/pages/library_previews.py b/docs/app/reflex_docs/pages/library_previews.py index 654d4d680aa..30a7421188b 100644 --- a/docs/app/reflex_docs/pages/library_previews.py +++ b/docs/app/reflex_docs/pages/library_previews.py @@ -5,24 +5,72 @@ from reflex_docs.templates.docpage import docpage, h1_comp, text_comp_2 +def get_display_name(name: str) -> str: + normalized = to_snake_case(name) + if normalized == "html": + return "HTML" + if normalized == "svg": + return "SVG" + return to_title_case(normalized, sep=" ") + + +HTML_COMPONENT_ORDER = { + "html": 0, + "text": 1, + "layout": 2, + "forms": 3, + "media": 4, + "tables": 5, + "svg": 6, +} + +HTML_CARD_PREVIEWS = { + "html": ("layout", "box"), + "text": ("typography", "text"), + "layout": ("layout", "grid"), + "forms": ("forms", "input"), + "media": ("media", "image"), + "tables": ("tables-and-data-grids", "table"), + "svg": ("data-display", "icon"), +} + + +def get_components_for_category(category: str, components: list) -> list: + if to_snake_case(category) != "html": + return components + return sorted( + components, + key=lambda component: HTML_COMPONENT_ORDER.get( + to_snake_case(component[0]), len(HTML_COMPONENT_ORDER) + ), + ) + + +def get_preview_asset(name: str, section: str) -> tuple[str, str]: + if to_snake_case(section) == "html": + return HTML_CARD_PREVIEWS.get(to_snake_case(name), ("layout", "box")) + return section.lower(), name.lower() + + def component_card(name: str, link: str, section: str) -> rx.Component: + preview_section, preview_name = get_preview_asset(name, section) return rx.link( rx.box( rx.image( - src=f"{REFLEX_ASSETS_CDN}components_previews/{section.lower()}/light/{name.lower()}.svg", + src=f"{REFLEX_ASSETS_CDN}components_previews/{preview_section}/light/{preview_name}.svg", loading="lazy", alt=f"Image preview of {name}", class_name="object-contain object-center h-full w-full dark:hidden", ), rx.image( - src=f"{REFLEX_ASSETS_CDN}components_previews/{section.lower()}/dark/{name.lower()}.svg", + src=f"{REFLEX_ASSETS_CDN}components_previews/{preview_section}/dark/{preview_name}.svg", loading="lazy", alt=f"Image preview of {name}", class_name="object-contain object-center h-full w-full dark:block hidden", ), rx.box( rx.text( - to_title_case(to_snake_case(name), sep=" "), + get_display_name(name), class_name="truncate font-base text-slate-12", ), rx.icon("chevron-right", size=14, class_name="!text-slate-9"), @@ -65,7 +113,7 @@ def page() -> rx.Component: component_list = get_component_list(type) return rx.box( rx.box( - h1_comp(text=to_title_case(to_snake_case(component_category), sep=" ")), + h1_comp(text=get_display_name(component_category)), text_comp_2( text=description, ), @@ -82,7 +130,9 @@ def page() -> rx.Component: ), section=component_category, ) - for component in component_list[component_category] + for component in get_components_for_category( + component_category, component_list[component_category] + ) ], class_name="gap-6 grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3", ), @@ -120,7 +170,7 @@ def page() -> rx.Component: }, "html": { "path": "html", - "description": "Components that help with dynamic rendering, such as conditional rendering and dynamic components. These are useful for creating responsive and interactive user interfaces.", + "description": "Low-level HTML elements exposed through the rx.el namespace. These are useful when you need native browser elements with direct styling control.", "component_category": "Html", }, "layout": { diff --git a/docs/app/reflex_docs/templates/docpage/docpage.py b/docs/app/reflex_docs/templates/docpage/docpage.py index 42682aec294..96dfe4b5d6b 100644 --- a/docs/app/reflex_docs/templates/docpage/docpage.py +++ b/docs/app/reflex_docs/templates/docpage/docpage.py @@ -19,7 +19,6 @@ from reflex_site_shared.components.marketing_button import button as marketing_button from reflex_site_shared.components.server_status import server_status from reflex_site_shared.route import Route, get_path -from reflex_site_shared.styles.colors import c_color from reflex_site_shared.utils.docpage import right_sidebar_item_highlight from reflex_site_shared.views.footer import dark_mode_toggle @@ -56,28 +55,51 @@ def footer_link_flex(heading: str, links): ) -def thumb_card(score: int, icon: str) -> rx.Component: +def thumb_card(score: int, icon: str, label: str) -> rx.Component: return rx.el.button( ui.icon( icon, - color=rx.cond( - FeedbackState.score == score, c_color("slate", 11), c_color("slate", 9) - ), size=16, ), - background_color=rx.cond( - FeedbackState.score == score, c_color("slate", 3), c_color("white", 1) - ), + label, + type="button", on_click=FeedbackState.set_score(score), - class_name="transition-bg hover:bg-slate-3 shadow-medium border border-slate-4 rounded-lg items-center justify-center cursor-pointer p-2 size-9 flex", + class_name=rx.cond( + FeedbackState.score == score, + "flex h-9 items-center justify-center gap-2 rounded-md border border-violet-6 bg-violet-3 px-3 text-sm font-medium text-violet-11 transition-colors", + "flex h-9 items-center justify-center gap-2 rounded-md border border-slate-5 bg-slate-1 px-3 text-sm font-medium text-slate-9 transition-colors hover:bg-slate-3 hover:text-slate-11", + ), ) def thumbs_cards() -> rx.Component: return rx.hstack( - thumb_card(1, "ThumbsUpIcon"), - thumb_card(0, "ThumbsDownIcon"), + thumb_card(1, "ThumbsUpIcon", "Helpful"), + thumb_card(0, "ThumbsDownIcon", "Not helpful"), gap="8px", + wrap="wrap", + ) + + +def feedback_choice_button(label: str, icon: str, score: int, class_name: str): + active = FeedbackState.score == score + return rx.el.button( + ui.icon(icon), + label, + type="button", + class_name=rx.cond( + active, + ui.cn( + "border-violet-6 bg-violet-3 text-violet-11 shadow-none", + class_name, + ), + ui.cn( + "border-slate-5 bg-slate-1 text-slate-9 shadow-large hover:bg-slate-3 hover:text-slate-11", + class_name, + ), + ), + aria_label=label, + on_click=FeedbackState.set_score(score), ) @@ -122,31 +144,21 @@ def feedback_content() -> rx.Component: def feedback_button() -> rx.Component: - thumb_cn = " flex flex-row items-center justify-center gap-2 text-slate-9 whitespace-nowrap border border-slate-5 bg-slate-1 shadow-large cursor-pointer transition-bg hover:bg-slate-3 font-small" + thumb_cn = "w-full gap-2 px-3 py-0.5 flex flex-row items-center justify-center whitespace-nowrap border cursor-pointer transition-colors font-small" return ui.popover.root( ui.popover.trigger( render_=rx.el.div( - rx.el.button( - ui.icon("ThumbsUpIcon"), + feedback_choice_button( "Yes", - type="button", - class_name=ui.cn( - "w-full gap-2 border-r-0 px-3 py-0.5 rounded-[20px_0_0_20px]", - thumb_cn, - ), - aria_label="Yes", - on_click=FeedbackState.set_score(1), + "ThumbsUpIcon", + 1, + ui.cn("rounded-[20px_0_0_20px] border-r-0", thumb_cn), ), - rx.el.button( - ui.icon("ThumbsDownIcon"), + feedback_choice_button( "No", - type="button", - class_name=ui.cn( - "w-full gap-2 border-r-0 px-3 py-0.5 rounded-[0_20px_20px_0]", - thumb_cn, - ), - aria_label="No", - on_click=FeedbackState.set_score(0), + "ThumbsDownIcon", + 0, + ui.cn("rounded-[0_20px_20px_0]", thumb_cn), ), class_name="w-full lg:w-auto items-center flex flex-row", ), @@ -241,11 +253,17 @@ def docpage_footer(path: str): rx.box( link_pill( "Raise an issue", - href=f"https://github.com/reflex-dev/reflex-web/issues/new?title=Issue with reflex.dev documentation&body=Path: {path}", + href=( + "https://github.com/reflex-dev/reflex/issues/new" + "?template=documentation.md" + "&labels=documentation" + f"&title=Issue with reflex.dev{path}" + f"&body=Path: {path}%0A%0A" + ), ), link_pill( "Edit this page", - f"https://github.com/reflex-dev/reflex-web/tree/main{path}.md", + f"https://github.com/reflex-dev/reflex/blob/main/docs{path}.md", ), class_name="lg:flex hidden flex-row items-center gap-2 w-auto", ), @@ -378,6 +396,7 @@ def _build_reflex_menu_item(path: str) -> rx.Component: class_name=( "flex size-8 items-center justify-center rounded-md " "bg-gradient-to-br from-primary-9 to-primary-11 " + "dark:from-primary-7 dark:to-primary-9 " "shadow-[0_0_0_1px_var(--primary-7),0_2px_8px_-2px_var(--primary-a8)] shrink-0" ), ), @@ -409,6 +428,8 @@ def _build_reflex_menu_item(path: str) -> rx.Component: "no-underline w-full text-left block " "bg-gradient-to-br from-primary-2 to-secondary-1 " "hover:from-primary-3 hover:to-primary-2 " + "dark:from-primary-a3 dark:to-secondary-2 " + "dark:hover:from-primary-a4 dark:hover:to-secondary-3 " "border-b border-secondary-4 transition-colors cursor-pointer" ), ) @@ -424,7 +445,10 @@ def _copy_page_button(doc_content: str, path: str = "") -> rx.Component: copy_action = run_script( """ ((function() { - const mdUrl = window.location.pathname.replace(/\\/$/, '') + '.md'; + const cleanPath = window.location.pathname.replace(/\\/$/, ''); + const mdUrl = cleanPath.endsWith('/low') + ? cleanPath.replace(/\\/low$/, '-ll.md') + : cleanPath + '.md'; const animate = () => { document.querySelectorAll('[data-copy-icon]').forEach((icon) => { if (icon.dataset.animating === '1') return; @@ -457,6 +481,10 @@ def _copy_page_button(doc_content: str, path: str = "") -> rx.Component: if (navigator.clipboard && typeof ClipboardItem !== 'undefined' && navigator.clipboard.write) { const blobPromise = fetch(mdUrl).then((r) => { if (!r.ok) throw new Error(r.status); + const ct = r.headers.get('content-type') || ''; + if (!ct.includes('markdown') && !ct.includes('text/plain')) { + throw new Error('not-markdown'); + } return r.text().then((t) => new Blob([t], { type: 'text/plain' })); }); navigator.clipboard @@ -465,7 +493,14 @@ def _copy_page_button(doc_content: str, path: str = "") -> rx.Component: return; } fetch(mdUrl) - .then((r) => (r.ok ? r.text() : Promise.reject(r.status))) + .then((r) => { + if (!r.ok) return Promise.reject(r.status); + const ct = r.headers.get('content-type') || ''; + if (!ct.includes('markdown') && !ct.includes('text/plain')) { + return Promise.reject('not-markdown'); + } + return r.text(); + }) .then((text) => { if (navigator.clipboard && navigator.clipboard.writeText) { return navigator.clipboard.writeText(text); @@ -548,7 +583,12 @@ def _copy_page_button(doc_content: str, path: str = "") -> rx.Component: "https://claude.ai/new?q=", path ), ), - class_name="flex flex-col min-w-[260px]", + class_name=( + "flex flex-col min-w-[260px] " + "bg-white dark:bg-secondary-2 border border-secondary-5 rounded-lg shadow-lg " + "data-[state=open]:animate-in data-[state=open]:fade-in-0 " + "data-[state=open]:zoom-in-95 data-[state=open]:slide-in-from-top-2" + ), ), class_name="p-0 overflow-hidden", ), @@ -770,7 +810,7 @@ def wrapper(*args, **kwargs) -> rx.Component: sidebar, class_name=( "w-[19.5rem] shrink-0 hidden lg:block z-10 border-r border-m-slate-4 dark:border-m-slate-10 sticky left-0 " - "before:content-[''] before:absolute before:top-0 before:bottom-0 before:right-0 before:w-[100vw] before:bg-white-1 dark:before:bg-m-slate-11 before:-z-10 " + "before:content-[''] before:absolute before:top-0 before:bottom-0 before:right-0 before:w-[100vw] before:bg-white-1 dark:before:bg-secondary-2 before:-z-10 " + rx.cond( HostingBannerState.is_banner_visible, " top-[113px] h-[calc(100vh-113px)]", @@ -866,10 +906,6 @@ def wrapper(*args, **kwargs) -> rx.Component: ), rx.el.div( feedback_button_toc(), - copy_to_markdown(text=doc_content) - if doc_content - else None, - ask_ai_chat(), class_name="flex flex-col mt-1.5 justify-start", ), class_name="flex flex-col justify-start gap-y-4 overflow-y-auto sticky top-4", diff --git a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar.py b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar.py index a41dfe03d0f..8a86bf0d3fa 100644 --- a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar.py +++ b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar.py @@ -15,7 +15,7 @@ mcp_items, skills_items, ) -from .sidebar_items.component_lib import component_lib, graphing_libs +from .sidebar_items.component_lib import component_lib, graphing_libs, html_lib from .sidebar_items.enterprise import ( enterprise_component_items, enterprise_items, @@ -119,7 +119,7 @@ def sidebar_leaf( item.link == url, sidebar_link( rx.el.div( - class_name="absolute left-0 top-1/2 -translate-y-1/2 w-full h-8 rounded-lg bg-m-slate-2 dark:bg-m-slate-10 z-[-1]", + class_name="absolute left-0 top-1/2 -translate-y-1/2 w-full h-8 rounded-lg bg-m-slate-2 dark:bg-slate-3 z-[-1]", ), rx.flex( rx.text( @@ -155,6 +155,7 @@ def sidebar_leaf( def sidebar_icon(name): icon_map = { "Getting Started": "rocket", + "Tutorials": "graduation-cap", "Advanced Onboarding": "newspaper", "Components": "layers", "Pages": "sticky-note", @@ -279,6 +280,7 @@ def append_to_items(items, flat_items): + hosting + component_lib + graphing_libs + + html_lib + recipes + ai_builder_overview_items + ai_builder_integrations @@ -333,7 +335,7 @@ def sidebar_category(name: str, url: str, icon: str, index: int): "flex flex-row justify-start items-center gap-2.5 w-full text-sm text-secondary-11 hover:text-secondary-12 h-8", rx.cond( SidebarState.sidebar_index == index, - "text-primary-10 dark:text-primary-9", + "text-slate-12", "", ), ), @@ -343,7 +345,7 @@ def sidebar_category(name: str, url: str, icon: str, index: int): rx.cond( SidebarState.sidebar_index == index, rx.el.div( - class_name="absolute left-0 top-0 w-full h-full bg-m-slate-2 dark:bg-m-slate-10 rounded-lg z-[-1]", + class_name="absolute left-0 top-0 w-full h-full bg-m-slate-2 dark:bg-slate-3 rounded-lg z-[-1]", ), ), rx.el.a( @@ -411,6 +413,7 @@ def sidebar_comp( frontend_index: list[int], backend_index: list[int], hosting_index: list[int], + html_lib_index: list[int], graphing_libs_index: list[int], api_reference_index: list[int], recipes_index: list[int], @@ -657,6 +660,13 @@ def sidebar_comp( graphing_libs_index, url, ), + create_sidebar_section( + "Other", + "/library/html/", + html_lib, + html_lib_index, + url, + ), rx.link( # pyright: ignore [reportCallIssue] rx.box( # pyright: ignore [reportCallIssue] rx.box( # pyright: ignore [reportCallIssue] @@ -736,6 +746,7 @@ def sidebar(url=None, width: str = "100%") -> rx.Component: frontend_index = calculate_index(frontend, url) backend_index = calculate_index(backend, url) hosting_index = calculate_index(hosting, url) + html_lib_index = calculate_index(html_lib, url) graphing_libs_index = calculate_index(graphing_libs, url) api_reference_index = calculate_index(api_reference, url) recipes_index = calculate_index(recipes, url) @@ -757,6 +768,7 @@ def sidebar(url=None, width: str = "100%") -> rx.Component: frontend_index=frontend_index, backend_index=backend_index, hosting_index=hosting_index, + html_lib_index=html_lib_index, graphing_libs_index=graphing_libs_index, api_reference_index=api_reference_index, recipes_index=recipes_index, diff --git a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/component_lib.py b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/component_lib.py index 670eed344fe..10260aaa5f0 100644 --- a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/component_lib.py +++ b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/component_lib.py @@ -4,6 +4,39 @@ from ..state import SideBarItem +def get_display_name(name: str) -> str: + normalized = to_snake_case(name) + if normalized == "html": + return "HTML" + if normalized == "svg": + return "SVG" + return to_title_case(normalized, sep=" ") + + +def is_html_category(category: str) -> bool: + return to_snake_case(category.replace("-", " ")) == "html" + + +HTML_COMPONENT_ORDER = { + "html": 0, + "text": 1, + "layout": 2, + "forms": 3, + "media": 4, + "tables": 5, + "svg": 6, +} + + +def sort_html_components(components: list) -> list: + return sorted( + components, + key=lambda component: HTML_COMPONENT_ORDER.get( + to_snake_case(component[0]), len(HTML_COMPONENT_ORDER) + ), + ) + + def get_component_link(category, clist, prefix="") -> str: component_name = rx.utils.format.to_kebab_case(clist[0]) # construct the component link. The component name points to the name of the md file. @@ -14,7 +47,7 @@ def get_category_children(category, category_list, prefix=""): category = category.replace("-", " ") if isinstance(category_list, dict): return SideBarItem( - names=category, + names=get_display_name(category), children=[ get_category_children(c, category_list[c]) for c in category_list ], @@ -27,14 +60,14 @@ def get_category_children(category, category_list, prefix=""): ) ) for c in category_list: - component_name = to_snake_case(c[0]) - name = to_title_case(component_name, sep=" ") item = SideBarItem( - names=name, + names=get_display_name(c[0]), link=get_component_link(category, c, prefix=prefix), ) category_item_children.append(item) - return SideBarItem(names=category, children=category_item_children) + return SideBarItem( + names=get_display_name(category), children=category_item_children + ) def get_sidebar_items_component_lib(): @@ -43,6 +76,8 @@ def get_sidebar_items_component_lib(): library_item_children = [] for category in component_list: + if is_html_category(category): + continue category_item = get_category_children(category, component_list[category]) library_item_children.append(category_item) @@ -51,6 +86,22 @@ def get_sidebar_items_component_lib(): ] +def get_sidebar_items_html(): + from reflex_docs.pages.docs import component_list + + html_children = [ + SideBarItem(names="Overview", link="/library/html/"), + ] + html_children.extend([ + SideBarItem( + names=get_display_name(component[0]), + link=get_component_link("Html", component), + ) + for component in sort_html_components(component_list.get("Html", [])) + ]) + return [SideBarItem(names="HTML", children=html_children)] + + def get_sidebar_items_graphings(): from reflex_docs.pages.docs import graphing_components @@ -65,4 +116,5 @@ def get_sidebar_items_graphings(): component_lib = get_sidebar_items_component_lib() +html_lib = get_sidebar_items_html() graphing_libs = get_sidebar_items_graphings() diff --git a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py index a0a2149b23e..0f4d652eb5d 100644 --- a/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py +++ b/docs/app/reflex_docs/templates/docpage/sidebar/sidebar_items/learn.py @@ -14,9 +14,14 @@ def get_sidebar_items_learn(): getting_started.introduction, getting_started.basics, getting_started.project_structure, + getting_started.open_source_templates, + ], + ), + create_item( + "Tutorials", + children=[ getting_started.dashboard_tutorial, getting_started.chatapp_tutorial, - getting_started.open_source_templates, ], ), create_item( diff --git a/docs/getting_started/basics.md b/docs/getting_started/basics.md index 46edf0647de..d90d631b813 100644 --- a/docs/getting_started/basics.md +++ b/docs/getting_started/basics.md @@ -48,7 +48,7 @@ def my_container(): ) ``` -You can also use any base HTML element through the [rx.el](/docs/library/other/html) namespace. This allows you to use standard HTML elements directly in your Reflex app when you need more control or when a specific component isn't available in the Reflex component library. +You can also use any base HTML element through the [rx.el](/docs/library/html/) namespace. This allows you to use standard HTML elements directly in your Reflex app when you need more control or when a specific component isn't available in the Reflex component library. ```python demo exec def my_div(): diff --git a/docs/getting_started/introduction.md b/docs/getting_started/introduction.md index 4240ce122a6..81960a5c6e6 100644 --- a/docs/getting_started/introduction.md +++ b/docs/getting_started/introduction.md @@ -55,6 +55,28 @@ class IntroTabsState(rx.State): self.value = val +def counter_code_section(code: str, tab: str) -> rx.Component: + active = IntroTabsState.value == tab + return rx.box( + rx.code_block( + code, + class_name="code-block counter-code-block", + ), + background=rx.cond(active, "var(--c-violet-3)", "transparent"), + border_left=rx.cond( + active, + "3px solid var(--c-violet-9)", + "3px solid transparent", + ), + padding="0.875rem 1.5rem", + class_name="w-full transition-colors", + ) + + +def counter_code_gap() -> rx.Component: + return rx.box(height="0.875rem", flex_shrink="0") + + def tabs(): return rx.tabs.root( rx.tabs.list( @@ -117,13 +139,11 @@ Here is the full code for this example: tabs() ``` -```python demo box +```python eval rx.box( - rx.code_block( - """import reflex as rx """, - class_name="code-block !bg-transparent !border-none", - ), - rx.code_block( + counter_code_section("""import reflex as rx """, ""), + counter_code_gap(), + counter_code_section( """class State(rx.State): count: int = 0 @@ -134,19 +154,10 @@ rx.box( @rx.event def decrement(self): self.count -= 1""", - background=rx.cond( - IntroTabsState.value == "tab2", - "var(--c-slate-3) !important", - "transparent", - ), - border=rx.cond( - IntroTabsState.value == "tab2", - "1px solid var(--c-slate-5)", - "none !important", - ), - class_name="code-block", + "tab2", ), - rx.code_block( + counter_code_gap(), + counter_code_section( """def index(): return rx.hstack( rx.button( @@ -162,34 +173,18 @@ rx.box( ), spacing="4", )""", - border=rx.cond( - IntroTabsState.value == "tab1", - "1px solid var(--c-slate-5)", - "none !important", - ), - background=rx.cond( - IntroTabsState.value == "tab1", - "var(--c-slate-3) !important", - "transparent", - ), - class_name="code-block", + "tab1", ), - rx.code_block( + counter_code_gap(), + counter_code_section( """app = rx.App() app.add_page(index)""", - background=rx.cond( - IntroTabsState.value == "tab3", - "var(--c-slate-3) !important", - "transparent", - ), - border=rx.cond( - IntroTabsState.value == "tab3", - "1px solid var(--c-slate-5)", - "none !important", - ), - class_name="code-block", + "tab3", + ), + class_name=( + "w-full flex flex-col overflow-hidden rounded-xl border " + "border-slate-4 bg-slate-2 py-1" ), - class_name="w-full flex flex-col", ) ``` @@ -277,16 +272,16 @@ Create the app and register the page at the base route. ```md alert info # Keep learning -- [Dashboard tutorial](/docs/getting-started/dashboard-tutorial) — build a real data app. -- [Chatapp tutorial](/docs/getting-started/chatapp-tutorial) — wire up streaming AI responses. -- [How Reflex works](/docs/advanced-onboarding/how-reflex-works) — what happens under the hood. +- [Dashboard tutorial](/docs/getting-started/dashboard-tutorial/) — build a real data app. +- [Chatapp tutorial](/docs/getting-started/chatapp-tutorial/) — wire up streaming AI responses. +- [How Reflex works](/docs/advanced-onboarding/how-reflex-works/) — what happens under the hood. ``` ```md alert info # Ship faster with AI - [Reflex Build](https://build.reflex.dev/) — generate a full app from a prompt. -- [Reflex Cloud](https://reflex.dev/docs/hosting/deploy-quick-start/) — one-command deploy. +- [Reflex Cloud](/docs/hosting/deploy-quick-start/) — one-command deploy. ``` -Browse our [open-source templates](/docs/getting-started/open-source-templates), or press `Cmd+K` / `Ctrl+K` to search the docs. +Browse our [open-source templates](/docs/getting-started/open-source-templates/), or press `Cmd+K` / `Ctrl+K` to search the docs. diff --git a/docs/library/data-display/data_list.md b/docs/library/data-display/data_list.md index 3de31747c4f..d9560ec1bca 100644 --- a/docs/library/data-display/data_list.md +++ b/docs/library/data-display/data_list.md @@ -14,9 +14,20 @@ DataListRoot: | ) DataListItem: | lambda **props: rx.data_list.root( - rx.foreach( - [["Status", "Authorized"], ["ID", "U-474747"], ["Name", "Developer Success"], ["Email", "foo@reflex.dev"]], - lambda item: rx.data_list.item(rx.data_list.label(item[0]), rx.data_list.value(item[1]), **props), + rx.data_list.item( + rx.data_list.label("Status"), + rx.data_list.value(rx.badge("Authorized", variant="soft", radius="full", size="2")), + **props, + ), + rx.data_list.item( + rx.data_list.label("Name"), + rx.data_list.value(rx.heading("Developer Success", size="4")), + **props, + ), + rx.data_list.item( + rx.data_list.label("Email"), + rx.data_list.value(rx.link("foo@reflex.dev", href="mailto:foo@reflex.dev")), + **props, ), ) DataListLabel: | diff --git a/docs/library/data-display/progress.md b/docs/library/data-display/progress.md index 4ba52b45fd1..245a1d6f3a9 100644 --- a/docs/library/data-display/progress.md +++ b/docs/library/data-display/progress.md @@ -3,7 +3,7 @@ components: - rx.progress Progress: | - lambda **props: rx.progress(value=50, **props) + lambda **props: rx.box(rx.progress(value=50, **props), width="20rem") --- # Progress diff --git a/docs/library/disclosure/accordion.md b/docs/library/disclosure/accordion.md index fea3f31dbe6..58183bdc51f 100644 --- a/docs/library/disclosure/accordion.md +++ b/docs/library/disclosure/accordion.md @@ -5,22 +5,32 @@ components: AccordionRoot: | lambda **props: rx.accordion.root( - rx.accordion.item(header="First Item", content="The first accordion item's content"), rx.accordion.item( - header="Second Item", content="The second accordion item's content", + header="First Item", + content="The first accordion item's content", + **props, + ), + rx.accordion.item( + header="Second Item", + content="The second accordion item's content", + **props, + ), + rx.accordion.item( + header="Third item", + content="The third accordion item's content", + **props, ), - rx.accordion.item(header="Third item", content="The third accordion item's content"), width="300px", **props, ) AccordionItem: | lambda **props: rx.accordion.root( - rx.accordion.item(header="First Item", content="The first accordion item's content", **props), rx.accordion.item( - header="Second Item", content="The second accordion item's content", **props, + header="Single Item", + content="The accordion item's content", + **props, ), - rx.accordion.item(header="Third item", content="The third accordion item's content", **props), width="300px", ) --- diff --git a/docs/library/disclosure/segmented_control.md b/docs/library/disclosure/segmented_control.md index d889b6b1229..67a7e12ddbf 100644 --- a/docs/library/disclosure/segmented_control.md +++ b/docs/library/disclosure/segmented_control.md @@ -2,6 +2,22 @@ components: - rx.segmented_control.root - rx.segmented_control.item + +SegmentedControlRoot: | + lambda **props: rx.segmented_control.root( + rx.segmented_control.item("Inbox", value="inbox"), + rx.segmented_control.item("Drafts", value="drafts"), + rx.segmented_control.item("Sent", value="sent"), + default_value="inbox", + **props, + ) + +SegmentedControlItem: | + lambda **props: rx.segmented_control.root( + rx.segmented_control.item("Inbox", value="inbox", **props), + rx.segmented_control.item("Drafts", value="drafts"), + default_value="inbox", + ) --- ```python exec diff --git a/docs/library/disclosure/tabs.md b/docs/library/disclosure/tabs.md index 04e44511b6c..4e633d66414 100644 --- a/docs/library/disclosure/tabs.md +++ b/docs/library/disclosure/tabs.md @@ -59,9 +59,9 @@ TabsList: | TabsTrigger: | lambda **props: rx.tabs.root( rx.tabs.list( - rx.tabs.trigger("Account", value="account", **props,), - rx.tabs.trigger("Documents", value="documents"), - rx.tabs.trigger("Settings", value="settings"), + rx.tabs.trigger("Account", value="account", **props), + rx.tabs.trigger("Documents", value="documents", **props), + rx.tabs.trigger("Settings", value="settings", **props), ), rx.box( rx.tabs.content( diff --git a/docs/library/dynamic-rendering/auto_scroll.md b/docs/library/dynamic-rendering/auto_scroll.md index 25cdc511c1f..73ff004feb4 100644 --- a/docs/library/dynamic-rendering/auto_scroll.md +++ b/docs/library/dynamic-rendering/auto_scroll.md @@ -1,3 +1,8 @@ +--- +components: + - rx.auto_scroll +--- + ```python exec import reflex as rx ``` diff --git a/docs/library/dynamic-rendering/cond.md b/docs/library/dynamic-rendering/cond.md index 4ba63ac6a70..4cf0d4b2bf6 100644 --- a/docs/library/dynamic-rendering/cond.md +++ b/docs/library/dynamic-rendering/cond.md @@ -1,3 +1,8 @@ +--- +components: + - rx.cond +--- + ```python exec import reflex as rx ``` diff --git a/docs/library/dynamic-rendering/foreach.md b/docs/library/dynamic-rendering/foreach.md index aee53edfc12..e32b38fa9eb 100644 --- a/docs/library/dynamic-rendering/foreach.md +++ b/docs/library/dynamic-rendering/foreach.md @@ -1,3 +1,8 @@ +--- +components: + - rx.foreach +--- + ```python exec import reflex as rx ``` diff --git a/docs/library/dynamic-rendering/match.md b/docs/library/dynamic-rendering/match.md index e9510e0430c..16e05a35aad 100644 --- a/docs/library/dynamic-rendering/match.md +++ b/docs/library/dynamic-rendering/match.md @@ -1,3 +1,8 @@ +--- +components: + - rx.match +--- + ```python exec import reflex as rx ``` diff --git a/docs/library/forms/input.md b/docs/library/forms/input.md index a26ddc9ad87..e8c766a93c9 100644 --- a/docs/library/forms/input.md +++ b/docs/library/forms/input.md @@ -3,7 +3,7 @@ components: - rx.input - rx.input.slot -Input: | +TextFieldRoot: | lambda **props: rx.input(placeholder="Search the docs", **props) TextFieldSlot: | diff --git a/docs/library/forms/radio_group.md b/docs/library/forms/radio_group.md index a7fcce9c716..bbd7e12bc75 100644 --- a/docs/library/forms/radio_group.md +++ b/docs/library/forms/radio_group.md @@ -21,9 +21,10 @@ RadioGroupItem: | lambda **props: rx.radio_group.root( rx.radio_group.item(value="1", **props), rx.radio_group.item(value="2", **props), - rx.radio_group.item(value="3",), - rx.radio_group.item(value="4",), - rx.radio_group.item(value="5",), + rx.radio_group.item(value="3", **props), + rx.radio_group.item(value="4", **props), + rx.radio_group.item(value="5", **props), + default_value="1", ) --- diff --git a/docs/library/forms/select.md b/docs/library/forms/select.md index 25f1951dbc7..8f5f3f49c85 100644 --- a/docs/library/forms/select.md +++ b/docs/library/forms/select.md @@ -59,8 +59,8 @@ SelectItem: | rx.select.content( rx.select.group( rx.select.item("apple", value="apple", **props), - rx.select.item("grape", value="grape"), - rx.select.item("pear", value="pear"), + rx.select.item("grape", value="grape", **props), + rx.select.item("pear", value="pear", **props), ), ), default_value="pear", diff --git a/docs/library/forms/upload.md b/docs/library/forms/upload.md index c2bce940c8f..38e93a1553f 100644 --- a/docs/library/forms/upload.md +++ b/docs/library/forms/upload.md @@ -151,7 +151,7 @@ def upload_component(): Below is an example of how to allow multiple file uploads (in this case images). ```python demo box -rx.image(src="https://web.reflex-assets.dev/other/upload.gif") +rx.image(src="https://web.reflex-assets.dev/other/upload.webp") ``` ```python diff --git a/docs/library/html/forms.md b/docs/library/html/forms.md new file mode 100644 index 00000000000..cd0a56f1047 --- /dev/null +++ b/docs/library/html/forms.md @@ -0,0 +1,18 @@ +--- +components: + - rx.el.fieldset + - rx.el.form + - rx.el.label + - rx.el.legend + - rx.el.meter + - rx.el.optgroup + - rx.el.option + - rx.el.output + - rx.el.progress + - rx.el.select + - rx.el.textarea +--- + +# HTML Form Elements + +Form-related HTML elements are available through the `rx.el` namespace. diff --git a/docs/library/html/html.md b/docs/library/html/html.md new file mode 100644 index 00000000000..b95278e9499 --- /dev/null +++ b/docs/library/html/html.md @@ -0,0 +1,22 @@ +--- +components: + - rx.el.button + - rx.el.div + - rx.el.input + - rx.el.p + - rx.el.span + - rx.el.a +--- + +# HTML + +Reflex also provides a set of HTML elements that can be used to create web pages. These elements are the same as the HTML elements that are used in web development. These elements come unstyled by default. You can style them using style props or tailwindcss classes. + +The most commonly used HTML elements are listed below. The full API reference is split into smaller pages: + +- [Text elements](/docs/library/html/text) +- [Document and layout elements](/docs/library/html/layout) +- [Form elements](/docs/library/html/forms) +- [Media and embedded elements](/docs/library/html/media) +- [Table elements](/docs/library/html/tables) +- [SVG elements](/docs/library/html/svg) diff --git a/docs/library/html/layout.md b/docs/library/html/layout.md new file mode 100644 index 00000000000..54f68001495 --- /dev/null +++ b/docs/library/html/layout.md @@ -0,0 +1,43 @@ +--- +components: + - rx.el.address + - rx.el.article + - rx.el.aside + - rx.el.blockquote + - rx.el.body + - rx.el.dd + - rx.el.Del + - rx.el.details + - rx.el.dialog + - rx.el.dl + - rx.el.dt + - rx.el.figcaption + - rx.el.footer + - rx.el.h1 + - rx.el.h2 + - rx.el.h3 + - rx.el.h4 + - rx.el.h5 + - rx.el.h6 + - rx.el.head + - rx.el.header + - rx.el.hr + - rx.el.html + - rx.el.li + - rx.el.link + - rx.el.main + - rx.el.nav + - rx.el.noscript + - rx.el.ol + - rx.el.portal + - rx.el.pre + - rx.el.script + - rx.el.section + - rx.el.template + - rx.el.title + - rx.el.ul +--- + +# HTML Layout Elements + +Document structure and layout HTML elements are available through the `rx.el` namespace. diff --git a/docs/library/html/media.md b/docs/library/html/media.md new file mode 100644 index 00000000000..3bc4aa76fb8 --- /dev/null +++ b/docs/library/html/media.md @@ -0,0 +1,20 @@ +--- +components: + - rx.el.area + - rx.el.audio + - rx.el.canvas + - rx.el.embed + - rx.el.iframe + - rx.el.img + - rx.el.math + - rx.el.meta + - rx.el.object + - rx.el.picture + - rx.el.source + - rx.el.track + - rx.el.video +--- + +# HTML Media Elements + +Media and embedded HTML elements are available through the `rx.el` namespace. diff --git a/docs/library/html/svg.md b/docs/library/html/svg.md new file mode 100644 index 00000000000..07717f4bf55 --- /dev/null +++ b/docs/library/html/svg.md @@ -0,0 +1,14 @@ +--- +components: + - rx.el.svg.circle + - rx.el.svg.defs + - rx.el.svg.linear_gradient + - rx.el.svg.polygon + - rx.el.svg.path + - rx.el.svg.rect + - rx.el.svg.stop +--- + +# HTML SVG Elements + +SVG elements are available through the `rx.el.svg` namespace. diff --git a/docs/library/html/tables.md b/docs/library/html/tables.md new file mode 100644 index 00000000000..ead725aa67e --- /dev/null +++ b/docs/library/html/tables.md @@ -0,0 +1,17 @@ +--- +components: + - rx.el.caption + - rx.el.col + - rx.el.colgroup + - rx.el.table + - rx.el.tbody + - rx.el.td + - rx.el.tfoot + - rx.el.th + - rx.el.thead + - rx.el.tr +--- + +# HTML Table Elements + +Table HTML elements are available through the `rx.el` namespace. diff --git a/docs/library/html/text.md b/docs/library/html/text.md new file mode 100644 index 00000000000..c6ec33edc53 --- /dev/null +++ b/docs/library/html/text.md @@ -0,0 +1,33 @@ +--- +components: + - rx.el.abbr + - rx.el.b + - rx.el.bdi + - rx.el.bdo + - rx.el.br + - rx.el.cite + - rx.el.code + - rx.el.data + - rx.el.dfn + - rx.el.em + - rx.el.i + - rx.el.kbd + - rx.el.mark + - rx.el.q + - rx.el.rp + - rx.el.rt + - rx.el.ruby + - rx.el.s + - rx.el.samp + - rx.el.small + - rx.el.strong + - rx.el.sub + - rx.el.sup + - rx.el.time + - rx.el.u + - rx.el.wbr +--- + +# HTML Text Elements + +Text-level HTML elements are available through the `rx.el` namespace. diff --git a/docs/library/layout/center.md b/docs/library/layout/center.md index 3d422553680..3ec68eac1c2 100644 --- a/docs/library/layout/center.md +++ b/docs/library/layout/center.md @@ -1,6 +1,18 @@ --- components: - rx.center + +Center: | + lambda **props: rx.center( + rx.card("Card 1", size="2"), + rx.card("Card 2", size="2"), + rx.card("Card 3", size="2"), + gap="0.75rem", + width="100%", + height="10rem", + border_radius="0.5rem", + **props, + ) --- ```python exec diff --git a/docs/library/layout/flex.md b/docs/library/layout/flex.md index 31f6bcd1458..1dbe38e488c 100644 --- a/docs/library/layout/flex.md +++ b/docs/library/layout/flex.md @@ -1,6 +1,20 @@ --- components: - rx.flex + +Flex: | + lambda **props: rx.flex( + rx.card("Card 1", size="2", width="5rem"), + rx.card("Card 2", size="2", width="5rem"), + rx.card("Card 3", size="2", width="5rem"), + gap="0.75rem", + width="100%", + min_height="10rem", + border="1px dashed var(--gray-7)", + border_radius="0.5rem", + padding="0.75rem", + **props, + ) --- ```python exec diff --git a/docs/library/layout/grid.md b/docs/library/layout/grid.md index f09071a2d71..72baa475857 100644 --- a/docs/library/layout/grid.md +++ b/docs/library/layout/grid.md @@ -1,6 +1,24 @@ --- components: - rx.grid + +Grid: | + lambda **props: rx.grid( + rx.card("Card 1", size="2", width="5rem"), + rx.card("Card 2", size="2", width="5rem"), + rx.card("Card 3", size="2", width="5rem"), + rx.card("Card 4", size="2", width="5rem"), + rx.card("Card 5", size="2", width="5rem"), + rx.card("Card 6", size="2", width="5rem"), + columns="3", + gap="0.75rem", + width="100%", + min_height="10rem", + border="1px dashed var(--gray-7)", + border_radius="0.5rem", + padding="0.75rem", + **props, + ) --- ```python exec diff --git a/docs/library/layout/section.md b/docs/library/layout/section.md index 05f818bd0e1..e7ed0779051 100644 --- a/docs/library/layout/section.md +++ b/docs/library/layout/section.md @@ -1,6 +1,20 @@ --- components: - rx.section + +Section: | + lambda **props: rx.section( + rx.text("Section content", color="black"), + background="white", + border_radius="0.5rem", + width="100%", + justify="center", + border="1px solid var(--gray-3)", + align="center", + display="flex", + padding="4rem", + **props, + ) --- ```python exec diff --git a/docs/library/layout/stack.md b/docs/library/layout/stack.md index 27a6f905513..5fd55d2be8c 100644 --- a/docs/library/layout/stack.md +++ b/docs/library/layout/stack.md @@ -10,6 +10,26 @@ Stack: | height="20vh", **props, ) + +VStack: | + lambda **props: rx.vstack( + rx.card("Card 1", size="2"), + rx.card("Card 2", size="2"), + rx.card("Card 3", size="2"), + width="100%", + border_radius="0.5rem", + **props, + ) + +HStack: | + lambda **props: rx.hstack( + rx.card("Card 1", size="2"), + rx.card("Card 2", size="2"), + rx.card("Card 3", size="2"), + width="100%", + border_radius="0.5rem", + **props, + ) --- ```python exec diff --git a/docs/library/other/html.md b/docs/library/other/html.md deleted file mode 100644 index f74ec858c9d..00000000000 --- a/docs/library/other/html.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -components: - - rx.el.a - - rx.el.abbr - - rx.el.address - - rx.el.area - - rx.el.article - - rx.el.aside - - rx.el.audio - - rx.el.b - - rx.el.bdi - - rx.el.bdo - - rx.el.blockquote - - rx.el.body - - rx.el.br - - rx.el.button - - rx.el.canvas - - rx.el.caption - - rx.el.cite - - rx.el.code - - rx.el.col - - rx.el.colgroup - - rx.el.data - - rx.el.dd - - rx.el.Del - - rx.el.details - - rx.el.dfn - - rx.el.dialog - - rx.el.div - - rx.el.dl - - rx.el.dt - - rx.el.em - - rx.el.embed - - rx.el.fieldset - - rx.el.figcaption - - rx.el.footer - - rx.el.form - - rx.el.h1 - - rx.el.h2 - - rx.el.h3 - - rx.el.h4 - - rx.el.h5 - - rx.el.h6 - - rx.el.head - - rx.el.header - - rx.el.hr - - rx.el.html - - rx.el.i - - rx.el.iframe - - rx.el.img - - rx.el.input - - rx.el.ins - - rx.el.kbd - - rx.el.label - - rx.el.legend - - rx.el.li - - rx.el.link - - rx.el.main - - rx.el.mark - - rx.el.math - - rx.el.meta - - rx.el.meter - - rx.el.nav - - rx.el.noscript - - rx.el.object - - rx.el.ol - - rx.el.optgroup - - rx.el.option - - rx.el.output - - rx.el.p - - rx.el.picture - - rx.el.portal - - rx.el.pre - - rx.el.progress - - rx.el.q - - rx.el.rp - - rx.el.rt - - rx.el.ruby - - rx.el.s - - rx.el.samp - - rx.el.script - - rx.el.section - - rx.el.select - - rx.el.small - - rx.el.source - - rx.el.span - - rx.el.strong - - rx.el.sub - - rx.el.sup - - rx.el.svg.circle - - rx.el.svg.defs - - rx.el.svg.linear_gradient - - rx.el.svg.polygon - - rx.el.svg.path - - rx.el.svg.rect - - rx.el.svg.stop - - rx.el.table - - rx.el.tbody - - rx.el.td - - rx.el.template - - rx.el.textarea - - rx.el.tfoot - - rx.el.th - - rx.el.thead - - rx.el.time - - rx.el.title - - rx.el.tr - - rx.el.track - - rx.el.u - - rx.el.ul - - rx.el.video - - rx.el.wbr ---- - -# HTML - -Reflex also provides a set of HTML elements that can be used to create web pages. These elements are the same as the HTML elements that are used in web development. These elements come unstyled bhy default. You can style them using style props or tailwindcss classes. - -The following is a list of the HTML elements that are available in Reflex: diff --git a/docs/library/other/memo.md b/docs/library/other/memo.md index ba61137957a..f108d34a6a2 100644 --- a/docs/library/other/memo.md +++ b/docs/library/other/memo.md @@ -171,3 +171,17 @@ Avoid using `rx.memo` for: - Simple components where the memoization overhead might exceed the performance gain - Components that almost always receive different props on re-render + +## API Reference + +### `rx.memo` + +```python +rx.memo(component_fn) +``` + +Decorates a function that returns a Reflex component so it can be reused as a memoized component. The function arguments must be type annotated, and memoized components should be called with keyword arguments. + +| Argument | Type | Description | +| --- | --- | --- | +| `component_fn` | `Callable[..., rx.Component]` | Function that returns the component to memoize. | diff --git a/docs/library/overlay/context_menu.md b/docs/library/overlay/context_menu.md index c25a4c202bd..16e26c99fae 100644 --- a/docs/library/overlay/context_menu.md +++ b/docs/library/overlay/context_menu.md @@ -15,7 +15,11 @@ only_low_level: ContextMenuRoot: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)") + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C"), @@ -36,8 +40,12 @@ ContextMenuRoot: | ContextMenuTrigger: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)"), - **props + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + **props, ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C"), @@ -57,7 +65,11 @@ ContextMenuTrigger: | ContextMenuContent: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)") + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C"), @@ -78,7 +90,11 @@ ContextMenuContent: | ContextMenuSub: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)") + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C"), @@ -99,7 +115,11 @@ ContextMenuSub: | ContextMenuSubTrigger: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)") + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C"), @@ -119,7 +139,11 @@ ContextMenuSubTrigger: | ContextMenuSubContent: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)") + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C"), @@ -140,7 +164,11 @@ ContextMenuSubContent: | ContextMenuItem: | lambda **props: rx.context_menu.root( rx.context_menu.trigger( - rx.text("Context Menu (right click)") + rx.button( + "Context Menu (right click)", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), ), rx.context_menu.content( rx.context_menu.item("Copy", shortcut="⌘ C", **props), diff --git a/docs/library/overlay/dropdown_menu.md b/docs/library/overlay/dropdown_menu.md index 4fb770a4a45..f4213dd8099 100644 --- a/docs/library/overlay/dropdown_menu.md +++ b/docs/library/overlay/dropdown_menu.md @@ -12,7 +12,13 @@ only_low_level: DropdownMenuRoot: | lambda **props: rx.menu.root( - rx.menu.trigger(rx.button("drop down menu")), + rx.menu.trigger( + rx.button( + "Click to open Dropdown Menu", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + ), rx.menu.content( rx.menu.item("Edit", shortcut="⌘ E"), rx.menu.item("Share"), @@ -26,12 +32,18 @@ DropdownMenuRoot: | ), ), ), - **props + **props, ) DropdownMenuContent: | lambda **props: rx.menu.root( - rx.menu.trigger(rx.button("drop down menu")), + rx.menu.trigger( + rx.button( + "Click to open Dropdown Menu", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + ), rx.menu.content( rx.menu.item("Edit", shortcut="⌘ E"), rx.menu.item("Share"), @@ -50,7 +62,13 @@ DropdownMenuContent: | DropdownMenuItem: | lambda **props: rx.menu.root( - rx.menu.trigger(rx.button("drop down menu")), + rx.menu.trigger( + rx.button( + "Click to open Dropdown Menu", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + ), rx.menu.content( rx.menu.item("Edit", shortcut="⌘ E", **props), rx.menu.item("Share", **props), @@ -68,7 +86,13 @@ DropdownMenuItem: | DropdownMenuSub: | lambda **props: rx.menu.root( - rx.menu.trigger(rx.button("drop down menu")), + rx.menu.trigger( + rx.button( + "Dropdown Menu", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + ), rx.menu.content( rx.menu.item("Edit", shortcut="⌘ E"), rx.menu.item("Share"), @@ -87,7 +111,13 @@ DropdownMenuSub: | DropdownMenuSubTrigger: | lambda **props: rx.menu.root( - rx.menu.trigger(rx.button("drop down menu")), + rx.menu.trigger( + rx.button( + "Dropdown Menu", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + ), rx.menu.content( rx.menu.item("Edit", shortcut="⌘ E"), rx.menu.item("Share"), @@ -105,7 +135,13 @@ DropdownMenuSubTrigger: | DropdownMenuSubContent: | lambda **props: rx.menu.root( - rx.menu.trigger(rx.button("drop down menu")), + rx.menu.trigger( + rx.button( + "Click to open Dropdown Menu", + color_scheme=props.get("color_scheme"), + variant=props.get("variant"), + ), + ), rx.menu.content( rx.menu.item("Edit", shortcut="⌘ E"), rx.menu.item("Share"), diff --git a/docs/styling/tailwind.md b/docs/styling/tailwind.md index ff9974c3080..12158903442 100644 --- a/docs/styling/tailwind.md +++ b/docs/styling/tailwind.md @@ -254,4 +254,4 @@ def tailwind_dynamic_class_demo(): Reflex core components are built on Radix Themes, which means they come with pre-defined styling. When you apply Tailwind classes to these components, you may encounter styling conflicts or unexpected behavior as the Tailwind styles compete with the built-in Radix styles. -For the best experience when using Tailwind CSS in your Reflex application, we recommend using the lower-level `rx.el` components. These components don't have pre-applied styles, giving you complete control over styling with Tailwind classes without any conflicts. Check the list of HTML components [here](/docs/library/other/html). +For the best experience when using Tailwind CSS in your Reflex application, we recommend using the lower-level `rx.el` components. These components don't have pre-applied styles, giving you complete control over styling with Tailwind classes without any conflicts. Check the list of HTML components [here](/docs/library/html/). diff --git a/packages/reflex-components-radix/src/reflex_components_radix/themes/layout/container.py b/packages/reflex-components-radix/src/reflex_components_radix/themes/layout/container.py index d9ef24a186b..5289e32cd33 100644 --- a/packages/reflex-components-radix/src/reflex_components_radix/themes/layout/container.py +++ b/packages/reflex-components-radix/src/reflex_components_radix/themes/layout/container.py @@ -16,10 +16,7 @@ class Container(elements.Div, RadixThemesComponent): - """Constrains the maximum width of page content. - - See https://www.radix-ui.com/themes/docs/components/container - """ + """Constrains the maximum width of page content.""" tag = "Container" diff --git a/packages/reflex-site-shared/src/reflex_site_shared/components/blocks/typography.py b/packages/reflex-site-shared/src/reflex_site_shared/components/blocks/typography.py index ccf0ed8edb6..a0928d7072f 100644 --- a/packages/reflex-site-shared/src/reflex_site_shared/components/blocks/typography.py +++ b/packages/reflex-site-shared/src/reflex_site_shared/components/blocks/typography.py @@ -110,12 +110,11 @@ def doclink(text: str, href: str, **props) -> rx.Component: Returns: The styled link. """ - return rx.link( + return rx.el.elements.a( text, - underline="always", href=href, **props, - class_name="!text-m-slate-12 dark:!text-m-slate-3 !decoration-m-slate-12 dark:!decoration-m-slate-3", + class_name="text-m-slate-12 dark:text-m-slate-3 decoration-m-slate-12 dark:decoration-m-slate-3 underline", ) @@ -130,10 +129,9 @@ def doclink2(text: str, **props) -> rx.Component: Returns: The styled link. """ - return rx.link( + return rx.el.elements.a( text, - underline="always", **props, style=fonts.base, - class_name="!text-m-slate-12 dark:!text-m-slate-3 !decoration-m-slate-12 dark:!decoration-m-slate-3", + class_name="text-m-slate-12 dark:text-m-slate-3 decoration-m-slate-12 dark:decoration-m-slate-3 underline", )