├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── api ├── .gitignore ├── api │ └── index.py ├── requirements.txt └── vercel.json ├── app ├── __init__.py ├── app.py ├── config.py ├── core │ ├── __init__.py │ ├── base.py │ ├── drawer.py │ ├── footer.py │ ├── header.py │ ├── left.py │ ├── middle.py │ ├── mobile.py │ ├── repository.py │ └── right.py ├── helpers │ ├── __init__.py │ ├── app_config.py │ ├── css_helpers.py │ └── nav_helpers.py ├── material │ ├── __init__.py │ ├── admonition.py │ ├── button.py │ └── typography.py ├── router.py ├── states │ ├── __init__.py │ ├── drawerState.py │ ├── headerState.py │ └── mainState.py ├── styles │ ├── __init__.py │ ├── _admonition.py │ ├── _base.py │ ├── _button.py │ └── _markdown.py └── utilities │ ├── __init__.py │ ├── rx_index.py │ ├── rx_page404.py │ └── rx_template.py ├── assets ├── favicon.ico ├── reflexify.png └── repo.js ├── docs └── app │ ├── app.py │ ├── config.py │ ├── core │ ├── __init__.py │ ├── base.py │ ├── drawer.py │ ├── footer.py │ ├── header.py │ ├── left.py │ ├── middle.py │ ├── mobile.py │ ├── repository.py │ └── right.py │ ├── helpers │ ├── __init__.py │ ├── app_config.py │ ├── css_helpers.py │ └── nav_helpers.py │ ├── material │ ├── __init__.py │ ├── admonition.py │ └── typography.py │ ├── pages │ ├── home │ │ ├── __pycache__ │ │ │ ├── create.cpython-311.pyc │ │ │ ├── publish.cpython-311.pyc │ │ │ ├── run.cpython-311.pyc │ │ │ └── start.cpython-311.pyc │ │ ├── create.py │ │ ├── publish.py │ │ ├── run.py │ │ └── start.py │ ├── index.py │ ├── material │ │ ├── accordion.py │ │ └── setup.py │ ├── page404.py │ └── setup │ │ ├── color.py │ │ ├── drawer.py │ │ ├── font.py │ │ ├── git.py │ │ ├── nav.py │ │ ├── setup.py │ │ └── socials.py │ ├── router.py │ ├── states │ ├── __init__.py │ ├── drawerState.py │ ├── headerState.py │ └── mainState.py │ ├── styles │ ├── __init__.py │ ├── _admonition.py │ ├── _base.py │ └── _markdown.py │ └── utilities │ ├── __init__.py │ ├── rx_index.py │ ├── rx_page404.py │ └── rx_template.py ├── reflexify_scripts ├── __init__.py ├── build.py └── create.py ├── requirements.txt ├── rxconfig.py ├── setup.py └── tests ├── __init__.py ├── test_config.py └── test_helpers.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | release: 9 | types: 10 | - published 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | python-version: [3.8, 3.9, 3.11] 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v3 22 | 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v4 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Upgrade Pip 29 | run: pip install --upgrade pip 30 | 31 | - name: Install dependencies 32 | run: pip install --no-cache-dir -r requirements.txt 33 | 34 | - name: Install build module 35 | run: pip install --no-cache-dir build 36 | 37 | - name: Run tests 38 | run: pytest tests 39 | 40 | - name: Build package 41 | run: python -m build 42 | 43 | publish: 44 | needs: build 45 | runs-on: ubuntu-latest 46 | steps: 47 | - name: Checkout code 48 | uses: actions/checkout@v3 49 | 50 | - name: Set up Python 51 | uses: actions/setup-python@v4 52 | with: 53 | python-version: 3.x 54 | 55 | - name: Install twine & wheel 56 | run: pip install --no-cache-dir twine wheel 57 | 58 | - name: Create Distribution Package 59 | run: python setup.py sdist bdist_wheel 60 | 61 | - name: Publish to PyPI 62 | env: 63 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 64 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 65 | run: | 66 | python -m twine upload --skip-existing dist/* 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.py[cod] 3 | .DS_Store 4 | .web 5 | __pycache__/ 6 | app/pages/ 7 | build 8 | dist 9 | pages 10 | reflex.db 11 | reflexify.egg-info 12 | reflexify.log 13 | venv -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/CHANGELOG.md -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Seyed Ahmad Pour Hakimi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

reflexify.

3 | 4 | 5 | 6 |
7 | 10 | 13 | 16 | 17 | 18 | PyPI version 19 | 20 | 21 | 22 | PyPI downloads 23 | 24 |
25 | 26 |
27 | 28 |

29 | Reflexify is a Python web boilerplate library designed to provide a solid foundation to rapidly build high quality web applications with Python and Reflex. The project comes pre-configured with a range of tools and features to make it easy for developers to get started building their applications, without the need to spend time setting up infrastructure or configuring tools.

30 | 31 | 32 | Reflexify Logo 33 | 34 | ## Installation 35 | 36 | To use Reflexify, you need to have the following installed: 37 | 38 | - Latest version of Reflex 39 | - Python 3.7+ 40 | 41 | If you don't have Reflex installed, installing Reflexify automatically installs it for you. You can install Reflexify using the following command: 42 | ```py 43 | $ pip install reflexify 44 | ``` 45 | 46 | 47 | 48 | ## Application Setup 49 | 50 | After installing Reflexify, you can test if it's working properly by running the following commands: 51 | 52 | Inside the root directory, initiate the reflex application: 53 | 54 | ```py 55 | $ reflex init 56 | ``` 57 | 58 | Once the Reflex application is created, run the following Reflexify command to setup the components: 59 | 60 | ```py 61 | $ rf-init 62 | ``` 63 | 64 | If the package was installed correctly, a folder called ```app``` will be generated inside the root directory. Other directories and files will also be generated. 65 | 66 | Next, run the watch script by entering the following command: 67 | 68 | ```py 69 | $ python3 reflexify_scripts/build.py 70 | ``` 71 | 72 | This script will now monitor your ```config.py``` file inside the ```app``` directory. Specifically, it will watch for changes to your navigation and update the changes accordingly. 73 | 74 | ## 3. Quick Start 75 | 76 | Open the ```config.py``` file inside the ```app``` folder and configure the document as needed. Change the site name, repository link, as well as any theme-related settings. You can also add/remove the navigation section as needed. 77 | 78 | When you're ready, open up a second terminal and move into the root directory of the project you are working on. Run the following command to run your application and view it (as you would normally a Reflex application) 79 | ```py 80 | reflex run 81 | ``` 82 | 83 | If successful, the script should generate the files inside the ```pages``` folder that correspond to the ```config.py``` navigation map. 84 | 85 | If the setup has no error, you can start customizing your pages by adding in your personal layout directly within the generated pages inside the ```pages``` directory. 86 | 87 | ## 4. Configuration API 88 | 89 | To see what goes inside the ```config.py``` file, you can visit the following [Reflexify-API](https://reflexify-api.vercel.app/) for more details. 90 | 91 | ## Contributing 92 | 93 | Contributions are highly encouraged and welcomed. 94 | 95 | 96 | ## License 97 | 98 | Reflexify is open-source and licensed under the [MIT License](LICENSE). 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | .vercel 2 | -------------------------------------------------------------------------------- /api/api/index.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | 3 | app = Flask(__name__) 4 | 5 | data = { 6 | "site_name": { 7 | "type": "string", 8 | "required": False, 9 | }, 10 | "repo_name": { 11 | "type": "string", 12 | "required": False, 13 | }, 14 | "repo_url": { 15 | "type": "string (URL format)", 16 | "required": False, 17 | }, 18 | "copy_right": { 19 | "type": "string", 20 | "required": False, 21 | }, 22 | "attribute": { 23 | "type": "string", 24 | "required": False, 25 | }, 26 | "drawer": { 27 | "type": "bool", 28 | "required": False, 29 | "description": "Removes side drawer from app if True. Note: turn off drawer (set to True) if app is static.", 30 | }, 31 | "theme": { 32 | "primary": { 33 | "type": "string", 34 | "required": True, 35 | }, 36 | "secondary": { 37 | "type": "string", 38 | "required": True, 39 | }, 40 | "font": { 41 | "type": "string", 42 | "required": False, 43 | }, 44 | }, 45 | "socials": { 46 | "key_names": [ 47 | "github", 48 | "twitter", 49 | "youtube", 50 | "mastodon", 51 | "discord", 52 | ], 53 | "value": "url of corresponding social media account.", 54 | "type": "string", 55 | "required": False, 56 | }, 57 | "navigation": { 58 | "description": "Single file or single-level sub-directory. Must have home:index.py key:value pair. Cannot have values with same name.", 59 | "required": True, 60 | }, 61 | } 62 | 63 | 64 | @app.route("/") 65 | def home(): 66 | return jsonify(data) 67 | -------------------------------------------------------------------------------- /api/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==2.2.2 -------------------------------------------------------------------------------- /api/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { "source": "/(.*)", "destination": "/api/index" } 4 | ], 5 | "headers": [ 6 | { 7 | "source": "/(.*)", 8 | "headers": [ 9 | { 10 | "key": "access-control-allow-origin", 11 | "value": "*" 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/app/__init__.py -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | # Import the Reflex library and Reflexify modules 2 | import reflex as rx 3 | from .states.mainState import MainState 4 | from .helpers.css_helpers import CSSHelper 5 | from .router import set_application_routes 6 | 7 | # Create a new Reflex Application instance + pass in main state and app css 8 | app = rx.App(state=MainState, style=CSSHelper.__app_css__()) 9 | 10 | # Call the router to get and set the pages to the application routes 11 | set_application_routes(app) 12 | 13 | # Compile the application and run 14 | app.compile() 15 | -------------------------------------------------------------------------------- /app/config.py: -------------------------------------------------------------------------------- 1 | app_configuration: dict = { 2 | "site_name": "Reflexify", 3 | "repo_name": "LineIndent/reflexify", 4 | "repo_url": "https://github.com/LineIndent/reflexify", 5 | "copy_right": "Copyright © 2022 - 2023 S. Ahmad P. Hakimi", 6 | "attribute": "Made with Reflex & Reflexify", 7 | "drawer": True, 8 | "theme": { 9 | "primary": "black", 10 | "secondary": "teal", 11 | "font": "Helvetica", 12 | }, 13 | "socials": { 14 | "github": "", 15 | "twitter": "", 16 | "youtube": "", 17 | "mastodon": "", 18 | "discord": "", 19 | }, 20 | "navigation": { 21 | "home": {"getting started": "start.py"}, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /app/core/__init__.py: -------------------------------------------------------------------------------- 1 | # from app.core.base import RxBasePage # noqa: F401 2 | -------------------------------------------------------------------------------- /app/core/base.py: -------------------------------------------------------------------------------- 1 | # import the CSSHelper class to easily get application stylesheet 2 | from app.helpers.css_helpers import CSSHelper 3 | from app.helpers.app_config import Config 4 | 5 | # import the core componenets of the web application 6 | from app.core.header import RxHeader 7 | from app.core.left import RxLeft 8 | from app.core.right import RxRight 9 | from app.core.middle import RxMiddle 10 | from app.core.footer import RxFooter 11 | from app.core.drawer import RxDrawer 12 | from app.core.mobile import RxMobileNav 13 | 14 | # import the Reflex library 15 | import reflex as rx 16 | 17 | 18 | class RxBasePage: 19 | def __init__( 20 | self, 21 | components: list, 22 | left_navigation: rx.Component, 23 | right_navigation: list, 24 | mobile_navigation: list, 25 | remove_drawer: bool = Config.__drawer__(), 26 | ): 27 | self.components = components 28 | self.left_navigation = left_navigation 29 | self.right_navigation = right_navigation 30 | self.mobile_navigation = mobile_navigation 31 | self.remove_drawer = remove_drawer 32 | 33 | self.rx_main_stack = rx.vstack(style=CSSHelper.__base_css__()) 34 | 35 | self.rx_header = RxHeader().build() 36 | 37 | nav = self.left_navigation 38 | self.rx_left = ( 39 | RxLeft( 40 | nav, 41 | CSSHelper.__left_css__(), 42 | ) 43 | if nav 44 | else rx.vstack() 45 | ) 46 | 47 | self.rx_mobile_nav = RxMobileNav(self.mobile_navigation) if nav else [] 48 | 49 | self.rx_right = RxRight( 50 | self.right_navigation, 51 | CSSHelper.__right_css__(), 52 | ) 53 | 54 | self.rx_middle = RxMiddle( 55 | self.components, 56 | CSSHelper.__middle_css__(), 57 | ) 58 | 59 | self.rx_footer = RxFooter( 60 | CSSHelper.__footer_style_css__(), 61 | CSSHelper.__footer_socials_css__(), 62 | ).build() 63 | 64 | self.rx_drawer = RxDrawer().build() 65 | 66 | self.rx_base_components = ( 67 | [ 68 | self.set_desktop_layout(), 69 | self.set_mobile_tablet_layout(), 70 | ] 71 | if self.remove_drawer 72 | else [ 73 | self.rx_drawer, 74 | self.set_desktop_layout(), 75 | self.set_mobile_tablet_layout(), 76 | ] 77 | ) 78 | 79 | def set_desktop_layout(self): 80 | return rx.desktop_only( 81 | self.rx_header, 82 | rx.hstack( 83 | self.rx_left, 84 | self.rx_middle, 85 | self.rx_right, 86 | width="100%", 87 | align_items="start", 88 | ), 89 | self.rx_footer, 90 | width="100%", 91 | ) 92 | 93 | def set_mobile_tablet_layout(self): 94 | return rx.mobile_and_tablet( 95 | self.rx_header, 96 | self.rx_mobile_nav, 97 | rx.hstack( 98 | self.rx_middle, 99 | width="100%", 100 | align_items="start", 101 | ), 102 | self.rx_footer, 103 | width="100%", 104 | ) 105 | 106 | def build(self): 107 | self.rx_main_stack.children = self.rx_base_components 108 | 109 | return self.rx_main_stack 110 | -------------------------------------------------------------------------------- /app/core/drawer.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from app.states.drawerState import DrawerState 3 | from app.states.headerState import HeaderState 4 | from app.helpers.app_config import Config 5 | from app.helpers.nav_helpers import NavHelper 6 | from app.helpers.css_helpers import CSSHelper 7 | 8 | 9 | class RxDrawer: 10 | def __init__(self): 11 | self.repo_data = NavHelper.__get_repository__() 12 | self.nav_panel = rx.vstack( 13 | rx.foreach(HeaderState.withNav, router), 14 | spacing="1.25rem", 15 | align_items="start", 16 | ) 17 | 18 | self.rx_drawer = rx.drawer( 19 | rx.drawer_overlay( 20 | rx.drawer_content( 21 | rx.hstack( 22 | rx.heading(Config.__site_name__(), size="lg"), 23 | style=CSSHelper.__drawer_heading_css__(), 24 | ), 25 | rx.hstack( 26 | self.repo_data, 27 | style=CSSHelper.__drawer_repo_css__(), 28 | ), 29 | rx.drawer_body(self.nav_panel), 30 | rx.drawer_footer( 31 | rx.button("Close", on_click=DrawerState.left), 32 | ), 33 | ), 34 | ), 35 | is_open=DrawerState.show_left, 36 | placement="left", 37 | ) 38 | 39 | def build(self): 40 | return self.rx_drawer 41 | 42 | 43 | def router(data: list[str]): 44 | return rx.hstack( 45 | NavHelper.__get_nav_link__( 46 | title=data[0], 47 | route_to=data[1], 48 | size=15, 49 | color=None, 50 | ), 51 | rx.spacer(), 52 | rx.icon( 53 | tag="arrow_forward", 54 | ), 55 | style=CSSHelper.__drawer_router_css__(), 56 | on_click=[DrawerState.left, rx.redirect(data[1])], 57 | ) 58 | -------------------------------------------------------------------------------- /app/core/footer.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from app.helpers.app_config import Config 3 | 4 | 5 | class RxFooter: 6 | def __init__(self, style: dict, socials: dict): 7 | self.style = style 8 | self.socials = socials 9 | 10 | self.rx_footer = rx.hstack(style=self.style) 11 | 12 | self.attribution = rx.vstack( 13 | rx.text(Config.__attribute__()), 14 | rx.text(Config.__copy_right__()), 15 | style=footer_css.get("attribute"), 16 | ) 17 | 18 | self.socials = self.get_user_socials() 19 | 20 | self.rx_footer_desktop = rx.desktop_only( 21 | rx.hstack(self.attribution, rx.spacer(), self.socials), 22 | style=footer_css.get("max_footer"), 23 | ) 24 | 25 | self.rx_footer_tablet = rx.tablet_only( 26 | rx.hstack(self.attribution, rx.spacer(), self.socials), 27 | style=footer_css.get("min_footer"), 28 | ) 29 | 30 | self.rx_footer_mobile = rx.mobile_only( 31 | rx.hstack( 32 | rx.vstack( 33 | self.attribution, self.socials, align_items="start", spacing="1rem" 34 | ) 35 | ), 36 | style=footer_css.get("min_footer"), 37 | ) 38 | 39 | self.rx_footer_components = [ 40 | self.rx_footer_desktop, 41 | self.rx_footer_tablet, 42 | self.rx_footer_mobile, 43 | ] 44 | 45 | def get_user_socials(self): 46 | stack = rx.hstack(spacing="2rem") 47 | socials = Config.__all_social__() 48 | if socials: 49 | for name, url in socials.items(): 50 | if url: 51 | stack.children.append( 52 | rx.link( 53 | rx.html( 54 | self.socials.get(name), 55 | # on_click=rx.redirect(url), 56 | cursor="pointer", 57 | ), 58 | href=url, 59 | _hover={"text_decoration": "None"}, 60 | ) 61 | ) 62 | 63 | return stack 64 | 65 | def build(self): 66 | self.rx_footer.children = self.rx_footer_components 67 | return self.rx_footer 68 | 69 | 70 | footer_css: dict = { 71 | "attribute": { 72 | "font_size": "80%", 73 | "color": "white", 74 | "transition": "all 550ms ease", 75 | "align_items": "start", 76 | }, 77 | "max_footer": { 78 | "width": "100%", 79 | "padding_left": ["", "", "", "4rem", "10rem"], 80 | "padding_right": ["", "", "", "4rem", "10rem"], 81 | "transition": "all 550ms ease", 82 | }, 83 | "min_footer": { 84 | "width": "100%", 85 | "padding_left": ["2rem", "2rem", "2rem", "", ""], 86 | "padding_right": ["2rem", "2rem", "2rem", "", ""], 87 | "transition": "all 550ms ease", 88 | }, 89 | } 90 | -------------------------------------------------------------------------------- /app/core/header.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | from app.states.drawerState import DrawerState 4 | from app.states.headerState import HeaderState 5 | 6 | from app.helpers.nav_helpers import NavHelper 7 | from app.helpers.css_helpers import CSSHelper 8 | from app.helpers.app_config import Config 9 | 10 | 11 | class RxHeader: 12 | def __init__(self): 13 | self.get_repository = NavHelper.__get_repository__() 14 | self.theme_toggle = rx.tooltip( 15 | rx.color_mode_button( 16 | rx.color_mode_icon(), color_scheme="None", color="white" 17 | ), 18 | label="Switch theme mode", 19 | ) 20 | 21 | self.rx_header = rx.hstack( 22 | style=CSSHelper.__header_main_css__(), 23 | ) 24 | 25 | self.site_name = rx.link( 26 | rx.heading( 27 | rx.tooltip(Config.__site_name__(), label="Reflexify", cursor="pointer"), 28 | style=CSSHelper.__header_site_name_css__(), 29 | ), 30 | href="/", 31 | _hover={"text_decoration": "None"}, 32 | ) 33 | self.rx_header_desktop = rx.desktop_only( 34 | rx.hstack( 35 | self.site_name, 36 | self.get_navigation_rail(), 37 | rx.spacer(), 38 | self.theme_toggle, 39 | self.get_repository, 40 | ), 41 | style=CSSHelper.__header_max_header_css__(), 42 | ) 43 | 44 | self.mobile_h_stack = rx.hstack( 45 | rx.hstack( 46 | self.site_name, 47 | spacing="1.5rem", 48 | ), 49 | rx.spacer(), 50 | self.theme_toggle, 51 | ) 52 | 53 | self.rx_header_mobile = ( 54 | rx.mobile_and_tablet( 55 | rx.hstack( 56 | rx.hstack( 57 | self.drawer_visible(), 58 | self.site_name, 59 | spacing="1.5rem", 60 | ), 61 | rx.spacer(), 62 | self.theme_toggle, 63 | ), 64 | style=CSSHelper.__header_min_header_css__(), 65 | ) 66 | if not Config.__drawer__() 67 | else rx.mobile_and_tablet( 68 | rx.hstack( 69 | rx.hstack( 70 | self.site_name, 71 | spacing="1.5rem", 72 | ), 73 | rx.spacer(), 74 | self.theme_toggle, 75 | ), 76 | style=CSSHelper.__header_min_header_css__(), 77 | ) 78 | ) 79 | 80 | self.rx_header_components = [ 81 | self.rx_header_desktop, 82 | self.rx_header_mobile, 83 | ] 84 | 85 | def drawer_visible(self): 86 | return rx.button( 87 | rx.icon(tag="hamburger", style=CSSHelper.__header_icon_css__()), 88 | on_click=DrawerState.left, 89 | color_scheme="None", 90 | ) 91 | 92 | def get_navigation_rail(self): 93 | rail = rx.hstack( 94 | rx.foreach(HeaderState.withNav, router), 95 | style=CSSHelper.__header_navigation_css__(), 96 | spacing="2rem", 97 | ) 98 | 99 | return rail 100 | 101 | def build(self): 102 | self.rx_header.children = self.rx_header_components 103 | return self.rx_header 104 | 105 | 106 | def router(data: list[str]): 107 | return rx.link( 108 | rx.heading( 109 | data[0], 110 | size="s", 111 | padding_top="0.3rem", 112 | color="white", 113 | font_weight="semibold", 114 | ), 115 | href=data[1], 116 | opacity="0.85", 117 | transition="opacity 600ms ease", 118 | _hover={ 119 | "text_decoration": "None", 120 | "opacity": "1", 121 | }, 122 | ) 123 | -------------------------------------------------------------------------------- /app/core/left.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class RxLeft(rx.Vstack): 5 | def __init__(self, components: rx.Component, style: dict): 6 | super().__init__(children=[components], style=style) 7 | -------------------------------------------------------------------------------- /app/core/middle.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class RxMiddle(rx.Vstack): 5 | def __init__( 6 | self, 7 | components: list, 8 | style: dict, 9 | ): 10 | super().__init__( 11 | children=components, 12 | style=style, 13 | ) 14 | -------------------------------------------------------------------------------- /app/core/mobile.py: -------------------------------------------------------------------------------- 1 | from app.helpers.nav_helpers import NavHelper 2 | import reflex as rx 3 | 4 | 5 | class RxMobileNav(rx.Accordion): 6 | def __init__( 7 | self, components: any, allow_multiple=True, width="100%", padding="1rem 2rem" 8 | ): 9 | super().__init__(allow_multiple=allow_multiple, width=width, padding=padding) 10 | 11 | self.components = components 12 | 13 | self.bread_crumb = rx.breadcrumb( 14 | padding="1.25rem 0.5rem", 15 | ) 16 | 17 | self.accordian_item = rx.accordion_item( 18 | rx.accordion_button( 19 | rx.heading("Navigation", size="xs"), 20 | rx.spacer(), 21 | rx.accordion_icon(), 22 | padding="1rem 0.5rem", 23 | ) 24 | ) 25 | 26 | for title, route in self.components: 27 | self.accordian_item.children.append( 28 | rx.accordion_panel( 29 | rx.link( 30 | rx.text(title), 31 | href=route, 32 | _hover={"text_decoration": "None"}, 33 | ), 34 | ), 35 | ) 36 | 37 | main_navigation = [ 38 | [title, route] 39 | for title, route in zip( 40 | NavHelper.__get_navigation_titles__(), 41 | NavHelper.__get_navigation_paths__(), 42 | ) 43 | ] 44 | 45 | for title, route in main_navigation: 46 | self.bread_crumb.children.append( 47 | rx.breadcrumb_item(rx.link(rx.text(title), href=route)) 48 | ) 49 | 50 | self.children = ( 51 | [self.bread_crumb, self.accordian_item] 52 | if self.components 53 | else [self.bread_crumb] 54 | ) 55 | -------------------------------------------------------------------------------- /app/core/repository.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from bs4 import BeautifulSoup 3 | import httpx 4 | from app.helpers.app_config import Config 5 | 6 | 7 | icon_list = [ 8 | "", 9 | "", 10 | "", 11 | ] 12 | 13 | 14 | class RepositoryData: 15 | def __init__(self): 16 | self.rx_repo_data = rx.hstack( 17 | spacing="1.15rem", 18 | cursor="pointer", 19 | ) 20 | 21 | self.git_repo_name = rx.hstack( 22 | rx.text(Config.__repo_name__(), color="white", font_weight="semibold"), 23 | ) 24 | 25 | if Config.__repo_url__(): 26 | self.git_icon = rx.html( 27 | "" 28 | ) 29 | 30 | self.git_repo_data = self.get_repository_data() 31 | 32 | else: 33 | self.git_icon = rx.container() 34 | self.git_repo_data = rx.hstack() 35 | 36 | self.git_data = rx.vstack( 37 | self.git_repo_name, 38 | self.git_repo_data, 39 | spacing="0rem", 40 | align_items="start", 41 | ) 42 | 43 | def get_repository_data(self): 44 | temp_repo_data = rx.hstack() 45 | 46 | span_elements = [ 47 | "css-truncate css-truncate-target text-bold mr-2", 48 | "Counter js-social-count", 49 | "Counter", 50 | ] 51 | 52 | id_elements = ["name", "stars", "forks"] 53 | 54 | with httpx.Client() as client: 55 | response = client.get(Config.__repo_url__()) 56 | data = response.content 57 | 58 | soup = BeautifulSoup(data, "html.parser") 59 | 60 | for i, span in enumerate(span_elements): 61 | span_element = soup.find("span", span) 62 | 63 | if span_element is not None: 64 | txt = span_element.text.strip() 65 | temp_repo_data.children.append( 66 | rx.hstack( 67 | rx.html(icon_list[i]), 68 | rx.text(txt, color="white", font_size=11, id=id_elements[i]), 69 | spacing="0.35rem", 70 | ) 71 | ) 72 | else: 73 | pass 74 | 75 | return temp_repo_data 76 | 77 | def build(self): 78 | self.rx_repo_data.children.append(self.git_icon) 79 | self.rx_repo_data.children.append(self.git_data) 80 | 81 | return rx.tooltip( 82 | rx.link( 83 | self.rx_repo_data, 84 | href=Config.__repo_url__(), 85 | _hover={"text_decoration": "None"}, 86 | ), 87 | label="Go to repository", 88 | ) 89 | -------------------------------------------------------------------------------- /app/core/right.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class RxRight(rx.Vstack): 5 | def __init__(self, components: list[list[str]], style: dict): 6 | super().__init__(style=style) 7 | self.components = components 8 | self.children = [ 9 | rx.link( 10 | rx.text(title), 11 | href=route, 12 | _hover={"text_decoration": "None"}, 13 | ) 14 | for title, route in self.components 15 | ] 16 | -------------------------------------------------------------------------------- /app/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/app/helpers/__init__.py -------------------------------------------------------------------------------- /app/helpers/app_config.py: -------------------------------------------------------------------------------- 1 | from app.config import app_configuration 2 | 3 | 4 | class Config: 5 | data: dict = app_configuration 6 | 7 | @staticmethod 8 | def __site_name__() -> str: 9 | value = Config.data.get("site_name", "Site Name") 10 | return value if value else "Site Name" 11 | 12 | @staticmethod 13 | def __repo_name__() -> str: 14 | value = Config.data.get("repo_name", "") 15 | return value if value else "" 16 | 17 | @staticmethod 18 | def __repo_url__() -> str: 19 | value = Config.data.get("repo_url", "") 20 | return value if value else "" 21 | 22 | @staticmethod 23 | def __copy_right__() -> str: 24 | value = Config.data.get("copy_right", "") 25 | return value if value else "" 26 | 27 | @staticmethod 28 | def __attribute__() -> str: 29 | value = Config.data.get("attribute", "") 30 | return value if value else "" 31 | 32 | @staticmethod 33 | def __drawer__() -> bool: 34 | value = Config.data.get("drawer", False) 35 | return value if value else False 36 | 37 | @staticmethod 38 | def __theme_primary__() -> str: 39 | value = Config.data["theme"].get("primary", "black") 40 | return value if value else "black" 41 | 42 | @staticmethod 43 | def __theme_secondary__() -> str: 44 | value = Config.data["theme"].get("secondary", "orange") 45 | return value if value else "orange" 46 | 47 | @staticmethod 48 | def __theme_font__() -> str: 49 | value = Config.data["theme"].get("font", "Times New Roman") 50 | return value if value else "Times New Roman" 51 | 52 | @staticmethod 53 | def __all_social__() -> dict: 54 | value = Config.data.get("socials", {}) 55 | return value if value else None 56 | 57 | @staticmethod 58 | def __navigation__() -> dict: 59 | value = Config.data.get("navigation", {}) 60 | return value if value else None 61 | -------------------------------------------------------------------------------- /app/helpers/css_helpers.py: -------------------------------------------------------------------------------- 1 | from app.styles._base import base_css 2 | 3 | 4 | class CSSHelper: 5 | @staticmethod 6 | def __base_css__() -> dict: 7 | return base_css 8 | 9 | @staticmethod 10 | def __app_css__() -> dict: 11 | return base_css.get("app", {}) 12 | 13 | @staticmethod 14 | def __base_css__() -> dict: 15 | return base_css.get("base", {}) 16 | 17 | @staticmethod 18 | def __left_css__() -> dict: 19 | return base_css.get("left", {}) 20 | 21 | @staticmethod 22 | def __middle_css__() -> dict: 23 | return base_css.get("middle", {}) 24 | 25 | @staticmethod 26 | def __right_css__() -> dict: 27 | return base_css.get("right", {}) 28 | 29 | @staticmethod 30 | def __header_css__() -> dict: 31 | return base_css.get("header", {}) 32 | 33 | @staticmethod 34 | def __header_main_css__() -> dict: 35 | return CSSHelper.__header_css__().get("main", {}) 36 | 37 | @staticmethod 38 | def __header_icon_css__() -> dict: 39 | return CSSHelper.__header_css__().get("icon", {}) 40 | 41 | @staticmethod 42 | def __header_navigation_css__() -> dict: 43 | return CSSHelper.__header_css__().get("navigation", {}) 44 | 45 | @staticmethod 46 | def __header_link_text_css__() -> dict: 47 | return CSSHelper.__header_css__().get("link_text", {}) 48 | 49 | @staticmethod 50 | def __header_site_name_css__() -> dict: 51 | return CSSHelper.__header_css__().get("site_name", {}) 52 | 53 | @staticmethod 54 | def __header_max_header_css__() -> dict: 55 | return CSSHelper.__header_css__().get("max_header", {}) 56 | 57 | @staticmethod 58 | def __header_min_header_css__() -> dict: 59 | return CSSHelper.__header_css__().get("min_header", {}) 60 | 61 | @staticmethod 62 | def __footer_css__() -> dict: 63 | return base_css.get("footer", {}) 64 | 65 | @staticmethod 66 | def __footer_style_css__() -> dict: 67 | return CSSHelper.__footer_css__().get("style", {}) 68 | 69 | @staticmethod 70 | def __footer_socials_css__() -> dict: 71 | return CSSHelper.__footer_css__().get("socials", {}) 72 | 73 | @staticmethod 74 | def __drawer_css__() -> dict: 75 | return base_css.get("drawer", {}) 76 | 77 | @staticmethod 78 | def __drawer_heading_css__() -> dict: 79 | return CSSHelper.__drawer_css__().get("heading", {}) 80 | 81 | @staticmethod 82 | def __drawer_repo_css__() -> dict: 83 | return CSSHelper.__drawer_css__().get("repo", {}) 84 | 85 | @staticmethod 86 | def __drawer_router_css__() -> dict: 87 | return CSSHelper.__drawer_css__().get("router", {}) 88 | -------------------------------------------------------------------------------- /app/helpers/nav_helpers.py: -------------------------------------------------------------------------------- 1 | from app.core.repository import RepositoryData 2 | from app.helpers.app_config import Config 3 | import reflex as rx 4 | 5 | 6 | class NavHelper: 7 | @staticmethod 8 | def __get_repository__(): 9 | data = RepositoryData().build() 10 | return data 11 | 12 | @staticmethod 13 | def __get_navigation_titles__() -> list: 14 | navigation = ( 15 | [ 16 | name.capitalize() 17 | for name in list( 18 | Config.__navigation__().keys(), 19 | ) 20 | ] 21 | if Config.__navigation__() 22 | else [] 23 | ) 24 | 25 | return navigation 26 | 27 | @staticmethod 28 | def __get_navigation_paths__( 29 | data: dict = Config.__navigation__(), 30 | parent: str = "/", 31 | paths: list = [], 32 | ) -> list[tuple]: 33 | for key, value in data.items(): 34 | if isinstance(value, str) and key == "home": 35 | paths.append("/") 36 | if isinstance(value, dict): 37 | for path in list(value.values()): 38 | paths.append(f"{parent}{key}/{path.split('.py')[0]}") 39 | break 40 | 41 | return paths 42 | 43 | @staticmethod 44 | def __get_nav_link__( 45 | title: str, 46 | route_to: str, 47 | size: str, 48 | color: str, 49 | ): 50 | style = nav_helper_css.copy() 51 | style["text"]["font_size"] = size 52 | style["text"]["color"] = color 53 | return rx.link( 54 | rx.text(title, style=style["text"]), href=route_to, style=style["link"] 55 | ) 56 | 57 | @staticmethod 58 | def __get_left_navigation__( 59 | ref: str, 60 | data: dict = Config.__navigation__(), 61 | ): 62 | paths: list = [] 63 | for key, value in data.items(): 64 | if isinstance(value, dict) and key == ref.lower(): 65 | for title, path in value.items(): 66 | paths.append( 67 | [ 68 | title.capitalize(), 69 | f"/{ref.lower()}/{path.split('.py')[0]}", 70 | ] 71 | ) 72 | return paths 73 | 74 | @staticmethod 75 | def __set_left_navigation__(inc_paths: list) -> rx.Component: 76 | out_paths: list = [] 77 | nav: rx.Component = rx.vstack(style=nav_helper_css["nav"]) 78 | if inc_paths: 79 | for title, route in inc_paths: 80 | out_paths.append( 81 | NavHelper.__get_nav_link__( 82 | title=title.capitalize(), 83 | route_to=route, 84 | size=13, 85 | color=None, 86 | ) 87 | ) 88 | 89 | nav.children = out_paths 90 | return nav 91 | 92 | 93 | nav_helper_css: dict = { 94 | "nav": { 95 | "align_items": "start", 96 | }, 97 | "link": { 98 | "_hover": {"text_decoration": "None"}, 99 | "padding": "0.25rem 0rem", 100 | }, 101 | "text": { 102 | "font_size": "%s", 103 | "font_weight": "500", 104 | "color": "%s", 105 | "opacity": "0.85", 106 | "transition": "opacity 350ms ease", 107 | "_hover": {"opacity": "1"}, 108 | }, 109 | } 110 | -------------------------------------------------------------------------------- /app/material/__init__.py: -------------------------------------------------------------------------------- 1 | from app.material.typography import Header, Title, SubHeader # noqa: F401 2 | from app.material.admonition import Admonition # noqa: F401 3 | from app.material.button import Button # noqa: F401 4 | -------------------------------------------------------------------------------- /app/material/admonition.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from app.styles._admonition import admonition_css 3 | 4 | 5 | css: dict = admonition_css 6 | 7 | 8 | class Admonition: 9 | def __init__( 10 | self, 11 | __type: str, 12 | __title: str, 13 | __components: list = [], 14 | ): 15 | self.__type = __type 16 | self.__title = __title 17 | self.__components = __components 18 | 19 | self.admonition = rx.accordion( 20 | rx.accordion_item( 21 | rx.accordion_button( 22 | rx.icon( 23 | tag=self.__type, 24 | style=css.get(self.__type, "").get("icon", ""), 25 | ), 26 | rx.heading(self.__title, size="sm", padding_left="0.75rem"), 27 | rx.spacer(), 28 | rx.accordion_icon(), 29 | style=css.get(self.__type, "").get("header", ""), 30 | margin="0", 31 | _hover={"opacity": "1"}, 32 | ), 33 | padding="0", 34 | margin="0", 35 | overflow="hidden", 36 | border="0px solid transparent", 37 | ), 38 | width="100%", 39 | allow_multiple=True, 40 | border_radius="6px", 41 | padding="0", 42 | margin="0", 43 | overflow="hidden", 44 | style=css.get(self.__type, "").get("body", ""), 45 | ) 46 | 47 | def build(self): 48 | for item in self.__components: 49 | self.admonition.children[0].children.append(item) 50 | 51 | return self.admonition 52 | -------------------------------------------------------------------------------- /app/material/button.py: -------------------------------------------------------------------------------- 1 | from app.styles._button import button_css 2 | import reflex as rx 3 | 4 | 5 | class Button: 6 | def __init__( 7 | self, 8 | __title: str, 9 | ): 10 | self.__title = __title 11 | 12 | self.button = rx.button( 13 | rx.text(self.__title, font_size="14px", color="white"), 14 | style=button_css, 15 | ) 16 | 17 | def build(self): 18 | return self.button 19 | -------------------------------------------------------------------------------- /app/material/typography.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class Title(rx.Heading): 5 | def __init__( 6 | self, 7 | title: str, 8 | size="2xl", 9 | opacity="0.80", 10 | padding="0rem 0rem", 11 | letter_spacing="0.01em", 12 | ): 13 | super().__init__( 14 | children=[rx.text(title)], 15 | size=size, 16 | opacity=opacity, 17 | padding=padding, 18 | letter_spacing=letter_spacing, 19 | ) 20 | 21 | 22 | class Header(rx.Heading): 23 | def __init__( 24 | self, 25 | title: str, 26 | size="lg", 27 | opacity="0.90", 28 | padding="2rem 0rem", 29 | letter_spacing="0.01em", 30 | ): 31 | super().__init__( 32 | children=[rx.text(title)], 33 | size=size, 34 | opacity=opacity, 35 | padding=padding, 36 | letter_spacing=letter_spacing, 37 | ) 38 | 39 | 40 | class SubHeader(rx.Heading): 41 | def __init__( 42 | self, 43 | title: str, 44 | id: str = None, 45 | size="md", 46 | opacity="1", 47 | padding="0.25rem 0rem", 48 | letter_spacing="0.01em", 49 | ): 50 | super().__init__( 51 | children=[rx.text(title)], 52 | id=id, 53 | size=size, 54 | opacity=opacity, 55 | padding=padding, 56 | letter_spacing=letter_spacing, 57 | ) 58 | -------------------------------------------------------------------------------- /app/router.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import os 3 | import reflex as rx 4 | 5 | 6 | def create_module_from_file_path(file: str, filepath: str): 7 | module_spec = importlib.util.spec_from_file_location(file, filepath) 8 | module = importlib.util.module_from_spec(module_spec) 9 | module_spec.loader.exec_module(module) 10 | return module.RxPage() 11 | 12 | 13 | def get_application_routes_from_pages_dir(app: rx.App): 14 | routes: dict = {} 15 | for root, dirs, files in os.walk("./app/pages"): 16 | for file in files: 17 | filepath = os.path.join(root, file) 18 | if file.endswith(".py") and file != "page404.py": 19 | page_module = create_module_from_file_path(file, filepath) 20 | routes[page_module.__route__()] = page_module 21 | 22 | if file == "page404.py": 23 | filepath = os.path.join(root, file) 24 | page_module = create_module_from_file_path(file, filepath) 25 | app.add_custom_404_page(page_module.build()) 26 | 27 | add_routes_to_app_pages(app, routes) 28 | 29 | 30 | def add_routes_to_app_pages(app: rx.App, routes: dict): 31 | for key, value in routes.items(): 32 | app.add_page( 33 | component=value.build(), 34 | route=key, 35 | title=value.__title__(), 36 | ) 37 | 38 | 39 | def set_application_routes(app: rx.App): 40 | get_application_routes_from_pages_dir(app=app) 41 | -------------------------------------------------------------------------------- /app/states/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/app/states/__init__.py -------------------------------------------------------------------------------- /app/states/drawerState.py: -------------------------------------------------------------------------------- 1 | from .mainState import MainState 2 | 3 | 4 | class DrawerState(MainState): 5 | show_left: bool = False 6 | 7 | def left(self): 8 | self.show_left = not (self.show_left) 9 | -------------------------------------------------------------------------------- /app/states/headerState.py: -------------------------------------------------------------------------------- 1 | from .mainState import MainState 2 | from app.helpers.nav_helpers import NavHelper 3 | 4 | 5 | def get_modified_navigation_list( 6 | titles: callable = NavHelper.__get_navigation_titles__(), 7 | paths: callable = NavHelper.__get_navigation_paths__(), 8 | ): 9 | return [[title, path] for title, path in zip(titles, paths)] 10 | 11 | 12 | class HeaderState(MainState): 13 | withNav: list[list[str]] = get_modified_navigation_list() 14 | -------------------------------------------------------------------------------- /app/states/mainState.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class MainState(rx.State): 5 | 6 | """The app state.""" 7 | 8 | pass 9 | -------------------------------------------------------------------------------- /app/styles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/app/styles/__init__.py -------------------------------------------------------------------------------- /app/styles/_admonition.py: -------------------------------------------------------------------------------- 1 | admonition_css: dict = { 2 | "info": { 3 | "body": { 4 | "transition": "all 550ms ease", 5 | "_light": { 6 | "border": "0.055rem solid #52b6d1", 7 | "_hover": { 8 | "box_shadow": "0px 0.75px 4px 4px rgba(231, 248, 251, 0.85)", 9 | }, 10 | }, 11 | "_dark": { 12 | "border": "0.055rem solid #52b6d1", 13 | "_hover": { 14 | "box_shadow": "0px 0.75px 4px 4px rgba(41, 63, 72, 0.65)", 15 | }, 16 | }, 17 | }, 18 | "header": { 19 | "_light": { 20 | "background": "rgba(231, 248, 251, 1)", 21 | }, 22 | "_dark": { 23 | "background": "rgba(41, 63, 72, 0.65)", 24 | }, 25 | }, 26 | "icon": {"color": "#52b6d1"}, 27 | }, 28 | "warning_two": { 29 | "body": { 30 | "transition": "all 550ms ease", 31 | "_light": { 32 | "border": "0.055rem solid #f09737", 33 | "_hover": { 34 | "box_shadow": "0px 0.75px 4px 4px rgba(253, 244, 231, 0.85)", 35 | }, 36 | }, 37 | "_dark": { 38 | "border": "0.055rem solid #f09737", 39 | "_hover": { 40 | "box_shadow": "0px 0.75px 4px 4px rgba(65, 58, 55, 0.65)", 41 | }, 42 | }, 43 | }, 44 | "header": { 45 | "_light": { 46 | "background": "rgba(253, 244, 231, 1)", 47 | }, 48 | "_dark": { 49 | "background": "rgba(65, 58, 55, 0.65)", 50 | }, 51 | }, 52 | "icon": {"color": "#f09737"}, 53 | }, 54 | "close": { 55 | "body": { 56 | "transition": "all 550ms ease", 57 | "_light": { 58 | "border": "0.055rem solid #ec5f59", 59 | "_hover": { 60 | "box_shadow": "0px 0.75px 4px 4px rgba(252, 238, 237, 0.85)", 61 | }, 62 | }, 63 | "_dark": { 64 | "border": "0.055rem solid #ec5f59", 65 | "_hover": { 66 | "box_shadow": "0px 0.75px 4px 4px rgba(64, 52, 62, 0.65)", 67 | }, 68 | }, 69 | }, 70 | "header": { 71 | "_light": { 72 | "background": "rgba(252, 238, 237, 1)", 73 | }, 74 | "_dark": { 75 | "background": "rgba(64, 52, 62, 0.65)", 76 | }, 77 | }, 78 | "icon": {"color": "#ec5f59"}, 79 | }, 80 | "calendar": { 81 | "body": { 82 | "transition": "all 550ms ease", 83 | "_light": { 84 | "border": "0.055rem solid #5688f7", 85 | "_hover": { 86 | "box_shadow": "0px 0.75px 4px 4px rgba(237, 243, 254, 0.85)", 87 | }, 88 | }, 89 | "_dark": { 90 | "border": "0.055rem solid #5688f7", 91 | "_hover": { 92 | "box_shadow": "0px 0.75px 4px 4px rgba(49, 56, 80, 0.65)", 93 | }, 94 | }, 95 | }, 96 | "header": { 97 | "_light": { 98 | "background": "rgba(237, 243, 254, 1)", 99 | }, 100 | "_dark": { 101 | "background": "rgba(49, 56, 80, 0.65)", 102 | }, 103 | }, 104 | "icon": {"color": "#5688f7"}, 105 | }, 106 | "question": { 107 | "body": { 108 | "transition": "all 550ms ease", 109 | "_light": { 110 | "border": "0.055rem solid #84db46", 111 | "_hover": { 112 | "box_shadow": "0px 0.75px 4px 4px rgba(241, 252, 233, 0.85)", 113 | }, 114 | }, 115 | "_dark": { 116 | "border": "0.055rem solid #84db46", 117 | "_hover": { 118 | "box_shadow": "0px 0.75px 4px 4px rgba(55, 66, 59, 0.65)", 119 | }, 120 | }, 121 | }, 122 | "header": { 123 | "_light": { 124 | "background": "rgba(241, 252, 233, 1)", 125 | }, 126 | "_dark": { 127 | "background": "rgba(55, 66, 59, 0.65)", 128 | }, 129 | }, 130 | "icon": {"color": "#84db46"}, 131 | }, 132 | "check": { 133 | "body": { 134 | "transition": "all 550ms ease", 135 | "_light": { 136 | "border": "0.055rem solid #5ac561", 137 | "_hover": { 138 | "box_shadow": "0px 0.75px 4px 4px rgba(233, 248, 238, 0.85)", 139 | }, 140 | }, 141 | "_dark": { 142 | "border": "0.055rem solid #5ac561", 143 | "_hover": { 144 | "box_shadow": "0px 0.75px 4px 4px rgba(46, 62, 64, 0.65)", 145 | }, 146 | }, 147 | }, 148 | "header": { 149 | "_light": { 150 | "background": "rgba(233, 248, 238, 1)", 151 | }, 152 | "_dark": { 153 | "background": "rgba(46, 62, 64, 0.65)", 154 | }, 155 | }, 156 | "icon": {"color": "#5ac561"}, 157 | }, 158 | } 159 | -------------------------------------------------------------------------------- /app/styles/_base.py: -------------------------------------------------------------------------------- 1 | from app.helpers.app_config import Config 2 | 3 | # set the width and height of social media icons 4 | SOCIAL_SIZE = 19 5 | 6 | # set inverse filteration color scheme for social media icons 7 | SOCIAL_COLOR = r"filter: brightness(0) invert(1)" 8 | 9 | # main base css stylesheet for preconfigured web application 10 | base_css: dict = { 11 | "app": {"font_family": Config.__theme_font__(), "_dark": {"bg": "#1f2028"}}, 12 | "base": { 13 | "width": "100%", 14 | "min_height": "100vh", 15 | "spacing": "0rem", 16 | "padding": "0", 17 | "margin": "0", 18 | }, 19 | "left": { 20 | "width": "20%", 21 | "top": "0", 22 | "position": "sticky", 23 | "padding_top": "5rem", 24 | "align_items": "start", 25 | "padding_left": ["", "", "", "4rem", "10rem"], 26 | "transition": "all 550ms ease", 27 | }, 28 | "middle": { 29 | "width": ["100%", "100%", "100%", "60%", "60%"], 30 | "top": "0", 31 | "position": "block", 32 | "padding_top": ["2rem", "2rem", "2rem", "5rem", "5rem"], 33 | "align_items": "start", 34 | "padding_left": ["2rem", "2rem", "2rem", "2rem", "2rem"], 35 | "padding_right": ["2rem", "2rem", "2rem", "2rem", "2rem"], 36 | "padding_bottom": "6rem", 37 | "transition": "all 550ms ease", 38 | "min_height": "100vh", 39 | }, 40 | "right": { 41 | "width": ["0%", "0%", "0%", "20%", "20%"], 42 | "top": "0", 43 | "position": "sticky", 44 | "padding_top": "5rem", 45 | "align_items": ["end", "end", "end", "start", "start"], 46 | "padding_right": ["1rem", "1rem", "1rem", "", ""], 47 | "transition": "all 550ms ease", 48 | }, 49 | "header": { 50 | "main": { 51 | "width": "100%", 52 | "height": "50px", 53 | "position": "sticky", 54 | "bg": Config.__theme_primary__(), 55 | "transition": "height 350ms ease", 56 | "top": "0", 57 | "box_shadow": "0 3px 6px 0 rgba(0, 0, 0, 0.5)", 58 | "z_index": "2", 59 | }, 60 | "icon": { 61 | "font_size": "xl", 62 | "cursor": "pointer", 63 | "color": "white", 64 | }, 65 | "navigation": { 66 | "align_items": "end", 67 | "transition": "opacity 500ms ease 500ms", 68 | }, 69 | "link_text": { 70 | "size": "s", 71 | "padding_top": "0.3rem", 72 | "color": "white", 73 | "font_weight": "semibold", 74 | }, 75 | "site_name": { 76 | "font_size": ["100%", "115%", "130%", "135%", "150%"], 77 | "color": "white", 78 | "transition": "all 550ms ease", 79 | "opacity": "1", 80 | "_hover": {"opacity": "0.85"}, 81 | "padding_right": "3.5rem", 82 | }, 83 | "max_header": { 84 | "width": "100%", 85 | "padding_left": ["", "", "", "4rem", "10rem"], 86 | "padding_right": ["", "", "", "4rem", "10rem"], 87 | "transition": "all 550ms ease", 88 | }, 89 | "min_header": { 90 | "width": "100%", 91 | "padding_left": ["1rem", "1rem", "0.5rem", "", ""], 92 | "padding_right": ["1rem", "1rem", "0.5rem", "", ""], 93 | "transition": "all 550ms ease", 94 | }, 95 | }, 96 | "footer": { 97 | "style": { 98 | "width": "100%", 99 | "height": ["105px", "75px", "65px", "65px", "65px"], 100 | "position": "sticky", 101 | "bg": "#15171b", 102 | "transition": "height 350ms ease", 103 | "top": "0", 104 | "overflow": "hidden", 105 | }, 106 | "socials": { 107 | "github": f"", # noqa: E501 108 | "twitter": f"", # noqa: E501 109 | "youtube": f"", # noqa: E501 110 | "mastodon": f"", # noqa: E501 111 | "discord": f"", # noqa: E501 112 | }, 113 | }, 114 | "drawer": { 115 | "heading": { 116 | "width": "100%", 117 | "height": "100px", 118 | "align_items": "end", 119 | "bg": Config.__theme_primary__(), 120 | "padding_left": "1rem", 121 | "padding_bottom": "1rem", 122 | "transition": "all 550ms ease", 123 | "color": "white", 124 | }, 125 | "repo": { 126 | "width": "100%", 127 | "height": "45px", 128 | "bg": Config.__theme_primary__(), 129 | "padding_left": "1rem", 130 | "transition": "all 550ms ease", 131 | }, 132 | "router": { 133 | "align_items": "center", 134 | "width": "100%", 135 | "cursor": "pointer", 136 | "opacity": "0.8", 137 | "_hover": {"opacity": "1"}, 138 | }, 139 | }, 140 | } 141 | -------------------------------------------------------------------------------- /app/styles/_button.py: -------------------------------------------------------------------------------- 1 | from app.helpers.app_config import Config 2 | 3 | button_css: dict = { 4 | "bg": Config.__theme_primary__(), 5 | "padding": "1.5rem 2rem", 6 | } 7 | -------------------------------------------------------------------------------- /app/styles/_markdown.py: -------------------------------------------------------------------------------- 1 | FONT_H1 = [f"{56 * size}px" for size in [0.70, 0.80, 0.80, 0.90, 1]] 2 | FONT_H2 = [f"{45 * size}px" for size in [0.70, 0.80, 0.80, 0.90, 1]] 3 | FONT_H3 = [f"{34 * size}px" for size in [0.70, 0.80, 0.80, 0.90, 1]] 4 | 5 | 6 | markdown_css: dict = { 7 | "h1": { 8 | "font_size": FONT_H1, 9 | "font_weight": "400", 10 | "line_height": "1.35", 11 | "letter_spacing": "-0.02em", 12 | "margin_bottom": "24px", 13 | "margin": "0", 14 | "padding": "0", 15 | "transition": "all 550ms ease", 16 | }, 17 | "h2": { 18 | "font_size": FONT_H2, 19 | "font_weight": "400", 20 | "line_height": "48px", 21 | "margin_top": "24px", 22 | "margin_bottom": "24px", 23 | "margin": "0", 24 | "padding": "0", 25 | "transition": "all 550ms ease", 26 | }, 27 | "h3": { 28 | "font_size": FONT_H3, 29 | "font_weight": "400", 30 | "line_height": "40px", 31 | "margin_top": "24px", 32 | "margin_bottom": "24px", 33 | "margin": "0", 34 | "padding": "0", 35 | "transition": "all 550ms ease", 36 | }, 37 | "p": { 38 | "font_size": "14px", 39 | "font_weight": "400", 40 | "line_height": "24px", 41 | "letter_spacing": "0", 42 | "margin_bottom": "16px", 43 | "margin": "0", 44 | "padding": "0", 45 | "transition": "all 550ms ease", 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /app/utilities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/app/utilities/__init__.py -------------------------------------------------------------------------------- /app/utilities/rx_index.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import reflex as rx 4 | 5 | 6 | class RxPage: 7 | # Title of page: must match high-level key in config.py 8 | def __title__(self): 9 | return "Reflexify" 10 | 11 | # Page route path: must follow /parent-key/file-name *without .py extension* 12 | def __route__(self): 13 | return "/" 14 | 15 | # Left navigation panel: automated based on config navigation order 16 | def __left_navigation__(self): 17 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 18 | return NavHelper.__set_left_navigation__(nav) 19 | 20 | # Right navigation panel: TBD 21 | def __right__navigation__(self): 22 | return [] 23 | 24 | # Mobile navigation drop down 25 | def __mobile_navigation__(self): 26 | return NavHelper.__get_left_navigation__(self.__title__()) 27 | 28 | # Main content area: takes in rx.Componenets and passes them to base file 29 | def __components__(self): 30 | return [ 31 | # add your components below # 32 | rx.heading("Welcome to Reflexify!", size="lg", padding_bottom="2rem"), 33 | rx.heading( 34 | "This is your index/landing page. Visit the documentation to get started!", 35 | size="sm", 36 | ) 37 | # end your components above # 38 | ] 39 | 40 | # Build method: creates a new instance for the page above 41 | def build(self): 42 | page = RxBasePage( 43 | self.__components__(), 44 | self.__left_navigation__(), 45 | self.__right__navigation__(), 46 | self.__mobile_navigation__(), 47 | ) 48 | return page.build() 49 | -------------------------------------------------------------------------------- /app/utilities/rx_page404.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | import reflex as rx 3 | 4 | 5 | class RxPage: 6 | # Title of page: must match high-level key in config.py 7 | def __title__(self): 8 | return "Error404" 9 | 10 | # Page route path: must follow /parent-key/file-name *without .py extension* 11 | def __route__(self): 12 | return "/page_error_404" 13 | 14 | # Left navigation panel: automated based on config navigation order 15 | def __left_navigation__(self): 16 | return [] 17 | 18 | # Right navigation panel: TBD 19 | def __right__navigation__(self): 20 | return [] 21 | 22 | # Mobile navigation drop down 23 | def __mobile_navigation__(self): 24 | return [] 25 | 26 | # Main content area: takes in rx.Componenets and passes them to base file 27 | def __components__(self): 28 | return [ 29 | rx.box( 30 | rx.heading("Error 404", size="2xl", padding_bottom="1.5rem"), 31 | rx.heading("The page you are looking for does not exist!", size="md"), 32 | padding_left=["1rem", "1rem", "1rem", "4rem", "9rem"], 33 | transition="all 550ms ease", 34 | ) 35 | ] 36 | 37 | # Build method: creates a new instance for the page above 38 | def build(self): 39 | page = RxBasePage( 40 | self.__components__(), 41 | self.__left_navigation__(), 42 | self.__right__navigation__(), 43 | self.__mobile_navigation__(), 44 | ) 45 | return page.build() 46 | -------------------------------------------------------------------------------- /app/utilities/rx_template.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | 4 | 5 | class RxPage: 6 | # Title of page: must match high-level key in config.py 7 | def __title__(self): 8 | return "" 9 | 10 | # Page route path: must follow /parent-key/file-name *without .py extension* 11 | def __route__(self): 12 | return "<route>" 13 | 14 | # Left navigation panel: automated based on config navigation order 15 | def __left_navigation__(self): 16 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 17 | return NavHelper.__set_left_navigation__(nav) 18 | 19 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 20 | def __right__navigation__(self): 21 | return [] 22 | 23 | # Mobile navigation drop down 24 | def __mobile_navigation__(self): 25 | return NavHelper.__get_left_navigation__(self.__title__()) 26 | 27 | # Main content area: takes in rx.Componenets and passes them to base file 28 | def __components__(self): 29 | return [ 30 | # add your components below # 31 | # end your components above # 32 | ] 33 | 34 | # Build method: creates a new instance for the page above 35 | def build(self): 36 | page = RxBasePage( 37 | self.__components__(), 38 | self.__left_navigation__(), 39 | self.__right__navigation__(), 40 | self.__mobile_navigation__(), 41 | ) 42 | return page.build() 43 | -------------------------------------------------------------------------------- /assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/assets/favicon.ico -------------------------------------------------------------------------------- /assets/reflexify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/assets/reflexify.png -------------------------------------------------------------------------------- /assets/repo.js: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch'; 2 | 3 | const owner = 'LineIndent'; 4 | const repoName = 'reflexify'; 5 | 6 | // GitHub REST API URL to get the repository information 7 | const apiUrl = `https://api.github.com/repos/${owner}/${repoName}`; 8 | 9 | // Function to format a number in shorthand like 'k' 10 | function formatNumberShorthand(number) { 11 | if (number >= 1000) { 12 | return (number / 1000).toFixed(1) + 'k'; 13 | } 14 | return number.toString(); 15 | } 16 | 17 | 18 | // Function to fetch and display repository information, including the latest release 19 | async function getRepoInfo() { 20 | try { 21 | // Dynamically import the 'node-fetch' module as an ES module 22 | // const fetch = (await import('../node-fetch')).default; 23 | 24 | 25 | // Send an HTTP GET request to the GitHub API 26 | const response = await fetch(apiUrl); 27 | const repoInfo = await response.json(); 28 | 29 | const name = document.getElementById("name") 30 | console.log(name) 31 | 32 | 33 | // NOTE * KEEP BELOW CODE * 34 | // const response_html = await fetch(apiUrl); 35 | // const html = await response_html.text(); 36 | 37 | // // Create a DOM using jsdom 38 | // const { window } = new (await import('jsdom')).JSDOM(html); 39 | // const document = window.document; 40 | 41 | 42 | // Check if the response contains the desired information 43 | if (response.status === 200) { 44 | const starsCount = repoInfo.stargazers_count; 45 | const forksCount = repoInfo.forks_count; 46 | 47 | // NOTE * KEEP THE BELOW CODE * 48 | // // Extract the latest release version 49 | // const releaseElement = document.querySelector('.css-truncate.css-truncate-target.text-bold.mr-2'); 50 | // const latestRelease = releaseElement ? releaseElement.textContent.trim() : 'N/A'; 51 | 52 | 53 | // Fetch the latest release information 54 | const releaseUrl = `${apiUrl}/releases/latest`; 55 | const releaseResponse = await fetch(releaseUrl); 56 | const releaseInfo = await releaseResponse.json(); 57 | const latestRelease = releaseInfo.tag_name; 58 | 59 | console.log(`Stars: ${formatNumberShorthand(starsCount)}`); 60 | console.log(`Forks: ${formatNumberShorthand(forksCount)}`); 61 | console.log(`Latest Release: ${latestRelease}`); 62 | } else { 63 | console.log('Error fetching repository information.'); 64 | } 65 | } catch (error) { 66 | console.error("Error fetching data:", error); 67 | } 68 | } 69 | 70 | // Call the function to get and display repository information 71 | getRepoInfo(); 72 | -------------------------------------------------------------------------------- /docs/app/app.py: -------------------------------------------------------------------------------- 1 | # Import the Reflex library and Reflexify modules 2 | import reflex as rx 3 | from .states.mainState import MainState 4 | from .helpers.css_helpers import CSSHelper 5 | from .router import set_application_routes 6 | 7 | # Create a new Reflex Application instance + pass in main state and app css 8 | app = rx.App(state=MainState, style=CSSHelper.__app_css__()) 9 | 10 | # Call the router to get and set the pages to the application routes 11 | set_application_routes(app) 12 | 13 | # Compile the application and run 14 | app.compile() 15 | -------------------------------------------------------------------------------- /docs/app/config.py: -------------------------------------------------------------------------------- 1 | app_configuration: dict = { 2 | "site_name": "Reflexify", 3 | "repo_name": "LineIndent/reflexify", 4 | "repo_url": "https://github.com/LineIndent/reflexify", 5 | "copy_right": "Copyright © 2022 - 2023 S. Ahmad P. Hakimi ", 6 | "attribute": "Made with Reflex & Reflexify", 7 | "drawer": True, 8 | "theme": { 9 | "primary": "black", 10 | "secondary": "teal", 11 | "font": "Helvetica", 12 | }, 13 | "socials": { 14 | "github": "https://github.com/LineIndent/reflexify", 15 | "twitter": "https://twitter.com/getreflex", 16 | "youtube": "https://www.youtube.com/playlist?list=PLDHA4931gtc7wHBDGQOYlmcpZm7qyici7", 17 | "mastodon": "", 18 | "discord": "https://discord.com/invite/T5WSbC2YtQ", 19 | }, 20 | "navigation": { 21 | "home": { 22 | "getting started": "start.py", 23 | "quick start": "run.py", 24 | "creating your site": "create.py", 25 | "publishing your site": "publish.py", 26 | }, 27 | "setup": { 28 | "setup": "setup.py", 29 | "setting up navigation": "nav.py", 30 | "setting up drawer": "drawer.py", 31 | "changing the colors": "color.py", 32 | "changing the fonts": "font.py", 33 | "adding a git repository": "git.py", 34 | "adding social media": "socials.py", 35 | }, 36 | "material": { 37 | "setup": "setup.py", 38 | "accordions": "accordion.py", 39 | }, 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /docs/app/core/__init__.py: -------------------------------------------------------------------------------- 1 | # from app.core.base import RxBasePage # noqa: F401 2 | -------------------------------------------------------------------------------- /docs/app/core/base.py: -------------------------------------------------------------------------------- 1 | # import the CSSHelper class to easily get application stylesheet 2 | from app.helpers.css_helpers import CSSHelper 3 | from app.helpers.app_config import Config 4 | 5 | # import the core componenets of the web application 6 | from app.core.header import RxHeader 7 | from app.core.left import RxLeft 8 | from app.core.right import RxRight 9 | from app.core.middle import RxMiddle 10 | from app.core.footer import RxFooter 11 | from app.core.drawer import RxDrawer 12 | from app.core.mobile import RxMobileNav 13 | 14 | # import the Reflex library 15 | import reflex as rx 16 | 17 | 18 | class RxBasePage: 19 | def __init__( 20 | self, 21 | components: list, 22 | left_navigation: rx.Component, 23 | right_navigation: list, 24 | mobile_navigation: list, 25 | remove_drawer: bool = Config.__drawer__(), 26 | ): 27 | self.components = components 28 | self.left_navigation = left_navigation 29 | self.right_navigation = right_navigation 30 | self.mobile_navigation = mobile_navigation 31 | self.remove_drawer = remove_drawer 32 | 33 | self.rx_main_stack = rx.vstack(style=CSSHelper.__base_css__()) 34 | 35 | self.rx_header = RxHeader().build() 36 | 37 | nav = self.left_navigation 38 | self.rx_left = ( 39 | RxLeft( 40 | nav, 41 | CSSHelper.__left_css__(), 42 | ) 43 | if nav 44 | else rx.vstack() 45 | ) 46 | 47 | self.rx_mobile_nav = RxMobileNav(self.mobile_navigation) if nav else [] 48 | 49 | self.rx_right = RxRight( 50 | self.right_navigation, 51 | CSSHelper.__right_css__(), 52 | ) 53 | 54 | self.rx_middle = RxMiddle( 55 | self.components, 56 | CSSHelper.__middle_css__(), 57 | ) 58 | 59 | self.rx_footer = RxFooter( 60 | CSSHelper.__footer_style_css__(), 61 | CSSHelper.__footer_socials_css__(), 62 | ).build() 63 | 64 | self.rx_drawer = RxDrawer().build() 65 | 66 | self.rx_base_components = ( 67 | [ 68 | self.set_desktop_layout(), 69 | self.set_mobile_tablet_layout(), 70 | ] 71 | if self.remove_drawer 72 | else [ 73 | self.rx_drawer, 74 | self.set_desktop_layout(), 75 | self.set_mobile_tablet_layout(), 76 | ] 77 | ) 78 | 79 | def set_desktop_layout(self): 80 | return rx.desktop_only( 81 | self.rx_header, 82 | rx.hstack( 83 | self.rx_left, 84 | self.rx_middle, 85 | self.rx_right, 86 | width="100%", 87 | align_items="start", 88 | ), 89 | self.rx_footer, 90 | width="100%", 91 | ) 92 | 93 | def set_mobile_tablet_layout(self): 94 | return rx.mobile_and_tablet( 95 | self.rx_header, 96 | self.rx_mobile_nav, 97 | rx.hstack( 98 | self.rx_middle, 99 | width="100%", 100 | align_items="start", 101 | ), 102 | self.rx_footer, 103 | width="100%", 104 | ) 105 | 106 | def build(self): 107 | self.rx_main_stack.children = self.rx_base_components 108 | 109 | return self.rx_main_stack 110 | -------------------------------------------------------------------------------- /docs/app/core/drawer.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from app.states.drawerState import DrawerState 3 | from app.states.headerState import HeaderState 4 | from app.helpers.app_config import Config 5 | from app.helpers.nav_helpers import NavHelper 6 | from app.helpers.css_helpers import CSSHelper 7 | 8 | 9 | class RxDrawer: 10 | def __init__(self): 11 | self.repo_data = NavHelper.__get_repository__() 12 | self.nav_panel = rx.vstack( 13 | rx.foreach(HeaderState.withNav, router), 14 | spacing="1.25rem", 15 | align_items="start", 16 | ) 17 | 18 | self.rx_drawer = rx.drawer( 19 | rx.drawer_overlay( 20 | rx.drawer_content( 21 | rx.hstack( 22 | rx.heading(Config.__site_name__(), size="lg"), 23 | style=CSSHelper.__drawer_heading_css__(), 24 | ), 25 | rx.hstack( 26 | self.repo_data, 27 | style=CSSHelper.__drawer_repo_css__(), 28 | ), 29 | rx.drawer_body(self.nav_panel), 30 | rx.drawer_footer( 31 | rx.button("Close", on_click=DrawerState.left), 32 | ), 33 | ), 34 | ), 35 | is_open=DrawerState.show_left, 36 | placement="left", 37 | ) 38 | 39 | def build(self): 40 | return self.rx_drawer 41 | 42 | 43 | def router(data: list[str]): 44 | return rx.hstack( 45 | NavHelper.__get_nav_link__( 46 | title=data[0], 47 | route_to=data[1], 48 | size=15, 49 | color=None, 50 | ), 51 | rx.spacer(), 52 | rx.icon( 53 | tag="arrow_forward", 54 | ), 55 | style=CSSHelper.__drawer_router_css__(), 56 | on_click=[DrawerState.left, rx.redirect(data[1])], 57 | ) 58 | -------------------------------------------------------------------------------- /docs/app/core/footer.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from app.helpers.app_config import Config 3 | 4 | 5 | class RxFooter: 6 | def __init__(self, style: dict, socials: dict): 7 | self.style = style 8 | self.socials = socials 9 | 10 | self.rx_footer = rx.hstack(style=self.style) 11 | 12 | self.attribution = rx.vstack( 13 | rx.text(Config.__attribute__()), 14 | rx.text(Config.__copy_right__()), 15 | style=footer_css.get("attribute"), 16 | ) 17 | 18 | self.socials = self.get_user_socials() 19 | 20 | self.rx_footer_desktop = rx.desktop_only( 21 | rx.hstack(self.attribution, rx.spacer(), self.socials), 22 | style=footer_css.get("max_footer"), 23 | ) 24 | 25 | self.rx_footer_tablet = rx.tablet_only( 26 | rx.hstack(self.attribution, rx.spacer(), self.socials), 27 | style=footer_css.get("min_footer"), 28 | ) 29 | 30 | self.rx_footer_mobile = rx.mobile_only( 31 | rx.hstack( 32 | rx.vstack( 33 | self.attribution, self.socials, align_items="start", spacing="1rem" 34 | ) 35 | ), 36 | style=footer_css.get("min_footer"), 37 | ) 38 | 39 | self.rx_footer_components = [ 40 | self.rx_footer_desktop, 41 | self.rx_footer_tablet, 42 | self.rx_footer_mobile, 43 | ] 44 | 45 | def get_user_socials(self): 46 | stack = rx.hstack(spacing="2rem") 47 | socials = Config.__all_social__() 48 | if socials: 49 | for name, url in socials.items(): 50 | if url: 51 | stack.children.append( 52 | rx.link( 53 | rx.html( 54 | self.socials.get(name), 55 | # on_click=rx.redirect(url), 56 | cursor="pointer", 57 | ), 58 | href=url, 59 | _hover={"text_decoration": "None"}, 60 | ) 61 | ) 62 | 63 | return stack 64 | 65 | def build(self): 66 | self.rx_footer.children = self.rx_footer_components 67 | return self.rx_footer 68 | 69 | 70 | footer_css: dict = { 71 | "attribute": { 72 | "font_size": "80%", 73 | "color": "white", 74 | "transition": "all 550ms ease", 75 | "align_items": "start", 76 | }, 77 | "max_footer": { 78 | "width": "100%", 79 | "padding_left": ["", "", "", "4rem", "10rem"], 80 | "padding_right": ["", "", "", "4rem", "10rem"], 81 | "transition": "all 550ms ease", 82 | }, 83 | "min_footer": { 84 | "width": "100%", 85 | "padding_left": ["2rem", "2rem", "2rem", "", ""], 86 | "padding_right": ["2rem", "2rem", "2rem", "", ""], 87 | "transition": "all 550ms ease", 88 | }, 89 | } 90 | -------------------------------------------------------------------------------- /docs/app/core/header.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | from app.states.drawerState import DrawerState 4 | from app.states.headerState import HeaderState 5 | 6 | from app.helpers.nav_helpers import NavHelper 7 | from app.helpers.css_helpers import CSSHelper 8 | from app.helpers.app_config import Config 9 | 10 | 11 | class RxHeader: 12 | def __init__(self): 13 | self.get_repository = NavHelper.__get_repository__() 14 | self.theme_toggle = rx.tooltip( 15 | rx.color_mode_button( 16 | rx.color_mode_icon(), color_scheme="None", color="white" 17 | ), 18 | label="Switch theme mode", 19 | ) 20 | self.rx_header = rx.hstack( 21 | style=CSSHelper.__header_main_css__(), 22 | id="header_color", 23 | ) 24 | 25 | self.site_name = rx.link( 26 | rx.heading( 27 | rx.tooltip(Config.__site_name__(), label="Reflexify", cursor="pointer"), 28 | style=CSSHelper.__header_site_name_css__(), 29 | ), 30 | href="/", 31 | _hover={"text_decoration": "None"}, 32 | ) 33 | self.rx_header_desktop = rx.desktop_only( 34 | rx.hstack( 35 | self.site_name, 36 | self.get_navigation_rail(), 37 | rx.spacer(), 38 | self.theme_toggle, 39 | self.get_repository, 40 | ), 41 | style=CSSHelper.__header_max_header_css__(), 42 | ) 43 | 44 | self.mobile_h_stack = rx.hstack( 45 | rx.hstack( 46 | self.site_name, 47 | spacing="1.5rem", 48 | ), 49 | rx.spacer(), 50 | self.theme_toggle, 51 | ) 52 | 53 | self.rx_header_mobile = ( 54 | rx.mobile_and_tablet( 55 | rx.hstack( 56 | rx.hstack( 57 | self.drawer_visible(), 58 | self.site_name, 59 | spacing="1.5rem", 60 | ), 61 | rx.spacer(), 62 | self.theme_toggle, 63 | ), 64 | style=CSSHelper.__header_min_header_css__(), 65 | ) 66 | if not Config.__drawer__() 67 | else rx.mobile_and_tablet( 68 | rx.hstack( 69 | rx.hstack( 70 | self.site_name, 71 | spacing="1.5rem", 72 | ), 73 | rx.spacer(), 74 | self.theme_toggle, 75 | ), 76 | style=CSSHelper.__header_min_header_css__(), 77 | ) 78 | ) 79 | 80 | self.rx_header_components = [ 81 | self.rx_header_desktop, 82 | self.rx_header_mobile, 83 | ] 84 | 85 | def drawer_visible(self): 86 | return rx.button( 87 | rx.icon(tag="hamburger", style=CSSHelper.__header_icon_css__()), 88 | on_click=DrawerState.left, 89 | color_scheme="None", 90 | ) 91 | 92 | def get_navigation_rail(self): 93 | rail = rx.hstack( 94 | rx.foreach(HeaderState.withNav, router), 95 | style=CSSHelper.__header_navigation_css__(), 96 | spacing="2rem", 97 | ) 98 | 99 | return rail 100 | 101 | def build(self): 102 | self.rx_header.children = self.rx_header_components 103 | return self.rx_header 104 | 105 | 106 | def router(data: list[str]): 107 | return rx.link( 108 | rx.heading( 109 | data[0], 110 | size="s", 111 | padding_top="0.3rem", 112 | color="white", 113 | font_weight="semibold", 114 | ), 115 | href=data[1], 116 | opacity="0.85", 117 | transition="opacity 600ms ease", 118 | _hover={ 119 | "text_decoration": "None", 120 | "opacity": "1", 121 | }, 122 | ) 123 | -------------------------------------------------------------------------------- /docs/app/core/left.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class RxLeft(rx.Vstack): 5 | def __init__(self, components: rx.Component, style: dict): 6 | super().__init__(children=[components], style=style) 7 | -------------------------------------------------------------------------------- /docs/app/core/middle.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class RxMiddle(rx.Vstack): 5 | def __init__( 6 | self, 7 | components: list, 8 | style: dict, 9 | ): 10 | super().__init__( 11 | children=components, 12 | style=style, 13 | ) 14 | -------------------------------------------------------------------------------- /docs/app/core/mobile.py: -------------------------------------------------------------------------------- 1 | from app.helpers.nav_helpers import NavHelper 2 | import reflex as rx 3 | 4 | 5 | class RxMobileNav(rx.Accordion): 6 | def __init__( 7 | self, components: any, allow_multiple=True, width="100%", padding="1rem 2rem" 8 | ): 9 | super().__init__(allow_multiple=allow_multiple, width=width, padding=padding) 10 | 11 | self.components = components 12 | 13 | self.bread_crumb = rx.breadcrumb( 14 | padding="1.25rem 0.5rem", 15 | ) 16 | 17 | self.accordian_item = rx.accordion_item( 18 | rx.accordion_button( 19 | rx.heading("Navigation", size="xs"), 20 | rx.spacer(), 21 | rx.accordion_icon(), 22 | padding="1rem 0.5rem", 23 | ) 24 | ) 25 | 26 | for title, route in self.components: 27 | self.accordian_item.children.append( 28 | rx.accordion_panel( 29 | rx.link( 30 | rx.text(title), 31 | href=route, 32 | _hover={"text_decoration": "None"}, 33 | ), 34 | ), 35 | ) 36 | 37 | main_navigation = [ 38 | [title, route] 39 | for title, route in zip( 40 | NavHelper.__get_navigation_titles__(), 41 | NavHelper.__get_navigation_paths__(), 42 | ) 43 | ] 44 | 45 | for title, route in main_navigation: 46 | self.bread_crumb.children.append( 47 | rx.breadcrumb_item(rx.link(rx.text(title), href=route)) 48 | ) 49 | 50 | self.children = ( 51 | [self.bread_crumb, self.accordian_item] 52 | if self.components 53 | else [self.bread_crumb] 54 | ) 55 | -------------------------------------------------------------------------------- /docs/app/core/repository.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from bs4 import BeautifulSoup 3 | import httpx 4 | from app.helpers.app_config import Config 5 | 6 | 7 | icon_list = [ 8 | "<img width='10' height='10' src='https://img.icons8.com/ios-filled/50/price-tag.png' style='filter: brightness(0) invert(1);' />", 9 | "<img width='10' height='10' src='https://img.icons8.com/ios-filled/50/star--v1.png' style='filter: brightness(0) invert(1);'/>", 10 | "<img width='10' height='10' src='https://img.icons8.com/ios/50/code-fork.png' style='filter: brightness(0) invert(1);'/>", 11 | ] 12 | 13 | 14 | class RepositoryData: 15 | def __init__(self): 16 | self.rx_repo_data = rx.hstack( 17 | spacing="1.15rem", 18 | cursor="pointer", 19 | ) 20 | 21 | self.git_repo_name = rx.hstack( 22 | rx.text(Config.__repo_name__(), color="white", font_weight="semibold"), 23 | ) 24 | 25 | if Config.__repo_url__(): 26 | self.git_icon = rx.html( 27 | "<img width='24' height='24' src='https://img.icons8.com/ios-filled/50/000000/git.png' style='filter: brightness(0) invert(1);'/>" 28 | ) 29 | 30 | self.git_repo_data = self.get_repository_data() 31 | 32 | else: 33 | self.git_icon = rx.container() 34 | self.git_repo_data = rx.hstack() 35 | 36 | self.git_data = rx.vstack( 37 | self.git_repo_name, 38 | self.git_repo_data, 39 | spacing="0rem", 40 | align_items="start", 41 | ) 42 | 43 | def get_repository_data(self): 44 | temp_repo_data = rx.hstack() 45 | 46 | span_elements = [ 47 | "css-truncate css-truncate-target text-bold mr-2", 48 | "Counter js-social-count", 49 | "Counter", 50 | ] 51 | 52 | with httpx.Client() as client: 53 | response = client.get(Config.__repo_url__()) 54 | data = response.content 55 | 56 | soup = BeautifulSoup(data, "html.parser") 57 | 58 | for i, span in enumerate(span_elements): 59 | span_element = soup.find("span", span) 60 | 61 | if span_element is not None: 62 | txt = span_element.text.strip() 63 | 64 | temp_repo_data.children.append( 65 | rx.hstack( 66 | rx.html(icon_list[i]), 67 | rx.text(txt, color="white", font_size=11), 68 | spacing="0.35rem", 69 | ) 70 | ) 71 | else: 72 | pass 73 | 74 | return temp_repo_data 75 | 76 | def build(self): 77 | self.rx_repo_data.children.append(self.git_icon) 78 | self.rx_repo_data.children.append(self.git_data) 79 | 80 | return rx.tooltip( 81 | rx.link( 82 | self.rx_repo_data, 83 | href=Config.__repo_url__(), 84 | _hover={"text_decoration": "None"}, 85 | ), 86 | label="Go to repository", 87 | ) 88 | -------------------------------------------------------------------------------- /docs/app/core/right.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class RxRight(rx.Vstack): 5 | def __init__(self, components: list[list[str]], style: dict): 6 | super().__init__(style=style) 7 | self.components = components 8 | self.children = [ 9 | rx.link( 10 | rx.text(title), 11 | href=route, 12 | _hover={"text_decoration": "None"}, 13 | ) 14 | for title, route in self.components 15 | ] 16 | -------------------------------------------------------------------------------- /docs/app/helpers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/helpers/__init__.py -------------------------------------------------------------------------------- /docs/app/helpers/app_config.py: -------------------------------------------------------------------------------- 1 | from app.config import app_configuration 2 | 3 | 4 | class Config: 5 | data: dict = app_configuration 6 | 7 | @staticmethod 8 | def __site_name__() -> str: 9 | value = Config.data.get("site_name", "Site Name") 10 | return value if value else "Site Name" 11 | 12 | @staticmethod 13 | def __repo_name__() -> str: 14 | value = Config.data.get("repo_name", "") 15 | return value if value else "" 16 | 17 | @staticmethod 18 | def __repo_url__() -> str: 19 | value = Config.data.get("repo_url", "") 20 | return value if value else "" 21 | 22 | @staticmethod 23 | def __copy_right__() -> str: 24 | value = Config.data.get("copy_right", "") 25 | return value if value else "" 26 | 27 | @staticmethod 28 | def __attribute__() -> str: 29 | value = Config.data.get("attribute", "") 30 | return value if value else "" 31 | 32 | @staticmethod 33 | def __drawer__() -> bool: 34 | value = Config.data.get("drawer", False) 35 | return value if value else False 36 | 37 | @staticmethod 38 | def __theme_primary__() -> str: 39 | value = Config.data["theme"].get("primary", "orange") 40 | return value if value else "orange" 41 | 42 | @staticmethod 43 | def __theme_secondary__() -> str: 44 | value = Config.data["theme"].get("secondary", "orange") 45 | return value if value else "orange" 46 | 47 | @staticmethod 48 | def __theme_font__() -> str: 49 | value = Config.data["theme"].get("font", "Times New Roman") 50 | return value if value else "Times New Roman" 51 | 52 | @staticmethod 53 | def __all_social__(): 54 | value = Config.data.get("socials", {}) 55 | return value if value else None 56 | 57 | @staticmethod 58 | def __navigation__(): 59 | value = Config.data.get("navigation", {}) 60 | return value if value else None 61 | -------------------------------------------------------------------------------- /docs/app/helpers/css_helpers.py: -------------------------------------------------------------------------------- 1 | from app.styles._base import base_css 2 | 3 | 4 | class CSSHelper: 5 | @staticmethod 6 | def __base_css__() -> dict: 7 | return base_css 8 | 9 | @staticmethod 10 | def __app_css__() -> dict: 11 | return base_css.get("app", {}) 12 | 13 | @staticmethod 14 | def __base_css__() -> dict: 15 | return base_css.get("base", {}) 16 | 17 | @staticmethod 18 | def __left_css__() -> dict: 19 | return base_css.get("left", {}) 20 | 21 | @staticmethod 22 | def __middle_css__() -> dict: 23 | return base_css.get("middle", {}) 24 | 25 | @staticmethod 26 | def __right_css__() -> dict: 27 | return base_css.get("right", {}) 28 | 29 | @staticmethod 30 | def __header_css__() -> dict: 31 | return base_css.get("header", {}) 32 | 33 | @staticmethod 34 | def __header_main_css__() -> dict: 35 | return CSSHelper.__header_css__().get("main", {}) 36 | 37 | @staticmethod 38 | def __header_icon_css__() -> dict: 39 | return CSSHelper.__header_css__().get("icon", {}) 40 | 41 | @staticmethod 42 | def __header_navigation_css__() -> dict: 43 | return CSSHelper.__header_css__().get("navigation", {}) 44 | 45 | @staticmethod 46 | def __header_link_text_css__() -> dict: 47 | return CSSHelper.__header_css__().get("link_text", {}) 48 | 49 | @staticmethod 50 | def __header_site_name_css__() -> dict: 51 | return CSSHelper.__header_css__().get("site_name", {}) 52 | 53 | @staticmethod 54 | def __header_max_header_css__() -> dict: 55 | return CSSHelper.__header_css__().get("max_header", {}) 56 | 57 | @staticmethod 58 | def __header_min_header_css__() -> dict: 59 | return CSSHelper.__header_css__().get("min_header", {}) 60 | 61 | @staticmethod 62 | def __footer_css__() -> dict: 63 | return base_css.get("footer", {}) 64 | 65 | @staticmethod 66 | def __footer_style_css__() -> dict: 67 | return CSSHelper.__footer_css__().get("style", {}) 68 | 69 | @staticmethod 70 | def __footer_socials_css__() -> dict: 71 | return CSSHelper.__footer_css__().get("socials", {}) 72 | 73 | @staticmethod 74 | def __drawer_css__() -> dict: 75 | return base_css.get("drawer", {}) 76 | 77 | @staticmethod 78 | def __drawer_heading_css__() -> dict: 79 | return CSSHelper.__drawer_css__().get("heading", {}) 80 | 81 | @staticmethod 82 | def __drawer_repo_css__() -> dict: 83 | return CSSHelper.__drawer_css__().get("repo", {}) 84 | 85 | @staticmethod 86 | def __drawer_router_css__() -> dict: 87 | return CSSHelper.__drawer_css__().get("router", {}) 88 | -------------------------------------------------------------------------------- /docs/app/helpers/nav_helpers.py: -------------------------------------------------------------------------------- 1 | from app.core.repository import RepositoryData 2 | from app.helpers.app_config import Config 3 | import reflex as rx 4 | 5 | 6 | class NavHelper: 7 | @staticmethod 8 | def __get_repository__(): 9 | data = RepositoryData().build() 10 | return data 11 | 12 | @staticmethod 13 | def __get_navigation_titles__() -> list: 14 | navigation = ( 15 | [ 16 | name.capitalize() 17 | for name in list( 18 | Config.__navigation__().keys(), 19 | ) 20 | ] 21 | if Config.__navigation__() 22 | else [] 23 | ) 24 | 25 | return navigation 26 | 27 | @staticmethod 28 | def __get_navigation_paths__( 29 | data: dict = Config.__navigation__(), 30 | parent: str = "/", 31 | paths: list = [], 32 | ) -> list[tuple]: 33 | for key, value in data.items(): 34 | if isinstance(value, str) and key == "home": 35 | paths.append("/") 36 | if isinstance(value, dict): 37 | for path in list(value.values()): 38 | paths.append(f"{parent}{key}/{path.split('.py')[0]}") 39 | break 40 | 41 | return paths 42 | 43 | @staticmethod 44 | def __get_nav_link__( 45 | title: str, 46 | route_to: str, 47 | size: str, 48 | color: str, 49 | ): 50 | style = nav_helper_css.copy() 51 | style["text"]["font_size"] = size 52 | style["text"]["color"] = color 53 | return rx.link( 54 | rx.text(title, style=style["text"]), href=route_to, style=style["link"] 55 | ) 56 | 57 | @staticmethod 58 | def __get_left_navigation__( 59 | ref: str, 60 | data: dict = Config.__navigation__(), 61 | ): 62 | paths: list = [] 63 | for key, value in data.items(): 64 | if isinstance(value, dict) and key == ref.lower(): 65 | for title, path in value.items(): 66 | paths.append( 67 | [ 68 | title.capitalize(), 69 | f"/{ref.lower()}/{path.split('.py')[0]}", 70 | ] 71 | ) 72 | return paths 73 | 74 | @staticmethod 75 | def __set_left_navigation__(inc_paths: list) -> rx.Component: 76 | out_paths: list = [] 77 | nav: rx.Component = rx.vstack(style=nav_helper_css["nav"]) 78 | if inc_paths: 79 | for title, route in inc_paths: 80 | out_paths.append( 81 | NavHelper.__get_nav_link__( 82 | title=title.capitalize(), 83 | route_to=route, 84 | size=13, 85 | color=None, 86 | ) 87 | ) 88 | 89 | nav.children = out_paths 90 | return nav 91 | 92 | 93 | nav_helper_css: dict = { 94 | "nav": { 95 | "align_items": "start", 96 | }, 97 | "link": { 98 | "_hover": {"text_decoration": "None"}, 99 | "padding": "0.25rem 0rem", 100 | }, 101 | "text": { 102 | "font_size": "%s", 103 | "font_weight": "500", 104 | "color": "%s", 105 | "opacity": "0.85", 106 | "transition": "opacity 350ms ease", 107 | "_hover": {"opacity": "1"}, 108 | }, 109 | } 110 | -------------------------------------------------------------------------------- /docs/app/material/__init__.py: -------------------------------------------------------------------------------- 1 | from app.material.typography import Header, Title, SubHeader # noqa: F401 2 | from app.material.admonition import Admonition # noqa: F401 3 | -------------------------------------------------------------------------------- /docs/app/material/admonition.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | from app.styles._admonition import admonition_css 3 | 4 | # tags: info, not_allowed, warning, warning_two, calendar 5 | 6 | css: dict = admonition_css 7 | 8 | 9 | class Admonition: 10 | def __init__( 11 | self, 12 | __type: str, 13 | __title: str, 14 | __components: list = [], 15 | ): 16 | self.__type = __type 17 | self.__title = __title 18 | self.__components = __components 19 | 20 | self.admonition = rx.accordion( 21 | rx.accordion_item( 22 | rx.accordion_button( 23 | rx.icon( 24 | tag=self.__type, 25 | style=css.get(self.__type, "").get("icon", ""), 26 | ), 27 | rx.heading(self.__title, size="sm", padding_left="0.75rem"), 28 | rx.spacer(), 29 | rx.accordion_icon(), 30 | style=css.get(self.__type, "").get("header", ""), 31 | margin="0", 32 | _hover={"opacity": "1"}, 33 | ), 34 | padding="0", 35 | margin="0", 36 | overflow="hidden", 37 | border="0px solid transparent", 38 | ), 39 | width="100%", 40 | allow_multiple=True, 41 | border_radius="6px", 42 | padding="0", 43 | margin="0", 44 | overflow="hidden", 45 | style=css.get(self.__type, "").get("body", ""), 46 | ) 47 | 48 | def build(self): 49 | for item in self.__components: 50 | self.admonition.children[0].children.append(item) 51 | 52 | return self.admonition 53 | -------------------------------------------------------------------------------- /docs/app/material/typography.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class Title(rx.Heading): 5 | def __init__( 6 | self, 7 | title: str, 8 | size="2xl", 9 | opacity="0.80", 10 | padding="0rem 0rem", 11 | letter_spacing="0.01em", 12 | ): 13 | super().__init__( 14 | children=[rx.text(title)], 15 | size=size, 16 | opacity=opacity, 17 | padding=padding, 18 | letter_spacing=letter_spacing, 19 | ) 20 | 21 | 22 | class Header(rx.Heading): 23 | def __init__( 24 | self, 25 | title: str, 26 | size="lg", 27 | opacity="0.90", 28 | padding="2rem 0rem", 29 | letter_spacing="0.01em", 30 | ): 31 | super().__init__( 32 | children=[rx.text(title)], 33 | size=size, 34 | opacity=opacity, 35 | padding=padding, 36 | letter_spacing=letter_spacing, 37 | ) 38 | 39 | 40 | class SubHeader(rx.Heading): 41 | def __init__( 42 | self, 43 | title: str, 44 | id: str = None, 45 | size="md", 46 | opacity="1", 47 | padding="0.75rem 0rem", 48 | letter_spacing="0.01em", 49 | ): 50 | super().__init__( 51 | children=[rx.text(title)], 52 | id=id, 53 | size=size, 54 | opacity=opacity, 55 | padding=padding, 56 | letter_spacing=letter_spacing, 57 | ) 58 | -------------------------------------------------------------------------------- /docs/app/pages/home/__pycache__/create.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/pages/home/__pycache__/create.cpython-311.pyc -------------------------------------------------------------------------------- /docs/app/pages/home/__pycache__/publish.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/pages/home/__pycache__/publish.cpython-311.pyc -------------------------------------------------------------------------------- /docs/app/pages/home/__pycache__/run.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/pages/home/__pycache__/run.cpython-311.pyc -------------------------------------------------------------------------------- /docs/app/pages/home/__pycache__/start.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/pages/home/__pycache__/start.cpython-311.pyc -------------------------------------------------------------------------------- /docs/app/pages/home/create.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | 7 | _static_tree = """_static/ 8 | │ 9 | ├── _next/ 10 | │ └── ... 11 | │ 12 | ├── home/ 13 | │ └── start.html 14 | │ 15 | ├── setup/ 16 | │ ├── color.html 17 | │ └── setup.html 18 | │ 19 | ├── 404.html 20 | ├── favicon.ico 21 | └── index.html 22 | """ 23 | 24 | 25 | def return_code_block(string: str): 26 | return rx.box( 27 | rx.code_block( 28 | string, 29 | language="python", 30 | can_copy=True, 31 | theme="dark", 32 | width="100%", 33 | ), 34 | width="100%", 35 | padding="1rem 0rem", 36 | ) 37 | 38 | 39 | class RxPage: 40 | # Title of page: must match high-level key in config.py 41 | def __title__(self): 42 | return "Home" 43 | 44 | # Page route path: must follow /parent-key/file-name *without .py extension* 45 | def __route__(self): 46 | return "/home/create" 47 | 48 | # Left navigation panel: automated based on config navigation order 49 | def __left_navigation__(self): 50 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 51 | return NavHelper.__set_left_navigation__(nav) 52 | 53 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 54 | def __right__navigation__(self): 55 | return [] 56 | 57 | # Mobile navigation drop down 58 | def __mobile_navigation__(self): 59 | return NavHelper.__get_left_navigation__(self.__title__()) 60 | 61 | # Main content area: takes in rx.Componenets and passes them to base file 62 | def __components__(self): 63 | return [ 64 | # add your components below # 65 | rf.Title("Creating your first application"), 66 | rf.Header("Exporting Site"), 67 | rx.markdown( 68 | "After putting on the finishing touches to your web application, we can look at creating the site itself. We'll be using **Reflex's** [documentation](https://reflex.dev/docs/hosting/self-hosting/) for the first part, i.e., exporting the application. To export the web application, run the following commands: " 69 | ), 70 | rx.markdown("Inside the root directory, run: "), 71 | return_code_block("$ reflex export --no-zip"), 72 | rx.markdown( 73 | "If the build is successful, you should be able to see changes to your ```.web``` directory. Move into the ```.web``` directory and locate a folder called ```_static``` :" 74 | ), 75 | return_code_block("$ cd .web"), 76 | rx.markdown( 77 | "Inside the ```.web``` folder, you'll see a buch of files, mainly ```.json``` and other HTML/JS files. What we're intrested in is the ```_static``` directory, so move into:" 78 | ), 79 | return_code_block("$ cd _static"), 80 | rx.markdown( 81 | "Once inside the ```_static``` directory, you'll find all your web application files and folders accordingly. For example, a web application with two folders, ```home``` and ```setup```, will have the following file structure: " 82 | ), 83 | return_code_block(_static_tree), 84 | rx.markdown( 85 | "If the export was successful, you now have your web application compiled and ready to be hosted online as a static website. To see how we can deploy the application, head over to the publish section to see how." 86 | ) 87 | # end your components above # 88 | ] 89 | 90 | # Build method: creates a new instance for the page above 91 | def build(self): 92 | page = RxBasePage( 93 | self.__components__(), 94 | self.__left_navigation__(), 95 | self.__right__navigation__(), 96 | self.__mobile_navigation__(), 97 | ) 98 | return page.build() 99 | -------------------------------------------------------------------------------- /docs/app/pages/home/publish.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | 7 | _static_tree = """_static/ 8 | │ 9 | ├── _next/ 10 | │ └── ... 11 | │ 12 | ├── home/ 13 | │ └── start.html 14 | │ 15 | ├── setup/ 16 | │ ├── color.html 17 | │ └── setup.html 18 | │ 19 | ├── 404.html 20 | ├── favicon.ico 21 | └── index.html 22 | """ 23 | 24 | 25 | def return_code_block(string: str): 26 | return rx.box( 27 | rx.code_block( 28 | string, 29 | language="python", 30 | can_copy=True, 31 | theme="dark", 32 | width="100%", 33 | ), 34 | width="100%", 35 | padding="1rem 0rem", 36 | ) 37 | 38 | 39 | class RxPage: 40 | # Title of page: must match high-level key in config.py 41 | def __title__(self): 42 | return "Home" 43 | 44 | # Page route path: must follow /parent-key/file-name *without .py extension* 45 | def __route__(self): 46 | return "/home/publish" 47 | 48 | # Left navigation panel: automated based on config navigation order 49 | def __left_navigation__(self): 50 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 51 | return NavHelper.__set_left_navigation__(nav) 52 | 53 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 54 | def __right__navigation__(self): 55 | return [] 56 | 57 | # Mobile navigation drop down 58 | def __mobile_navigation__(self): 59 | return NavHelper.__get_left_navigation__(self.__title__()) 60 | 61 | # Main content area: takes in rx.Componenets and passes them to base file 62 | def __components__(self): 63 | return [ 64 | # add your components below # 65 | rf.Title("Publishing your first application"), 66 | rf.Header("Hosting Your Site"), 67 | rx.hstack( 68 | rx.icon(tag="warning_two", color="red"), 69 | rx.heading("Hosting static site on Vercel", size="sm"), 70 | spacing="2rem", 71 | border="0.1rem solid red", 72 | border_radius="6px", 73 | width="100%", 74 | padding="1rem 1rem", 75 | bg="rgba(210, 9, 9, 0.39)", 76 | ), 77 | rx.box( 78 | rx.markdown( 79 | "This guide uses **[Vercel](https://vercel.com/)** as the hosting provider. Morover, we'll be hosting a static app, which are limited in some aspects. These limitations are discussed here." 80 | ), 81 | padding="1rem 0rem", 82 | ), 83 | rx.hstack( 84 | rx.icon(tag="warning", color="orange"), 85 | rx.heading( 86 | "You need to have NodeJS installed to use Vercel", size="sm" 87 | ), 88 | spacing="2rem", 89 | border="0.1rem solid orange", 90 | border_radius="6px", 91 | width="100%", 92 | padding="1rem 1rem", 93 | bg="rgba(246, 139, 23, 0.54)", 94 | ), 95 | rx.box( 96 | rx.markdown( 97 | "In order to host your web application as a static site, you'll need to check off a few things. First, a vercel account is required. If you don't have one already, headover to Vercel and create one. You can also link your Vercel account to GitHub. Second, you need to have the latest version of ```vercel cli``` installed. If you don't already have it installed, you can visit their documentation **[here](https://vercel.com/docs/cli)** to see how to install." 98 | ), 99 | padding="1rem 0rem", 100 | ), 101 | rx.markdown( 102 | "To quickly get started, run the following command to install ```Vercel CLI```:" 103 | ), 104 | return_code_block("$ npm i -g vercel"), 105 | rx.markdown( 106 | "If you already have ```Vercel CLI``` installed, make sure it's the altest version by running: " 107 | ), 108 | return_code_block("$ npm i -g vercel@latest"), 109 | rx.box( 110 | rx.markdown( 111 | "Once you have the above, navigate to the root of your ```.web``` folder. Make sure you're in the root and not inside ```_static``` fodler. Then, run the following command to start deployment: " 112 | ), 113 | padding="1rem 0rem", 114 | ), 115 | return_code_block("$ vercel deploy"), 116 | rx.box( 117 | rx.markdown( 118 | "The above command will guide you through a series of steps that will deploy your website to vercel, if there are no previous issues. Once the deployment process is complete, you'll get a production link where you can view your static site hosted on Vercel." 119 | ), 120 | padding="1rem 0rem", 121 | ), 122 | # end your components above # 123 | ] 124 | 125 | # Build method: creates a new instance for the page above 126 | def build(self): 127 | page = RxBasePage( 128 | self.__components__(), 129 | self.__left_navigation__(), 130 | self.__right__navigation__(), 131 | self.__mobile_navigation__(), 132 | ) 133 | return page.build() 134 | -------------------------------------------------------------------------------- /docs/app/pages/home/run.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | 4 | import app.material as rf 5 | import reflex as rx 6 | 7 | 8 | one = rx.box( 9 | rx.code_block( 10 | "$ reflex init", 11 | language="python", 12 | can_copy=True, 13 | theme="dark", 14 | width="100%", 15 | ), 16 | width="100%", 17 | padding="1rem 0rem", 18 | ) 19 | 20 | 21 | two = rx.box( 22 | rx.code_block( 23 | """$ rf-init""", 24 | language="python", 25 | can_copy=True, 26 | theme="dark", 27 | width="100%", 28 | ), 29 | width="100%", 30 | padding="1rem 0rem", 31 | ) 32 | 33 | three = rx.box( 34 | rx.code_block( 35 | """$ python3 reflexify_scripts/build.py""", 36 | language="python", 37 | can_copy=True, 38 | theme="dark", 39 | width="100%", 40 | ), 41 | width="100%", 42 | padding="1rem 0rem", 43 | ) 44 | 45 | 46 | class RxPage: 47 | # Title of page: must match high-level key in config.py 48 | def __title__(self): 49 | return "Home" 50 | 51 | # Page route path: must follow /parent-key/file-name *without .py extension* 52 | def __route__(self): 53 | return "/home/run" 54 | 55 | # Left navigation panel: automated based on config navigation order 56 | def __left_navigation__(self): 57 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 58 | return NavHelper.__set_left_navigation__(nav) 59 | 60 | # Right navigation panel: TBD 61 | def __right__navigation__(self): 62 | return [] 63 | 64 | # Mobile navigation drop down 65 | def __mobile_navigation__(self): 66 | return NavHelper.__get_left_navigation__(self.__title__()) 67 | 68 | # Main content area: takes in rx.Componenets and passes them to base file 69 | def __components__(self): 70 | return [ 71 | rf.Title("Running your first application"), 72 | rf.Header("Quick Start"), 73 | rx.markdown( 74 | "After installing **Reflexify**, you can test if the library is working properly by going over the following steps in order." 75 | ), 76 | rx.markdown( 77 | "1. Inside the root directory, initiate the reflex application (as you would a normal Reflex app):" 78 | ), 79 | one, 80 | rx.markdown( 81 | "2. Once the Reflex application is created, run the following Reflexify command to setup the components:" 82 | ), 83 | two, 84 | rx.markdown( 85 | "If the package was installed correctly, a folder called app will be generated inside the root directory. Other directories and files will also be generated." 86 | ), 87 | rx.markdown( 88 | "3. Next, run the watch script by entering the following command:" 89 | ), 90 | three, 91 | rx.markdown( 92 | "This script will now monitor your ```config.py``` file inside the app directory. Specifically, it will watch for changes to your navigation and update the changes accordingly inside the ```pages``` directory." 93 | ), 94 | rx.spacer(), 95 | ] 96 | 97 | # Build method: creates a new instance for the page above 98 | def build(self): 99 | page = RxBasePage( 100 | self.__components__(), 101 | self.__left_navigation__(), 102 | self.__right__navigation__(), 103 | self.__mobile_navigation__(), 104 | ) 105 | return page.build() 106 | -------------------------------------------------------------------------------- /docs/app/pages/home/start.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | 4 | import app.material as rf 5 | import reflex as rx 6 | 7 | intro = """ 8 | **Reflexify** is a Python web boilerplate project built with **[Reflex](https://reflex.dev)**. and designed to provide a solid foundation for building web applications with Python and Reflex. The project comes pre-configured with a range of tools and features to make it easy for developers to get started building their applications, without the need to spend time setting up infrastructure or configuring tools. 9 | """ 10 | 11 | installation = """ 12 | To use **Reflexify**, you need to have have the latest version of **Reflex** and **Python 3.5+** installed. If you don't have Reflex installed, installing Reflexify automatically installs it for you. You can install Reflexify using the following command: 13 | 14 | """ 15 | 16 | pip_install = """Currently you can get the **Reflexify** library by using the popular Python package manager `pip`. Ideally, you'll want to install this, as well as the main Reflexify library in a virtual environment. Open up a terminal and enter the following command: 17 | """ 18 | 19 | rf_command = """pip install reflexify""" 20 | 21 | git_clone = """To clone the Reflexify repository from GitHub, open a terminal or command prompt and navigate to the directory where you want to store the cloned repository. Then, run the following command:""" 22 | 23 | git_command = """git clone https://github.com/LineIndent/reflexify.git""" 24 | 25 | git_conclusion = """This will download the repository to your local machine, and you can then start using the library.""" 26 | 27 | 28 | class RxPage: 29 | # Title of page: must match high-level key in config.py 30 | def __title__(self): 31 | return "Home" 32 | 33 | # Page route path: must follow /parent-key/file-name *without .py extension* 34 | def __route__(self): 35 | return "/home/start" 36 | 37 | # Left navigation panel: automated based on config navigation order 38 | def __left_navigation__(self): 39 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 40 | return NavHelper.__set_left_navigation__(nav) 41 | 42 | # Right navigation panel: TBD 43 | def __right__navigation__(self): 44 | return [ 45 | ["Prerequisites", "/home/start#prereq"], 46 | ["PIP Package", "/home/start#pip"], 47 | ["GitHub Clone", "/home/start#git"], 48 | ] 49 | 50 | # Mobile navigation drop down 51 | def __mobile_navigation__(self): 52 | return NavHelper.__get_left_navigation__(self.__title__()) 53 | 54 | # Main content area: takes in rx.Componenets and passes them to base file 55 | def __components__(self): 56 | return [ 57 | rf.Title("Welcome to Reflexify!"), 58 | rf.Header("Getting started"), 59 | rx.markdown(intro), 60 | rx.spacer(), 61 | rf.Header("Installation guide"), 62 | rf.SubHeader("Prerequisites", id="prereq"), 63 | rx.markdown(installation), 64 | rf.SubHeader("Python PIP Package", id="pip"), 65 | rx.markdown(pip_install), 66 | rx.box( 67 | rx.code_block( 68 | rf_command, 69 | language="python", 70 | can_copy=True, 71 | theme="dark", 72 | width="100%", 73 | ), 74 | width="100%", 75 | padding="1rem 0rem", 76 | ), 77 | rf.SubHeader("GitHub Clone", id="git"), 78 | rx.markdown(git_clone), 79 | rx.box( 80 | rx.code_block( 81 | git_command, 82 | language="python", 83 | can_copy=True, 84 | theme="dark", 85 | width="100%", 86 | ), 87 | width="100%", 88 | padding="1rem 0rem", 89 | ), 90 | rx.markdown( 91 | git_conclusion, 92 | ), 93 | ] 94 | 95 | # Build method: creates a new instance for the page above 96 | def build(self): 97 | page = RxBasePage( 98 | self.__components__(), 99 | self.__left_navigation__(), 100 | self.__right__navigation__(), 101 | self.__mobile_navigation__(), 102 | ) 103 | return page.build() 104 | -------------------------------------------------------------------------------- /docs/app/pages/index.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import reflex as rx 4 | 5 | 6 | class RxPage: 7 | # Title of page: must match high-level key in config.py 8 | def __title__(self): 9 | return "Reflexify" 10 | 11 | # Page route path: must follow /parent-key/file-name *without .py extension* 12 | def __route__(self): 13 | return "/" 14 | 15 | # Left navigation panel: automated based on config navigation order 16 | def __left_navigation__(self): 17 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 18 | return NavHelper.__set_left_navigation__(nav) 19 | 20 | # Right navigation panel: TBD 21 | def __right__navigation__(self): 22 | return [] 23 | 24 | # Mobile navigation drop down 25 | def __mobile_navigation__(self): 26 | return NavHelper.__get_left_navigation__(self.__title__()) 27 | 28 | # Main content area: takes in rx.Componenets and passes them to base file 29 | def __components__(self): 30 | return [ 31 | # add your components below # 32 | rx.heading("Welcome to Reflexify!", size="xl", padding_bottom="2rem"), 33 | rx.heading( 34 | "This is the library's documentation. Select a link above to get started.", 35 | size="sm", 36 | ) 37 | # end your components above # 38 | ] 39 | 40 | # Build method: creates a new instance for the page above 41 | def build(self): 42 | page = RxBasePage( 43 | self.__components__(), 44 | self.__left_navigation__(), 45 | self.__right__navigation__(), 46 | self.__mobile_navigation__(), 47 | ) 48 | return page.build() 49 | -------------------------------------------------------------------------------- /docs/app/pages/material/accordion.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | from app.styles._markdown import markdown_css 4 | import app.material as rf 5 | import reflex as rx 6 | 7 | 8 | intro = """`Accordions` are dynamic user interface elements often used in web and app design to present content in a space-efficient manner. Comprising vertically stacked panels, accordions enable users to expand sections of interest while collapsing others, promoting an organized and uncluttered display. By clicking on a section's header, users can reveal underlying information, making accordions particularly effective for presenting FAQs, navigation menus, or categorized content. 9 | 10 | """ 11 | 12 | accord_code = """from app.core.base import RxBasePage 13 | from app.helpers.nav_helpers import NavHelper 14 | import reflex as rx 15 | import app.material as rf 16 | 17 | class RxPage: 18 | ... 19 | 20 | def __components__(self): 21 | return [ 22 | rf.Admonition( 23 | "info", 24 | "Info", 25 | [ 26 | rx.accordion_panel("Insert text here...") 27 | ], 28 | ).build(), 29 | ] 30 | 31 | def build(self): 32 | ... 33 | """ 34 | 35 | 36 | class RxPage: 37 | # Title of page: must match high-level key in config.py 38 | def __title__(self): 39 | return "Material" 40 | 41 | # Page route path: must follow /parent-key/file-name *without .py extension* 42 | def __route__(self): 43 | return "/material/accordion" 44 | 45 | # Left navigation panel: automated based on config navigation order 46 | def __left_navigation__(self): 47 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 48 | return NavHelper.__set_left_navigation__(nav) 49 | 50 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 51 | def __right__navigation__(self): 52 | return [] 53 | 54 | # Mobile navigation drop down 55 | def __mobile_navigation__(self): 56 | return NavHelper.__get_left_navigation__(self.__title__()) 57 | 58 | # Main content area: takes in rx.Componenets and passes them to base file 59 | def __components__(self): 60 | accordion: list = [ 61 | # add your components below # 62 | rf.Title("Accordion setup"), 63 | rx.box(rx.markdown(intro, custom_styles=markdown_css), padding="2rem 0rem"), 64 | rx.heading("Basic accordion useage", size="lg"), 65 | rx.box( 66 | rx.markdown( 67 | "Implementing an accordion is fairly straightforward. Simply pass in the type of accordian, a title, and a list of accordion, if needed, and that will generate a custom accordion for you.", 68 | custom_styles=markdown_css, 69 | ), 70 | padding="1rem 0rem", 71 | ), 72 | rx.box( 73 | rx.code_block( 74 | accord_code, 75 | language="python", 76 | theme="dark", 77 | width="100%", 78 | can_copy=True, 79 | show_line_numbers=True, 80 | ), 81 | width="100%", 82 | ), 83 | rf.SubHeader("Supported types"), 84 | rx.box( 85 | rx.markdown( 86 | "There are several supported accordions, each with a unique icon. Moreover, you can pass in a title and more items inside. Here are the available accordions:", 87 | custom_styles=markdown_css, 88 | ), 89 | padding="0.75rem 0rem", 90 | ), 91 | ] 92 | 93 | exple: list = [ 94 | ["info", "Information"], 95 | ["warning_two", "Warning"], 96 | ["close", "Failure"], 97 | ["calendar", "Note"], 98 | ["question", "Question"], 99 | ["check", "Success"], 100 | ] 101 | 102 | for icon, title in exple: 103 | accordion.append( 104 | rx.vstack( 105 | rx.markdown(f"`{icon}`"), 106 | rf.Admonition( 107 | icon, 108 | title, 109 | [ 110 | rx.accordion_panel( 111 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor massa, nec semper lorem quam in massa.", 112 | ) 113 | ], 114 | ).build(), 115 | padding="1rem 0rem", 116 | width="100%", 117 | spacing="1rem", 118 | align_items="start", 119 | ), 120 | ) 121 | 122 | return accordion 123 | 124 | # Build method: creates a new instance for the page above 125 | def build(self): 126 | page = RxBasePage( 127 | self.__components__(), 128 | self.__left_navigation__(), 129 | self.__right__navigation__(), 130 | self.__mobile_navigation__(), 131 | ) 132 | return page.build() 133 | -------------------------------------------------------------------------------- /docs/app/pages/material/setup.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import reflex as rx 4 | from app.styles._markdown import markdown_css 5 | import app.material as rf 6 | 7 | intro = """A `material library` is a comprehensive collection of pre-designed and pre-engineered materials that serve as a valuable resource for designers, architects, engineers, and artists. It encompasses a diverse range of materials, each with specific properties, textures, colors, and performance characteristics. Material libraries provide a structured repository of options for use in various projects, enabling creators to efficiently select and apply suitable materials without the need for extensive research or testing. These libraries often include samples, specifications, and digital representations of materials, fostering creativity, standardization, and informed decision-making in design and construction processes. 8 | 9 | """ 10 | 11 | 12 | temp_with_import = """from app.core.base import RxBasePage 13 | from app.helpers.nav_helpers import NavHelper 14 | 15 | # Add the following import statement... 16 | import app.material as rf 17 | """ 18 | 19 | 20 | class RxPage: 21 | # Title of page: must match high-level key in config.py 22 | def __title__(self): 23 | return "Material" 24 | 25 | # Page route path: must follow /parent-key/file-name *without .py extension* 26 | def __route__(self): 27 | return "/material/setup" 28 | 29 | # Left navigation panel: automated based on config navigation order 30 | def __left_navigation__(self): 31 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 32 | return NavHelper.__set_left_navigation__(nav) 33 | 34 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 35 | def __right__navigation__(self): 36 | return [] 37 | 38 | # Mobile navigation drop down 39 | def __mobile_navigation__(self): 40 | return NavHelper.__get_left_navigation__(self.__title__()) 41 | 42 | # Main content area: takes in rx.Componenets and passes them to base file 43 | def __components__(self): 44 | return [ 45 | # add your components below # 46 | rf.Title("Material setup"), 47 | rx.box( 48 | rx.markdown(intro, custom_styles=markdown_css), padding="1.5rem 0rem" 49 | ), 50 | rx.heading("Importing material library", size="lg"), 51 | rx.markdown( 52 | "To import and use the material library, we need to import the module at the top first. Add the following import statment to your page: ", 53 | custom_styles=markdown_css, 54 | ), 55 | rx.box( 56 | rx.code_block( 57 | temp_with_import, 58 | theme="dark", 59 | language="python", 60 | width="100%", 61 | ), 62 | width="100%", 63 | ), 64 | rx.markdown( 65 | "Once you have the above module imported, you can start using the various material design library available. Check out the left panel for a list of available material design for `Reflex`", 66 | custom_styles=markdown_css, 67 | ), 68 | # end your components above # 69 | ] 70 | 71 | # Build method: creates a new instance for the page above 72 | def build(self): 73 | page = RxBasePage( 74 | self.__components__(), 75 | self.__left_navigation__(), 76 | self.__right__navigation__(), 77 | self.__mobile_navigation__(), 78 | ) 79 | return page.build() 80 | -------------------------------------------------------------------------------- /docs/app/pages/page404.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | import reflex as rx 3 | 4 | 5 | class RxPage: 6 | # Title of page: must match high-level key in config.py 7 | def __title__(self): 8 | return "Error404" 9 | 10 | # Page route path: must follow /parent-key/file-name *without .py extension* 11 | def __route__(self): 12 | return "/page_error_404" 13 | 14 | # Left navigation panel: automated based on config navigation order 15 | def __left_navigation__(self): 16 | return [] 17 | 18 | # Right navigation panel: TBD 19 | def __right__navigation__(self): 20 | return [] 21 | 22 | # Mobile navigation drop down 23 | def __mobile_navigation__(self): 24 | return [] 25 | 26 | # Main content area: takes in rx.Componenets and passes them to base file 27 | def __components__(self): 28 | return [ 29 | rx.box( 30 | rx.heading("Error 404", size="2xl", padding_bottom="1.5rem"), 31 | rx.heading("The page you are looking for does not exist!", size="md"), 32 | padding_left=["1rem", "1rem", "1rem", "4rem", "9rem"], 33 | transition="all 550ms ease", 34 | ) 35 | ] 36 | 37 | # Build method: creates a new instance for the page above 38 | def build(self): 39 | page = RxBasePage( 40 | self.__components__(), 41 | self.__left_navigation__(), 42 | self.__right__navigation__(), 43 | self.__mobile_navigation__(), 44 | ) 45 | return page.build() 46 | -------------------------------------------------------------------------------- /docs/app/pages/setup/color.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | intro = """ 7 | The way **Reflexify** is setup makes changing theme properties, such as colors, very easiy and user-friendly. You can configure the **theme** properties by changing the values of the **theme** key in `config.py`. 8 | """ 9 | 10 | theme = """ 11 | Reflexify supports two color schemes: a light mode and a dark mode. The color scheme can be set by triggering the toggle button below, also located in the header section above. 12 | """ 13 | 14 | primary = """ 15 | The primary color is used for the header, the sidebar, and several other components. In order to change the primary color, set the following value in `config.py` to a color supported by **Reflex**: 16 | """ 17 | 18 | config_1 = """{ 19 | "theme": { 20 | "primary": "black", 21 | } 22 | }""" 23 | 24 | secondary = """ 25 | The secondary color is used for events such as link and selected words highlights. In order to change the secondary color, set the following value in `config.py` to a color supported by **Reflex**: 26 | """ 27 | 28 | config_2 = """{ 29 | "theme": { 30 | "secondary": "teal", 31 | } 32 | }""" 33 | 34 | 35 | class RxPage: 36 | # Title of page: must match high-level key in config.py 37 | def __title__(self): 38 | return "Setup" 39 | 40 | # Page route path: must follow /parent-key/file-name *without .py extension* 41 | def __route__(self): 42 | return "/setup/color" 43 | 44 | # Left navigation panel: automated based on config navigation order 45 | def __left_navigation__(self): 46 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 47 | return NavHelper.__set_left_navigation__(nav) 48 | 49 | # Right navigation panel: TBD 50 | def __right__navigation__(self): 51 | return [] 52 | 53 | # Mobile navigation drop down 54 | def __mobile_navigation__(self): 55 | return NavHelper.__get_left_navigation__(self.__title__()) 56 | 57 | # Main content area: takes in rx.Componenets and passes them to base file 58 | def __components__(self): 59 | stack: rx.Componenet = rx.hstack( 60 | rx.script( 61 | src="/index.js", 62 | ), 63 | ) 64 | colors: list[str] = [ 65 | "black", 66 | "teal", 67 | "blue", 68 | "yellow", 69 | "orange", 70 | "indigo", 71 | "cyan", 72 | "purple", 73 | ] 74 | 75 | for color in colors: 76 | stack.children.append( 77 | rx.container( 78 | color, 79 | w="64px", 80 | h="64px", 81 | bg=color, 82 | on_click=rx.client_side(f"setHeaderColor('{color}')"), 83 | ), 84 | ) 85 | 86 | return [ 87 | rf.Title("Changing App Colors"), 88 | rf.Header("Configuration"), 89 | rx.markdown(intro), 90 | rf.SubHeader("Theme Mode"), 91 | rx.markdown(theme), 92 | rx.color_mode_button(rx.color_mode_icon(), width="100%"), 93 | rf.SubHeader("Primary color"), 94 | rx.markdown(primary), 95 | rx.box( 96 | rx.code_block( 97 | config_1, 98 | language="python", 99 | theme="dark", 100 | width="100%", 101 | ), 102 | width="100%", 103 | ), 104 | rf.SubHeader("Secondary color: TBD"), 105 | rx.markdown(secondary), 106 | rx.box( 107 | rx.code_block( 108 | config_2, 109 | language="python", 110 | theme="dark", 111 | width="100%", 112 | ), 113 | width="100%", 114 | ), 115 | stack, 116 | ] 117 | 118 | # Build method: creates a new instance for the page above 119 | def build(self): 120 | page = RxBasePage( 121 | self.__components__(), 122 | self.__left_navigation__(), 123 | self.__right__navigation__(), 124 | self.__mobile_navigation__(), 125 | ) 126 | return page.build() 127 | -------------------------------------------------------------------------------- /docs/app/pages/setup/drawer.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | 7 | drawer = """{ 8 | "drawer": True 9 | } 10 | """ 11 | 12 | 13 | def return_code_block(string: str, copy: bool = False): 14 | return rx.box( 15 | rx.code_block( 16 | string, 17 | language="python", 18 | can_copy=copy, 19 | theme="dark", 20 | width="100%", 21 | ), 22 | width="100%", 23 | padding="1rem 0rem", 24 | ) 25 | 26 | 27 | class RxPage: 28 | # Title of page: must match high-level key in config.py 29 | def __title__(self): 30 | return "Setup" 31 | 32 | # Page route path: must follow /parent-key/file-name *without .py extension* 33 | def __route__(self): 34 | return "/setup/drawer" 35 | 36 | # Left navigation panel: automated based on config navigation order 37 | def __left_navigation__(self): 38 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 39 | return NavHelper.__set_left_navigation__(nav) 40 | 41 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 42 | def __right__navigation__(self): 43 | return [] 44 | 45 | # Mobile navigation drop down 46 | def __mobile_navigation__(self): 47 | return NavHelper.__get_left_navigation__(self.__title__()) 48 | 49 | # Main content area: takes in rx.Componenets and passes them to base file 50 | def __components__(self): 51 | return [ 52 | # add your components below # 53 | rf.Title("Setting up drawer menu"), 54 | rf.Header("Configuring drawer"), 55 | rx.markdown( 56 | "The drawer is a side menu that is prsent when the screen breaks after a certain point, typically at screen widths for tablets and mobile devices. The drawer contains the main header navigation. You can disable the drawer by setting it's key value in ```config.py``` to ```True```: " 57 | ), 58 | return_code_block(drawer), 59 | rx.box( 60 | rx.hstack( 61 | rx.icon(tag="warning", color="orange"), 62 | rx.heading("Static sites with side menus", size="sm"), 63 | spacing="2rem", 64 | border="0.1rem solid orange", 65 | border_radius="6px", 66 | width="100%", 67 | padding="1rem 1rem", 68 | bg="rgba(246, 139, 23, 0.54)", 69 | ), 70 | width="100%", 71 | padding="1rem 0rem", 72 | ), 73 | rx.markdown( 74 | "Because we're focusing mainly on static sites, any compoenent that needs to send a event to Reflex will not work. Therefore the click event on the icon to trigger the drawer will not work when deploying the site statically. As a result, the drawer is disabled by default and should be kept that away unless your app is deployed with a back end." 75 | ), 76 | # end your components above # 77 | ] 78 | 79 | # Build method: creates a new instance for the page above 80 | def build(self): 81 | page = RxBasePage( 82 | self.__components__(), 83 | self.__left_navigation__(), 84 | self.__right__navigation__(), 85 | self.__mobile_navigation__(), 86 | ) 87 | return page.build() 88 | -------------------------------------------------------------------------------- /docs/app/pages/setup/font.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | intro = """ 7 | **Reflexify** makes it super easy to change the typface of your application by simply changing the value of your ```config.py``` file. Let's take a look at how we can do this. 8 | 9 | """ 10 | 11 | fonts = """ 12 | Reflexify supports fonts that are in turn supported by it's parent library, **Reflex**. So any fonts supported within Reflex can be used here as well. 13 | """ 14 | 15 | font_code = """{ 16 | "theme": { 17 | "fonts": "Times New Roman", 18 | } 19 | }""" 20 | 21 | fonts_2 = """ 22 | If you prefer to keep the default system fonts, you can simply leave the value of the ```fonts``` key blank, and this will trigger a fall back to systems fonts. 23 | """ 24 | font_code_2 = """{ 25 | "theme": { 26 | "fonts": "", 27 | } 28 | }""" 29 | 30 | 31 | class RxPage: 32 | # Title of page: must match high-level key in config.py 33 | def __title__(self): 34 | return "Setup" 35 | 36 | # Page route path: must follow /parent-key/file-name *without .py extension* 37 | def __route__(self): 38 | return "/setup/font" 39 | 40 | # Left navigation panel: automated based on config navigation order 41 | def __left_navigation__(self): 42 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 43 | return NavHelper.__set_left_navigation__(nav) 44 | 45 | # Right navigation panel: TBD 46 | def __right__navigation__(self): 47 | return [] 48 | 49 | # Mobile navigation drop down 50 | def __mobile_navigation__(self): 51 | return NavHelper.__get_left_navigation__(self.__title__()) 52 | 53 | # Main content area: takes in rx.Componenets and passes them to base file 54 | def __components__(self): 55 | return [ 56 | rf.Title("Changing App Fonts"), 57 | rf.Header("Configuration"), 58 | rx.markdown(intro), 59 | rf.SubHeader("Fonts"), 60 | rx.markdown(fonts), 61 | rx.box( 62 | rx.code_block( 63 | font_code, 64 | language="python", 65 | theme="dark", 66 | width="100%", 67 | ), 68 | width="100%", 69 | ), 70 | rx.markdown(fonts_2), 71 | rx.box( 72 | rx.code_block( 73 | font_code_2, 74 | language="python", 75 | theme="dark", 76 | width="100%", 77 | ), 78 | width="100%", 79 | ), 80 | ] 81 | 82 | # Build method: creates a new instance for the page above 83 | def build(self): 84 | page = RxBasePage( 85 | self.__components__(), 86 | self.__left_navigation__(), 87 | self.__right__navigation__(), 88 | self.__mobile_navigation__(), 89 | ) 90 | return page.build() 91 | -------------------------------------------------------------------------------- /docs/app/pages/setup/git.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | repo = """{ 7 | "repo_name": "LineIndent/reflexify", 8 | "repo_url": "https://github.com/LineIndent/reflexify", 9 | } 10 | """ 11 | 12 | 13 | def return_code_block(string: str, copy: bool = False): 14 | return rx.box( 15 | rx.code_block( 16 | string, 17 | language="python", 18 | can_copy=copy, 19 | theme="dark", 20 | width="100%", 21 | ), 22 | width="100%", 23 | padding="1rem 0rem", 24 | ) 25 | 26 | 27 | class RxPage: 28 | # Title of page: must match high-level key in config.py 29 | def __title__(self): 30 | return "Setup" 31 | 32 | # Page route path: must follow /parent-key/file-name *without .py extension* 33 | def __route__(self): 34 | return "/setup/git" 35 | 36 | # Left navigation panel: automated based on config navigation order 37 | def __left_navigation__(self): 38 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 39 | return NavHelper.__set_left_navigation__(nav) 40 | 41 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 42 | def __right__navigation__(self): 43 | return [] 44 | 45 | # Mobile navigation drop down 46 | def __mobile_navigation__(self): 47 | return NavHelper.__get_left_navigation__(self.__title__()) 48 | 49 | # Main content area: takes in rx.Componenets and passes them to base file 50 | def __components__(self): 51 | return [ 52 | # add your components below # 53 | rf.Title("Adding a Git repository"), 54 | rf.Header("Config.py file"), 55 | rx.markdown( 56 | "To add a GitHub repository and it's details (stars, forks, and version number), you can add the following paramters in your ```config.py``` file: " 57 | ), 58 | return_code_block(repo), 59 | rx.markdown( 60 | "Add the URL to your repository will scrape for the necessary details and display them as they are displayed on this page. This is a nice data visual for documentation or technical sites." 61 | ) 62 | # end your components above # 63 | ] 64 | 65 | # Build method: creates a new instance for the page above 66 | def build(self): 67 | page = RxBasePage( 68 | self.__components__(), 69 | self.__left_navigation__(), 70 | self.__right__navigation__(), 71 | self.__mobile_navigation__(), 72 | ) 73 | return page.build() 74 | -------------------------------------------------------------------------------- /docs/app/pages/setup/nav.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | nav_config = """"navigation": { 7 | "home": { 8 | "getting started": "start.py", 9 | "quick start": "run.py", 10 | "creating your site": "create.py", 11 | "publishing your site": "publish.py", 12 | }, 13 | "setup": { 14 | "setup": "setup.py", 15 | "setting up navigation": "nav.py", 16 | "setting up drawer": "drawer.py", 17 | "changing the colors": "color.py", 18 | "changing the fonts": "font.py", 19 | "adding a git repository": "git.py", 20 | "adding social media": "socials.py", 21 | }, 22 | "material": {}, 23 | }, 24 | """ 25 | 26 | path_ex = """"home": { 27 | "getting started": "start.py", 28 | }, 29 | """ 30 | 31 | path_ex_2 = """/home/start""" 32 | 33 | right = """# Right navigation panel: manually add your page-specific TOC. 34 | def __right__navigation__(self): 35 | return [] 36 | """ 37 | 38 | right_2 = """["Example", "/home/start#example"]""" 39 | 40 | 41 | right_3 = """# Right navigation panel: manually add your page-specific TOC. 42 | def __right__navigation__(self): 43 | return [ 44 | ["Example", "/home/start#example"] 45 | ] 46 | 47 | # Main content area: takes in rx.Componenets and passes them to base file 48 | def __components__(self): 49 | return [ 50 | rf.SubHeader("Example", id="example"), 51 | ] 52 | """ 53 | 54 | 55 | def return_code_block(string: str, copy: bool = False): 56 | return rx.box( 57 | rx.code_block( 58 | string, 59 | language="python", 60 | can_copy=copy, 61 | theme="dark", 62 | width="100%", 63 | ), 64 | width="100%", 65 | padding="1rem 0rem", 66 | ) 67 | 68 | 69 | class RxPage: 70 | # Title of page: must match high-level key in config.py 71 | def __title__(self): 72 | return "Setup" 73 | 74 | # Page route path: must follow /parent-key/file-name *without .py extension* 75 | def __route__(self): 76 | return "/setup/nav" 77 | 78 | # Left navigation panel: automated based on config navigation order 79 | def __left_navigation__(self): 80 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 81 | return NavHelper.__set_left_navigation__(nav) 82 | 83 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 84 | def __right__navigation__(self): 85 | return [] 86 | 87 | # Mobile navigation drop down 88 | def __mobile_navigation__(self): 89 | return NavHelper.__get_left_navigation__(self.__title__()) 90 | 91 | # Main content area: takes in rx.Componenets and passes them to base file 92 | def __components__(self): 93 | return [ 94 | # add your components below # 95 | rf.Title("Setting up site navigation"), 96 | rf.Header("Config.py file"), 97 | rx.markdown( 98 | "To understand how navigation works in **Reflexify**, let's take a look at this site's navigation below. A few important notes on the navigation logic for this library. The outter keys, ```home```, ```setup```, etc. correspond to the navigation bar at the top. The inner keys, these correspond to the ```left panel``` navigation rail. Therefore the keys themeselves are the titles that re displayed on screen. Finally, the values of the inner keys are the file names and file paths, as described below." 99 | ), 100 | return_code_block(nav_config), 101 | rf.Header("Navigation paths and routing"), 102 | rx.markdown( 103 | "File paths are automatically created everytime the ```config.py``` file is changes for the navigation key. In brief, the file path, meaning the page route, corresponds to a string starting from the outter key and ending in the file name (without the .py exxtension). Here's an example: " 104 | ), 105 | rx.markdown("The following navigation key-value pairs: "), 106 | return_code_block(path_ex), 107 | rx.markdown("Corresponds to the following route path: "), 108 | return_code_block(path_ex_2), 109 | rx.markdown( 110 | "The route above, and any others taken from the config.py, will automatically be added into the application logic with each reload or startup. This makes generating routes fast, reliable, and easy for developers. " 111 | ), 112 | rf.Header("Right panel navigation"), 113 | rx.markdown( 114 | "**Reflexify** offers a third navigation system, the ```table of contents``` navigation. This is located on the right side and can be manually added. The table of contents function is to navigate to different sections within the same page. Let's see how to set this up. Every Reflexy generated page consists of a method called ```__right__navigation__```: " 115 | ), 116 | return_code_block(right), 117 | rx.markdown( 118 | "To utilize this method, you need to pass in a list that consists of two elements: a title and a special string that consists of the page route, a hash, and an ID: " 119 | ), 120 | return_code_block(right_2), 121 | rx.markdown( 122 | "The final setup should look something like this. Note that you need to pass in the ```id``` parameter, either to Reflexify's rf.SubHeading() or the usual Reflex component: " 123 | ), 124 | return_code_block(right_3), 125 | rx.markdown("Make sure the ```id``` matches the word after the ```#```."), 126 | # end your components above # 127 | ] 128 | 129 | # Build method: creates a new instance for the page above 130 | def build(self): 131 | page = RxBasePage( 132 | self.__components__(), 133 | self.__left_navigation__(), 134 | self.__right__navigation__(), 135 | self.__mobile_navigation__(), 136 | ) 137 | return page.build() 138 | -------------------------------------------------------------------------------- /docs/app/pages/setup/setup.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | intro = """ 7 | Reflexify's base compoenents come pre-configured in order to provide rapid devleopment of web applications or Python documentation. However, a wide range of options for customizing your application are available. We'll go over the options and explain each one in this section. 8 | """ 9 | 10 | structure = """ 11 | As a developer, you have access to the base configurations of your application, and as such can access base attributes such as the app header, footer, and navigation. Below are the main file structure for **Reflexify**: 12 | """ 13 | 14 | purpose = """ 15 | Even though each above component of your application comes preconfigured, you can always go to the base configuration files and personalize them as needed. This gives developers the flexibility to make each application unique. 16 | """ 17 | 18 | drop_items = [ 19 | """│app/ 20 | ├── core/ 21 | │ ├── __init__.py 22 | │ ├── base.py 23 | │ ├── drawer.py 24 | │ ├── footer.py 25 | │ ├── header.py 26 | │ ├── left.py 27 | │ ├── middle.py 28 | │ ├── mobile.py 29 | │ ├── repository.py 30 | │ └── right.py 31 | """, 32 | """│app/ 33 | ├── helpers/ 34 | │ ├── app_config.py 35 | │ ├── css_helpers.py 36 | │ └── nav_helpers.py 37 | """, 38 | """│app/ 39 | ├── material/ 40 | │ ├── __init__.py 41 | │ └── typography.py 42 | │ 43 | """, 44 | """│app/ 45 | ├── states/ 46 | │ ├── __init__.py 47 | │ ├── drawerState.py 48 | │ ├── headerState.py 49 | │ ├── mainState.py 50 | 51 | """, 52 | """│app/ 53 | ├── styles/ 54 | │ ├── _base.py 55 | """, 56 | ] 57 | drop_titles = ["Core", "Helpers", "Material", "States", "Styles"] 58 | 59 | drop_list = rx.accordion( 60 | allow_multiple=True, 61 | width="100%", 62 | padding_top="1rem", 63 | padding_bottom="1rem", 64 | ) 65 | for index, item in enumerate(drop_items): 66 | drop_list.children.append( 67 | rx.accordion_item( 68 | rx.accordion_button( 69 | rx.heading(drop_titles[index], size="md"), 70 | rx.spacer(), 71 | rx.accordion_icon(), 72 | ), 73 | rx.accordion_panel( 74 | rx.code_block( 75 | item, 76 | language="plaintext", 77 | theme="dark", 78 | width="100%", 79 | ), 80 | ), 81 | padding="0.25rem 0rem", 82 | ), 83 | ) 84 | 85 | 86 | class RxPage: 87 | # Title of page: must match high-level key in config.py 88 | def __title__(self): 89 | return "Setup" 90 | 91 | # Page route path: must follow /parent-key/file-name *without .py extension* 92 | def __route__(self): 93 | return "/setup/setup" 94 | 95 | # Left navigation panel: automated based on config navigation order 96 | def __left_navigation__(self): 97 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 98 | return NavHelper.__set_left_navigation__(nav) 99 | 100 | # Right navigation panel: TBD 101 | def __right__navigation__(self): 102 | return [] 103 | 104 | # Mobile navigation drop down 105 | def __mobile_navigation__(self): 106 | return NavHelper.__get_left_navigation__(self.__title__()) 107 | 108 | # Main content area: takes in rx.Componenets and passes them to base file 109 | def __components__(self): 110 | return [ 111 | rf.Title("Application File Setup"), 112 | rf.Header("Setup"), 113 | rx.markdown(intro), 114 | rf.SubHeader("Application structure"), 115 | rx.markdown(structure), 116 | drop_list, 117 | rx.markdown(purpose), 118 | ] 119 | 120 | # Build method: creates a new instance for the page above 121 | def build(self): 122 | page = RxBasePage( 123 | self.__components__(), 124 | self.__left_navigation__(), 125 | self.__right__navigation__(), 126 | self.__mobile_navigation__(), 127 | ) 128 | return page.build() 129 | -------------------------------------------------------------------------------- /docs/app/pages/setup/socials.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import app.material as rf 4 | import reflex as rx 5 | 6 | repo = """"socials": { 7 | "github": "https://github.com/LineIndent/reflexify", 8 | "twitter": "https://twitter.com/getreflex", 9 | "youtube": "https://www.youtube.com/playlist?list=PLDHA4931gtc7wHBDGQOYlmcpZm7qyici7", 10 | "mastodon": "", 11 | "discord": "https://discord.com/invite/T5WSbC2YtQ", 12 | } 13 | """ 14 | 15 | 16 | def return_code_block(string: str, copy: bool = False): 17 | return rx.box( 18 | rx.code_block( 19 | string, 20 | language="python", 21 | can_copy=copy, 22 | theme="dark", 23 | width="100%", 24 | ), 25 | width="100%", 26 | padding="1rem 0rem", 27 | ) 28 | 29 | 30 | class RxPage: 31 | # Title of page: must match high-level key in config.py 32 | def __title__(self): 33 | return "Setup" 34 | 35 | # Page route path: must follow /parent-key/file-name *without .py extension* 36 | def __route__(self): 37 | return "/setup/socials" 38 | 39 | # Left navigation panel: automated based on config navigation order 40 | def __left_navigation__(self): 41 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 42 | return NavHelper.__set_left_navigation__(nav) 43 | 44 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 45 | def __right__navigation__(self): 46 | return [] 47 | 48 | # Mobile navigation drop down 49 | def __mobile_navigation__(self): 50 | return NavHelper.__get_left_navigation__(self.__title__()) 51 | 52 | # Main content area: takes in rx.Componenets and passes them to base file 53 | def __components__(self): 54 | return [ 55 | # add your components below # 56 | rf.Title("Adding social media"), 57 | rf.Header("Config.py file"), 58 | rx.markdown( 59 | "To add social media to your static website, you can add/edit the following paramters in your ```config.py``` file: " 60 | ), 61 | return_code_block(repo), 62 | rx.markdown( 63 | "The corresponding social media icon will then appear and be displayed inside the site footer. The links to these external websites are functional even with a static website. " 64 | ) 65 | # end your components above # 66 | ] 67 | 68 | # Build method: creates a new instance for the page above 69 | def build(self): 70 | page = RxBasePage( 71 | self.__components__(), 72 | self.__left_navigation__(), 73 | self.__right__navigation__(), 74 | self.__mobile_navigation__(), 75 | ) 76 | return page.build() 77 | -------------------------------------------------------------------------------- /docs/app/router.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import os 3 | import reflex as rx 4 | 5 | 6 | def create_module_from_file_path(file: str, filepath: str): 7 | module_spec = importlib.util.spec_from_file_location(file, filepath) 8 | module = importlib.util.module_from_spec(module_spec) 9 | module_spec.loader.exec_module(module) 10 | return module.RxPage() 11 | 12 | 13 | def get_application_routes_from_pages_dir(app: rx.App): 14 | routes: dict = {} 15 | for root, dirs, files in os.walk("./app/pages"): 16 | for file in files: 17 | filepath = os.path.join(root, file) 18 | if file.endswith(".py") and file != "page404.py": 19 | page_module = create_module_from_file_path(file, filepath) 20 | routes[page_module.__route__()] = page_module 21 | 22 | if file == "page404.py": 23 | filepath = os.path.join(root, file) 24 | page_module = create_module_from_file_path(file, filepath) 25 | app.add_custom_404_page(page_module.build()) 26 | 27 | add_routes_to_app_pages(app, routes) 28 | 29 | 30 | def add_routes_to_app_pages(app: rx.App, routes: dict): 31 | for key, value in routes.items(): 32 | app.add_page( 33 | component=value.build(), 34 | route=key, 35 | title=value.__title__(), 36 | ) 37 | 38 | 39 | def set_application_routes(app: rx.App): 40 | get_application_routes_from_pages_dir(app) 41 | -------------------------------------------------------------------------------- /docs/app/states/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/states/__init__.py -------------------------------------------------------------------------------- /docs/app/states/drawerState.py: -------------------------------------------------------------------------------- 1 | from .mainState import MainState 2 | 3 | 4 | class DrawerState(MainState): 5 | show_left: bool = False 6 | 7 | def left(self): 8 | self.show_left = not (self.show_left) 9 | -------------------------------------------------------------------------------- /docs/app/states/headerState.py: -------------------------------------------------------------------------------- 1 | from .mainState import MainState 2 | from app.helpers.nav_helpers import NavHelper 3 | 4 | 5 | def get_modified_navigation_list( 6 | titles: callable = NavHelper.__get_navigation_titles__(), 7 | paths: callable = NavHelper.__get_navigation_paths__(), 8 | ): 9 | return [[title, path] for title, path in zip(titles, paths)] 10 | 11 | 12 | class HeaderState(MainState): 13 | withNav: list[list[str]] = get_modified_navigation_list() 14 | -------------------------------------------------------------------------------- /docs/app/states/mainState.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class MainState(rx.State): 5 | """The app state.""" 6 | 7 | pass 8 | -------------------------------------------------------------------------------- /docs/app/styles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/styles/__init__.py -------------------------------------------------------------------------------- /docs/app/styles/_admonition.py: -------------------------------------------------------------------------------- 1 | # tags: info, not_allowed, warning, warning_two, calendar, question, check?, 2 | 3 | admonition_css: dict = { 4 | "info": { 5 | "body": { 6 | "transition": "all 550ms ease", 7 | "_light": { 8 | "border": "0.055rem solid #52b6d1", 9 | "_hover": { 10 | "box_shadow": "0px 0.75px 4px 4px rgba(231, 248, 251, 0.85)", 11 | }, 12 | }, 13 | "_dark": { 14 | "border": "0.055rem solid #52b6d1", 15 | "_hover": { 16 | "box_shadow": "0px 0.75px 4px 4px rgba(41, 63, 72, 0.65)", 17 | }, 18 | }, 19 | }, 20 | "header": { 21 | "_light": { 22 | "background": "rgba(231, 248, 251, 1)", 23 | }, 24 | "_dark": { 25 | "background": "rgba(41, 63, 72, 0.65)", 26 | }, 27 | }, 28 | "icon": {"color": "#52b6d1"}, 29 | }, 30 | "warning_two": { 31 | "body": { 32 | "transition": "all 550ms ease", 33 | "_light": { 34 | "border": "0.055rem solid #f09737", 35 | "_hover": { 36 | "box_shadow": "0px 0.75px 4px 4px rgba(253, 244, 231, 0.85)", 37 | }, 38 | }, 39 | "_dark": { 40 | "border": "0.055rem solid #f09737", 41 | "_hover": { 42 | "box_shadow": "0px 0.75px 4px 4px rgba(65, 58, 55, 0.65)", 43 | }, 44 | }, 45 | }, 46 | "header": { 47 | "_light": { 48 | "background": "rgba(253, 244, 231, 1)", 49 | }, 50 | "_dark": { 51 | "background": "rgba(65, 58, 55, 0.65)", 52 | }, 53 | }, 54 | "icon": {"color": "#f09737"}, 55 | }, 56 | "close": { 57 | "body": { 58 | "transition": "all 550ms ease", 59 | "_light": { 60 | "border": "0.055rem solid #ec5f59", 61 | "_hover": { 62 | "box_shadow": "0px 0.75px 4px 4px rgba(252, 238, 237, 0.85)", 63 | }, 64 | }, 65 | "_dark": { 66 | "border": "0.055rem solid #ec5f59", 67 | "_hover": { 68 | "box_shadow": "0px 0.75px 4px 4px rgba(64, 52, 62, 0.65)", 69 | }, 70 | }, 71 | }, 72 | "header": { 73 | "_light": { 74 | "background": "rgba(252, 238, 237, 1)", 75 | }, 76 | "_dark": { 77 | "background": "rgba(64, 52, 62, 0.65)", 78 | }, 79 | }, 80 | "icon": {"color": "#ec5f59"}, 81 | }, 82 | "calendar": { 83 | "body": { 84 | "transition": "all 550ms ease", 85 | "_light": { 86 | "border": "0.055rem solid #5688f7", 87 | "_hover": { 88 | "box_shadow": "0px 0.75px 4px 4px rgba(237, 243, 254, 0.85)", 89 | }, 90 | }, 91 | "_dark": { 92 | "border": "0.055rem solid #5688f7", 93 | "_hover": { 94 | "box_shadow": "0px 0.75px 4px 4px rgba(49, 56, 80, 0.65)", 95 | }, 96 | }, 97 | }, 98 | "header": { 99 | "_light": { 100 | "background": "rgba(237, 243, 254, 1)", 101 | }, 102 | "_dark": { 103 | "background": "rgba(49, 56, 80, 0.65)", 104 | }, 105 | }, 106 | "icon": {"color": "#5688f7"}, 107 | }, 108 | "question": { 109 | "body": { 110 | "transition": "all 550ms ease", 111 | "_light": { 112 | "border": "0.055rem solid #84db46", 113 | "_hover": { 114 | "box_shadow": "0px 0.75px 4px 4px rgba(241, 252, 233, 0.85)", 115 | }, 116 | }, 117 | "_dark": { 118 | "border": "0.055rem solid #84db46", 119 | "_hover": { 120 | "box_shadow": "0px 0.75px 4px 4px rgba(55, 66, 59, 0.65)", 121 | }, 122 | }, 123 | }, 124 | "header": { 125 | "_light": { 126 | "background": "rgba(241, 252, 233, 1)", 127 | }, 128 | "_dark": { 129 | "background": "rgba(55, 66, 59, 0.65)", 130 | }, 131 | }, 132 | "icon": {"color": "#84db46"}, 133 | }, 134 | "check": { 135 | "body": { 136 | "transition": "all 550ms ease", 137 | "_light": { 138 | "border": "0.055rem solid #5ac561", 139 | "_hover": { 140 | "box_shadow": "0px 0.75px 4px 4px rgba(233, 248, 238, 0.85)", 141 | }, 142 | }, 143 | "_dark": { 144 | "border": "0.055rem solid #5ac561", 145 | "_hover": { 146 | "box_shadow": "0px 0.75px 4px 4px rgba(46, 62, 64, 0.65)", 147 | }, 148 | }, 149 | }, 150 | "header": { 151 | "_light": { 152 | "background": "rgba(233, 248, 238, 1)", 153 | }, 154 | "_dark": { 155 | "background": "rgba(46, 62, 64, 0.65)", 156 | }, 157 | }, 158 | "icon": {"color": "#5ac561"}, 159 | }, 160 | } 161 | -------------------------------------------------------------------------------- /docs/app/styles/_base.py: -------------------------------------------------------------------------------- 1 | from app.helpers.app_config import Config 2 | 3 | # set the width and height of social media icons 4 | SOCIAL_SIZE = 19 5 | 6 | # set inverse filteration color scheme for social media icons 7 | SOCIAL_COLOR = r"filter: brightness(0) invert(1)" 8 | 9 | # main base css stylesheet for preconfigured web application 10 | base_css: dict = { 11 | "app": { 12 | "font_family": Config.__theme_font__(), 13 | }, 14 | "base": { 15 | "width": "100%", 16 | "min_height": "100vh", 17 | "spacing": "0rem", 18 | "padding": "0", 19 | "margin": "0", 20 | }, 21 | "left": { 22 | "width": "20%", 23 | "top": "0", 24 | "position": "sticky", 25 | "padding_top": "5rem", 26 | "align_items": "start", 27 | "padding_left": ["", "", "", "4rem", "10rem"], 28 | "transition": "all 550ms ease", 29 | }, 30 | "middle": { 31 | "width": ["100%", "100%", "100%", "60%", "60%"], 32 | "top": "0", 33 | "position": "block", 34 | "padding_top": ["2rem", "2rem", "2rem", "5rem", "5rem"], 35 | "align_items": "start", 36 | "padding_left": ["2rem", "2rem", "2rem", "2rem", "2rem"], 37 | "padding_right": ["2rem", "2rem", "2rem", "2rem", "2rem"], 38 | "padding_bottom": "6rem", 39 | "transition": "all 550ms ease", 40 | "min_height": "100vh", 41 | }, 42 | "right": { 43 | "width": ["0%", "0%", "0%", "20%", "20%"], 44 | "top": "0", 45 | "position": "sticky", 46 | "padding_top": "5rem", 47 | "align_items": ["end", "end", "end", "start", "start"], 48 | "padding_right": ["1rem", "1rem", "1rem", "", ""], 49 | "transition": "all 550ms ease", 50 | }, 51 | "header": { 52 | "main": { 53 | "width": "100%", 54 | "height": "50px", 55 | "position": "sticky", 56 | "background_color": Config.__theme_primary__(), 57 | "box_shadow": "0 3px 6px 0 rgba(0, 0, 0, 0.5)", 58 | "transition": "height 350ms ease", 59 | "top": "0", 60 | "z_index": "2", 61 | }, 62 | "icon": { 63 | "font_size": "xl", 64 | "cursor": "pointer", 65 | "color": "white", 66 | }, 67 | "navigation": { 68 | "align_items": "end", 69 | "transition": "opacity 500ms ease 500ms", 70 | }, 71 | "link_text": { 72 | "size": "s", 73 | "padding_top": "0.3rem", 74 | "color": "white", 75 | "font_weight": "semibold", 76 | }, 77 | "site_name": { 78 | "font_size": ["100%", "115%", "130%", "135%", "150%"], 79 | "color": "white", 80 | "transition": "all 550ms ease", 81 | "opacity": "1", 82 | "_hover": {"opacity": "0.85"}, 83 | "padding_right": "3.5rem", 84 | }, 85 | "max_header": { 86 | "width": "100%", 87 | "padding_left": ["", "", "", "4rem", "10rem"], 88 | "padding_right": ["", "", "", "4rem", "10rem"], 89 | "transition": "all 550ms ease", 90 | }, 91 | "min_header": { 92 | "width": "100%", 93 | "padding_left": ["1rem", "1rem", "0.5rem", "", ""], 94 | "padding_right": ["1rem", "1rem", "0.5rem", "", ""], 95 | "transition": "all 550ms ease", 96 | }, 97 | }, 98 | "footer": { 99 | "style": { 100 | "width": "100%", 101 | "height": ["105px", "75px", "65px", "65px", "65px"], 102 | "position": "sticky", 103 | "bg": "#15171b", 104 | "transition": "height 350ms ease", 105 | "top": "0", 106 | "overflow": "hidden", 107 | }, 108 | "socials": { 109 | "github": f"<img width='{SOCIAL_SIZE}' height='{SOCIAL_SIZE}' src='https://img.icons8.com/material-outlined/24/github.png' style='{SOCIAL_COLOR}';/>", # noqa: E501 110 | "twitter": f"<img width='{SOCIAL_SIZE}' height='{SOCIAL_SIZE}' src='https://img.icons8.com/ios-filled/24/twitter.png' style='{SOCIAL_COLOR}';/>", # noqa: E501 111 | "youtube": f"<img width='{SOCIAL_SIZE}' height='{SOCIAL_SIZE}' src='https://img.icons8.com/ios-filled/24/youtube.png' style='{SOCIAL_COLOR}';/>", # noqa: E501 112 | "mastodon": f"<img width='{SOCIAL_SIZE}' height='{SOCIAL_SIZE}' src='https://img.icons8.com/windows/24/mastodon.png' style='{SOCIAL_COLOR}';/>", # noqa: E501 113 | "discord": f"<img width='{SOCIAL_SIZE}' height='{SOCIAL_SIZE}' src='https://img.icons8.com/ios-filled/24/discord.png' style='{SOCIAL_COLOR}';/>", # noqa: E501 114 | }, 115 | }, 116 | "drawer": { 117 | "heading": { 118 | "width": "100%", 119 | "height": "100px", 120 | "align_items": "end", 121 | "bg": Config.__theme_primary__(), 122 | "padding_left": "1rem", 123 | "padding_bottom": "1rem", 124 | "transition": "all 550ms ease", 125 | "color": "white", 126 | }, 127 | "repo": { 128 | "width": "100%", 129 | "height": "45px", 130 | "bg": Config.__theme_primary__(), 131 | "padding_left": "1rem", 132 | "transition": "all 550ms ease", 133 | }, 134 | "router": { 135 | "align_items": "center", 136 | "width": "100%", 137 | "cursor": "pointer", 138 | "opacity": "0.8", 139 | "_hover": {"opacity": "1"}, 140 | }, 141 | }, 142 | } 143 | -------------------------------------------------------------------------------- /docs/app/styles/_markdown.py: -------------------------------------------------------------------------------- 1 | FONT_H1 = [f"{56 * size}px" for size in [0.70, 0.80, 0.80, 0.90, 1]] 2 | FONT_H2 = [f"{45 * size}px" for size in [0.70, 0.80, 0.80, 0.90, 1]] 3 | FONT_H3 = [f"{34 * size}px" for size in [0.70, 0.80, 0.80, 0.90, 1]] 4 | 5 | 6 | markdown_css: dict = { 7 | "h1": { 8 | "font_size": FONT_H1, 9 | "font_weight": "400", 10 | "line_height": "1.35", 11 | "letter_spacing": "-0.02em", 12 | "margin_bottom": "24px", 13 | "margin": "0", 14 | "padding": "0", 15 | "transition": "all 550ms ease", 16 | }, 17 | "h2": { 18 | "font_size": FONT_H2, 19 | "font_weight": "400", 20 | "line_height": "48px", 21 | "margin_top": "24px", 22 | "margin_bottom": "24px", 23 | "margin": "0", 24 | "padding": "0", 25 | "transition": "all 550ms ease", 26 | }, 27 | "h3": { 28 | "font_size": FONT_H3, 29 | "font_weight": "400", 30 | "line_height": "40px", 31 | "margin_top": "24px", 32 | "margin_bottom": "24px", 33 | "margin": "0", 34 | "padding": "0", 35 | "transition": "all 550ms ease", 36 | }, 37 | "p": { 38 | "font_size": "14px", 39 | "font_weight": "400", 40 | "line_height": "24px", 41 | "letter_spacing": "0", 42 | "margin_bottom": "16px", 43 | "margin": "0", 44 | "padding": "0", 45 | "transition": "all 550ms ease", 46 | }, 47 | } 48 | -------------------------------------------------------------------------------- /docs/app/utilities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/docs/app/utilities/__init__.py -------------------------------------------------------------------------------- /docs/app/utilities/rx_index.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | import reflex as rx 4 | 5 | 6 | class RxPage: 7 | # Title of page: must match high-level key in config.py 8 | def __title__(self): 9 | return "Reflexify" 10 | 11 | # Page route path: must follow /parent-key/file-name *without .py extension* 12 | def __route__(self): 13 | return "/" 14 | 15 | # Left navigation panel: automated based on config navigation order 16 | def __left_navigation__(self): 17 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 18 | return NavHelper.__set_left_navigation__(nav) 19 | 20 | # Right navigation panel: TBD 21 | def __right__navigation__(self): 22 | return [] 23 | 24 | # Mobile navigation drop down 25 | def __mobile_navigation__(self): 26 | return NavHelper.__get_left_navigation__(self.__title__()) 27 | 28 | # Main content area: takes in rx.Componenets and passes them to base file 29 | def __components__(self): 30 | return [ 31 | # add your components below # 32 | rx.heading("Welcome to Reflexify!", size="lg", padding_bottom="2rem"), 33 | rx.heading( 34 | "This is your index/landing page. Visit the documentation to get started!", 35 | size="sm", 36 | ) 37 | # end your components above # 38 | ] 39 | 40 | # Build method: creates a new instance for the page above 41 | def build(self): 42 | page = RxBasePage( 43 | self.__components__(), 44 | self.__left_navigation__(), 45 | self.__right__navigation__(), 46 | self.__mobile_navigation__(), 47 | ) 48 | return page.build() 49 | -------------------------------------------------------------------------------- /docs/app/utilities/rx_page404.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | import reflex as rx 3 | 4 | 5 | class RxPage: 6 | # Title of page: must match high-level key in config.py 7 | def __title__(self): 8 | return "Error404" 9 | 10 | # Page route path: must follow /parent-key/file-name *without .py extension* 11 | def __route__(self): 12 | return "/page_error_404" 13 | 14 | # Left navigation panel: automated based on config navigation order 15 | def __left_navigation__(self): 16 | return [] 17 | 18 | # Right navigation panel: TBD 19 | def __right__navigation__(self): 20 | return [] 21 | 22 | # Mobile navigation drop down 23 | def __mobile_navigation__(self): 24 | return [] 25 | 26 | # Main content area: takes in rx.Componenets and passes them to base file 27 | def __components__(self): 28 | return [ 29 | rx.box( 30 | rx.heading("Error 404", size="2xl", padding_bottom="1.5rem"), 31 | rx.heading("The page you are looking for does not exist!", size="md"), 32 | padding_left=["1rem", "1rem", "1rem", "4rem", "9rem"], 33 | transition="all 550ms ease", 34 | ) 35 | ] 36 | 37 | # Build method: creates a new instance for the page above 38 | def build(self): 39 | page = RxBasePage( 40 | self.__components__(), 41 | self.__left_navigation__(), 42 | self.__right__navigation__(), 43 | self.__mobile_navigation__(), 44 | ) 45 | return page.build() 46 | -------------------------------------------------------------------------------- /docs/app/utilities/rx_template.py: -------------------------------------------------------------------------------- 1 | from app.core.base import RxBasePage 2 | from app.helpers.nav_helpers import NavHelper 3 | 4 | 5 | class RxPage: 6 | # Title of page: must match high-level key in config.py 7 | def __title__(self): 8 | return "<title>" 9 | 10 | # Page route path: must follow /parent-key/file-name *without .py extension* 11 | def __route__(self): 12 | return "<route>" 13 | 14 | # Left navigation panel: automated based on config navigation order 15 | def __left_navigation__(self): 16 | nav: list = NavHelper.__get_left_navigation__(self.__title__()) 17 | return NavHelper.__set_left_navigation__(nav) 18 | 19 | # Right navigation panel: manually add your page-specific TOC (table fo content) to navigate wwithin page 20 | def __right__navigation__(self): 21 | return [] 22 | 23 | # Mobile navigation drop down 24 | def __mobile_navigation__(self): 25 | return NavHelper.__get_left_navigation__(self.__title__()) 26 | 27 | # Main content area: takes in rx.Componenets and passes them to base file 28 | def __components__(self): 29 | return [ 30 | # add your components below # 31 | # end your components above # 32 | ] 33 | 34 | # Build method: creates a new instance for the page above 35 | def build(self): 36 | page = RxBasePage( 37 | self.__components__(), 38 | self.__left_navigation__(), 39 | self.__right__navigation__(), 40 | self.__mobile_navigation__(), 41 | ) 42 | return page.build() 43 | -------------------------------------------------------------------------------- /reflexify_scripts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/reflexify_scripts/__init__.py -------------------------------------------------------------------------------- /reflexify_scripts/build.py: -------------------------------------------------------------------------------- 1 | import importlib.util 2 | import logging 3 | import os 4 | import shutil 5 | import time 6 | 7 | 8 | def check_if_pages_directory_exists() -> None: 9 | for __, dirs, __ in os.walk("./app/"): 10 | if "pages" not in dirs: 11 | page = os.path.join("./app/", "pages") 12 | os.mkdir(page) 13 | break 14 | else: 15 | break 16 | 17 | 18 | def get_list_of_pages_from_directory(): 19 | pages_list = set() 20 | dirs_list: list = [] 21 | 22 | def loop_over_sub_folders(path: str): 23 | for item in os.listdir(path): 24 | item_path = os.path.join(path, item) 25 | if item == "__pycache__": 26 | _path = path + "/" + item 27 | shutil.rmtree(_path) 28 | logging.info(f"Directory __pycache__ removed: {_path}") 29 | if os.path.isfile(item_path) and item_path.endswith(".py"): 30 | pages_list.add(item_path) 31 | elif os.path.isdir(item_path): 32 | dirs_list.append(item_path) 33 | loop_over_sub_folders(item_path) 34 | 35 | for root, folders, files in os.walk("./app/pages"): 36 | for folder in folders: 37 | if folder != "__pycache__": 38 | path = os.path.join(root, folder) 39 | dirs_list.append(path) 40 | loop_over_sub_folders(path) 41 | 42 | for file in files: 43 | item_path = os.path.join(root, file) 44 | if os.path.isfile(item_path) and item_path.endswith(".py"): 45 | pages_list.add(item_path) 46 | 47 | return dirs_list, pages_list 48 | 49 | 50 | def get_list_of_pages_from_config_file(docs: dict, parent_path: str = "./app/pages"): 51 | pages_list: list = [] 52 | dirs_list: list = [] 53 | 54 | def loop_over_nested_dict(docs: dict, current_path: str): 55 | if docs.get("navigation") is not None: 56 | docs = docs.get("navigation").items() 57 | else: 58 | docs = docs.items() 59 | 60 | for key, value in docs: 61 | if isinstance(value, dict): 62 | new_path = os.path.join(current_path, key) 63 | dirs_list.append(new_path) 64 | loop_over_nested_dict(value, new_path) 65 | else: 66 | new_path = os.path.join(current_path, value) 67 | file_path = new_path 68 | pages_list.append(file_path) 69 | 70 | loop_over_nested_dict(docs, parent_path) 71 | 72 | return dirs_list, pages_list 73 | 74 | 75 | def synchronize_directories(docs: dict): 76 | pages_dirs, pages_files = get_list_of_pages_from_directory() 77 | dict_dirs, dict_files = get_list_of_pages_from_config_file(docs) 78 | 79 | for pages_dir in pages_dirs: 80 | try: 81 | if pages_dir not in dict_dirs: 82 | shutil.rmtree(pages_dir) 83 | logging.info(f"Directory removed: {pages_dir}") 84 | except: # noqa: E722 85 | pass 86 | 87 | for dict_dir in dict_dirs: 88 | if not os.path.exists(dict_dir): 89 | os.makedirs(dict_dir) 90 | logging.info(f"Directory created: {dict_dir}") 91 | 92 | for file_path in pages_files: 93 | try: 94 | if ( 95 | file_path not in dict_files 96 | and file_path != "./app/pages/page404.py" 97 | and file_path != "./app/pages/index.py" 98 | ): 99 | os.remove(file_path) 100 | logging.info(f"File removed: {file_path}") 101 | except: # noqa: E722 102 | pass 103 | 104 | for key, value in docs.get("navigation").items(): 105 | if isinstance(value, dict): 106 | for __, path in value.items(): 107 | file_path = f"./app/pages/{key}/{path}" 108 | if not os.path.exists(file_path): 109 | with open("./app/utilities/rx_template.py", "r") as file: 110 | template_page = file.read() 111 | 112 | new_template_page = template_page.replace( 113 | "<title>", key.capitalize() 114 | ) 115 | 116 | new_template_page = new_template_page.replace( 117 | "<route>", f"/{key}/{path.split('.py')[0]}" 118 | ) 119 | 120 | with open(file_path, "w") as file: 121 | file.write(new_template_page) 122 | logging.info(f"File created: {file_path}") 123 | 124 | 125 | def set_template_and_error_file(): 126 | with open("./app/utilities/rx_page404.py", "r") as file: 127 | error = file.read() 128 | 129 | error_path = os.path.join("./app/pages/" + "page404.py") 130 | if not os.path.exists(error_path): 131 | with open(error_path, "w") as file: 132 | file.write(error) 133 | 134 | with open("./app/utilities/rx_index.py", "r") as file: 135 | rx_page = file.read() 136 | 137 | index_path = os.path.join("./app/pages/" + "index.py") 138 | if not os.path.exists(index_path): 139 | with open(index_path, "w") as file: 140 | file.write(rx_page) 141 | 142 | 143 | def get_dict_file(): 144 | filepath = os.path.join("./app/", "config.py") 145 | module_spec = importlib.util.spec_from_file_location("config.py", filepath) 146 | module = importlib.util.module_from_spec(module_spec) 147 | module_spec.loader.exec_module(module) 148 | 149 | return module.app_configuration 150 | 151 | 152 | def build(): 153 | app_configuration = get_dict_file() 154 | check_if_pages_directory_exists() 155 | set_template_and_error_file() 156 | synchronize_directories(app_configuration) 157 | 158 | 159 | if __name__ == "__main__": 160 | # Set up the logging configuration... 161 | logging.basicConfig( 162 | filename="./app/reflexify.log", 163 | level=logging.INFO, 164 | format="%(asctime)s - %(levelname)s - %(message)s", 165 | ) 166 | 167 | # Create the loggin stream so that changes also appear on the terminal. 168 | console_handler = logging.StreamHandler() 169 | console_handler.setLevel(logging.INFO) 170 | console_handler.setFormatter( 171 | logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") 172 | ) 173 | 174 | logging.getLogger().addHandler(console_handler) 175 | try: 176 | logging.info("Program started. Monitoring config.py file...") 177 | while True: 178 | build() 179 | time.sleep(1) 180 | 181 | except KeyboardInterrupt: 182 | logging.info("Program interrupted. Exiting...") 183 | -------------------------------------------------------------------------------- /reflexify_scripts/create.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | import shutil 4 | import click 5 | 6 | 7 | file_structure = { 8 | "core": [ 9 | "__init__.py", 10 | "base.py", 11 | "drawer.py", 12 | "footer.py", 13 | "header.py", 14 | "left.py", 15 | "middle.py", 16 | "mobile.py", 17 | "repository.py", 18 | "right.py", 19 | ], 20 | "helpers": ["__init__.py", "app_config.py", "css_helpers.py", "nav_helpers.py"], 21 | "material": ["__init__.py", "typography.py"], 22 | "states": ["__init__.py", "drawerState.py", "headerState.py", "mainState.py"], 23 | "styles": ["__init__.py", "_base.py"], 24 | "utilities": ["__init__.py", "rx_page404.py", "rx_template.py", "rx_index.py"], 25 | "app.py": None, 26 | "config.py": None, 27 | "router.py": None, 28 | } 29 | 30 | 31 | scripts_structure = { 32 | "reflexify_scripts": ["__init__.py", "build.py", "create.py"], 33 | } 34 | 35 | 36 | def create_src_directory(): 37 | if not os.path.exists("./app"): 38 | os.mkdir("./app") 39 | 40 | if not os.path.exists("./reflexify_scripts"): 41 | os.mkdir("./reflexify_scripts") 42 | 43 | 44 | def create_src_file_structure(): 45 | source = os.path.join(Path(__file__).parent.parent, "app") 46 | replicate = Path("./app") 47 | 48 | def create_dir_file_structure(dir_path: str, key: str, files): 49 | os.mkdir(dir_path) 50 | for file in files: 51 | source_path = os.path.join(source, key, file) 52 | replicate_path = os.path.join(dir_path, file) 53 | with open(source_path, "r") as src_file, open( 54 | replicate_path, "w" 55 | ) as dst_file: 56 | dst_file.write(src_file.read()) 57 | 58 | for key, value in file_structure.items(): 59 | if value is not None: 60 | dir_path = os.path.join(replicate, key) 61 | if isinstance(value, list): 62 | create_dir_file_structure(dir_path, key, value) 63 | else: 64 | source_file = os.path.join(source, key) 65 | file_path = os.path.join(replicate, key) 66 | with open(source_file, "r") as src_file, open(file_path, "w") as file: 67 | file.write(src_file.read()) 68 | 69 | 70 | def change_rx_config_details(): 71 | dir_name = os.getcwd().split("/")[-1] 72 | 73 | for file in os.listdir(os.getcwd()): 74 | if file == "rxconfig.py": 75 | file_path = os.path.join(os.getcwd(), file) 76 | with open(file_path, "r") as rder: 77 | config = rder.read() 78 | config = config.replace(f"{dir_name}", "app") 79 | with open(file_path, "w") as wter: 80 | wter.write(config) 81 | 82 | remove_folder = os.path.join(os.getcwd(), dir_name) 83 | shutil.rmtree(remove_folder) 84 | 85 | 86 | def set_up_reflexify_scripts(): 87 | scripts_src = Path(__file__).parent 88 | scripts_dst = "./reflexify_scripts" 89 | 90 | for key, values in scripts_structure.items(): 91 | for value in values: 92 | src_file = os.path.join(scripts_src, value) 93 | dst_file = os.path.join(scripts_dst, value) 94 | 95 | with open(src_file, "r") as rder, open(dst_file, "w") as wter: 96 | wter.write(rder.read()) 97 | 98 | 99 | @click.command() 100 | def create(): 101 | click.echo("Setting up Reflexify directory...") 102 | create_src_directory() 103 | create_src_file_structure() 104 | set_up_reflexify_scripts() 105 | click.echo("Source directory created successfully.") 106 | 107 | click.echo("Configuring rxconfig.py file...") 108 | change_rx_config_details() 109 | 110 | 111 | @click.group() 112 | def reflexify(): 113 | pass 114 | 115 | 116 | reflexify.add_command(create) 117 | 118 | if __name__ == "__main__": 119 | reflexify() 120 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | click>=8.1.3 2 | reflex>=0.2.6 3 | pytest>=7.4.0 4 | beautifulsoup4>=4.12.2 -------------------------------------------------------------------------------- /rxconfig.py: -------------------------------------------------------------------------------- 1 | import reflex as rx 2 | 3 | 4 | class ReflexifyConfig(rx.Config): 5 | pass 6 | 7 | 8 | config = ReflexifyConfig( 9 | app_name="app", 10 | db_url="sqlite:///reflex.db", 11 | env=rx.Env.DEV, 12 | ) 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name="reflexify", 5 | version="0.0.9", 6 | author="S. Ahmad P. Hakimi", 7 | author_email="pourhakimi@pm.me", 8 | description="Document, build, and showcase - all in one place, all in Python.", 9 | long_description="Reflexify is a Python web boilerplate library designed to provide a solid foundation to rapidly build high quality web applications with Python and Reflex. The project comes pre-configured with a range of tools and features to make it easy for developers to get started building their applications, without the need to spend time setting up infrastructure or configuring tools.", # noqa: E501 10 | long_description_content_type="text/markdown", 11 | url="https://github.com/LineIndent/reflexify", 12 | packages=find_packages(), 13 | data_files=[("app", ["app/app.py", "app/config.py", "app/router.py"])], 14 | include_package_data=True, 15 | install_requires=[ 16 | "click>=8.1.3", 17 | "reflex>=0.2.4", 18 | "beautifulsoup4>=4.12.2", 19 | ], 20 | classifiers=[ 21 | "Programming Language :: Python :: 3", 22 | "License :: OSI Approved :: MIT License", 23 | "Operating System :: OS Independent", 24 | ], 25 | entry_points={ 26 | "console_scripts": [ 27 | "rf-init=reflexify_scripts.create:create", 28 | ], 29 | }, 30 | keywords=["python web template", "web application", "documentation"], 31 | ) 32 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LineIndent/reflexify/fd04eef7dcb81fa3b38d847fb41f0b89b0d045c4/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | import pytest # noqa: F401 2 | from app.config import app_configuration 3 | 4 | 5 | @pytest.fixture 6 | def config(): 7 | return app_configuration 8 | 9 | 10 | def test_config_data_type(config): 11 | assert isinstance(config, dict) 12 | 13 | 14 | def test_config_navigation_key(config): 15 | assert "navigation" in config.keys() 16 | -------------------------------------------------------------------------------- /tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | import pytest # noqa: F401 2 | from app.helpers.app_config import Config 3 | 4 | 5 | def test_config_site_name(): 6 | assert isinstance(Config.__site_name__(), str) 7 | 8 | 9 | def test_config_repo_name(): 10 | assert isinstance(Config.__repo_name__(), str) 11 | 12 | 13 | def test_config_repo_url(): 14 | assert isinstance(Config.__repo_url__(), str) 15 | 16 | 17 | def test_config_drawer(): 18 | assert isinstance(Config.__drawer__(), bool) 19 | 20 | 21 | def test_config_navigation(): 22 | assert isinstance(Config.__navigation__(), dict) 23 | --------------------------------------------------------------------------------