reflex icon indicating copy to clipboard operation
reflex copied to clipboard

[REF-2574] Default width for Stack (+children) and default padding for container

Open masenf opened this issue 4 months ago β€’ 2 comments

Stack Width

Opt-in to 2 tweaks to improve out of the box experience using layout stacks via the new rx.style.STACK_CHILDREN_FULL_WIDTH style snippet

  • Width is 100%
  • Width of child div (except Box, Upload, and Html), table, and other form controls is 100%

Having a default width of 100% makes components fit nicely in their parent container. This prop often needs to be specified to have things look aligned when placing elements in a stack.

Container prop: stack_children_full_width

An easy way to get the above behavior in your top level container is by passing stack_children_full_width=True. Eventually the experimental opinionated layout component may treat this as its default, but rx.container will remain unopinionated about the width of its children.

Container Padding

The rx.container centers content to a particular width, however when the viewport is less than that width, the content runs directly to the edge, which isn't very aesthetically pleasing. The default padding of 16px means that viewports narrower than the container still get some space on the edge to breathe.

Reducing the default size of the container from "4" to "3" fits better with more screen sizes and is easily changed for apps that want a wider content area.

Results

Sample Code
"""Welcome to Reflex! This file outlines the steps to create a basic app."""

from rxconfig import config

import reflex as rx

docs_url = "https://reflex.dev/docs/getting-started/introduction/"
filename = f"{config.app_name}/{config.app_name}.py"


class State(rx.State):
    """The app state."""


def buttons_card():
    return rx.vstack(
        "Buttons",
        rx.divider(),
        rx.button("Yay"),
        rx.button("Party on"),
        rx.hstack(
            rx.button("Hello"),
            rx.button("world"),
        ),
        rx.card(
            rx.vstack(
                "Child",
                rx.button("Raise"),
            ),
        ),
        rx.hstack(
            rx.icon_button(rx.icon("pencil")),
            rx.icon_button(rx.icon("x"), color_scheme="tomato"),
            rx.icon_button(rx.icon("check"), color_scheme="green"),
            rx.button("Skip", variant="outline"),
        ),
        rx.hstack(
            rx.button("1", color_scheme="crimson"),
            rx.button("2", color_scheme="plum", width="20%"),
            rx.button("3", color_scheme="lime", width="20%"),
        ),
    )


def form_with_labels_card():
    return rx.form(
        rx.vstack(
            "Controls with Labels",
            rx.divider(),
            rx.progress(
                value=45,
            ),
            rx.form.field(
                rx.form.label("How much?"),
                rx.slider(default_value=[24]),
            ),
            rx.form.field(
                rx.form.label("Something to choose"),
                rx.select(
                    items=["One", "Two", "Three"],
                    placeholder="Select an item",
                ),
            ),
            rx.form.field(
                rx.form.label("Input"),
                rx.input(
                    placeholder="Type something",
                ),
            ),
            rx.form.field(
                rx.form.label("This input grows with you"),
                rx.text_area(
                    auto_height=True,
                    rows="1",
                    min_height="0px",
                ),
            ),
            rx.button("Submit"),
        ),
    )


def plain_controls_card():
    return rx.vstack(
        "Plain Controls",
        rx.divider(),
        rx.card(
            rx.vstack(
                rx.hstack(
                    rx.avatar(src="https://reflex.dev/logo.jpg", fallback="R"),
                    rx.text("Reflex User"),
                ),
                rx.hstack(
                    rx.text(
                        "Here's a little bit about the user that you might want to know.",
                        size="1",
                    ),
                    rx.icon_button(
                        rx.icon("pencil"),
                        variant="outline",
                    ),
                ),
            ),
        ),
        rx.progress(
            value=85,
            color_scheme="green",
        ),
        rx.hstack(
            rx.badge("new"),
            rx.badge("fancy"),
            rx.badge("pretty", color_scheme="gold"),
        ),
        rx.slider(default_value=[24]),
        rx.select(
            items=["One", "Two", "Three"],
            placeholder="Select an item",
        ),
        rx.input(
            placeholder="Type something",
        ),
        rx.text_area(
            auto_height=True,
            rows="1",
            min_height="0px",
        ),
        rx.button("Submit"),
    )

def data_display_card():
    return rx.vstack(
        "Data Display",
        rx.divider(),
        rx.callout("This is a callout!"),
        rx.table.root(
            rx.table.header(
                rx.table.row(
                    rx.table.column_header_cell("Name"),
                    rx.table.column_header_cell("Age"),
                ),
            ),
            rx.table.body(
                rx.table.row(
                    rx.table.cell("Alice"),
                    rx.table.cell("25"),
                ),
                rx.table.row(
                    rx.table.cell("Bob"),
                    rx.table.cell("30"),
                ),
                rx.table.row(
                    rx.table.cell("Charlie"),
                    rx.table.cell("35"),
                ),
            ),
        ),
        rx.hstack(
            rx.badge("metal"),
            rx.badge("pop punk"),
            rx.badge("country"),
            rx.badge("hip hop"),
            flex_wrap="wrap",
        ),
        rx.scroll_area(
            rx.text(
                """We the People of the United States, in Order to form a more perfect Union, establish Justice, insure domestic Tranquility, provide for the common defence, promote the general Welfare, and secure the Blessings of Liberty to ourselves and our Posterity, do ordain and establish this Constitution for the United States of America.""",
                size="1",
            ),
            height="80px",
        ),
        rx.list.unordered(
            items=[
                "Alice",
                "Bob",
                "Charlie",
                "John Jacob Jingleheimer Schmidt (that's my name too)",
            ],
        ),
    )


def icon_palette_card():
    return rx.vstack(
        "Icons",
        rx.divider(),
        rx.hstack(
            rx.icon("pencil"),
            rx.icon("trash"),
            rx.icon("octagon_alert"),
            rx.icon("check"),
            rx.icon("x"),
            rx.icon("save"),
            rx.icon("search"),
            rx.icon("accessibility"),
            rx.icon("activity"),
            rx.icon("mail"),
            flex_wrap="wrap",
        ),
    )

def accordion_card():
    return rx.vstack(
        "Accordion",
        rx.divider(),
        rx.accordion.root(
            rx.accordion.item(
                header="Accordion Item 1",
                content=rx.text("Content 1"),
            ),
            rx.accordion.item(
                header="Accordion Item 2",
                content=rx.text("Content 2"),
            ),
            rx.accordion.item(
                header="Accordion Item 3",
                content=rx.text("Content 3"),
            ),
            collapsible=True,
            variant="outline",
        ),
    )


def tabs_card():
    return rx.tabs.root(
        rx.tabs.list(
            rx.tabs.trigger("Foo", value="foo", color_scheme="green"),
            rx.tabs.trigger("Bar", value="bar", color_scheme="red"),
        ),
        rx.tabs.content(
            rx.scroll_area(
                rx.text(
                    """"Foo" is a placeholder name commonly used in computer programming, mathematics, and other technical contexts. It's often used in examples and illustrations when the specific name is not relevant to the concept being discussed. Along with "foo," you might also encounter "bar" and "baz" used in a similar manner. These placeholders help to focus on the structure or logic of the example rather than the specifics of the names involved.""",
                ),
                height="300px",
            ),
            value="foo",
        ),
        rx.tabs.content(
            rx.scroll_area(
                rx.text(
                    """The term "bar" can refer to various things depending on the context:""",
                ),
                rx.list.ordered(
                    items=[
                        """Drinking establishment: A bar is a place where alcoholic beverages are served for consumption on the premises. It's a social venue where people gather to relax, socialize, and enjoy drinks.""",
                        """Unit of Pressure: In physics and engineering, a bar is a unit of pressure. One bar is equivalent to 100,000 pascals, which is roughly equal to atmospheric pressure at sea level.""",
                        """Legal profession: In law, "bar" can refer to the legal profession as a whole, or more specifically, to the barrier or railing separating the area in a courtroom where lawyers, judges, and other legal professionals sit from the rest of the courtroom.""",
                        """Music: In musical notation, a bar (or measure) is a segment of time corresponding to a specific number of beats in which each beat is represented by a particular note value.""",
                        """Solid object: A bar can also refer to a long, narrow, rigid piece of material, such as a metal bar or a candy bar.""",
                    ],
                ),
                height="300px",
            ),
            value="bar",
        ),
        default_value="foo",
    )

def other_form_controls():
    return rx.vstack(
        "Other Form Controls",
        rx.divider(),
        rx.checkbox("Do you Accept?"),
        rx.hstack(
            rx.switch(),
            rx.text("More privacy?", size="2"),
            align="center",
        ),
        rx.radio(
            items=[
                "Option 1",
                "Option 2",
                "Option 3",
            ],
        ),
    )

def charts_card(**props):
    data01 = [
        {"name": "Group A", "value": 400, "fill": rx.color("accent", 10)},
        {"name": "Group B", "value": 300, "fill": rx.color("accent", 9)},
        {"name": "Group C", "value": 300, "fill": rx.color("accent", 8)},
        {"name": "Group D", "value": 200, "fill": rx.color("accent", 7)},
        {"name": "Group E", "value": 278, "fill": rx.color("accent", 6)},
        {"name": "Group F", "value": 189, "fill": rx.color("accent", 5)},
    ]
    data02 = [
        {"name": "Group H", "value": 240, "fill": rx.color("accent", 5)},
        {"name": "Group I", "value": 456, "fill": rx.color("accent", 6)},
        {"name": "Group J", "value": 139, "fill": rx.color("accent", 7)},
        {"name": "Group K", "value": 980, "fill": rx.color("accent", 8)},
        {"name": "Group L", "value": 390, "fill": rx.color("accent", 9)},
        {"name": "Group M", "value": 480, "fill": rx.color("accent", 10)},
    ]
    range_data = [
        {"day": "05-01", "temperature": [-1, 10], "fill": rx.color("accent", 1)},
        {"day": "05-02", "temperature": [2, 15], "fill": rx.color("accent", 2)},
        {"day": "05-03", "temperature": [3, 12], "fill": rx.color("accent", 3)},
        {"day": "05-04", "temperature": [4, 12], "fill": rx.color("accent", 4)},
        {"day": "05-05", "temperature": [12, 16], "fill": rx.color("accent", 5)},
        {"day": "05-06", "temperature": [5, 16], "fill": rx.color("accent", 6)},
        {"day": "05-07", "temperature": [3, 12], "fill": rx.color("accent", 7)},
        {"day": "05-08", "temperature": [0, 8], "fill": rx.color("accent", 8)},
        {"day": "05-09", "temperature": [-3, 5], "fill": rx.color("accent", 9)},
    ]
    sdata01 = [
        {"x": 100, "y": 200, "z": 200},
        {"x": 120, "y": 100, "z": 260},
        {"x": 170, "y": 300, "z": 400},
        {"x": 170, "y": 250, "z": 280},
        {"x": 150, "y": 400, "z": 500},
        {"x": 110, "y": 280, "z": 200},
    ]
    sdata02 = [
        {"x": 200, "y": 260, "z": 240},
        {"x": 240, "y": 290, "z": 220},
        {"x": 190, "y": 290, "z": 250},
        {"x": 198, "y": 250, "z": 210},
        {"x": 180, "y": 280, "z": 260},
        {"x": 210, "y": 220, "z": 230},
    ]
    return rx.vstack(
        "Charts",
        rx.divider(),
        rx.recharts.pie_chart(
            rx.recharts.pie(
                data=data01,
                data_key="value",
                name_key="name",
                cx="75%",
                cy="50%",
                fill="#8dd1e1",
                label=True,
            ),
            rx.recharts.pie(
                data=data02,
                data_key="value",
                name_key="name",
                cx="20%",
                cy="50%",
                fill="#8dd1e1",
                label=True,
            ),
            **props,
        ),
        rx.recharts.bar_chart(
            rx.recharts.bar(
                data_key="temperature",
                stroke="#8884d8",
                fill="#8884d8",
            ),
            rx.recharts.x_axis(data_key="day"),
            rx.recharts.y_axis(),
            data=range_data,
            **props,
        ),
        rx.recharts.scatter_chart(
            rx.recharts.scatter(
                data=sdata01, fill="#8884d8", name="A"
            ),
            rx.recharts.scatter(
                data=sdata02, fill="#82ca9d", name="B"
            ),
            rx.recharts.cartesian_grid(stroke_dasharray="3 3"),
            rx.recharts.x_axis(data_key="x", type_="number"),
            rx.recharts.y_axis(data_key="y"),
            rx.recharts.z_axis(
                data_key="z", range=[60, 400], name="score"
            ),
            rx.recharts.legend(),
            rx.recharts.graphing_tooltip(),
            **props,
        )
    )

def fib_dp_code():
    return rx.vstack(
        rx.text("Implementing Fibonacci with Dynamic Programming"),
        rx.code_block(
            """cache = {0: 0, 1: 1}

def fibonacci_dp(n):
    if n in cache:  # Base case
        return cache[n]
    # Compute and cache the Fibonacci number
    cache[n] = fibonacci_of(n - 1) + fibonacci_of(n - 2)  # Recursive case
    return cache[n]
""",
            max_width="100%",
        ),
        align="center",
    )

def content() -> rx.Component:
    return rx.vstack(
        rx.heading("Welcome to Reflex!", size="9"),
        rx.text("Get started by editing ", rx.code(filename), font_size="2em"),
        rx.button(
            "Check out our docs!",
            on_click=lambda: rx.redirect(docs_url),
            size="4",
        ),
        rx.hstack(
            rx.card(buttons_card()),
            rx.card(form_with_labels_card()),
            rx.card(plain_controls_card()),
        ),
        rx.divider(),
        fib_dp_code(),
        rx.divider(),
        rx.hstack(
            rx.card(data_display_card()),
            rx.vstack(
                rx.card(icon_palette_card()),
                rx.card(accordion_card()),
                rx.card(tabs_card()),
            ),
            rx.vstack(
                rx.card(other_form_controls()),
                rx.card(charts_card()),
            ),
        ),
        rx.hstack(
            rx.button("Hello"),
            rx.button("World"),
            width="50%",
        ),
        rx.vstack(
            rx.upload(
                rx.button("Upload My Dude"),
                class_name=["foo", "bar"],
            ),
            rx.box(
                rx.text("Foo"),
                rx.text("Mcgoo"),
            ),
            rx.button("Button in stack"),
            rx.button("Button with width", width="auto"),
            rx.box(rx.button("Button in box")),
            align="center",
        ),
        rx.box(
            charts_card(height=300),
            width="75%",
        ),
        rx.logo(),
        align="center",
        spacing="7",
    )


def index():
    return rx.container(content())


@rx.page(route="/wide")
def index_wide():
    return rx.container(content(), stack_children_full_width=True)


app = rx.App()
app.add_page(index)

Try out the / and /wide routes to see the difference for yourself.

Before

https://github.com/reflex-dev/reflex/assets/1524005/f5a682a0-19ac-461f-87fe-5edfef8f9e4d

After

Note: this was before changing the buttons to not be stretched! Not the latest version of the PR or sample code.

https://github.com/reflex-dev/reflex/assets/1524005/44682bba-bb2b-41a8-abcf-cf84833420c3

masenf avatar Apr 17 '24 21:04 masenf

Wow looks way better! Nice

Alek99 avatar Apr 18 '24 01:04 Alek99