From 7d4194be3490a21839ff54a367e06c5978af5744 Mon Sep 17 00:00:00 2001 From: Alek Petuskey Date: Sun, 15 Oct 2023 20:02:41 -0700 Subject: [PATCH] Added base template + improve templating code (#1937) --- .../.templates/apps/base/assets/favicon.ico | Bin 0 -> 4286 bytes reflex/.templates/apps/base/assets/github.svg | 10 ++ reflex/.templates/apps/base/assets/icon.svg | 37 ++++ reflex/.templates/apps/base/assets/logo.svg | 68 ++++++++ .../.templates/apps/base/assets/paneleft.svg | 13 ++ reflex/.templates/apps/base/code/__init__.py | 1 + reflex/.templates/apps/base/code/base.py | 90 ++++++++++ .../apps/base/code/pages/__init__.py | 4 + .../apps/base/code/pages/dashboard.py | 28 +++ .../.templates/apps/base/code/pages/home.py | 28 +++ .../apps/base/code/pages/settings.py | 28 +++ reflex/.templates/apps/base/code/sidebar.py | 159 ++++++++++++++++++ reflex/.templates/apps/base/code/state.py | 16 ++ reflex/.templates/apps/base/code/styles.py | 41 +++++ reflex/.templates/apps/counter/counter.py | 53 ------ reflex/.templates/apps/default/__init__.py | 0 .../apps/default/assets/favicon.ico | Bin 0 -> 4286 bytes .../{counter => default/code}/__init__.py | 0 .../apps/default/{ => code}/default.py | 0 reflex/.templates/assets/favicon.ico | Bin 15086 -> 0 bytes reflex/constants/base.py | 11 +- reflex/reflex.py | 2 +- reflex/utils/prerequisites.py | 8 +- 23 files changed, 535 insertions(+), 62 deletions(-) create mode 100644 reflex/.templates/apps/base/assets/favicon.ico create mode 100644 reflex/.templates/apps/base/assets/github.svg create mode 100644 reflex/.templates/apps/base/assets/icon.svg create mode 100644 reflex/.templates/apps/base/assets/logo.svg create mode 100644 reflex/.templates/apps/base/assets/paneleft.svg create mode 100644 reflex/.templates/apps/base/code/__init__.py create mode 100644 reflex/.templates/apps/base/code/base.py create mode 100644 reflex/.templates/apps/base/code/pages/__init__.py create mode 100644 reflex/.templates/apps/base/code/pages/dashboard.py create mode 100644 reflex/.templates/apps/base/code/pages/home.py create mode 100644 reflex/.templates/apps/base/code/pages/settings.py create mode 100644 reflex/.templates/apps/base/code/sidebar.py create mode 100644 reflex/.templates/apps/base/code/state.py create mode 100644 reflex/.templates/apps/base/code/styles.py delete mode 100644 reflex/.templates/apps/counter/counter.py delete mode 100644 reflex/.templates/apps/default/__init__.py create mode 100644 reflex/.templates/apps/default/assets/favicon.ico rename reflex/.templates/apps/{counter => default/code}/__init__.py (100%) rename reflex/.templates/apps/default/{ => code}/default.py (100%) delete mode 100644 reflex/.templates/assets/favicon.ico diff --git a/reflex/.templates/apps/base/assets/favicon.ico b/reflex/.templates/apps/base/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..166ae995eaa63fc96771410a758282dc30e925cf GIT binary patch literal 4286 zcmeHL>rYc>81ELdEe;}zmYd}cUgmJRfwjUwD1`#s5KZP>mMqza#Viv|_7|8f+0+bX zHuqusuw-7Ca`DTu#4U4^o2bjO#K>4%N?Wdi*wZ3Vx%~Ef4}D1`U_EMRg3u z#2#M|V>}}q-@IaO@{9R}d*u7f&~5HfxSkmHVcazU#i30H zAGxQ5Spe!j9`KuGqR@aExK`-}sH1jvqoIp3C7Vm)9Tu=UPE;j^esN~a6^a$ZILngo;^ zGLXl(ZFyY&U!li`6}y-hUQ99v?s`U4O!kgog74FPw-9g+V)qs!jFGEQyvBf><U|E2vRmx|+(VI~S=lT?@~C5pvZOd`x{Q_+3tG6H=gtdWcf z)+7-Zp=UqH^J4sk^>_G-Ufn-2Hz z2mN12|C{5}U`^eCQuFz=F%wp@}SzA1MHEaM^CtJs<{}Tzu$bx2orTKiedgmtVGM{ zdd#vX`&cuiec|My_KW;y{Ryz2kFu9}=~us6hvx1ZqQCk(d+>HP>ks>mmHCjjDh{pe zKQkKpk0SeDX#XMqf$}QV{z=xrN!mQczJAvud@;zFqaU1ocq==Py)qsa=8UKrt!J7r z{RsTo^rgtZo%$rak)DN*D)!(Y^$@yL6Nd=#eu&?unzhH8yq>v{gkt8xcG3S%H)-y_ zqQ1|v|JT$0R~Y}omg2Y+nDvR+K|kzR5i^fmKF>j~N;A35Vr`JWh4yRqKl#P|qlx?` z@|CmBiP}ysYO%m2{eBG6&ix5 zr#u((F2{vb=W4jNmTQh3M^F2o80T49?w>*rv0mt)-o1y!{hRk`E#UVPdna6jnz`rw dKpn)r^--YJZpr;bYU`N~>#v3X5BRU&{{=gv-{1fM literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..166ae995eaa63fc96771410a758282dc30e925cf GIT binary patch literal 4286 zcmeHL>rYc>81ELdEe;}zmYd}cUgmJRfwjUwD1`#s5KZP>mMqza#Viv|_7|8f+0+bX zHuqusuw-7Ca`DTu#4U4^o2bjO#K>4%N?Wdi*wZ3Vx%~Ef4}D1`U_EMRg3u z#2#M|V>}}q-@IaO@{9R}d*u7f&~5HfxSkmHVcazU#i30H zAGxQ5Spe!j9`KuGqR@aExK`-}sH1jvqoIp3C7Vm)9Tu=UPE;j^esN~a6^a$ZILngo;^ zGLXl(ZFyY&U!li`6}y-hUQ99v?s`U4O!kgog74FPw-9g+V)qs!jFGEQyvBf><U|E2vRmx|+(VI~S=lT?@~C5pvZOd`x{Q_+3tG6H=gtdWcf z)+7-Zp=UqH^J4sk^>_G-Ufn-2Hz z2mN12|C{5}U`^eCQuFz=F%wp@}SzA1MHEaM^CtJs<{}Tzu$bx2orTKiedgmtVGM{ zdd#vX`&cuiec|My_KW;y{Ryz2kFu9}=~us6hvx1ZqQCk(d+>HP>ks>mmHCjjDh{pe zKQkKpk0SeDX#XMqf$}QV{z=xrN!mQczJAvud@;zFqaU1ocq==Py)qsa=8UKrt!J7r z{RsTo^rgtZo%$rak)DN*D)!(Y^$@yL6Nd=#eu&?unzhH8yq>v{gkt8xcG3S%H)-y_ zqQ1|v|JT$0R~Y}omg2Y+nDvR+K|kzR5i^fmKF>j~N;A35Vr`JWh4yRqKl#P|qlx?` z@|CmBiP}ysYO%m2{eBG6&ix5 zr#u((F2{vb=W4jNmTQh3M^F2o80T49?w>*rv0mt)-o1y!{hRk`E#UVPdna6jnz`rw dKpn)r^--YJZpr;bYU`N~>#v3X5BRU&{{=gv-{1fM literal 0 HcmV?d00001 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 609f6abcbeb0fec5157b54a36d7b2aeb759f9e1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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