diff --git a/reflex/.templates/apps/base/assets/favicon.ico b/reflex/.templates/apps/base/assets/favicon.ico new file mode 100644 index 000000000..166ae995e Binary files /dev/null and b/reflex/.templates/apps/base/assets/favicon.ico differ diff --git a/reflex/.templates/apps/base/assets/github.svg b/reflex/.templates/apps/base/assets/github.svg new file mode 100644 index 000000000..61c9d791b --- /dev/null +++ b/reflex/.templates/apps/base/assets/github.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/reflex/.templates/apps/base/assets/icon.svg b/reflex/.templates/apps/base/assets/icon.svg new file mode 100644 index 000000000..b9cc89da9 --- /dev/null +++ b/reflex/.templates/apps/base/assets/icon.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reflex/.templates/apps/base/assets/logo.svg b/reflex/.templates/apps/base/assets/logo.svg new file mode 100644 index 000000000..94fe1f511 --- /dev/null +++ b/reflex/.templates/apps/base/assets/logo.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reflex/.templates/apps/base/assets/paneleft.svg b/reflex/.templates/apps/base/assets/paneleft.svg new file mode 100644 index 000000000..ac9c5040a --- /dev/null +++ b/reflex/.templates/apps/base/assets/paneleft.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/reflex/.templates/apps/base/code/__init__.py b/reflex/.templates/apps/base/code/__init__.py new file mode 100644 index 000000000..e1d286346 --- /dev/null +++ b/reflex/.templates/apps/base/code/__init__.py @@ -0,0 +1 @@ +"""Base template for Reflex.""" diff --git a/reflex/.templates/apps/base/code/base.py b/reflex/.templates/apps/base/code/base.py new file mode 100644 index 000000000..a0f9654d9 --- /dev/null +++ b/reflex/.templates/apps/base/code/base.py @@ -0,0 +1,90 @@ +"""Welcome to Reflex! This file outlines the steps to create a basic app.""" +from typing import Callable + +import reflex as rx + +from .pages import dashboard_page, home_page, settings_page +from .sidebar import sidebar +from .styles import * + + +def template(main_content: Callable[[], rx.Component]) -> rx.Component: + """The template for each page of the app. + + Args: + main_content (Callable[[], rx.Component]): The main content of the page. + + Returns: + rx.Component: The template for each page of the app. + """ + menu_button = rx.box( + rx.menu( + rx.menu_button( + rx.icon( + tag="hamburger", + size="4em", + color=text_color, + ), + ), + rx.menu_list( + rx.menu_item(rx.link("Home", href="/", width="100%")), + rx.menu_divider(), + rx.menu_item( + rx.link("About", href="https://github.com/reflex-dev", width="100%") + ), + rx.menu_item( + rx.link("Contact", href="mailto:founders@=reflex.dev", width="100%") + ), + ), + ), + position="fixed", + right="1.5em", + top="1.5em", + z_index="500", + ) + + return rx.hstack( + sidebar(), + main_content(), + rx.spacer(), + menu_button, + align_items="flex-start", + ) + + +@rx.page("/") +@template +def home() -> rx.Component: + """Home page. + + Returns: + rx.Component: The home page. + """ + return home_page() + + +@rx.page("/settings") +@template +def settings() -> rx.Component: + """Settings page. + + Returns: + rx.Component: The settings page. + """ + return settings_page() + + +@rx.page("/dashboard") +@template +def dashboard() -> rx.Component: + """Dashboard page. + + Returns: + rx.Component: The dashboard page. + """ + return dashboard_page() + + +# Add state and page to the app. +app = rx.App(style=base_style) +app.compile() diff --git a/reflex/.templates/apps/base/code/pages/__init__.py b/reflex/.templates/apps/base/code/pages/__init__.py new file mode 100644 index 000000000..b8bb99da9 --- /dev/null +++ b/reflex/.templates/apps/base/code/pages/__init__.py @@ -0,0 +1,4 @@ +"""The pages of the app.""" +from .dashboard import dashboard_page +from .home import home_page +from .settings import settings_page diff --git a/reflex/.templates/apps/base/code/pages/dashboard.py b/reflex/.templates/apps/base/code/pages/dashboard.py new file mode 100644 index 000000000..81beb4a74 --- /dev/null +++ b/reflex/.templates/apps/base/code/pages/dashboard.py @@ -0,0 +1,28 @@ +"""The dashboard page for the template.""" +import reflex as rx + +from ..styles import * + + +def dashboard_page() -> rx.Component: + """The UI for the dashboard page. + + Returns: + rx.Component: The UI for the dashboard page. + """ + return rx.box( + rx.vstack( + rx.heading( + "Dashboard", + size="3em", + ), + rx.text( + "Welcome to Reflex!", + ), + rx.text( + "You can use this template to get started with Reflex.", + ), + style=template_content_style, + ), + style=template_page_style, + ) diff --git a/reflex/.templates/apps/base/code/pages/home.py b/reflex/.templates/apps/base/code/pages/home.py new file mode 100644 index 000000000..aae7f1fb6 --- /dev/null +++ b/reflex/.templates/apps/base/code/pages/home.py @@ -0,0 +1,28 @@ +"""The home page of the app.""" +import reflex as rx + +from ..styles import * + + +def home_page() -> rx.Component: + """The UI for the home page. + + Returns: + rx.Component: The UI for the home page. + """ + return rx.box( + rx.vstack( + rx.heading( + "Home", + size="3em", + ), + rx.text( + "Welcome to Reflex!", + ), + rx.text( + "You can use this template to get started with Reflex.", + ), + style=template_content_style, + ), + style=template_page_style, + ) diff --git a/reflex/.templates/apps/base/code/pages/settings.py b/reflex/.templates/apps/base/code/pages/settings.py new file mode 100644 index 000000000..fdd4eeb52 --- /dev/null +++ b/reflex/.templates/apps/base/code/pages/settings.py @@ -0,0 +1,28 @@ +"""The settings page for the template.""" +import reflex as rx + +from ..styles import * + + +def settings_page() -> rx.Component: + """The UI for the settings page. + + Returns: + rx.Component: The UI for the settings page. + """ + return rx.box( + rx.vstack( + rx.heading( + "Settings", + size="3em", + ), + rx.text( + "Welcome to Reflex!", + ), + rx.text( + "You can use this template to get started with Reflex.", + ), + style=template_content_style, + ), + style=template_page_style, + ) diff --git a/reflex/.templates/apps/base/code/sidebar.py b/reflex/.templates/apps/base/code/sidebar.py new file mode 100644 index 000000000..342d52633 --- /dev/null +++ b/reflex/.templates/apps/base/code/sidebar.py @@ -0,0 +1,159 @@ +"""Sidebar component for the app.""" + +import reflex as rx + +from .state import State +from .styles import * + + +def sidebar_header() -> rx.Component: + """Sidebar header. + + Returns: + rx.Component: The sidebar header component. + """ + return rx.hstack( + rx.image( + src="/icon.svg", + height="2em", + ), + rx.spacer(), + rx.link( + rx.center( + rx.image( + src="/github.svg", + height="3em", + padding="0.5em", + ), + box_shadow=box_shadow, + bg="transparent", + border_radius=border_radius, + _hover={ + "bg": accent_color, + }, + ), + href="https://github.com/reflex-dev/reflex", + ), + width="100%", + border_bottom=border, + padding="1em", + ) + + +def sidebar_footer() -> rx.Component: + """Sidebar footer. + + Returns: + rx.Component: The sidebar footer component. + """ + return rx.hstack( + rx.link( + rx.center( + rx.image( + src="/paneleft.svg", + height="2em", + padding="0.5em", + ), + bg="transparent", + border_radius=border_radius, + _hover={ + "bg": accent_color, + }, + ), + href="https://github.com/reflex-dev/reflex", + ), + rx.spacer(), + rx.link( + rx.text( + "Docs", + ), + href="https://reflex.dev/docs/getting-started/introduction/", + ), + rx.link( + rx.text( + "Blog", + ), + href="https://reflex.dev/blog/", + ), + width="100%", + border_top=border, + padding="1em", + ) + + +def sidebar_item(text: str, icon: str, url: str) -> rx.Component: + """Sidebar item. + + Args: + text (str): The text of the item. + icon (str): The icon of the item. + url (str): The URL of the item. + + Returns: + rx.Component: The sidebar item component. + """ + return rx.link( + rx.hstack( + rx.image( + src=icon, + height="2.5em", + padding="0.5em", + ), + rx.text( + text, + ), + bg=rx.cond( + State.origin_url == f"/{text.lower()}/", + accent_color, + "transparent", + ), + color=rx.cond( + State.origin_url == f"/{text.lower()}/", + accent_text_color, + text_color, + ), + border_radius=border_radius, + box_shadow=box_shadow, + width="100%", + padding_x="1em", + ), + href=url, + width="100%", + ) + + +def sidebar() -> rx.Component: + """Sidebar. + + Returns: + rx.Component: The sidebar component. + """ + return rx.box( + rx.vstack( + sidebar_header(), + rx.vstack( + sidebar_item( + "Dashboard", + "/github.svg", + "/dashboard", + ), + sidebar_item( + "Settings", + "/github.svg", + "/settings", + ), + width="100%", + align_items="flex-start", + padding="1em", + ), + rx.spacer(), + sidebar_footer(), + height="100vh", + ), + min_width="20em", + width="25em", + height="100%", + left="0px", + top="0px", + border_right=border, + ) diff --git a/reflex/.templates/apps/base/code/state.py b/reflex/.templates/apps/base/code/state.py new file mode 100644 index 000000000..573f12684 --- /dev/null +++ b/reflex/.templates/apps/base/code/state.py @@ -0,0 +1,16 @@ +"""Base state for the app.""" + +import reflex as rx + + +class State(rx.State): + """State for the app.""" + + @rx.var + def origin_url(self) -> str: + """Get the url of the current page. + + Returns: + str: The url of the current page. + """ + return self.router_data.get("asPath", "") diff --git a/reflex/.templates/apps/base/code/styles.py b/reflex/.templates/apps/base/code/styles.py new file mode 100644 index 000000000..0290d0bec --- /dev/null +++ b/reflex/.templates/apps/base/code/styles.py @@ -0,0 +1,41 @@ +"""Styles for the app.""" +import reflex as rx + +border_radius = ("0.375rem",) +box_shadow = ("0px 0px 0px 1px rgba(84, 82, 95, 0.14)",) +border = "1px solid #F4F3F6" +text_color = "black" +accent_text_color = "#1A1060" +accent_color = "#F5EFFE" + +template_page_style = { + "height": "100vh", + "width": "100%", + "padding_top": "5em", + "padding_x": "2em", +} + +template_content_style = { + "width": "100%", + "align_items": "flex-start", + "height": "90%", + "box_shadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14)", + "border_radius": border_radius, + "padding": "1em", +} + +link_style = { + "color": text_color, + "text_decoration": "none", + "_hover": { + "color": accent_color, + }, +} + +base_style = { + rx.MenuItem: { + "_hover": { + "bg": accent_color, + }, + }, +} diff --git a/reflex/.templates/apps/counter/counter.py b/reflex/.templates/apps/counter/counter.py deleted file mode 100644 index 5bf2ab4ae..000000000 --- a/reflex/.templates/apps/counter/counter.py +++ /dev/null @@ -1,53 +0,0 @@ -"""Welcome to Reflex! This file creates a counter app.""" -import random - -import reflex as rx - - -class State(rx.State): - """The app state.""" - - count = 0 - - def increment(self): - """Increment the count.""" - self.count += 1 - - def decrement(self): - """Decrement the count.""" - self.count -= 1 - - def random(self): - """Randomize the count.""" - self.count = random.randint(0, 100) - - -def index() -> rx.Component: - return rx.center( - rx.vstack( - rx.heading(State.count), - rx.hstack( - rx.button("Decrement", on_click=State.decrement, color_scheme="red"), - rx.button( - "Randomize", - on_click=State.random, - background_image="linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(0,176,34,1) 100%)", - color="white", - ), - rx.button("Increment", on_click=State.increment, color_scheme="green"), - ), - padding="1em", - bg="#ededed", - border_radius="1em", - box_shadow="lg", - ), - padding_y="5em", - font_size="2em", - text_align="center", - ) - - -# Add state and page to the app. -app = rx.App() -app.add_page(index, title="Counter") -app.compile() diff --git a/reflex/.templates/apps/default/__init__.py b/reflex/.templates/apps/default/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/reflex/.templates/apps/default/assets/favicon.ico b/reflex/.templates/apps/default/assets/favicon.ico new file mode 100644 index 000000000..166ae995e Binary files /dev/null and b/reflex/.templates/apps/default/assets/favicon.ico differ diff --git a/reflex/.templates/apps/counter/__init__.py b/reflex/.templates/apps/default/code/__init__.py similarity index 100% rename from reflex/.templates/apps/counter/__init__.py rename to reflex/.templates/apps/default/code/__init__.py diff --git a/reflex/.templates/apps/default/default.py b/reflex/.templates/apps/default/code/default.py similarity index 100% rename from reflex/.templates/apps/default/default.py rename to reflex/.templates/apps/default/code/default.py diff --git a/reflex/.templates/assets/favicon.ico b/reflex/.templates/assets/favicon.ico deleted file mode 100644 index 609f6abcb..000000000 Binary files a/reflex/.templates/assets/favicon.ico and /dev/null differ diff --git a/reflex/constants/base.py b/reflex/constants/base.py index 490ea2887..ca173d792 100644 --- a/reflex/constants/base.py +++ b/reflex/constants/base.py @@ -77,11 +77,12 @@ class Reflex(SimpleNamespace): class Templates(SimpleNamespace): """Constants related to Templates.""" - class Kind(str, Enum): - """The templates to use for the app.""" + # Dynamically get the enum values from the .templates folder + template_dir = os.path.join(Reflex.ROOT_DIR, Reflex.MODULE_NAME, ".templates/apps") + template_dirs = next(os.walk(template_dir))[1] - DEFAULT = "default" - COUNTER = "counter" + # Create an enum value for each directory in the .templates folder + Kind = Enum("Kind", {template.upper(): template for template in template_dirs}) class Dirs(SimpleNamespace): """Folders used by the template system of Reflex.""" @@ -90,8 +91,6 @@ class Templates(SimpleNamespace): BASE = os.path.join(Reflex.ROOT_DIR, Reflex.MODULE_NAME, ".templates") # The web subdirectory of the template directory. WEB_TEMPLATE = os.path.join(BASE, "web") - # The assets subdirectory of the template directory. - ASSETS_TEMPLATE = os.path.join(BASE, Dirs.APP_ASSETS) # The jinja template directory. JINJA_TEMPLATE = os.path.join(BASE, "jinja") diff --git a/reflex/reflex.py b/reflex/reflex.py index 1c4ce739d..3dc792d63 100644 --- a/reflex/reflex.py +++ b/reflex/reflex.py @@ -54,7 +54,7 @@ def init( None, metavar="APP_NAME", help="The name of the app to initialize." ), template: constants.Templates.Kind = typer.Option( - constants.Templates.Kind.DEFAULT, + constants.Templates.Kind.DEFAULT.value, help="The template to initialize the app with.", ), loglevel: constants.LogLevel = typer.Option( diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index fa78acfb8..fd81419d3 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -209,13 +209,17 @@ def initialize_app_directory(app_name: str, template: constants.Templates.Kind): """ console.log("Initializing the app directory.") path_ops.cp( - os.path.join(constants.Templates.Dirs.BASE, "apps", template.value), app_name + os.path.join(constants.Templates.Dirs.BASE, "apps", template.value, "code"), + app_name, ) path_ops.mv( os.path.join(app_name, template.value + ".py"), os.path.join(app_name, app_name + constants.Ext.PY), ) - path_ops.cp(constants.Templates.Dirs.ASSETS_TEMPLATE, constants.Dirs.APP_ASSETS) + path_ops.cp( + os.path.join(constants.Templates.Dirs.BASE, "apps", template.value, "assets"), + constants.Dirs.APP_ASSETS, + ) def initialize_web_directory():