Repository: reflex-dev/reflex Branch: main Commit: 7ee3026b8884 Files: 488 Total size: 3.4 MB Directory structure: gitextract_r7dp3u9n/ ├── .devcontainer/ │ └── devcontainer.json ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── build_issue.md │ │ ├── cloud_issue.md │ │ ├── custom_component_request.md │ │ ├── enhancement_request.md │ │ ├── enterprise_issue.md │ │ └── feature_request.md │ ├── actions/ │ │ └── setup_build_env/ │ │ └── action.yml │ ├── codeql-config.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── check_node_latest.yml │ ├── check_outdated_dependencies.yml │ ├── codeql.yml │ ├── dependency-review.yml │ ├── integration_app_harness.yml │ ├── integration_tests.yml │ ├── performance.yml │ ├── pre-commit.yml │ ├── publish.yml │ ├── reflex_init_in_docker_test.yml │ └── unit_tests.yml ├── .gitignore ├── .python-version ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MCP_README.md ├── README.md ├── SECURITY.md ├── docker-example/ │ ├── README.md │ ├── production-app-platform/ │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ └── README.md │ ├── production-compose/ │ │ ├── .dockerignore │ │ ├── Caddy.Dockerfile │ │ ├── Caddyfile │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── compose.prod.yaml │ │ ├── compose.tools.yaml │ │ └── compose.yaml │ ├── production-one-port/ │ │ ├── .dockerignore │ │ ├── Caddyfile │ │ ├── Dockerfile │ │ └── README.md │ ├── simple-one-port/ │ │ ├── .dockerignore │ │ ├── Caddyfile │ │ ├── Dockerfile │ │ └── README.md │ └── simple-two-port/ │ ├── .dockerignore │ ├── Dockerfile │ └── README.md ├── docs/ │ ├── DEBUGGING.md │ ├── de/ │ │ └── README.md │ ├── es/ │ │ └── README.md │ ├── in/ │ │ └── README.md │ ├── it/ │ │ └── README.md │ ├── ja/ │ │ └── README.md │ ├── kr/ │ │ └── README.md │ ├── pe/ │ │ └── README.md │ ├── pt/ │ │ └── pt_br/ │ │ └── README.md │ ├── tr/ │ │ └── README.md │ ├── vi/ │ │ └── README.md │ └── zh/ │ ├── zh_cn/ │ │ └── README.md │ └── zh_tw/ │ └── README.md ├── pyi_hashes.json ├── pyproject.toml ├── reflex/ │ ├── .templates/ │ │ ├── apps/ │ │ │ └── blank/ │ │ │ └── code/ │ │ │ ├── __init__.py │ │ │ └── blank.py │ │ └── web/ │ │ ├── .gitignore │ │ ├── app/ │ │ │ ├── entry.client.js │ │ │ └── routes.js │ │ ├── components/ │ │ │ ├── reflex/ │ │ │ │ └── radix_themes_color_mode_provider.js │ │ │ └── shiki/ │ │ │ └── code.js │ │ ├── jsconfig.json │ │ ├── postcss.config.js │ │ ├── react-router.config.js │ │ ├── styles/ │ │ │ └── __reflex_style_reset.css │ │ ├── utils/ │ │ │ ├── helpers/ │ │ │ │ ├── dataeditor.js │ │ │ │ ├── debounce.js │ │ │ │ ├── paste.js │ │ │ │ ├── range.js │ │ │ │ ├── throttle.js │ │ │ │ └── upload.js │ │ │ ├── react-theme.js │ │ │ └── state.js │ │ └── vite-plugin-safari-cachebust.js │ ├── __init__.py │ ├── __main__.py │ ├── admin.py │ ├── app.py │ ├── app_mixins/ │ │ ├── __init__.py │ │ ├── lifespan.py │ │ ├── middleware.py │ │ └── mixin.py │ ├── assets.py │ ├── base.py │ ├── compiler/ │ │ ├── __init__.py │ │ ├── compiler.py │ │ ├── templates.py │ │ └── utils.py │ ├── components/ │ │ ├── __init__.py │ │ ├── base/ │ │ │ ├── __init__.py │ │ │ ├── app_wrap.py │ │ │ ├── bare.py │ │ │ ├── body.py │ │ │ ├── document.py │ │ │ ├── error_boundary.py │ │ │ ├── fragment.py │ │ │ ├── link.py │ │ │ ├── meta.py │ │ │ ├── script.py │ │ │ └── strict_mode.py │ │ ├── component.py │ │ ├── core/ │ │ │ ├── __init__.py │ │ │ ├── auto_scroll.py │ │ │ ├── banner.py │ │ │ ├── breakpoints.py │ │ │ ├── clipboard.py │ │ │ ├── colors.py │ │ │ ├── cond.py │ │ │ ├── debounce.py │ │ │ ├── foreach.py │ │ │ ├── helmet.py │ │ │ ├── html.py │ │ │ ├── layout/ │ │ │ │ └── __init__.py │ │ │ ├── match.py │ │ │ ├── responsive.py │ │ │ ├── sticky.py │ │ │ ├── upload.py │ │ │ └── window_events.py │ │ ├── datadisplay/ │ │ │ ├── __init__.py │ │ │ ├── code.py │ │ │ ├── dataeditor.py │ │ │ ├── logo.py │ │ │ └── shiki_code_block.py │ │ ├── dynamic.py │ │ ├── el/ │ │ │ ├── __init__.py │ │ │ ├── element.py │ │ │ └── elements/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── forms.py │ │ │ ├── inline.py │ │ │ ├── media.py │ │ │ ├── metadata.py │ │ │ ├── other.py │ │ │ ├── scripts.py │ │ │ ├── sectioning.py │ │ │ ├── tables.py │ │ │ └── typography.py │ │ ├── field.py │ │ ├── gridjs/ │ │ │ ├── __init__.py │ │ │ └── datatable.py │ │ ├── literals.py │ │ ├── lucide/ │ │ │ ├── __init__.py │ │ │ └── icon.py │ │ ├── markdown/ │ │ │ ├── __init__.py │ │ │ └── markdown.py │ │ ├── moment/ │ │ │ ├── __init__.py │ │ │ └── moment.py │ │ ├── plotly/ │ │ │ ├── __init__.py │ │ │ └── plotly.py │ │ ├── props.py │ │ ├── radix/ │ │ │ ├── __init__.py │ │ │ ├── primitives/ │ │ │ │ ├── __init__.py │ │ │ │ ├── accordion.py │ │ │ │ ├── base.py │ │ │ │ ├── dialog.py │ │ │ │ ├── drawer.py │ │ │ │ ├── form.py │ │ │ │ ├── progress.py │ │ │ │ └── slider.py │ │ │ └── themes/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── color_mode.py │ │ │ ├── components/ │ │ │ │ ├── __init__.py │ │ │ │ ├── alert_dialog.py │ │ │ │ ├── aspect_ratio.py │ │ │ │ ├── avatar.py │ │ │ │ ├── badge.py │ │ │ │ ├── button.py │ │ │ │ ├── callout.py │ │ │ │ ├── card.py │ │ │ │ ├── checkbox.py │ │ │ │ ├── checkbox_cards.py │ │ │ │ ├── checkbox_group.py │ │ │ │ ├── context_menu.py │ │ │ │ ├── data_list.py │ │ │ │ ├── dialog.py │ │ │ │ ├── dropdown_menu.py │ │ │ │ ├── hover_card.py │ │ │ │ ├── icon_button.py │ │ │ │ ├── inset.py │ │ │ │ ├── popover.py │ │ │ │ ├── progress.py │ │ │ │ ├── radio.py │ │ │ │ ├── radio_cards.py │ │ │ │ ├── radio_group.py │ │ │ │ ├── scroll_area.py │ │ │ │ ├── segmented_control.py │ │ │ │ ├── select.py │ │ │ │ ├── separator.py │ │ │ │ ├── skeleton.py │ │ │ │ ├── slider.py │ │ │ │ ├── spinner.py │ │ │ │ ├── switch.py │ │ │ │ ├── table.py │ │ │ │ ├── tabs.py │ │ │ │ ├── text_area.py │ │ │ │ ├── text_field.py │ │ │ │ └── tooltip.py │ │ │ ├── layout/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── box.py │ │ │ │ ├── center.py │ │ │ │ ├── container.py │ │ │ │ ├── flex.py │ │ │ │ ├── grid.py │ │ │ │ ├── list.py │ │ │ │ ├── section.py │ │ │ │ ├── spacer.py │ │ │ │ └── stack.py │ │ │ └── typography/ │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── blockquote.py │ │ │ ├── code.py │ │ │ ├── heading.py │ │ │ ├── link.py │ │ │ └── text.py │ │ ├── react_player/ │ │ │ ├── __init__.py │ │ │ ├── audio.py │ │ │ ├── react_player.py │ │ │ └── video.py │ │ ├── react_router/ │ │ │ ├── __init__.py │ │ │ └── dom.py │ │ ├── recharts/ │ │ │ ├── __init__.py │ │ │ ├── cartesian.py │ │ │ ├── charts.py │ │ │ ├── general.py │ │ │ ├── polar.py │ │ │ └── recharts.py │ │ ├── sonner/ │ │ │ ├── __init__.py │ │ │ └── toast.py │ │ └── tags/ │ │ ├── __init__.py │ │ ├── cond_tag.py │ │ ├── iter_tag.py │ │ ├── match_tag.py │ │ ├── tag.py │ │ └── tagless.py │ ├── config.py │ ├── constants/ │ │ ├── __init__.py │ │ ├── base.py │ │ ├── colors.py │ │ ├── compiler.py │ │ ├── config.py │ │ ├── custom_components.py │ │ ├── event.py │ │ ├── installer.py │ │ ├── route.py │ │ ├── state.py │ │ └── utils.py │ ├── custom_components/ │ │ ├── __init__.py │ │ └── custom_components.py │ ├── environment.py │ ├── event.py │ ├── experimental/ │ │ ├── __init__.py │ │ ├── client_state.py │ │ └── hooks.py │ ├── istate/ │ │ ├── __init__.py │ │ ├── data.py │ │ ├── dynamic.py │ │ ├── manager/ │ │ │ ├── __init__.py │ │ │ ├── disk.py │ │ │ ├── memory.py │ │ │ └── redis.py │ │ ├── proxy.py │ │ ├── shared.py │ │ ├── storage.py │ │ └── wrappers.py │ ├── middleware/ │ │ ├── __init__.py │ │ ├── hydrate_middleware.py │ │ └── middleware.py │ ├── model.py │ ├── page.py │ ├── plugins/ │ │ ├── __init__.py │ │ ├── _screenshot.py │ │ ├── base.py │ │ ├── shared_tailwind.py │ │ ├── sitemap.py │ │ ├── tailwind_v3.py │ │ └── tailwind_v4.py │ ├── py.typed │ ├── reflex.py │ ├── route.py │ ├── state.py │ ├── style.py │ ├── testing.py │ ├── utils/ │ │ ├── __init__.py │ │ ├── build.py │ │ ├── codespaces.py │ │ ├── compat.py │ │ ├── console.py │ │ ├── decorator.py │ │ ├── exceptions.py │ │ ├── exec.py │ │ ├── export.py │ │ ├── format.py │ │ ├── frontend_skeleton.py │ │ ├── imports.py │ │ ├── js_runtimes.py │ │ ├── lazy_loader.py │ │ ├── misc.py │ │ ├── monitoring.py │ │ ├── net.py │ │ ├── path_ops.py │ │ ├── prerequisites.py │ │ ├── processes.py │ │ ├── pyi_generator.py │ │ ├── redir.py │ │ ├── registry.py │ │ ├── rename.py │ │ ├── serializers.py │ │ ├── tasks.py │ │ ├── telemetry.py │ │ ├── templates.py │ │ ├── token_manager.py │ │ └── types.py │ └── vars/ │ ├── __init__.py │ ├── base.py │ ├── color.py │ ├── datetime.py │ ├── dep_tracking.py │ ├── function.py │ ├── number.py │ ├── object.py │ └── sequence.py ├── scripts/ │ ├── __init__.py │ ├── bun_install.sh │ ├── darglint_test.bat │ ├── hatch_build.py │ ├── install.ps1 │ ├── integration.sh │ ├── make_pyi.py │ └── wait_for_listening_port.py └── tests/ ├── __init__.py ├── benchmarks/ │ ├── __init__.py │ ├── conftest.py │ ├── fixtures.py │ ├── test_compilation.py │ └── test_evaluate.py ├── integration/ │ ├── __init__.py │ ├── conftest.py │ ├── init-test/ │ │ ├── Dockerfile │ │ └── in_docker_test_script.sh │ ├── shared/ │ │ └── state.py │ ├── test_background_task.py │ ├── test_call_script.py │ ├── test_client_storage.py │ ├── test_component_state.py │ ├── test_computed_vars.py │ ├── test_connection_banner.py │ ├── test_deploy_url.py │ ├── test_dynamic_components.py │ ├── test_dynamic_routes.py │ ├── test_event_actions.py │ ├── test_event_chain.py │ ├── test_exception_handlers.py │ ├── test_extra_overlay_function.py │ ├── test_form_submit.py │ ├── test_icon.py │ ├── test_input.py │ ├── test_large_state.py │ ├── test_lifespan.py │ ├── test_linked_state.py │ ├── test_login_flow.py │ ├── test_media.py │ ├── test_memo.py │ ├── test_navigation.py │ ├── test_server_side_event.py │ ├── test_shared_state.py │ ├── test_state_inheritance.py │ ├── test_tailwind.py │ ├── test_upload.py │ ├── test_urls.py │ ├── test_var_operations.py │ ├── tests_playwright/ │ │ ├── test_appearance.py │ │ ├── test_datetime_operations.py │ │ ├── test_link_hover.py │ │ ├── test_stateless_app.py │ │ └── test_table.py │ └── utils.py ├── test_node_version.py └── units/ ├── __init__.py ├── assets/ │ ├── custom_script.js │ └── test_assets.py ├── compiler/ │ ├── __init__.py │ ├── test_compiler.py │ └── test_compiler_utils.py ├── components/ │ ├── __init__.py │ ├── base/ │ │ ├── test_bare.py │ │ ├── test_link.py │ │ └── test_script.py │ ├── core/ │ │ ├── __init__.py │ │ ├── test_banner.py │ │ ├── test_colors.py │ │ ├── test_cond.py │ │ ├── test_debounce.py │ │ ├── test_foreach.py │ │ ├── test_html.py │ │ ├── test_match.py │ │ ├── test_responsive.py │ │ └── test_upload.py │ ├── datadisplay/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_code.py │ │ ├── test_dataeditor.py │ │ ├── test_datatable.py │ │ └── test_shiki_code.py │ ├── el/ │ │ └── test_svg.py │ ├── forms/ │ │ ├── __init__.py │ │ └── test_form.py │ ├── graphing/ │ │ ├── __init__.py │ │ ├── test_plotly.py │ │ └── test_recharts.py │ ├── layout/ │ │ └── __init__.py │ ├── lucide/ │ │ └── test_icon.py │ ├── markdown/ │ │ ├── __init__.py │ │ └── test_markdown.py │ ├── media/ │ │ ├── __init__.py │ │ └── test_image.py │ ├── radix/ │ │ ├── test_icon_button.py │ │ └── test_layout.py │ ├── recharts/ │ │ ├── test_cartesian.py │ │ └── test_polar.py │ ├── test_component.py │ ├── test_component_future_annotations.py │ ├── test_component_state.py │ ├── test_props.py │ ├── test_tag.py │ └── typography/ │ ├── __init__.py │ └── test_markdown.py ├── conftest.py ├── istate/ │ ├── __init__.py │ ├── manager/ │ │ ├── __init__.py │ │ └── test_redis.py │ └── test_proxy.py ├── middleware/ │ ├── __init__.py │ ├── conftest.py │ └── test_hydrate_middleware.py ├── mock_redis.py ├── plugins/ │ └── test_sitemap.py ├── states/ │ ├── __init__.py │ ├── mutation.py │ └── upload.py ├── test_app.py ├── test_attribute_access_type.py ├── test_base.py ├── test_config.py ├── test_db_config.py ├── test_environment.py ├── test_event.py ├── test_health_endpoint.py ├── test_model.py ├── test_page.py ├── test_prerequisites.py ├── test_route.py ├── test_sqlalchemy.py ├── test_state.py ├── test_state_tree.py ├── test_style.py ├── test_telemetry.py ├── test_testing.py ├── test_var.py ├── utils/ │ ├── __init__.py │ ├── test_format.py │ ├── test_imports.py │ ├── test_processes.py │ ├── test_serializers.py │ ├── test_tasks.py │ ├── test_token_manager.py │ ├── test_types.py │ └── test_utils.py └── vars/ ├── test_base.py ├── test_dep_tracking.py ├── test_dep_tracking_integration.py └── test_object.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "image": "mcr.microsoft.com/devcontainers/python:3.14-trixie", "postCreateCommand": "/bin/bash -c 'python -m pip install uv && python -m uv sync & git clone https://github.com/reflex-dev/reflex-examples; wait'", "forwardPorts": [3000, 8000], "portsAttributes": { "3000": { "label": "Frontend", "onAutoForward": "notify" }, "8000": { "label": "Backend" } } } ================================================ FILE: .github/CODEOWNERS ================================================ @reflex-dev/reflex-team ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: "" assignees: "" --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: - Code/Link to Repo: **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Specifics (please complete the following information):** - Python Version: - Reflex Version: - OS: - Browser (Optional): **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/build_issue.md ================================================ --- name: Build Issue about: Report an issue related to reflex.build title: "[BUILD] " # This acts as a hint, but users can change it. labels: build assignees: "" --- ## Describe the issue Provide details about the issue. ... ## Expected behavior What should have happened? ... ## Steps to reproduce (if applicable) 1. 2. 3. ## Environment - Reflex Version: - Python Version: - OS: - Browser: ================================================ FILE: .github/ISSUE_TEMPLATE/cloud_issue.md ================================================ --- name: Cloud Issue about: Report an issue related to Reflex Cloud title: "[CLOUD] " # This acts as a hint, but users can change it. labels: cloud assignees: "" --- ## Describe the issue Provide details about the issue. ... ## Expected behavior What should have happened? ... ## Steps to reproduce (if applicable) 1. 2. 3. ## Environment - Reflex Version: - Python Version: - OS: - Browser: ================================================ FILE: .github/ISSUE_TEMPLATE/custom_component_request.md ================================================ --- name: Custom Component Request about: Suggest a new custom component for Reflex title: "" labels: "custom component request" assignees: "" --- **Describe the Custom Component** A clear and concise description of what the custom component does. - What is the purpose of the custom component? - What is the expected behavior of the custom component? - What are the use cases for the custom component? **Specifics (please complete the following information):** - Do you have a specific react package in mind? (Optional): **Additional context** Add any other context about the custom component here. ================================================ FILE: .github/ISSUE_TEMPLATE/enhancement_request.md ================================================ --- name: Enhancement Request about: Suggest an enhancement for an existing Reflex feature. title: "" labels: "enhancement" assignees: "" --- **Describe the Enhancement you want** A clear and concise description of what the improvement does. - Which feature do you want to improve? (and what problem does it have) - What is the benefit of the enhancement? - Show an example/usecase were the improvement are needed. **Additional context** Add any other context here. ================================================ FILE: .github/ISSUE_TEMPLATE/enterprise_issue.md ================================================ --- name: Enterprise Issue about: Report an issue related to Reflex Enterprise title: "[ENTERPRISE] " # This acts as a hint, but users can change it. labels: enterprise assignees: "" --- ## Describe the issue Provide details about the issue. ... ## Expected behavior What should have happened? ... ## Steps to reproduce (if applicable) 1. 2. 3. ## Environment - Reflex Version: - Python Version: - OS: - Browser: ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature Request about: Suggest a new feature for Reflex title: "" labels: "feature request" assignees: "" --- **Describe the Features** A clear and concise description of what the features does. - What is the purpose of the feature? - Show an example / use cases for the new feature. **Additional context** Add any other context here. ================================================ FILE: .github/actions/setup_build_env/action.yml ================================================ # Entry conditions: # - `setup/checkout` has already happened # - working dir is the root directory of your project (e.g. `reflex/`). # - You have a `uv.lock` file in the root directory of your project # - You have a `pyproject.toml` file in the root directory of your project # # Exit conditions: # - Python of version `python-version` is ready to be invoked as `python`. # - If `run-uv-sync` is true, deps as defined in `pyproject.toml` will have been installed into the venv at `create-venv-at-path`. name: "Setup Reflex build environment" description: "Sets up Python, install uv (cached), install project deps (cached)" inputs: python-version: description: "Python version setup" required: true run-uv-sync: description: "Whether to run uv sync on current dir" required: false default: false create-venv-at-path: description: "Path to venv (if uv sync is enabled)" required: false default: ".venv" runs: using: "composite" steps: - name: Install UV uses: astral-sh/setup-uv@v6 with: python-version: ${{ inputs.python-version }} enable-cache: true prune-cache: false activate-environment: true cache-dependency-glob: "uv.lock" - name: Setup Node uses: actions/setup-node@v4 with: node-version: 22 - name: Install Dependencies if: inputs.run-uv-sync == 'true' run: uv sync shell: bash ================================================ FILE: .github/codeql-config.yml ================================================ paths: - .github - reflex - reflex/.templates paths-ignore: - "**/tests/**" ================================================ FILE: .github/pull_request_template.md ================================================ ### All Submissions: - [ ] Have you followed the guidelines stated in [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) file? - [ ] Have you checked to ensure there aren't any other open [Pull Requests](https://github.com/reflex-dev/reflex/pulls) for the desired changed? ### Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update ### New Feature Submission: - [ ] Does your submission pass the tests? - [ ] Have you linted your code locally prior to submission? ### Changes To Core Features: - [ ] Have you added an explanation of what your changes do and why you'd like us to include them? - [ ] Have you written new tests for your core changes, as applicable? - [ ] Have you successfully ran tests with your changes locally? ### **After** these steps, you're ready to open a pull request. a. Give a descriptive title to your PR. b. Describe your changes. c. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). ================================================ FILE: .github/workflows/check_node_latest.yml ================================================ name: integration-node-latest permissions: contents: read on: push: branches: - main pull_request: branches: - main env: REFLEX_TELEMETRY_ENABLED: false jobs: check_latest_node: runs-on: ubuntu-22.04 strategy: matrix: split_index: [1, 2] node-version: ["node"] fail-fast: false steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: 3.13 run-uv-sync: true - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: uv run playwright install --with-deps - run: | uv run pytest tests/test_node_version.py -v uv run pytest tests/integration --splits 2 -v --group ${{matrix.split_index}} ================================================ FILE: .github/workflows/check_outdated_dependencies.yml ================================================ name: check-outdated-dependencies permissions: contents: read on: push: # This will trigger the action when a pull request is opened or updated. branches: - "release/**" # This will trigger the action when any branch starting with "release/" is created. workflow_dispatch: # Allow manual triggering if needed. jobs: backend: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - uses: ./.github/actions/setup_build_env with: python-version: 3.13 run-uv-sync: true - name: Check outdated backend dependencies run: | outdated=$(uv pip list --outdated) echo "Outdated:" echo "$outdated" filtered_outdated=$(echo "$outdated" | grep -vE 'pyright|ruff' || true) if [ ! -z "$filtered_outdated" ]; then echo "Outdated dependencies found:" echo "$filtered_outdated" exit 1 else echo "All dependencies are up to date. (pyright and ruff are ignored)" fi frontend: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: 3.13 run-uv-sync: true - name: Clone Reflex Website Repo uses: actions/checkout@v4 with: repository: reflex-dev/reflex-web ref: main path: reflex-web - name: Compile pyproject.toml into requirements.txt working-directory: ./reflex-web run: | uv pip compile pyproject.toml --no-annotate --no-header --no-deps --output-file requirements.txt grep -ivE "reflex " requirements.txt > requirements.txt.tmp && mv requirements.txt.tmp requirements.txt - name: Install Requirements for reflex-web working-directory: ./reflex-web run: uv pip install -r requirements.txt - name: Init Website for reflex-web working-directory: ./reflex-web run: uv run reflex init - name: Run Website and Check for errors run: | uv run bash scripts/integration.sh ./reflex-web dev - name: Check outdated frontend dependencies working-directory: ./reflex-web/.web run: | raw_outdated=$(/home/runner/.local/share/reflex/bun/bin/bun outdated) outdated=$(echo "$raw_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\|' || true) echo "Outdated:" echo "$outdated" # Ignore 3rd party dependencies that are not updated. filtered_outdated=$(echo "$outdated" | grep -vE 'Package|@chakra-ui|lucide-react|@splinetool/runtime|ag-grid-react|framer-motion|ag-grid' || true) no_extra=$(echo "$filtered_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-' || true) if [ ! -z "$no_extra" ]; then echo "Outdated dependencies found:" echo "$filtered_outdated" exit 1 else echo "All dependencies are up to date. (3rd party packages are ignored)" fi ================================================ FILE: .github/workflows/codeql.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL Advanced" on: push: branches: ["main"] pull_request: branches: ["main"] schedule: - cron: "36 7 * * 4" jobs: analyze: name: Analyze (${{ matrix.language }}) # Runner size impacts CodeQL analysis time. To learn more, please see: # - https://gh.io/recommended-hardware-resources-for-running-codeql # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners (GitHub.com only) # Consider using larger runners or machines with greater resources for possible analysis time improvements. runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} permissions: # required for all workflows security-events: write # required to fetch internal or private CodeQL packs packages: read # only required for workflows in private repositories actions: read contents: read strategy: fail-fast: false matrix: include: - language: javascript-typescript build-mode: none - language: python build-mode: none - language: actions build-mode: none # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' # Use `c-cpp` to analyze code written in C, C++ or both # Use 'java-kotlin' to analyze code written in Java, Kotlin or both # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository uses: actions/checkout@v4 # Add any setup steps before running the `github/codeql-action/init` action. # This includes steps like installing compilers or runtimes (`actions/setup-node` # or others). This is typically only required for manual builds. # - name: Setup runtime (example) # uses: actions/setup-example@v1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} config-file: .github/codeql-config.yml build-mode: ${{ matrix.build-mode }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # If the analyze step fails for one of the languages you are analyzing with # "We were unable to automatically build your code", modify the matrix above # to set the build mode to "manual" for that language. Then modify this step # to build your code. # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - if: matrix.build-mode == 'manual' shell: bash run: | echo 'If you are using a "manual" build mode for one or more of the' \ 'languages you are analyzing, replace this with the commands to build' \ 'your code, for example:' echo ' make bootstrap' echo ' make release' exit 1 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" ================================================ FILE: .github/workflows/dependency-review.yml ================================================ name: "Dependency Review" on: [pull_request] permissions: contents: read jobs: dependency-review: runs-on: ubuntu-latest steps: - name: "Checkout Repository" uses: actions/checkout@v4 - name: "Dependency Review" uses: actions/dependency-review-action@v4 with: allow-licenses: Apache-2.0, BSD-2-Clause, BSD-3-Clause, HPND, ISC, MIT, MPL-2.0, Unlicense, Python-2.0, Python-2.0.1, Apache-2.0 AND MIT, BSD-2-Clause AND BSD-3-Clause, Apache-2.0 AND BSD-3-Clause allow-dependencies-licenses: "pkg:pypi/lazy-loader" ================================================ FILE: .github/workflows/integration_app_harness.yml ================================================ name: integration-app-harness concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} cancel-in-progress: true on: push: branches: ["main"] paths-ignore: - "**/*.md" pull_request: branches: ["main"] paths-ignore: - "**/*.md" env: APP_HARNESS_HEADLESS: 1 PYTHONUNBUFFERED: 1 permissions: contents: read jobs: integration-app-harness: timeout-minutes: 30 strategy: matrix: state_manager: ["redis", "memory"] python-version: ["3.11", "3.12", "3.13", "3.14"] split_index: [1, 2] fail-fast: false runs-on: ubuntu-22.04 services: # Label used to access the service container redis: image: ${{ matrix.state_manager == 'redis' && 'redis' || '' }} # Set health checks to wait until redis has started options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 ports: # Maps port 6379 on service container to the host - 6379:6379 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-uv-sync: true - name: Install playwright run: uv run playwright install chromium --only-shell - name: Run app harness tests env: REFLEX_REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} run: uv run pytest tests/integration --reruns 3 -v --maxfail=5 --splits 2 --group ${{matrix.split_index}} ================================================ FILE: .github/workflows/integration_tests.yml ================================================ name: integration-tests on: push: branches: ["main"] paths-ignore: - "**/*.md" pull_request: branches: ["main"] paths-ignore: - "**/*.md" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} cancel-in-progress: true permissions: contents: read defaults: run: shell: bash env: # Windows CI would fail without this. # Ref: https://gist.github.com/NodeJSmith/e7e37f2d3f162456869f015f842bcf15 # TODO: can we fix windows encoding natively within reflex? Bug above can hit real users too (less common, but possible) # - Catch encoding errors when printing logs # - Best effort print lines that contain illegal chars (map to some default char, etc.) PYTHONIOENCODING: "utf8" REFLEX_TELEMETRY_ENABLED: false NODE_OPTIONS: "--max_old_space_size=8192" PR_TITLE: ${{ github.event.pull_request.title }} jobs: example-counter-and-nba-proxy: env: OUTPUT_FILE: import_benchmark.json timeout-minutes: 30 strategy: # Prioritize getting more information out of the workflow (even if something fails) fail-fast: false matrix: # Show OS combos first in GUI os: [ubuntu-latest, windows-latest] python-version: ["3.10", "3.11", "3.12", "3.13"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-uv-sync: true - name: Clone Reflex Examples Repo uses: actions/checkout@v4 with: repository: reflex-dev/reflex-examples path: reflex-examples - name: Install requirements for counter example working-directory: ./reflex-examples/counter run: | uv pip install -r requirements.txt - name: Check export --backend-only before init for counter example working-directory: ./reflex-examples/counter run: | uv run reflex export --backend-only - name: Check run --backend-only before init for counter example run: | uv run bash scripts/integration.sh ./reflex-examples/counter dev 8001 --backend-only --backend-port 8001 - name: Init Website for counter example working-directory: ./reflex-examples/counter run: | uv run reflex init --loglevel debug - name: Check export for counter example working-directory: ./reflex-examples/counter run: | uv run reflex export - name: Run Website and Check for errors run: | # Check that npm is home npm -v uv run bash scripts/integration.sh ./reflex-examples/counter dev - name: Install requirements for nba proxy example working-directory: ./reflex-examples/nba-proxy run: | uv pip install -r requirements.txt - name: Check export --backend-only before init for nba-proxy example working-directory: ./reflex-examples/nba-proxy run: | uv run reflex export --backend-only - name: Init Website for nba-proxy example working-directory: ./reflex-examples/nba-proxy run: | uv run reflex init --loglevel debug - name: Run Website and Check for errors run: | # Check that npm is home npm -v uv run bash scripts/integration.sh ./reflex-examples/nba-proxy dev reflex-web: strategy: fail-fast: false matrix: python-version: ["3.11", "3.12"] env: REFLEX_WEB_WINDOWS_OVERRIDE: "1" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-uv-sync: true - name: Clone Reflex Website Repo uses: actions/checkout@v4 with: repository: reflex-dev/reflex-web ref: main path: reflex-web submodules: recursive - name: Compile pyproject.toml into requirements.txt working-directory: ./reflex-web run: | uv pip compile pyproject.toml --no-annotate --no-header --no-deps --output-file requirements.txt grep -ivE "reflex " requirements.txt > requirements.txt.tmp && mv requirements.txt.tmp requirements.txt - name: Install Requirements for reflex-web working-directory: ./reflex-web run: uv pip install -r requirements.txt - name: Init Website for reflex-web working-directory: ./reflex-web run: uv run --active --no-sync reflex init - name: Run Website and Check for errors run: | # Check that npm is home npm -v uv run --active --no-sync bash scripts/integration.sh ./reflex-web prod rx-shout-from-template: strategy: fail-fast: false runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: 3.14 run-uv-sync: true - name: Create app directory run: mkdir rx-shout-from-template - name: Init reflex-web from template run: uv run reflex init --template https://github.com/masenf/rx_shout working-directory: ./rx-shout-from-template - name: ignore reflex pin in requirements run: sed -i -e '/reflex==/d' requirements.txt working-directory: ./rx-shout-from-template - name: Install additional dependencies run: uv pip install -r requirements.txt working-directory: ./rx-shout-from-template - name: Run Website and Check for errors run: | # Check that npm is home npm -v uv run bash scripts/integration.sh ./rx-shout-from-template prod reflex-web-macos: if: github.event_name == 'push' && github.ref == 'refs/heads/main' strategy: fail-fast: false matrix: # Note: py311 version chosen due to available arm64 darwin builds. python-version: ["3.11", "3.12"] runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-uv-sync: true - name: Clone Reflex Website Repo uses: actions/checkout@v4 with: repository: reflex-dev/reflex-web ref: main path: reflex-web submodules: recursive - name: Compile pyproject.toml into requirements.txt working-directory: ./reflex-web run: | uv pip compile pyproject.toml --no-annotate --no-header --no-deps --output-file requirements.txt grep -ivE "reflex " requirements.txt > requirements.txt.tmp && mv requirements.txt.tmp requirements.txt - name: Install Requirements for reflex-web working-directory: ./reflex-web run: uv pip install -r requirements.txt - name: Init Website for reflex-web working-directory: ./reflex-web run: uv run --active --no-sync reflex init - name: Run Website and Check for errors run: | # Check that npm is home npm -v uv run --active --no-sync bash scripts/integration.sh ./reflex-web prod ================================================ FILE: .github/workflows/performance.yml ================================================ name: performance-tests permissions: contents: read on: push: branches: - "main" # or "master" paths-ignore: - "**/*.md" pull_request: workflow_dispatch: env: REFLEX_TELEMETRY_ENABLED: false NODE_OPTIONS: "--max_old_space_size=8192" PR_TITLE: ${{ github.event.pull_request.title }} APP_HARNESS_HEADLESS: 1 PYTHONUNBUFFERED: 1 jobs: benchmarks: name: Run benchmarks runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v6 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.14" - name: Install dependencies run: uv sync --all-extras --dev - name: Run benchmarks uses: CodSpeedHQ/action@v4 with: mode: instrumentation run: uv run pytest -v tests/benchmarks --codspeed ================================================ FILE: .github/workflows/pre-commit.yml ================================================ name: pre-commit permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} cancel-in-progress: true on: pull_request: branches: ["main"] push: # Note even though this job is called "pre-commit" and runs "pre-commit", this job will run # also POST-commit on main also! In case there are mishandled merge conflicts / bad auto-resolves # when merging into main branch. branches: ["main"] jobs: pre-commit: timeout-minutes: 30 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: 3.14 run-uv-sync: true - uses: actions/checkout@v4 with: clean: false - run: uv run pre-commit run --all-files --show-diff-on-failure ================================================ FILE: .github/workflows/publish.yml ================================================ name: Publish to PyPI on: workflow_dispatch: jobs: publish: runs-on: ubuntu-latest environment: name: pypi permissions: id-token: write contents: read steps: - name: Checkout uses: actions/checkout@v6 - name: Install uv uses: astral-sh/setup-uv@v7 - name: Build run: uv build - name: Verify .pyi files in wheel run: | if unzip -l dist/*.whl | grep '\.pyi$'; then echo "✓ .pyi files found in distribution" else echo "Error: No .pyi files found in wheel" exit 1 fi - name: Publish run: uv publish ================================================ FILE: .github/workflows/reflex_init_in_docker_test.yml ================================================ name: reflex-init-in-docker-test permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} cancel-in-progress: true on: push: branches: ["main"] paths-ignore: - "**/*.md" pull_request: branches: ["main"] paths-ignore: - "**/*.md" jobs: # TODO we can extend to various starting points (e.g. Ubuntu with node, without node, with unzip, without unzip, etc.) # Currently starting point is: Ubuntu + unzip, xz-utils, Python suite. No node. reflex-install-and-init: timeout-minutes: 30 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - shell: bash run: | # Run reflex init in a docker container # cwd is repo root docker build -f tests/integration/init-test/Dockerfile -t reflex-init-test tests/integration/init-test docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/tests/integration/init-test/in_docker_test_script.sh ================================================ FILE: .github/workflows/unit_tests.yml ================================================ name: unit-tests concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.id }} cancel-in-progress: true on: push: branches: ["main"] paths-ignore: - "**/*.md" pull_request: branches: ["main"] paths-ignore: - "**/*.md" permissions: contents: read defaults: run: shell: bash jobs: unit-tests: timeout-minutes: 30 strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: ${{ matrix.os }} # Service containers to run with `runner-job` services: # Label used to access the service container redis: image: ${{ matrix.os == 'ubuntu-latest' && 'redis' || '' }} # Set health checks to wait until redis has started options: >- --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 ports: # Maps port 6379 on service container to the host - 6379:6379 steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-uv-sync: true - name: Run unit tests run: | export PYTHONUNBUFFERED=1 uv run pytest tests/units --cov --no-cov-on-fail --cov-report= - name: Run unit tests w/ redis if: ${{ matrix.os == 'ubuntu-latest' }} run: | export PYTHONUNBUFFERED=1 export REFLEX_REDIS_URL=redis://localhost:6379 uv run pytest tests/units --cov --no-cov-on-fail --cov-report= - name: Run unit tests w/ redis and OPLOCK_ENABLED if: ${{ matrix.os == 'ubuntu-latest' }} run: | export PYTHONUNBUFFERED=1 export REFLEX_REDIS_URL=redis://localhost:6379 export REFLEX_OPLOCK_ENABLED=true uv run pytest tests/units --cov --no-cov-on-fail --cov-report= # Change to explicitly install v1 when reflex-hosting-cli is compatible with v2 - name: Run unit tests w/ pydantic v1 run: | export PYTHONUNBUFFERED=1 uv pip install "pydantic~=1.10" uv run pytest tests/units --cov --no-cov-on-fail --cov-report= - name: Generate coverage report run: uv run coverage html unit-tests-macos: timeout-minutes: 30 if: github.event_name == 'push' && github.ref == 'refs/heads/main' strategy: fail-fast: false matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-uv-sync: true - name: Run unit tests run: | export PYTHONUNBUFFERED=1 uv run pytest tests/units --cov --no-cov-on-fail --cov-report= - name: Run unit tests w/ pydantic v1 run: | export PYTHONUNBUFFERED=1 uv pip install "pydantic~=1.10" uv run pytest tests/units --cov --no-cov-on-fail --cov-report= ================================================ FILE: .gitignore ================================================ **/.DS_Store **/*.pyc assets/external/* dist/* examples/ .web .states .idea .vscode .coverage .coverage.* .venv venv requirements.txt .pyi_generator_last_run .pyi_generator_diff reflex.db .codspeed .env .env.* node_modules package-lock.json *.pyi .pre-commit-config.yaml ================================================ FILE: .python-version ================================================ 3.14 ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at opensource@reflex.dev. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community as well as any sort of private interaction with the people. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: CONTRIBUTING.md ================================================ # Reflex Contributing Guidelines For an extensive guide on the different ways to contribute to Reflex see our [Contributing Guide on Notion](https://www.notion.so/reflex-dev/2107ab2bc166497db951b8d742748284?v=f0eaff78fa984b5ab15d204af58907d7). ## Running a Local Build of Reflex Here is a quick guide on how to run Reflex repo locally so you can start contributing to the project. **Prerequisites:** - uv version >= 0.6.0 and add it to your path (see [UV Docs](https://docs.astral.sh/uv/getting-started/installation/) for more info). **1. Fork this repository:** Fork this repository by clicking on the `Fork` button on the top right. **2. Clone Reflex and navigate into the repo:** ```bash git clone https://github.com//reflex.git cd reflex ``` **3. Install your local Reflex build:** ```bash uv sync ``` **4. Now create an examples folder so you can test the local Python build in this repository.** - We have the `examples` folder in the `.gitignore`, so your changes in `reflex/examples` won't be reflected in your commit. ```bash mkdir examples cd examples ``` **5. Init and Run** ```bash uv run reflex init uv run reflex run ``` All the changes you make to the repository will be reflected in your running app. - We have the examples folder in the .gitignore, so your changes in reflex/examples won't be reflected in your commit. ## 🧪 Testing and QA Any feature or significant change added should be accompanied with unit tests. Within the 'test' directory of Reflex you can add to a test file already there or create a new test python file if it doesn't fit into the existing layout. #### What to unit test? - Any feature or significant change that has been added. - Any edge cases or potential problem areas. - Any interactions between different parts of the code. ## ✅ Making a PR Once you solve a current issue or improvement to Reflex, you can make a PR, and we will review the changes. Before submitting, a pull request, ensure the following steps are taken and test passing. In your `reflex` directory run make sure all the unit tests are still passing using the following command. This will fail if code coverage is below 70%. ```bash uv run pytest tests/units --cov --no-cov-on-fail --cov-report= ``` Next make sure all the following tests pass. This ensures that every new change has proper documentation and type checking. ```bash uv run ruff check . uv run pyright reflex tests find reflex tests -name "*.py" -not -path reflex/reflex.py | xargs uv run darglint ``` Finally, run `ruff` to format your code. ```bash uv run ruff format . ``` Consider installing git pre-commit hooks so Ruff, Pyright, Darglint and `make_pyi` will run automatically before each commit. ```bash uv run pre-commit install ``` That's it you can now submit your PR. Thanks for contributing to Reflex! ## Editing Templates To edit the templates in Reflex you can do so in two way. Change to the basic `blank` template can be done in the `reflex/.templates/apps/blank` directory. Others templates can be edited in their own repository. For example the `sidebar` template can be found in the [`reflex-sidebar`](https://github.com/reflex-dev/sidebar-template) repository. ## Other Notes For some pull requests when adding new components you will have to generate a pyi file for the new component. This is done by running the following command in the `reflex` directory. (Please check in with the team before adding a new component to Reflex we are cautious about adding new components to Reflex's core.) ```bash uv run python -m reflex.utils.pyi_generator ``` ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/LICENSE-2.0 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2023, Pynecone, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MCP_README.md ================================================ # Reflex MCP Server The Reflex MCP Server provides comprehensive access to Reflex framework documentation and component information through the Model Context Protocol (MCP). This server is deployed and ready to use with your MCP-compatible AI tools. Check the documentation at https://reflex.dev/docs/ai-builder/integrations/mcp-overview ================================================ FILE: README.md ================================================
Reflex Logo
### **✨ Performant, customizable web apps in pure Python. Deploy in seconds. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ) [![Twitter](https://img.shields.io/twitter/follow/getreflex)](https://x.com/getreflex)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- > [!NOTE] > 🚀 **Try [Reflex Build](https://build.reflex.dev/)** – our AI-powered app builder that generates full-stack Reflex applications in seconds. --- # Introduction Reflex is a library to build full-stack web apps in pure Python. Key features: - **Pure Python** - Write your app's frontend and backend all in Python, no need to learn Javascript. - **Full Flexibility** - Reflex is easy to get started with, but can also scale to complex apps. - **Deploy Instantly** - After building, deploy your app with a [single command](https://reflex.dev/docs/hosting/deploy-quick-start/) or host it on your own server. See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) to learn how Reflex works under the hood. ## ⚙️ Installation **Important:** We strongly recommend using a virtual environment to ensure the `reflex` command is available in your PATH. ## 🥳 Create your first app ### 1. Create the project directory Replace `my_app_name` with your project name: ```bash mkdir my_app_name cd my_app_name ``` ### 2. Set up a virtual environment Create and activate virtual environment ```bash # On Windows: python -m venv .venv .venv\Scripts\activate # On macOS/Linux: python3 -m venv .venv source .venv/bin/activate ``` ### 3. Install Reflex Reflex is available as a pip package (Requires Python 3.10+): ```bash pip install reflex ``` ### 4. Initialize the project This command initializes a template app in your new directory: ```bash reflex init ``` ### 5. Run the app You can run this app in development mode: ```bash reflex run ``` You should see your app running at http://localhost:3000. Now you can modify the source code in `my_app_name/my_app_name.py`. Reflex has fast refreshes so you can see your changes instantly when you save your code. ### Troubleshooting If you installed Reflex without a virtual environment and the `reflex` command is not found, you can run commands using: `python3 -m reflex init` and `python3 -m reflex run` ## 🫧 Example App Let's go over an example: creating an image generation UI around [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). For simplicity, we just call the [OpenAI API](https://platform.openai.com/docs/api-reference/authentication), but you could replace this with an ML model run locally.  
A frontend wrapper for DALL·E, shown in the process of generating an image.
  Here is the complete code to create this. This is all done in one Python file! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Let's break this down.
Explaining the differences between backend and frontend parts of the DALL-E app.
### **Reflex UI** Let's start with the UI. ```python def index(): return rx.center( ... ) ``` This `index` function defines the frontend of the app. We use different components such as `center`, `vstack`, `input`, and `button` to build the frontend. Components can be nested within each other to create complex layouts. And you can use keyword args to style them with the full power of CSS. Reflex comes with [60+ built-in components](https://reflex.dev/docs/library) to help you get started. We are actively adding more components, and it's easy to [create your own components](https://reflex.dev/docs/wrapping-react/overview/). ### **State** Reflex represents your UI as a function of your state. ```python class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False ``` The state defines all the variables (called vars) in an app that can change and the functions that change them. Here the state is comprised of a `prompt` and `image_url`. There are also the booleans `processing` and `complete` to indicate when to disable the button (during image generation) and when to show the resulting image. ### **Event Handlers** ```python def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Within the state, we define functions called event handlers that change the state vars. Event handlers are the way that we can modify the state in Reflex. They can be called in response to user actions, such as clicking a button or typing in a text box. These actions are called events. Our DALL·E app has an event handler, `get_image` which gets this image from the OpenAI API. Using `yield` in the middle of an event handler will cause the UI to update. Otherwise the UI will update at the end of the event handler. ### **Routing** Finally, we define our app. ```python app = rx.App() ``` We add a page from the root of the app to the index component. We also add a title that will show up in the page preview/browser tab. ```python app.add_page(index, title="DALL-E") ``` You can create a multi-page app by adding more pages. ## 📑 Resources
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Component Library](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Status Reflex launched in December 2022 with the name Pynecone. 🚀 Introducing [Reflex Build](https://build.reflex.dev/) — Our AI-Powered Builder Reflex Build uses AI to generate complete full-stack Python applications. It helps you quickly create, customize, and refine your Reflex apps — from frontend components to backend logic — so you can focus on your ideas instead of boilerplate code. Whether you’re prototyping or scaling, Reflex Build accelerates development by intelligently scaffolding and optimizing your app’s entire stack. Alongside this, [Reflex Cloud](https://cloud.reflex.dev) launched in 2025 to offer the best hosting experience for your Reflex apps. We’re continuously improving the platform with new features and capabilities. Reflex has new releases and features coming every week! Make sure to :star: star and :eyes: watch this repository to stay up to date. ## Contributing We welcome contributions of any size! Below are some good ways to get started in the Reflex community. - **Join Our Discord**: Our [Discord](https://discord.gg/T5WSbC2YtQ) is the best place to get help on your Reflex project and to discuss how you can contribute. - **GitHub Discussions**: A great way to talk about features you want added or things that are confusing/need clarification. - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) are an excellent way to report bugs. Additionally, you can try and solve an existing issue and submit a PR. We are actively looking for contributors, no matter your skill level or experience. To contribute check out [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## All Thanks To Our Contributors: ## License Reflex is open-source and licensed under the [Apache License 2.0](https://raw.githubusercontent.com/reflex-dev/reflex/main/LICENSE). ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions | Version | Supported | | -------- | ------------------ | | >= 0.7.0 | :white_check_mark: | ## Reporting a Vulnerability Please report any security vulnerabilities by using: https://github.com/reflex-dev/reflex/security/advisories/new ================================================ FILE: docker-example/README.md ================================================ # Reflex Docker Examples This directory contains several examples of how to deploy Reflex apps using docker. In all cases, ensure that your `requirements.txt` file is up to date and includes the `reflex` package. ## `simple-two-port` The most basic production deployment exposes two HTTP ports and relies on an existing load balancer to forward the traffic appropriately. ## `simple-one-port` This deployment exports the frontend statically and serves it via a single HTTP port using Caddy. This is useful for platforms that only support a single port or where running a node server in the container is undesirable. ## `production-compose` This deployment is intended for use with a standalone VPS that is only hosting a single Reflex app. It provides the entire stack in a single `compose.yaml` including a webserver, one or more backend instances, redis, and a postgres database. ## `production-app-platform` This example deployment is intended for use with App hosting platforms, like Azure, AWS, or Google Cloud Run. It is the backend of the deployment, which depends on a separately hosted redis instance and static frontend deployment. ================================================ FILE: docker-example/production-app-platform/.dockerignore ================================================ .web .git __pycache__/* Dockerfile uploaded_files ================================================ FILE: docker-example/production-app-platform/Dockerfile ================================================ # This docker file is intended to be used with container hosting services # # After deploying this image, get the URL pointing to the backend service # and run API_URL=https://path-to-my-container.example.com reflex export frontend # then copy the contents of `frontend.zip` to your static file server (github pages, s3, etc). # # Azure Static Web App example: # npx @azure/static-web-apps-cli deploy --env production --app-location .web/build/client # # For dynamic routes to function properly, ensure that 404s are redirected to /404 on the # static file host (for github pages, this works out of the box; remember to create .nojekyll). # # For azure static web apps, add `staticwebapp.config.json` to to `.web/build/client` with the following: # { # "responseOverrides": { # "404": { # "rewrite": "/404.html" # } # } # } # # Note: many container hosting platforms require amd64 images, so when building on an M1 Mac # for example, pass `docker build --platform=linux/amd64 ...` # Stage 1: init FROM python:3.13 as init ARG uv=/root/.local/bin/uv # Install `uv` for faster package bootstrapping ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh RUN /install.sh && rm /install.sh # Copy local context to `/app` inside container (see .dockerignore) WORKDIR /app COPY . . RUN mkdir -p /app/data /app/uploaded_files # Create virtualenv which will be copied into final container ENV VIRTUAL_ENV=/app/.venv ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN $uv venv # Install app requirements and reflex inside virtualenv RUN $uv pip install -r requirements.txt # Deploy templates and prepare app RUN reflex init # Stage 2: copy artifacts into slim image FROM python:3.13-slim WORKDIR /app RUN adduser --disabled-password --home /app reflex COPY --chown=reflex --from=init /app /app # Install libpq-dev for psycopg (skip if not using postgres). RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* USER reflex ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1 # Needed until Reflex properly passes SIGTERM on backend. STOPSIGNAL SIGKILL # Always apply migrations before starting the backend. CMD [ -d alembic ] && reflex db migrate; \ exec reflex run --env prod --backend-only --backend-port ${PORT:-8000} ================================================ FILE: docker-example/production-app-platform/README.md ================================================ # production-app-platform This example deployment is intended for use with App hosting platforms, like Azure, AWS, or Google Cloud Run. ## Architecture The production deployment consists of a few pieces: - Backend container - built by `Dockerfile` Runs the Reflex backend service on port 8000 and is scalable to multiple instances. - Redis container - A single instance the standard `redis` docker image should share private networking with the backend - Static frontend - HTML/CSS/JS files that are hosted via a CDN or static file server. This is not included in the docker image. ## Deployment These general steps do not cover the specifics of each platform, but all platforms should support the concepts described here. ### Vnet All containers in the deployment should be hooked up to the same virtual private network so they can access the redis service and optionally the database server. The vnet should not be exposed to the internet, use an ingress rule to terminate TLS at the load balancer and forward the traffic to a backend service replica. ### Redis Deploy a `redis` instance on the vnet. ### Backend The backend is built by the `Dockerfile` in this directory. When deploying the backend, be sure to set REFLEX_REDIS_URL=redis://internal-redis-hostname to connect to the redis service. ### Ingress Configure the load balancer for the app to forward traffic to port 8000 on the backend service replicas. Most platforms will generate an ingress hostname automatically. Make sure when you access the ingress endpoint on `/ping` that it returns "pong", indicating that the backend is up an available. ### Frontend The frontend should be hosted on a static file server or CDN. **Important**: when exporting the frontend, set the API_URL environment variable to the ingress hostname of the backend service. If you will host the frontend from a path other than the root, set the `REFLEX_FRONTEND_PATH` environment variable appropriately when exporting the frontend. Most static hosts will automatically use the `/404.html` file to handle 404 errors. _This is essential for dynamic routes to work correctly._ Ensure that missing routes return the `/404.html` content to the user if this is not the default behavior. _For Github Pages_: ensure the file `.nojekyll` is present in the root of the repo to avoid special processing of underscore-prefix directories, like `_next`. ## Platform Notes The following sections are currently a work in progress and may be incomplete. ### Azure In the Azure load balancer, per-message deflate is not supported. Add the following to your `rxconfig.py` to workaround this issue. ```python import uvicorn.workers import reflex as rx class NoWSPerMessageDeflate(uvicorn.workers.UvicornH11Worker): CONFIG_KWARGS = { **uvicorn.workers.UvicornH11Worker.CONFIG_KWARGS, "ws_per_message_deflate": False, } config = rx.Config( app_name="my_app", gunicorn_worker_class="rxconfig.NoWSPerMessageDeflate", ) ``` #### Persistent Storage If you need to use a database or upload files, you cannot save them to the container volume. Use Azure Files and mount it into the container at /app/uploaded_files. #### Resource Types - Create a new vnet with 10.0.0.0/16 - Create a new subnet for redis, database, and containers - Deploy redis as a Container Instances - Deploy database server as "Azure Database for PostgreSQL" - Create a new database for the app - Set db-url as a secret containing the db user/password connection string - Deploy Storage account for uploaded files - Enable access from the vnet and container subnet - Create a new file share - In the environment, create a new files share (get the storage key) - Deploy the backend as a Container App - Create a custom Container App Environment linked up to the same vnet as the redis container. - Set REFLEX_REDIS_URL and REFLEX_DB_URL environment variables - Add the volume from the environment - Add the volume mount to the container - Deploy the frontend as a Static Web App ================================================ FILE: docker-example/production-compose/.dockerignore ================================================ .web .git __pycache__/* Dockerfile Caddy.Dockerfile compose.yaml compose.*.yaml uploaded_files ================================================ FILE: docker-example/production-compose/Caddy.Dockerfile ================================================ FROM library/caddy COPY --from=local/reflex-app /app/.web/build/client /srv ADD Caddyfile /etc/caddy/Caddyfile ================================================ FILE: docker-example/production-compose/Caddyfile ================================================ {$DOMAIN} encode gzip @backend_routes path /_event/* /ping /_upload /_upload/* handle @backend_routes { reverse_proxy app:8000 } root * /srv route { try_files {path} {path}/ /404.html file_server } ================================================ FILE: docker-example/production-compose/Dockerfile ================================================ # This docker file is intended to be used with docker compose to deploy a production # instance of a Reflex app. # Stage 1: init FROM python:3.13 as init ARG uv=/root/.local/bin/uv # Install `uv` for faster package bootstrapping ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh RUN /install.sh && rm /install.sh # Copy local context to `/app` inside container (see .dockerignore) WORKDIR /app COPY . . RUN mkdir -p /app/data /app/uploaded_files # Create virtualenv which will be copied into final container ENV VIRTUAL_ENV=/app/.venv ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN $uv venv # Install app requirements and reflex inside virtualenv RUN $uv pip install -r requirements.txt # Deploy templates and prepare app RUN reflex init # Export static copy of frontend to /app/.web/build/client RUN reflex export --frontend-only --no-zip # Copy static files out of /app to save space in backend image RUN mv .web/build/client /tmp/client RUN rm -rf .web && mkdir -p .web/build RUN mv /tmp/client .web/build/client # Stage 2: copy artifacts into slim image FROM python:3.13-slim WORKDIR /app RUN adduser --disabled-password --home /app reflex COPY --chown=reflex --from=init /app /app # Install libpq-dev for psycopg (skip if not using postgres). RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* USER reflex ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1 # Needed until Reflex properly passes SIGTERM on backend. STOPSIGNAL SIGKILL # Always apply migrations before starting the backend. CMD [ -d alembic ] && reflex db migrate; \ exec reflex run --env prod --backend-only ================================================ FILE: docker-example/production-compose/README.md ================================================ # production-compose This example production deployment uses automatic TLS with Caddy serving static files for the frontend and proxying requests to both the frontend and backend. It is intended for use with a standalone VPS that is only hosting a single Reflex app. The production app container (`Dockerfile`), builds and exports the frontend statically (to be served by Caddy). The resulting image only runs the backend service. The `webserver` service, based on `Caddy.Dockerfile`, copies the static frontend and `Caddyfile` into the container to configure the reverse proxy routes that will forward requests to the backend service. Caddy will automatically provision TLS for localhost or the domain specified in the environment variable `DOMAIN`. This type of deployment should use less memory and be more performant since nodejs is not required at runtime. ## Customize `Caddyfile` (optional) If the app uses additional backend API routes, those should be added to the `@backend_routes` path matcher to ensure they are forwarded to the backend. ## Build Reflex Production Service During build, set `DOMAIN` environment variable to the domain where the app will be hosted! (Do not include http or https, it will always use https). **If `DOMAIN` is not provided, the service will default to `localhost`.** ```bash DOMAIN=example.com docker compose build ``` This will build both the `app` service from the `prod.Dockerfile` and the `webserver` service via `Caddy.Dockerfile`. ## Run Reflex Production Service ```bash DOMAIN=example.com docker compose up ``` The app should be available at the specified domain via HTTPS. Certificate provisioning will occur automatically and may take a few minutes. ### Data Persistence Named docker volumes are used to persist the app database (`db-data`), uploaded_files (`upload-data`), and caddy TLS keys and certificates (`caddy-data`). ## More Robust Deployment For a more robust deployment, consider bringing the service up with `compose.prod.yaml` which includes postgres database and redis cache, allowing the backend to run with multiple workers and service more requests. ```bash DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml up -d ``` Postgres uses its own named docker volume for data persistence. ## Admin Tools When needed, the services in `compose.tools.yaml` can be brought up, providing graphical database administration (Adminer on http://localhost:8080) and a redis cache browser (redis-commander on http://localhost:8081). It is not recommended to deploy these services if they are not in active use. ```bash DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml -f compose.tools.yaml up -d ``` ================================================ FILE: docker-example/production-compose/compose.prod.yaml ================================================ # Use this override file to run the app in prod mode with postgres and redis # docker compose -f compose.yaml -f compose.prod.yaml up -d services: db: image: postgres restart: always environment: POSTGRES_PASSWORD: secret volumes: - postgres-data:/var/lib/postgresql/data redis: image: redis restart: always app: environment: REFLEX_DB_URL: postgresql+psycopg://postgres:secret@db/postgres REFLEX_REDIS_URL: redis://redis:6379 depends_on: - db - redis volumes: postgres-data: ================================================ FILE: docker-example/production-compose/compose.tools.yaml ================================================ # Use this override file with `compose.prod.yaml` to run admin tools # for production services. # docker compose -f compose.yaml -f compose.prod.yaml -f compose.tools.yaml up -d services: adminer: image: adminer ports: - 8080:8080 redis-commander: image: ghcr.io/joeferner/redis-commander:latest environment: - REDIS_HOSTS=local:redis:6379 ports: - "8081:8081" volumes: redis-ui-settings: ================================================ FILE: docker-example/production-compose/compose.yaml ================================================ # Base compose file production deployment of reflex app with Caddy webserver # providing TLS termination and reverse proxying. # # See `compose.prod.yaml` for more robust and performant deployment option. # # During build and run, set environment DOMAIN pointing # to publicly accessible domain where app will be hosted services: app: image: local/reflex-app environment: REFLEX_DB_URL: sqlite:///data/reflex.db build: context: . volumes: - db-data:/app/data - upload-data:/app/uploaded_files restart: always webserver: environment: DOMAIN: ${DOMAIN:-localhost} ports: - 443:443 - 80:80 # For acme-challenge via HTTP. build: context: . dockerfile: Caddy.Dockerfile volumes: - caddy-data:/root/.caddy restart: always depends_on: - app volumes: # SQLite data db-data: # Uploaded files upload-data: # TLS keys and certificates caddy-data: ================================================ FILE: docker-example/production-one-port/.dockerignore ================================================ .web !.web/bun.lockb !.web/package.json ================================================ FILE: docker-example/production-one-port/Caddyfile ================================================ :{$PORT} encode gzip @backend_routes path /_event/* /ping /_upload /_upload/* handle @backend_routes { reverse_proxy localhost:8000 } root * /srv route { try_files {path} {path}/ /404.html file_server } ================================================ FILE: docker-example/production-one-port/Dockerfile ================================================ # This Dockerfile is used to deploy a single-container Reflex app instance # to services like Render, Railway, Heroku, GCP, and others. # If the service expects a different port, provide it here (f.e Render expects port 10000) ARG PORT=8080 # Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend. ARG API_URL # It uses a reverse proxy to serve the frontend statically and proxy to backend # from a single exposed port, expecting TLS termination to be handled at the # edge by the given platform. FROM python:3.13 as builder RUN mkdir -p /app/.web RUN python -m venv /app/.venv ENV PATH="/app/.venv/bin:$PATH" WORKDIR /app # Install python app requirements and reflex in the container COPY requirements.txt . RUN pip install -r requirements.txt # Install reflex helper utilities like bun/node COPY rxconfig.py ./ RUN reflex init # Install pre-cached frontend dependencies (if exist) COPY *.web/bun.lockb *.web/package.json .web/ RUN if [ -f .web/bun.lockb ]; then cd .web && ~/.local/share/reflex/bun/bin/bun install --frozen-lockfile; fi # Copy local context to `/app` inside container (see .dockerignore) COPY . . ARG PORT API_URL # Download other npm dependencies and compile frontend RUN REFLEX_API_URL=${API_URL:-http://localhost:$PORT} reflex export --loglevel debug --frontend-only --no-zip && mv .web/build/client/* /srv/ && rm -rf .web # Final image with only necessary files FROM python:3.13-slim # Install Caddy and redis server inside image RUN apt-get update -y && apt-get install -y caddy redis-server && rm -rf /var/lib/apt/lists/* ARG PORT API_URL ENV PATH="/app/.venv/bin:$PATH" PORT=$PORT REFLEX_API_URL=${API_URL:-http://localhost:$PORT} REFLEX_REDIS_URL=redis://localhost PYTHONUNBUFFERED=1 WORKDIR /app COPY --from=builder /app /app COPY --from=builder /srv /srv # Needed until Reflex properly passes SIGTERM on backend. STOPSIGNAL SIGKILL EXPOSE $PORT # Apply migrations before starting the backend. CMD [ -d alembic ] && reflex db migrate; \ caddy start && \ redis-server --daemonize yes && \ exec reflex run --env prod --backend-only ================================================ FILE: docker-example/production-one-port/README.md ================================================ # production-one-port This docker deployment runs Reflex in prod mode, exposing a single HTTP port: - `8080` (`$PORT`) - Caddy server hosting the frontend statically and proxying requests to the backend. The deployment also runs a local Redis server to store state for each user. Conceptually it is similar to the `simple-one-port` example except it: - has layer caching for python, reflex, and node dependencies - uses multi-stage build to reduce the size of the final image Using this method may be preferable for deploying in memory constrained environments, because it serves a static frontend export, rather than running the Vite server via node. ## Build ```console docker build -t reflex-production-one-port . ``` ## Run ```console docker run -p 8080:8080 reflex-production-one-port ``` Note that this container has _no persistence_ and will lose all data when stopped. You can use bind mounts or named volumes to persist the database and uploaded_files directories as needed. ## Usage This container should be used with an existing load balancer or reverse proxy to terminate TLS. It is also useful for deploying to simple app platforms, such as Render or Heroku. ================================================ FILE: docker-example/simple-one-port/.dockerignore ================================================ .web .git __pycache__/* Dockerfile uploaded_files ================================================ FILE: docker-example/simple-one-port/Caddyfile ================================================ :{$PORT} encode gzip @backend_routes path /_event/* /ping /_upload /_upload/* handle @backend_routes { reverse_proxy localhost:8000 } root * /srv route { try_files {path} {path}/ /404.html file_server } ================================================ FILE: docker-example/simple-one-port/Dockerfile ================================================ # This Dockerfile is used to deploy a single-container Reflex app instance # to services like Render, Railway, Heroku, GCP, and others. # It uses a reverse proxy to serve the frontend statically and proxy to backend # from a single exposed port, expecting TLS termination to be handled at the # edge by the given platform. FROM python:3.13 # If the service expects a different port, provide it here (f.e Render expects port 10000) ARG PORT=8080 # Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend. ARG API_URL ENV PORT=$PORT REFLEX_API_URL=${API_URL:-http://localhost:$PORT} REFLEX_REDIS_URL=redis://localhost PYTHONUNBUFFERED=1 # Install Caddy and redis server inside image RUN apt-get update -y && apt-get install -y caddy redis-server && rm -rf /var/lib/apt/lists/* WORKDIR /app # Copy local context to `/app` inside container (see .dockerignore) COPY . . # Install app requirements and reflex in the container RUN pip install -r requirements.txt # Deploy templates and prepare app RUN reflex init # Download all npm dependencies and compile frontend RUN reflex export --frontend-only --no-zip && mv .web/build/client/* /srv/ && rm -rf .web # Needed until Reflex properly passes SIGTERM on backend. STOPSIGNAL SIGKILL EXPOSE $PORT # Apply migrations before starting the backend. CMD [ -d alembic ] && reflex db migrate; \ caddy start && \ redis-server --daemonize yes && \ exec reflex run --env prod --backend-only ================================================ FILE: docker-example/simple-one-port/README.md ================================================ # simple-one-port This docker deployment runs Reflex in prod mode, exposing a single HTTP port: - `8080` (`$PORT`) - Caddy server hosting the frontend statically and proxying requests to the backend. The deployment also runs a local Redis server to store state for each user. Using this method may be preferable for deploying in memory constrained environments, because it serves a static frontend export, rather than running the Vite server via node. For platforms which only terminate TLS to a single port, this container can be deployed instead of the `simple-two-port` example. ## Build ```console docker build -t reflex-simple-one-port . ``` ## Run ```console docker run -p 8080:8080 reflex-simple-one-port ``` Note that this container has _no persistence_ and will lose all data when stopped. You can use bind mounts or named volumes to persist the database and uploaded_files directories as needed. ## Usage This container should be used with an existing load balancer or reverse proxy to terminate TLS. It is also useful for deploying to simple app platforms, such as Render or Heroku. ================================================ FILE: docker-example/simple-two-port/.dockerignore ================================================ .web .git __pycache__/* Dockerfile uploaded_files ================================================ FILE: docker-example/simple-two-port/Dockerfile ================================================ # This Dockerfile is used to deploy a simple single-container Reflex app instance. FROM python:3.13 RUN apt-get update && apt-get install -y redis-server && rm -rf /var/lib/apt/lists/* ENV REFLEX_REDIS_URL=redis://localhost PYTHONUNBUFFERED=1 # Copy local context to `/app` inside container (see .dockerignore) WORKDIR /app COPY . . # Install app requirements and reflex in the container RUN pip install -r requirements.txt # Deploy templates and prepare app RUN reflex init # Download all npm dependencies and compile frontend RUN reflex export --frontend-only --no-zip # Needed until Reflex properly passes SIGTERM on backend. STOPSIGNAL SIGKILL # Always apply migrations before starting the backend. CMD [ -d alembic ] && reflex db migrate; \ redis-server --daemonize yes && \ exec reflex run --env prod ================================================ FILE: docker-example/simple-two-port/README.md ================================================ # simple-two-port This docker deployment runs Reflex in prod mode, exposing two HTTP ports: - `3000` - node server using optimized production build - `8000` - python gunicorn server hosting the Reflex backend The deployment also runs a local Redis server to store state for each user. ## Build ```console docker build -t reflex-simple-two-port . ``` ## Run ```console docker run -p 3000:3000 -p 8000:8000 reflex-simple-two-port ``` Note that this container has _no persistence_ and will lose all data when stopped. You can use bind mounts or named volumes to persist the database and uploaded_files directories as needed. ## Usage This container should be used with an existing load balancer or reverse proxy to route traffic to the appropriate port inside the container. For example, the following Caddyfile can be used to terminate TLS and forward traffic to the frontend and backend from outside the container. ``` my-domain.com encode gzip @backend_routes path /_event/* /ping /_upload /_upload/* handle @backend_routes { reverse_proxy localhost:8000 } reverse_proxy localhost:3000 ``` ================================================ FILE: docs/DEBUGGING.md ================================================ # Debugging It is possible to run Reflex apps in dev mode under a debugger. 1. Run Reflex as a module: `python -m reflex run --env dev` 2. Set current working directory to the dir containing `rxconfig.py` ## VSCode The following launch configuration can be used to interactively debug a Reflex app with breakpoints. ```json { "version": "0.2.0", "configurations": [ { "name": "Reflex App", "type": "python", "request": "launch", "module": "reflex", "args": "run --env dev", "justMyCode": true, "cwd": "${fileDirname}/.." } ] } ``` ================================================ FILE: docs/de/README.md ================================================
Reflex Logo
### **✨ Performante, anpassbare Web-Apps in purem Python. Bereitstellung in Sekunden. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex ist eine Bibliothek, mit der man Full-Stack-Web-Applikationen in purem Python erstellen kann. Wesentliche Merkmale: - **Pures Python** - Schreibe dein Front- und Backend in Python, es gibt also keinen Grund, JavaScript zu lernen. - **Volle Flexibilität** - Reflex ist einfach zu handhaben, kann aber auch für komplexe Anwendungen skaliert werden. - **Sofortige Bereitstellung** - Nach dem Erstellen kannst du deine App mit einem [einzigen Befehl](https://reflex.dev/docs/hosting/deploy-quick-start/) bereitstellen oder auf deinem eigenen Server hosten. Auf unserer [Architektur-Seite](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) erfahren Sie, wie Reflex unter der Haube funktioniert. ## ⚙️ Installation Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.10+): ```bash pip install reflex ``` ## 🥳 Erstelle deine erste App Die Installation von `reflex` installiert auch das `reflex`-Kommandozeilen-Tool. Teste, ob die Installation erfolgreich war, indem du ein neues Projekt erstellst. (Ersetze `my_app_name` durch deinen Projektnamen): ```bash mkdir my_app_name cd my_app_name reflex init ``` Dieser Befehl initialisiert eine Vorlage in deinem neuen Verzeichnis. Du kannst diese App im Entwicklungsmodus ausführen: ```bash reflex run ``` Du solltest deine App unter http://localhost:3000 laufen sehen. Nun kannst du den Quellcode in `my_app_name/my_app_name.py` ändern. Reflex hat schnelle Aktualisierungen, sodass du deine Änderungen sofort siehst, wenn du deinen Code speicherst. ## 🫧 Beispiel-App Lass uns ein Beispiel durchgehen: die Erstellung einer Benutzeroberfläche für die Bildgenerierung mit [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). Zur Vereinfachung rufen wir einfach die [OpenAI-API](https://platform.openai.com/docs/api-reference/authentication) auf, aber du könntest dies auch durch ein lokal ausgeführtes ML-Modell ersetzen.  
Eine Benutzeroberfläche für DALL·E, die im Prozess der Bildgenerierung gezeigt wird.
  Hier ist der komplette Code, um dies zu erstellen. Das alles wird in einer Python-Datei gemacht! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """Der Zustand der App.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Hole das Bild aus dem Prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Füge Zustand und Seite zur App hinzu. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Schauen wir uns das mal genauer an.
Erläuterung der Unterschiede zwischen Backend- und Frontend-Teilen der DALL-E-App.
### **Reflex-UI** Fangen wir mit der Benutzeroberfläche an. ```python def index(): return rx.center( ... ) ``` Diese `index`-Funktion definiert das Frontend der App. Wir verwenden verschiedene Komponenten wie `center`, `vstack`, `input` und `button`, um das Frontend zu erstellen. Komponenten können ineinander verschachtelt werden, um komplexe Layouts zu erstellen. Und du kannst Schlüsselwortargumente verwenden, um sie mit der vollen Kraft von CSS zu stylen. Reflex wird mit [über 60 eingebauten Komponenten](https://reflex.dev/docs/library) geliefert, die dir den Einstieg erleichtern. Wir fügen aktiv weitere Komponenten hinzu, und es ist einfach, [eigene Komponenten zu erstellen](https://reflex.dev/docs/wrapping-react/overview/). ### **State** Reflex stellt deine Benutzeroberfläche als Funktion deines Zustands dar. ```python class State(rx.State): """Der Zustand der App.""" prompt = "" image_url = "" processing = False complete = False ``` Der Zustand definiert alle Variablen (genannt Vars) in einer App, die sich ändern können, und die Funktionen, die sie ändern. Hier besteht der Zustand aus einem `prompt` und einer `image_url`. Es gibt auch die Booleans `processing` und `complete`, um anzuzeigen, wann der Button deaktiviert werden soll (während der Bildgenerierung) und wann das resultierende Bild angezeigt werden soll. ### **Event-Handler** ```python def get_image(self): """Hole das Bild aus dem Prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Innerhalb des Zustands definieren wir Funktionen, die als Event-Handler bezeichnet werden und die Zustand-Variablen ändern. Event-Handler sind die Art und Weise, wie wir den Zustand in Reflex ändern können. Sie können als Reaktion auf Benutzeraktionen aufgerufen werden, z.B. beim Klicken auf eine Schaltfläche oder bei der Eingabe in ein Textfeld. Diese Aktionen werden als Ereignisse bezeichnet. Unsere DALL-E.-App hat einen Event-Handler, `get_image`, der dieses Bild von der OpenAI-API abruft. Die Verwendung von `yield` in der Mitte eines Event-Handlers führt zu einer Aktualisierung der Benutzeroberfläche. Andernfalls wird die Benutzeroberfläche am Ende des Ereignishandlers aktualisiert. ### **Routing** Schließlich definieren wir unsere App. ```python app = rx.App() ``` Wir fügen der Indexkomponente eine Seite aus dem Stammverzeichnis der Anwendung hinzu. Wir fügen auch einen Titel hinzu, der in der Seitenvorschau/Browser-Registerkarte angezeigt wird. ```python app.add_page(index, title="DALL-E") ``` Du kannst eine mehrseitige App erstellen, indem du weitere Seiten hinzufügst. ## 📑 Ressourcen
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Komponentenbibliothek](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Bereitstellung](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Status Reflex wurde im Dezember 2022 unter dem Namen Pynecone gestartet. Ab 2025 wurde [Reflex Cloud](https://cloud.reflex.dev) gestartet, um die beste Hosting-Erfahrung für Reflex-Apps zu bieten. Wir werden es weiterhin entwickeln und mehr Funktionen implementieren. Reflex hat wöchentliche Veröffentlichungen und neue Features! Stelle sicher, dass du dieses Repository mit einem :star: Stern markierst und :eyes: beobachtest, um auf dem Laufenden zu bleiben. ## Beitragende Wir begrüßen Beiträge jeder Größe! Hier sind einige gute Möglichkeiten, um in der Reflex-Community zu starten. - **Tritt unserem Discord bei**: Unser [Discord](https://discord.gg/T5WSbC2YtQ) ist der beste Ort, um Hilfe für dein Reflex-Projekt zu bekommen und zu besprechen, wie du beitragen kannst. - **GitHub-Diskussionen**: Eine großartige Möglichkeit, über Funktionen zu sprechen, die du hinzugefügt haben möchtest oder Dinge, die verwirrend sind/geklärt werden müssen. - **GitHub-Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) sind eine ausgezeichnete Möglichkeit, Bugs zu melden. Außerdem kannst du versuchen, ein bestehendes Problem zu lösen und eine PR einzureichen. Wir suchen aktiv nach Mitwirkenden, unabhängig von deinem Erfahrungslevel oder deiner Erfahrung. Um beizutragen, sieh dir [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) an. ## Vielen Dank an unsere Mitwirkenden: ## Lizenz Reflex ist Open-Source und lizenziert unter der [Apache License 2.0](/LICENSE). ================================================ FILE: docs/es/README.md ================================================
Reflex Logo
### **✨ Aplicaciones web personalizables y eficaces en Python puro. Despliega tu aplicación en segundos. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![Versiones](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentación](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex es una biblioteca para construir aplicaciones web full-stack en Python puro. Características clave: - **Python puro** - Escribe el frontend y backend de tu aplicación en Python, sin necesidad de aprender JavaScript. - **Flexibilidad total** - Reflex es fácil para empezar, pero también puede escalar a aplicaciones complejas. - **Despliegue instantáneo** - Después de construir, despliega tu aplicación con un [solo comando](https://reflex.dev/docs/hosting/deploy-quick-start/) u hospédala en tu propio servidor. Consulta nuestra [página de arquitectura](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) para aprender cómo funciona Reflex en detalle. ## ⚙️ Instalación Abra un terminal y ejecute (Requiere Python 3.10+): ```bash pip install reflex ``` ## 🥳 Crea tu primera aplicación Al instalar `reflex` también se instala la herramienta de línea de comandos `reflex`. Compruebe que la instalación se ha realizado correctamente creando un nuevo proyecto. (Sustituye `my_app_name` por el nombre de tu proyecto): ```bash mkdir my_app_name cd my_app_name reflex init ``` Este comando inicializa una plantilla en tu nuevo directorio. Puedes iniciar esta aplicación en modo de desarrollo: ```bash reflex run ``` Debería ver su aplicación ejecutándose en http://localhost:3000. Ahora puede modificar el código fuente en `my_app_name/my_app_name.py`. Reflex se actualiza rápidamente para que pueda ver los cambios al instante cuando guarde el código. ## 🫧 Ejemplo de una Aplicación Veamos un ejemplo: crearemos una UI de generación de imágenes en torno a [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). Para simplificar, solo llamamos a la [API de OpenAI](https://platform.openai.com/docs/api-reference/authentication), pero podrías reemplazar esto con un modelo ML ejecutado localmente.  
Un envoltorio frontend para DALL·E, mostrado en el proceso de generar una imagen.
  Aquí está el código completo para crear esto. ¡Todo esto se hace en un archivo de Python! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """El estado de la aplicación""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Obtiene la imagen desde la consulta.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Agrega el estado y la pagina a la aplicación app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Vamos a analizarlo.
Explicando las diferencias entre las partes del backend y frontend de la aplicación DALL-E.
### **Reflex UI** Empezemos por la interfaz de usuario (UI). ```python def index(): return rx.center( ... ) ``` Esta función `index` define el frontend de la aplicación. Utilizamos diferentes componentes como `center`, `vstack`, `input`, y `button` para construir el frontend. Los componentes pueden anidarse unos dentro de otros para crear diseños complejos. Además, puedes usar argumentos de tipo keyword para darles estilo con toda la potencia de CSS. Reflex viene con [mas de 60 componentes incorporados](https://reflex.dev/docs/library) para ayudarle a empezar. Estamos añadiendo activamente más componentes y es fácil [crear sus propios componentes](https://reflex.dev/docs/wrapping-react/overview/). ### **Estado** Reflex representa su UI como una función de su estado (State). ```python class State(rx.State): """El estado de la aplicación""" prompt = "" image_url = "" processing = False complete = False ``` El estado (State) define todas las variables (llamadas vars) de una aplicación que pueden cambiar y las funciones que las modifican. Aquí el estado se compone de `prompt` e `image_url`. También están los booleanos `processing` y `complete` para indicar cuando se deshabilite el botón (durante la generación de la imagen) y cuando se muestre la imagen resultante. ### **Manejadores de Evento** ```python def get_image(self): """Obtiene la imagen desde la consulta.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Dentro del estado, definimos funciones llamadas manejadores de eventos que cambian las variables de estado. Los Manejadores de Evento son la manera que podemos modificar el estado en Reflex. Pueden ser activados en respuesta a las acciones del usuario, como hacer clic en un botón o escribir en un cuadro de texto. Estas acciones se llaman eventos. Nuestra aplicación DALL·E tiene un manipulador de eventos, `get_image` que recibe esta imagen del OpenAI API. El uso de `yield` en medio de un manipulador de eventos hará que la UI se actualice. De lo contrario, la interfaz se actualizará al final del manejador de eventos. ### **Enrutamiento** Por último, definimos nuestra app. ```python app = rx.App() ``` Añadimos una página desde la raíz (root) de la aplicación al componente de índice (index). También agregamos un título que se mostrará en la vista previa de la página/pestaña del navegador. ```python app.add_page(index, title="DALL-E") ``` Puedes crear una aplicación multipágina añadiendo más páginas. ## 📑 Recursos
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Librería de componentes](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Despliegue](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Estado Reflex se lanzó en diciembre de 2022 con el nombre de Pynecone. A partir de 2025, [Reflex Cloud](https://cloud.reflex.dev) se ha lanzado para proporcionar la mejor experiencia de alojamiento para aplicaciones Reflex. Continuaremos desarrollándolo e implementando más características. ¡Reflex tiene nuevas versiones y características cada semana! Asegúrate de :star: marcar como favorito y :eyes: seguir este repositorio para mantenerte actualizado. ## Contribuciones ¡Aceptamos contribuciones de cualquier tamaño! A continuación encontrará algunas buenas formas de iniciarse en la comunidad Reflex. - **Únete a nuestro Discord**: Nuestro [Discord](https://discord.gg/T5WSbC2YtQ) es el mejor lugar para obtener ayuda en su proyecto Reflex y discutir cómo puedes contribuir. - **Discusiones de GitHub**: Una excelente manera de hablar sobre las características que deseas agregar o las cosas que te resultan confusas o necesitan aclaración. - **GitHub Issues**: Las incidencias son una forma excelente de informar de errores. Además, puedes intentar resolver un problema existente y enviar un PR. Buscamos colaboradores, sin importar su nivel o experiencia. Para contribuir consulta [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## Todo Gracias A Nuestros Contribuidores: ## Licencia Reflex es de código abierto y está licenciado bajo la [Apache License 2.0](/LICENSE). ================================================ FILE: docs/in/README.md ================================================
Reflex लोगो Reflex लोगो
### **✨ प्रदर्शनकारी, अनुकूलित वेब ऐप्स, शुद्ध Python में। सेकंडों में तैनात करें। ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) # Reflex Reflex शुद्ध पायथन में पूर्ण-स्टैक वेब ऐप्स बनाने के लिए एक लाइब्रेरी है। मुख्य विशेषताएँ: - **शुद्ध पायथन** - अपने ऐप के फ्रंटएंड और बैकएंड को पायथन में लिखें, जावास्क्रिप्ट सीखने की जरूरत नहीं है। - **पूर्ण लचीलापन** - Reflex के साथ शुरुआत करना आसान है, लेकिन यह जटिल ऐप्स के लिए भी स्केल कर सकता है। - **तुरंत तैनाती** - बिल्डिंग के बाद, अपने ऐप को [एकल कमांड](https://reflex.dev/docs/hosting/deploy-quick-start/) के साथ तैनात करें या इसे अपने सर्वर पर होस्ट करें। Reflex के अंदर के कामकाज को जानने के लिए हमारे [आर्किटेक्चर पेज](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) को देखें। ## ⚙️ इंस्टॉलेशन (Installation) एक टर्मिनल खोलें और चलाएं (Python 3.10+ की आवश्यकता है): ```bash pip install reflex ``` ## 🥳 अपना पहला ऐप बनाएं (Create your first App) reflex को इंस्टॉल करने से ही reflex कमांड लाइन टूल भी इंस्टॉल हो जाता है। सुनिश्चित करें कि इंस्टॉलेशन सफल थी, एक नया प्रोजेक्ट बनाकर इसे टेस्ट करें। ('my_app_name' की जगह अपने प्रोजेक्ट का नाम रखें): ```bash mkdir my_app_name cd my_app_name reflex init ``` यह कमांड आपकी नयी डायरेक्टरी में एक टेम्पलेट ऐप को प्रारंभ करता है। आप इस ऐप को development मोड में चला सकते हैं: ```bash reflex run ``` आपको http://localhost:3000 पर अपने ऐप को चलते हुए देखना चाहिए। अब आप my_app_name/my_app_name.py में source कोड को संशोधित कर सकते हैं। Reflex में तेज रिफ्रेश की सुविधा है, इसलिए जब आप अपनी कोड को सहेजते हैं, तो आप अपने बदलावों को तुरंत देख सकते हैं। ## 🫧 उदाहरण ऐप (Example App) एक उदाहरण पर चलते हैं: [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node) से एक इमेज उत्पन्न करने के लिए UI। सरलता के लिए, हम सिर्फ [OpenAI API](https://platform.openai.com/docs/api-reference/authentication) को बुलाते हैं, लेकिन आप इसे ML मॉडल से बदल सकते हैं locally।  
DALL·E के लिए एक फ्रंटएंड रैपर, छवि उत्पन्न करने की प्रक्रिया में दिखाया गया।
  यहाँ पर इसका पूरा कोड है जिससे यह बनाया जा सकता है। यह सब एक ही Python फ़ाइल में किया गया है! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## इसे समझते हैं।
DALL-E ऐप के बैकएंड और फ्रंटएंड भागों के बीच के अंतर की व्याख्या करता है।
### **Reflex UI** हम UI के साथ शुरू करेंगे। ```python def index(): return rx.center( ... ) ``` यह `index` फ़ंक्शन एप्लिकेशन की फ़्रंटएंड को परिभाषित करता है। हम फ़्रंटएंड बनाने के लिए `center`, `vstack`, `input`, और `button` जैसे विभिन्न components का उपयोग करते हैं। Components को एक-दूसरे के भीतर डाल सकते हैं विस्तारित लेआउट बनाने के लिए। और आप CSS की पूरी ताक़त के साथ इन्हें स्टाइल करने के लिए कीवर्ड आर्ग्यूमेंट (keyword args) का उपयोग कर सकते हैं। रिफ़्लेक्स के पास [60+ built-in components](https://reflex.dev/docs/library) हैं जो आपको शुरुआती मदद के लिए हैं। हम बहुत से components जोड़ रहे हैं, और अपने खुद के components बनाना भी आसान है। [create your own components](https://reflex.dev/docs/wrapping-react/overview/) ### **स्टेट (State)** Reflex आपके UI को आपकी स्टेट (state) के एक फ़ंक्शन के रूप में प्रस्तुत करता है। ```python class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False ``` स्टेट (state) ऐप में उन सभी वेरिएबल्स (vars) को परिभाषित करती है जो बदल सकती हैं और उन फ़ंक्शनों को जो उन्हें बदलते हैं। यहां स्टेट (state) में `prompt` और `image_url` शामिल हैं। प्रगति और छवि दिखाने के लिए `processing` और `complete` बूलियन भी हैं। ### **इवेंट हैंडलर (Event Handlers)** ```python def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` स्टेट (state) के अंदर, हम इवेंट हैंडलर्स (event handlers) को परिभाषित करते हैं जो स्टेट वेरिएबल्स को बदलते हैं। इवेंट हैंडलर्स (event handlers) से reflex में स्टेट (state) को मॉडिफ़ाय किया जा सकता हैं। इन्हें उपयोगकर्ता क्रियाओं (user actions) के प्रति प्रतिक्रिया (response) के रूप में बुलाया जा सकता है, जैसे कि बटन को क्लिक करना या टेक्स्ट बॉक्स में टाइप करना। इन क्रियाओं को इवेंट्स (events) कहा जाता है। हमारे DALL·E. ऐप में एक इवेंट हैंडलर `get_image` है जिससे यह OpenAI API से इमेज प्राप्त करता है। इवेंट हैंडलर में `yield` का उपयोग करने कि वजह से UI अपडेट हो जाएगा। अन्यथा UI इवेंट हैंडलर के अंत में अपडेट होगा। ### **रूटिंग (Routing)** आखिरकार, हम अपने एप्लिकेशन को परिभाषित करते हैं। ```python app = rx.App() ``` हम अपने एप्लिकेशन के रूट से इंडेक्स कॉम्पोनेंट तक एक पेज को जोड़ते हैं। हम एक शीर्षक भी जोड़ते हैं जो पेज प्रीव्यू/ब्राउज़र टैब में दिखाई देगा। ```python app.add_page(index, title="DALL-E") ``` आप और पेज जोड़कर एक मल्टी-पेज एप्लिकेशन बना सकते हैं। ## 📑 संसाधन (Resources)
📑 [दस्तावेज़](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [ब्लॉग](https://reflex.dev/blog)   |   📱 [कॉम्पोनेंट लाइब्रेरी](https://reflex.dev/docs/library)   |   🖼️ [टेम्पलेट्स](https://reflex.dev/templates/)   |   🛸 [तैनाती](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ स्टेटस (Status) Reflex दिसंबर 2022 में Pynecone नाम से शुरू हुआ। 2025 की शुरुआत से, [Reflex Cloud](https://cloud.reflex.dev) लॉन्च किया गया है जो Reflex ऐप्स के लिए सर्वोत्तम होस्टिंग अनुभव प्रदान करता है। हम इसे विकसित करना और अधिक सुविधाएँ लागू करना जारी रखेंगे। Reflex में हर सप्ताह नए रिलीज़ और फीचर्स आ रहे हैं! सुनिश्चित करें कि :star: स्टार और :eyes: वॉच इस रेपोजिटरी को अपडेट रहने के लिए। ## (योगदान) Contributing हम हर तरह के योगदान का स्वागत करते हैं! रिफ्लेक्स कम्यूनिटी में शुरुआत करने के कुछ अच्छे तरीके नीचे दिए गए हैं। - **Join Our Discord** (डिस्कॉर्ड सर्वर से जुड़ें): हमारा [Discord](https://discord.gg/T5WSbC2YtQ) रिफ्लेक्स प्रोजेक्ट पर सहायता प्राप्त करने और आप कैसे योगदान दे सकते हैं, इस पर चर्चा करने के लिए सबसे अच्छी जगह है। - **GitHub Discussions** (गिटहब चर्चाएँ): उन सुविधाओं के बारे में बात करने का एक शानदार तरीका जिन्हें आप जोड़ना चाहते हैं या ऐसी चीज़ें जो भ्रमित करने वाली हैं/स्पष्टीकरण की आवश्यकता है। - **GitHub Issues** (गिटहब समस्याएं): [Issues](https://github.com/reflex-dev/reflex/issues) बग की रिपोर्ट करने का एक शानदार तरीका है। इसके अतिरिक्त, आप किसी मौजूदा समस्या को हल करने का प्रयास कर सकते हैं और एक पीआर सबमिट कर सकते हैं। हम सक्रिय रूप से योगदानकर्ताओं की तलाश कर रहे हैं, चाहे आपका कौशल स्तर या अनुभव कुछ भी हो। योगदान करने के लिए [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) देखें। ## हमारे सभी योगदानकर्ताओं का धन्यवाद: ## लाइसेंस (License) रिफ्लेक्स ओपन-सोर्स है और [अपाचे लाइसेंस 2.0](/LICENSE) के तहत लाइसेंस प्राप्त है। ================================================ FILE: docs/it/README.md ================================================
Reflex Logo
### **✨ App web performanti e personalizzabili in puro Python. Distribuisci in pochi secondi. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex è una libreria per sviluppare applicazioni web full-stack in Python puro. Caratteristiche principali: - **Python Puro** - Scrivi il frontend e il backend della tua app interamente in Python, senza bisogno di imparare Javascript. - **Flessibilità Totale** - Reflex è facile da iniziare, ma può anche gestire app complesse. - **Distribuzione Istantanea** - Dopo lo sviluppo, distribuisci la tua app con un [singolo comando](https://reflex.dev/docs/hosting/deploy-quick-start/) o ospitala sul tuo server. Consulta la nostra [pagina di architettura](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) per scoprire come funziona Reflex sotto il cofano. ## ⚙️ Installazione Apri un terminale ed esegui (Richiede Python 3.10+): ```bash pip install reflex ``` ## 🥳 Crea la tua prima app Installando `reflex` si installa anche lo strumento da riga di comando `reflex`. Verifica che l'installazione sia stata eseguita correttamente creando un nuovo progetto. (Sostituisci `nome_app` con il nome del tuo progetto): ```bash mkdir nome_app cd nome_app reflex init ``` Questo comando inizializza un'app template nella tua nuova directory. Puoi eseguire questa app in modalità sviluppo con: ```bash reflex run ``` Dovresti vedere la tua app in esecuzione su http://localhost:3000. Ora puoi modificare il codice sorgente in `nome_app/nome_app.py`. Reflex offre aggiornamenti rapidi, così puoi vedere le tue modifiche istantaneamente quando salvi il tuo codice. ## 🫧 Esempio App Esaminiamo un esempio: creare un'interfaccia utente per la generazione di immagini attorno a [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). Per semplicità, chiamiamo semplicemente l'[API OpenAI](https://platform.openai.com/docs/api-reference/authentication), ma potresti sostituirla con un modello ML eseguito localmente.  
Un wrapper frontend per DALL·E, mostrato nel processo di generazione di un'immagine.
  Ecco il codice completo per crearlo. Tutto fatto in un unico file Python! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """Lo stato dell'app.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Ottieni l'immagine dal prompt.""" if self.prompt == "": return rx.window_alert("Prompt Vuoto") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Inserisci un prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Genera Immagine", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Aggiungi stato e pagina all'app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Analizziamolo.
Spiegazione delle differenze tra le parti backend e frontend dell'app DALL-E.
### **Reflex UI** Cominciamo con l'UI. ```python def index(): return rx.center( ... ) ``` Questa funzione `index` definisce il frontend dell'app. Utilizziamo diversi componenti come `center`, `vstack`, `input`, e `button` per costruire il frontend. I componenti possono essere annidati gli uni negli altri per creare layout complessi. E puoi utilizzare argomenti chiave per stilizzarli con tutta la potenza di CSS. Reflex offre [più di 60 componenti integrati](https://reflex.dev/docs/library) per aiutarti a iniziare. Stiamo attivamente aggiungendo più componenti ed è facile [creare i tuoi componenti](https://reflex.dev/docs/wrapping-react/overview/). ### **Stato (State)** Reflex rappresenta la tua UI come una funzione del tuo stato. ```python class State(rx.State): """Lo stato dell'app.""" prompt = "" image_url = "" processing = False complete = False ``` Lo stato definisce tutte le variabili (chiamate vars) in un'app che possono cambiare e le funzioni che le cambiano. Qui lo stato è composto da un `prompt` e `image_url`. Ci sono anche i booleani `processing` e `complete` per indicare quando disabilitare il pulsante (durante la generazione dell'immagine) e quando mostrare l'immagine risultante. ### **Gestori di Eventi (Event Handlers)** ```python def get_image(self): """Ottieni l'immagine dal prompt.""" if self.prompt == "": return rx.window_alert("Prompt Vuoto") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Dentro lo stato, definiamo funzioni chiamate gestori di eventi che cambiano le vars dello stato. I gestori di eventi sono il modo in cui possiamo modificare lo stato in Reflex. Possono essere chiamati in risposta alle azioni dell'utente, come fare clic su un pulsante o digitare in una casella di testo. Queste azioni vengono chiamate eventi. La nostra app DALL·E ha un gestore di eventi, `get_image` con cui ottiene questa immagine dall'API OpenAI. Utilizzando `yield` nel mezzo di un gestore di eventi farà sì che l'UI venga aggiornata. Altrimenti, l'UI verrà aggiornata alla fine del gestore di eventi. ### **Instradamento (Routing)** Infine, definiamo la nostra app. ```python app = rx.App() ``` Aggiungiamo una pagina dalla radice dell'app al componente dell'indice. Aggiungiamo anche un titolo che apparirà nell'anteprima della pagina/scheda del browser. ```python app.add_page(index, title="DALL-E") ``` Puoi creare un'app multi-pagina aggiungendo altre pagine. ## 📑 Risorse
📑 [Documentazione](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Libreria Componenti](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Distribuzione](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Stato Reflex è stato lanciato nel dicembre 2022 con il nome Pynecone. A partire dal 2025, [Reflex Cloud](https://cloud.reflex.dev) è stato lanciato per fornire la migliore esperienza di hosting per le app Reflex. Continueremo a svilupparlo e implementare più funzionalità. Reflex ha nuove versioni e funzionalità in arrivo ogni settimana! Assicurati di :star: mettere una stella e :eyes: osservare questa repository per rimanere aggiornato. ## Contribuire Diamo il benvenuto a contributi di qualsiasi dimensione! Di seguito sono alcuni modi per iniziare nella comunità Reflex. - **Unisciti al nostro Discord**: Il nostro [Discord](https://discord.gg/T5WSbC2YtQ) è posto migliore per ottenere aiuto sul tuo progetto Reflex e per discutere come puoi contribuire. - **Discussioni su GitHub**: Un ottimo modo per parlare delle funzionalità che desideri aggiungere o di cose che creano confusione o necessitano chiarimenti. - **GitHub Issues**: Le [Issues](https://github.com/reflex-dev/reflex/issues) sono un ottimo modo per segnalare bug. Inoltre, puoi provare a risolvere un problema esistente e inviare un PR. Stiamo attivamente cercando collaboratori, indipendentemente dal tuo livello di abilità o esperienza. Per contribuire, consulta [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## Un Grazie a Tutti i Nostri Contributori: ## Licenza Reflex è open-source e rilasciato sotto la [Licenza Apache 2.0](/LICENSE). ================================================ FILE: docs/ja/README.md ================================================
Reflex Logo
### **✨ 即時デプロイが可能な、Pure Python で作ったパフォーマンスと汎用性が高い Web アプリケーション ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex は Python のみでフルスタック Web アプリケーションを作成できるライブラリです。 主な特徴: - **Pure Python** - Web アプリケーションのフロントエンドとバックエンドを Python のみで実装できるため、Javascript を学ぶ必要がありません。 - **高い柔軟性** - Reflex は簡単に始められて、複雑なアプリケーションまで作成できます。 - **即時デプロイ** - ビルド後、すぐにデプロイが可能です。[単純な CLI コマンド](https://reflex.dev/docs/hosting/deploy-quick-start/)を使ったアプリケーションのデプロイや、自身のサーバーへのホストができます。 Reflex がどのように動作しているかを知るには、[アーキテクチャページ](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture)をご覧ください。 ## ⚙️ インストール ターミナルを開いて以下のコマンドを実行してください。(Python 3.10 以上が必要です。): ```bash pip install reflex ``` ## 🥳 最初のアプリケーションを作ろう `reflex`をインストールすると、`reflex`の CLI ツールが自動でインストールされます。 新しいプロジェクトを作成して、インストールが成功しているかを確認しましょう。(`my_app_name`を自身のプロジェクト名に書き換えて実行ください。): ```bash mkdir my_app_name cd my_app_name reflex init ``` 上記のコマンドを実行すると、新しいフォルダにテンプレートアプリを作成します。 下記のコマンドを実行すると、開発モードでアプリを開始します。 ```bash reflex run ``` http://localhost:3000 にアクセスしてアプリの動作を見ることができます。 `my_app_name/my_app_name.py`のソースコードを編集してみましょう!Reflex は fast refresh なので、ソースを保存した直後に変更が Web ページに反映されます。 ## 🫧 実装例 実装例を見てみましょう: [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node)を中心とした画像生成 UI を作成しました。説明を簡単にするためにここでは[OpenAI API](https://platform.openai.com/docs/api-reference/authentication)を呼んでいますが、ローカルで動作している機械学習モデルに置き換えることも可能です。  
DALL·Eのフロントエンドラッパーです。画像を生成している過程を表示しています。
  画像生成 UI のソースコードの全貌を見てみましょう。下記のように、単一の Python ファイルで作れます! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """アプリのステート""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """プロンプトからイメージを取得する""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # ステートとページをアプリに追加 app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## それぞれの実装を見てみましょう
DALL-E appのフロントエンドとバックエンドのパーツの違いを説明しています。
### **Reflex UI** UI から見てみましょう。 ```python def index(): return rx.center( ... ) ``` `index`関数において、アプリのフロントエンドを定義しています。 フロントエンドを実装するにあたり、`center`、`vstack`、`input`、`button`など異なるコンポーネントを使用しています。コンポーネントはお互いにネストが可能であり、複雑なレイアウトを作成できます。また、keyword args を使うことで、CSS の機能をすべて使ったスタイルが可能です。 Reflex は[60 を超える内臓コンポーネント](https://reflex.dev/docs/library)があるため、すぐに始められます。私たちは、積極的にコンポーネントを追加していますが、簡単に[自身のコンポーネントを追加](https://reflex.dev/docs/wrapping-react/overview/)することも可能です。 ### **ステート** Reflex はステートの関数を用いて UI を表示します。 ```python class State(rx.State): """アプリのステート""" prompt = "" image_url = "" processing = False complete = False ``` ステートでは、アプリで変更が可能な全ての変数(vars と呼びます)と、vars の変更が可能な関数を定義します。 この例では、ステートを`prompt`と`image_url`で構成しています。そして、ブール型の`processing`と`complete`を用いて、ボタンを無効にするタイミング(画像生成中)や生成された画像を表示するタイミングを示しています。 ### **イベントハンドラ** ```python def get_image(self): """プロンプトからイメージを取得する""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` ステートにおいて、ステートの vars を変更できるイベントハンドラ関数を定義しています。イベントハンドラは Reflex において、ステートの vars を変更する方法です。ボタンクリックやテキストボックスの入力など、ユーザのアクションに応じてイベントハンドラが呼ばれます。 DALL·E.アプリには、OpenAI API からイメージを取得する`get_image`関数があります。イベントハンドラの最後で UI の更新がかかるため、関数の途中に`yield`を入れることで先に UI を更新しています。 ### **ルーティング** 最後に、アプリを定義します。 ```python app = rx.App() ``` アプリにページを追加し、ドキュメントルートを index コンポーネントにルーティングしています。更に、ページのプレビューやブラウザタブに表示されるタイトルを記載しています。 ```python app.add_page(index, title="DALL-E") ``` ページを追加することで、マルチページアプリケーションを作成できます。 ## 📑 リソース
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Component Library](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ ステータス 2022 年 12 月に、Reflex は Pynecone という名前でローンチしました。 2025 年から [Reflex Cloud](https://cloud.reflex.dev) がローンチされ、Reflex アプリケーションの最高のホスティング体験を提供しています。私たちは引き続き開発を続け、より多くの機能を実装していきます。 Reflex は毎週、新しいリリースや機能追加を行っています!最新情報を逃さないために、 :star: Star や :eyes: Watch をお願いします。 ## コントリビュート 様々なサイズのコントリビュートを歓迎しています!Reflex コミュニティに入るための方法を、いくつかリストアップします。 - **Discord に参加**: [Discord](https://discord.gg/T5WSbC2YtQ)は、Reflex プロジェクトの相談や、コントリビュートについての話し合いをするための、最適な場所です。 - **GitHub Discussions**: GitHub Discussions では、追加したい機能や、複雑で解明が必要な事柄についての議論に適している場所です。 - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues)はバグの報告に適している場所です。また、課題を解決した PR のサブミットにチャレンジしていただくことも、可能です。 スキルや経験に関わらず、私たちはコントリビュータを積極的に探しています。コントリビュートするために、[CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md)をご覧ください。 ## 私たちのコントリビュータに感謝!: ## ライセンス Reflex はオープンソースであり、[Apache License 2.0](/LICENSE)に基づいてライセンス供与されます。 ================================================ FILE: docs/kr/README.md ================================================
Reflex Logo
### **✨ 순수 Python으로 고성능 사용자 정의 웹앱을 만들어 보세요. 몇 초만에 배포 가능합니다. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex는 순수 Python으로 풀스택 웹 앱을 구축하기 위한 라이브러리입니다. 주요 기능: - **순수 Python** - 앱의 프론트엔드와 백엔드를 모두 Python으로 작성하며, Javascript를 배울 필요가 없습니다. - **완전한 유연성** - Reflex는 시작하기 쉽지만, 복잡한 앱으로도 확장할 수 있습니다. - **즉시 배포** - 앱을 빌드한 후 [단일 명령어](https://reflex.dev/docs/hosting/deploy-quick-start/)로 배포하거나 자체 서버에서 호스팅할 수 있습니다. Reflex가 내부적으로 어떻게 작동하는지 알아보려면 [아키텍처 페이지](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture)를 참조하세요. ## ⚙️ 설치 터미널을 열고 실행하세요. (Python 3.10+ 필요): ```bash pip install reflex ``` ## 🥳 첫 앱 만들기 `reflex`를 설치하면, `reflex` 명령어 라인 도구도 설치됩니다. 새 프로젝트를 생성하여 설치가 성공적인지 확인합니다. (`my_app_name`을 프로젝트 이름으로 변경합니다.): ```bash mkdir my_app_name cd my_app_name reflex init ``` 이 명령어는 새 디렉토리에 템플릿 앱을 초기화합니다. 개발 모드에서 이 앱을 실행할 수 있습니다: ```bash reflex run ``` http://localhost:3000 에서 앱이 실행 됩니다. 이제 `my_app_name/my_app_name.py`에서 소스코드를 수정할 수 있습니다. Reflex는 빠른 새로고침을 지원하므로 코드를 저장할 때마다 즉시 변경 사항을 볼 수 있습니다. ## 🫧 예시 앱 예시를 살펴보겠습니다: [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node)를 중심으로 이미지 생성 UI를 만들어 보겠습니다. 간단하게 하기 위해 [OpenAI API](https://platform.openai.com/docs/api-reference/authentication)를 호출했지만, 이를 로컬에서 실행되는 ML 모델로 대체할 수 있습니다.  
A frontend wrapper for DALL·E, shown in the process of generating an image.
  이것이 완성된 코드입니다. 이 모든 것은 하나의 Python 파일에서 이루어집니다! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## 하나씩 살펴보겠습니다.
Explaining the differences between backend and frontend parts of the DALL-E app.
### **Reflex UI** UI부터 시작해봅시다. ```python def index(): return rx.center( ... ) ``` `index` 함수는 앱의 프론트엔드를 정의합니다. `center`, `vstack`, `input`, `button`과 같은 다양한 컴포넌트를 사용하여 프론트엔드를 구축합니다. 컴포넌트들은 복잡한 레이아웃을 만들기 위해 서로 중첩될 수 있습니다. 그리고 키워드 인자를 사용하여 CSS의 모든 기능을 사용하여 스타일을 지정할 수 있습니다. Reflex는 시작하기 위한 [60개 이상의 기본 컴포넌트](https://reflex.dev/docs/library)를 제공하고 있습니다. 더 많은 컴포넌트를 추가하고 있으며, [자신만의 컴포넌트를 생성하는 것](https://reflex.dev/docs/wrapping-react/overview/)도 쉽습니다. ### **State** Reflex는 UI를 State 함수로 표현합니다. ```python class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False ``` state는 앱에서 변경될 수 있는 모든 변수(vars로 불림)와 이러한 변수를 변경하는 함수를 정의합니다. 여기서 state는 `prompt`와 `image_url`로 구성됩니다. 또한 `processing`과 `complete`라는 불리언 값이 있습니다. 이 값들은 이미지 생성 중 버튼을 비활성화할 때와, 결과 이미지를 표시할 때를 나타냅니다. ### **Event Handlers** ```python def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` State 내에서, state vars를 변경하는 이벤트 핸들러라고 불리는 함수를 정의합니다. 이벤트 핸들러는 Reflex에서 state를 변경하는 방법입니다. 버튼을 클릭하거나 텍스트 상자에 입력하는 것과 같이 사용자 동작에 응답하여 호출될 수 있습니다. 이러한 동작을 이벤트라고 합니다. DALL·E. 앱에는 OpenAI API에서 이미지를 가져오는 `get_image` 이벤트 핸들러가 있습니다. 이벤트 핸들러의 중간에 `yield`를 사용하면 UI가 업데이트됩니다. 그렇지 않으면 UI는 이벤트 핸들러의 끝에서 업데이트됩니다. ### **Routing** 마지막으로, 앱을 정의합니다. ```python app = rx.App() ``` 앱의 루트에서 index 컴포넌트로 페이지를 추가합니다. 또한 페이지 미리보기/브라우저 탭에 표시될 제목도 추가합니다. ```python app.add_page(index, title="DALL-E") ``` 여러 페이지를 추가하여 멀티 페이지 앱을 만들 수 있습니다. ## 📑 자료
📑 [문서](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [블로그](https://reflex.dev/blog)   |   📱 [컴포넌트 라이브러리](https://reflex.dev/docs/library)   |   🖼️ [템플릿](https://reflex.dev/templates/)   |   🛸 [배포](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ 상태 Reflex는 2022년 12월 Pynecone이라는 이름으로 출시되었습니다. 2025년부터 [Reflex Cloud](https://cloud.reflex.dev)가 출시되어 Reflex 앱을 위한 최상의 호스팅 경험을 제공합니다. 우리는 계속해서 개발하고 더 많은 기능을 구현할 예정입니다. Reflex는 매주 새로운 릴리즈와 기능을 제공합니다! 최신 정보를 확인하려면 :star: Star와 :eyes: Watch를 눌러 이 저장소를 확인하세요. ## 기여 우리는 모든 기여를 환영합니다! 아래는 Reflex 커뮤니티에 참여하는 좋은 방법입니다. - **Discord 참여**: 우리의 [Discord](https://discord.gg/T5WSbC2YtQ)는 Reflex 프로젝트에 대한 도움을 받고 기여하는 방법을 논의하는 최고의 장소입니다. - **GitHub Discussions**: 추가하고 싶은 기능이나 혼란스럽거나 해결이 필요한 것들에 대해 이야기하는 좋은 방법입니다. - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues)는 버그를 보고하는 훌륭한 방법입니다. 또한, 기존의 이슈를 해결하고 PR을 제출할 수 있습니다. 우리는 능력이나 경험에 상관없이 적극적으로 기여자를 찾고 있습니다. 기여하려면 [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md)를 확인하세요. ## 모든 기여자들에게 감사드립니다: ## License Reflex는 오픈소스이며 [Apache License 2.0](/LICENSE)로 라이선스가 부여됩니다. ================================================ FILE: docs/pe/README.md ================================================
Reflex Logo
### **✨ برنامه های تحت وب قابل تنظیم، کارآمد تماما پایتونی که در چند ثانیه مستقر(دپلوی) می‎شود. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex - رفلکس رفلکس(Reflex) یک کتابخانه برای ساخت برنامه های وب فول استک تماما پایتونی است. ویژگی های کلیدی: - **تماما پایتونی** - فرانت اند و بک اند برنامه خود را همه در پایتون بنویسید، بدون نیاز به یادگیری جاوا اسکریپت. - **انعطاف پذیری کامل** - شروع به کار با Reflex آسان است، اما می تواند به برنامه های پیچیده نیز تبدیل شود. - **دپلوی فوری** - پس از ساخت، برنامه خود را با [یک دستور](https://reflex.dev/docs/hosting/deploy-quick-start/) دپلوی کنید یا آن را روی سرور خود میزبانی کنید. برای آشنایی با نحوه عملکرد Reflex [صفحه معماری](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) را ببینید. ## ⚙️ Installation - نصب و راه اندازی یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.10+): ```bash pip install reflex ``` ## 🥳 اولین برنامه خود را ایجاد کنید نصب `reflex` همچنین `reflex` در خط فرمان را نصب میکند. با ایجاد یک پروژه جدید موفقیت آمیز بودن نصب را تست کنید. (`my_app_name` را با اسم پروژه خودتان جایگزین کنید): ```bash mkdir my_app_name cd my_app_name reflex init ``` این دستور یک برنامه الگو(تمپلیت) را در فهرست(دایرکتوری) جدید شما مقداردهی اولیه می کند می توانید این برنامه را در حالت توسعه(development) اجرا کنید: ```bash reflex run ``` باید برنامه خود را در حال اجرا ببینید در http://localhost:3000. اکنون می‌توانید کد منبع را در «my_app_name/my_app_name.py» تغییر دهید. Reflex دارای تازه‌سازی‌های سریعی است، بنابراین می‌توانید تغییرات خود را بلافاصله پس از ذخیره کد خود مشاهده کنید. ## 🫧 Example App - برنامه نمونه بیایید یک مثال بزنیم: ایجاد یک رابط کاربری برای تولید تصویر [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). برای سادگی، ما فراخوانی میکنیم [OpenAI API](https://platform.openai.com/docs/api-reference/authentication), اما می توانید آن را با یک مدل ML که به صورت محلی اجرا می شود جایگزین کنید.  
A frontend wrapper for DALL·E, shown in the process of generating an image.
  در اینجا کد کامل برای ایجاد این پروژه آمده است. همه اینها در یک فایل پایتون انجام می شود! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## بیاید سادش کنیم
Explaining the differences between backend and frontend parts of the DALL-E app.
### **Reflex UI - رابط کاربری رفلکس** بیایید با رابط کاربری شروع کنیم. ```python def index(): return rx.center( ... ) ``` تابع `index` قسمت فرانت اند برنامه را تعریف می کند. ما از اجزای مختلفی مثل `center`, `vstack`, `input` و `button` استفاده میکنیم تا فرانت اند را بسازیم. اجزاء را می توان درون یکدیگر قرار داد برای ایجاد طرح بندی های پیچیده می توانید از args کلمات کلیدی برای استایل دادن به آنها از CSS استفاده کنید. رفلکس دارای [بیش از 60 جزء](https://reflex.dev/docs/library) برای کمک به شما برای شروع. ما به طور فعال اجزای بیشتری را اضافه می کنیم, و این خیلی آسان است که [اجزا خود را بسازید](https://reflex.dev/docs/wrapping-react/overview/). ### **State - حالت** رفلکس رابط کاربری شما را به عنوان تابعی از وضعیت شما نشان می دهد. ```python class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False ``` حالت تمام متغیرها(variables) (به نام vars) را در یک برنامه که می توانند تغییر دهند و توابعی که آنها را تغییر می دهند تعریف می کند.. در اینجا حالت از یک `prompt` و `image_url` تشکیل شده است. همچنین دو بولی `processing` و `complete` برای نشان دادن زمان غیرفعال کردن دکمه (در طول تولید تصویر) و زمان نمایش تصویر نتیجه وجود دارد. ### **Event Handlers - کنترل کنندگان رویداد** ```python def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` در داخل حالت، توابعی به نام کنترل کننده رویداد تعریف می کنیم که متغیرهای حالت را تغییر می دهند. کنترل کننده های رویداد راهی هستند که می توانیم وضعیت را در Reflex تغییر دهیم. آنها را می توان در پاسخ به اقدامات کاربر، مانند کلیک کردن روی یک دکمه یا تایپ کردن در یک متن، فراخوانی کرد. به این اعمال وقایع می گویند. برنامه DALL·E ما دارای یک کنترل کننده رویداد، `get_image` است که این تصویر را از OpenAI API دریافت می کند. استفاده از `yield` در وسط کنترل‌کننده رویداد باعث به‌روزرسانی رابط کاربری می‌شود. در غیر این صورت رابط کاربری در پایان کنترل کننده رویداد به روز می شود. ### **Routing - مسیریابی** بالاخره اپلیکیشن خود را تعریف می کنیم. ```python app = rx.App() ``` ما یک صفحه از root برنامه را به جزء index اضافه می کنیم. ما همچنین عنوانی را اضافه می کنیم که در برگه پیش نمایش/مرورگر صفحه نمایش داده می شود. ```python app.add_page(index, title="DALL-E") ``` با افزودن صفحات بیشتر می توانید یک برنامه چند صفحه ای ایجاد کنید. ## 📑 Resources - منابع
📑 [اسناد](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [وبلاگ](https://reflex.dev/blog)   |   📱 [کتابخانه جزء](https://reflex.dev/docs/library)   |   🖼️ [قالب ها](https://reflex.dev/templates/)   |   🛸 [استقرار](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Status - وضعیت رفلکس(reflex) در دسامبر 2022 با نام Pynecone راه اندازی شد. از سال 2025، [Reflex Cloud](https://cloud.reflex.dev) برای فراهم کردن بهترین تجربه میزبانی برای برنامه های Reflex راه‌اندازی شده است. ما به توسعه آن ادامه خواهیم داد و ویژگی‌های بیشتری را پیاده‌سازی خواهیم کرد. رفلکس(reflex) هر هفته نسخه ها و ویژگی های جدیدی دارد! مطمئن شوید که :star: ستاره و :eyes: این مخزن را تماشا کنید تا به روز بمانید. ## Contributing - مشارکت کردن ما از مشارکت در هر اندازه استقبال می کنیم! در زیر چند راه خوب برای شروع در انجمن رفلکس آورده شده است. - **به Discord ما بپیوندید**: [Discord](https://discord.gg/T5WSbC2YtQ) ما بهترین مکان برای دریافت کمک در مورد پروژه Reflex و بحث در مورد اینکه چگونه می توانید کمک کنید است. - **بحث های GitHub**: راهی عالی برای صحبت در مورد ویژگی هایی که می خواهید اضافه کنید یا چیزهایی که گیج کننده هستند/نیاز به توضیح دارند. - **قسمت مشکلات GitHub**: [قسمت مشکلات](https://github.com/reflex-dev/reflex/issues) یک راه عالی برای گزارش اشکال هستند. علاوه بر این، می توانید یک مشکل موجود را حل کنید و یک PR(pull request) ارسال کنید. ما فعالانه به دنبال مشارکت کنندگان هستیم، فارغ از سطح مهارت یا تجربه شما. برای مشارکت [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) را بررسی کنید. ## All Thanks To Our Contributors - با تشکر از همکاران ما: ## License - مجوز رفلکس متن باز و تحت مجوز [Apache License 2.0](/LICENSE) است. ================================================ FILE: docs/pt/pt_br/README.md ================================================
Reflex Logo
### **✨ Web apps customizáveis, performáticos, em Python puro. Faça deploy em segundos. ✨** [![Versão PyPI](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versões](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentação](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex é uma biblioteca para construir aplicações web full-stack em Python puro. Principais características: - **Python Puro** - Escreva o frontend e o backend da sua aplicação inteiramente em Python, sem necessidade de aprender Javascript. - **Flexibilidade Total** - O Reflex é fácil de começar a usar, mas também pode escalar para aplicações complexas. - **Deploy Instantâneo** - Após a construção, faça o deploy da sua aplicação com um [único comando](https://reflex.dev/docs/hosting/deploy-quick-start/) ou hospede-a em seu próprio servidor. Veja nossa [página de arquitetura](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) para aprender como o Reflex funciona internamente. ## ⚙️ Instalação Abra um terminal e execute (Requer Python 3.10+): ```bash pip install reflex ``` ## 🥳 Crie o seu primeiro app Instalar `reflex` também instala a ferramenta de linha de comando `reflex`. Crie um novo projeto para verificar se a instalação foi bem sucedida. (Mude `nome_do_meu_app` com o nome do seu projeto): ```bash mkdir nome_do_meu_app cd nome_do_meu_app reflex init ``` Este comando inicializa um app base no seu novo diretório. Você pode executar este app em modo desenvolvimento: ```bash reflex run ``` Você deve conseguir verificar seu app sendo executado em http://localhost:3000. Agora, você pode modificar o código fonte em `nome_do_meu_app/nome_do_meu_app.py`. O Reflex apresenta recarregamento rápido para que você possa ver suas mudanças instantaneamente quando você salva o seu código. ## 🫧 Exemplo de App Veja o seguinte exemplo: criar uma interface de criação de imagens por meio do [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). Para fins de simplicidade, vamos apenas chamar a [API da OpenAI](https://platform.openai.com/docs/api-reference/authentication), mas você pode substituir esta solução por um modelo de ML executado localmente.  
Um encapsulador frontend para o DALL-E, mostrado no processo de criação de uma imagem.
  Aqui está o código completo para criar este projeto. Isso tudo foi feito apenas em um arquivo Python! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """Estado da aplicação.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Obtenção da imagem a partir do prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Adição do estado e da página no app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Vamos por partes.
Explicando as diferenças entre as partes de backend e frontend do app DALL-E.
### **Reflex UI** Vamos começar com a UI (Interface de Usuário) ```python def index(): return rx.center( ... ) ``` Esta função `index` define o frontend do app. Usamos diferentes componentes, como `center`, `vstack`, `input` e `button`, para construir o frontend. Componentes podem ser aninhados um no do outro para criar layouts mais complexos. E você pode usar argumentos de chave-valor para estilizá-los com todo o poder do CSS. O Reflex vem com [60+ componentes nativos](https://reflex.dev/docs/library) para te ajudar a começar. Estamos adicionando ativamente mais componentes, e é fácil [criar seus próprios componentes](https://reflex.dev/docs/wrapping-react/overview/). ### **Estado** O Reflex representa a sua UI como uma função do seu estado. ```python class State(rx.State): """Estado da aplicação.""" prompt = "" image_url = "" processing = False complete = False ``` O estado define todas as variáveis (chamadas de vars) em um app que podem mudar e as funções que as alteram. Aqui, o estado é composto por um `prompt` e uma `image_url`. Há também os booleanos `processing` e `complete` para indicar quando desabilitar o botão (durante a geração da imagem) e quando mostrar a imagem resultante. ### **Handlers de Eventos** ```python def get_image(self): """Obtenção da imagem a partir do prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Dentro do estado, são definidas funções chamadas de Handlers de Eventos, que podem mudar as variáveis do estado. Handlers de Eventos são a forma com a qual podemos modificar o estado dentro do Reflex. Eles podem ser chamados como resposta a uma ação do usuário, como o clique de um botão ou a escrita em uma caixa de texto. Estas ações são chamadas de eventos. Nosso app DALL-E possui um Handler de Evento chamado `get_image`, que obtêm a imagem da API da OpenAI. Usar `yield` no meio de um Handler de Evento causa a atualização da UI do seu app. Caso contrário, a UI seria atualizada no fim da execução de um Handler de Evento. ### **Rotas** Finalmente, definimos nosso app. ```python app = rx.App() ``` Adicionamos uma página na raíz do app, apontando para o componente index. Também adicionamos um título que irá aparecer na visualização da página/aba do navegador. ```python app.add_page(index, title="DALL-E") ``` Você pode criar um app com múltiplas páginas adicionando mais páginas. ## 📑 Recursos
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Biblioteca de Componentes](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Status O Reflex foi lançado em Dezembro de 2022 com o nome Pynecone. A partir de 2025, o [Reflex Cloud](https://cloud.reflex.dev) foi lançado para fornecer a melhor experiência de hospedagem para apps Reflex. Continuaremos a desenvolvê-lo e implementar mais recursos. O Reflex tem novas versões e recursos chegando toda semana! Certifique-se de marcar com :star: estrela e :eyes: observar este repositório para se manter atualizado. ## Contribuições Nós somos abertos a contribuições de qualquer tamanho! Abaixo, seguem algumas boas formas de começar a contribuir para a comunidade do Reflex. - **Entre no nosso Discord**: Nosso [Discord](https://discord.gg/T5WSbC2YtQ) é o melhor lugar para conseguir ajuda no seu projeto Reflex e para discutir como você pode contribuir. - **Discussões no GitHub**: Uma boa forma de conversar sobre funcionalidades que você gostaria de ver ou coisas que ainda estão confusas/exigem ajuda. - **Issues no GitHub**: [Issues](https://github.com/reflex-dev/reflex/issues) são uma excelente forma de reportar bugs. Além disso, você pode tentar resolver alguma issue existente e enviar um PR. Estamos ativamente buscando novos contribuidores, não importa o seu nível de habilidade ou experiência. Para contribuir, confira [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md). ## Todo Agradecimento aos Nossos Contribuidores: ## Licença O Reflex é de código aberto e licenciado sob a [Apache License 2.0](/LICENSE). ================================================ FILE: docs/tr/README.md ================================================
Reflex Logo
### **✨ Saf Python'da performanslı, özelleştirilebilir web uygulamaları. Saniyeler içinde dağıtın. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex, saf Python'da tam yığın web uygulamaları oluşturmak için bir kütüphanedir. Temel özellikler: - **Saf Python** - Uygulamanızın ön uç ve arka uç kısımlarının tamamını Python'da yazın, Javascript öğrenmenize gerek yok. - **Tam Esneklik** - Reflex ile başlamak kolaydır, ancak karmaşık uygulamalara da ölçeklenebilir. - **Anında Dağıtım** - Oluşturduktan sonra, uygulamanızı [tek bir komutla](https://reflex.dev/docs/hosting/deploy-quick-start/) dağıtın veya kendi sunucunuzda barındırın. Reflex'in perde arkasında nasıl çalıştığını öğrenmek için [mimari sayfamıza](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) göz atın. ## ⚙️ Kurulum Bir terminal açın ve çalıştırın (Python 3.10+ gerekir): ```bash pip install reflex ``` ## 🥳 İlk Uygulamanı Oluştur `reflex`'i kurduğunuzda `reflex` komut satırı aracınıda kurmuş olursunuz. Kurulumun başarılı olduğunu test etmek için yeni bir proje oluşturun. (`my_app_name`'i proje ismiyle değiştirin.): ```bash mkdir my_app_name cd my_app_name reflex init ``` Bu komut ile birlikte yeni oluşturduğunuz dizinde bir şablon uygulaması oluşturur. Uygulamanızı geliştirme modunda başlatabilirsiniz: ```bash reflex run ``` Uygulamanızın http://localhost:3000 adresinde çalıştığını görmelisiniz. Şimdi `my_app_name/my_app_name.py` yolundaki kaynak kodu düzenleyebilirsiniz. Reflex'in hızlı yenileme özelliği vardır, böylece kodunuzu kaydettiğinizde değişikliklerinizi anında görebilirsiniz. ## 🫧 Örnek Uygulama Bir örnek üzerinden gidelim: [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node) kullanarak bir görüntü oluşturma arayüzü oluşturalım. Basit olması açısından, yalnızca [OpenAI API](https://platform.openai.com/docs/api-reference/authentication)'ını kullanıyoruz, ancak bunu yerel olarak çalıştırılan bir ML modeliyle değiştirebilirsiniz.  
A frontend wrapper for DALL·E, shown in the process of generating an image.
  İşte bunu oluşturmak için kodun tamamı. Her şey sadece bir Python dosyasıyla hazırlandı! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """Uygulama durumu.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Prompt'tan görüntüyü alın.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Sayfa ve durumu uygulamaya ekleyin. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Daha Detaylı İceleyelim
DALL-E uygulamasının arka uç ve ön uç kısımları arasındaki farkları açıklama.
### **Reflex UI** UI (Kullanıcı Arayüzü) ile başlayalım. ```python def index(): return rx.center( ... ) ``` Bu `index` fonkisyonu uygulamanın frontend'ini tanımlar. Frontend'i oluşturmak için `center`, `vstack`, `input`, ve `button` gibi farklı bileşenler kullanıyoruz. Karmaşık düzenler oluşturmak için bileşenleri birbirinin içine yerleştirilebiliriz. Ayrıca bunları CSS'nin tüm gücüyle şekillendirmek için anahtar kelime argümanları kullanabilirsiniz. Reflex, işinizi kolaylaştırmak için [60'tan fazla dahili bileşen](https://reflex.dev/docs/library) içerir. Aktif olarak yeni bileşen ekliyoruz ve [kendi bileşenlerinizi oluşturmak](https://reflex.dev/docs/wrapping-react/overview/) oldukça kolay. ### **Durum (State)** Reflex arayüzünüzü durumunuzun bir fonksiyonu olarak temsil eder. ```python class State(rx.State): """Uygulama durumu.""" prompt = "" image_url = "" processing = False complete = False ``` Durum (State), bir uygulamadaki değişebilen tüm değişkenleri (vars olarak adlandırılır) ve bunları değiştiren fonksiyonları tanımlar. Burada durum `prompt` ve `image_url`inden oluşur. Ayrıca düğmenin ne zaman devre dışı bırakılacağını (görüntü oluşturma sırasında) ve görüntünün ne zaman gösterileceğini belirtmek için `processing` ve `complete` booleanları da vardır. ### **Olay İşleyicileri (Event Handlers)** ```python def get_image(self): """Prompt'tan görüntüyü alın.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Durum içinde, durum değişkenlerini değiştiren olay işleyicileri adı verilen fonksiyonları tanımlarız. Olay işleyicileri, Reflex'te durumu değiştirebilmemizi sağlar. Bir düğmeye tıklamak veya bir metin kutusuna yazı yazmak gibi kullanıcı eylemlerine yanıt olarak çağrılabilirler. Bu eylemlere olay denir. DALL·E uygulamamız OpenAI API'ından bu görüntüyü almak için `get_image` adlı bir olay işleyicisine sahiptir. Bir olay işleyicisinin ortasında `yield`ın kullanılması UI'ın güncellenmesini sağlar. Aksi takdirde UI olay işleyicisinin sonunda güncellenecektir. ### **Yönlendirme (Routing)** En sonunda uygulamamızı tanımlıyoruz. ```python app = rx.App() ``` Uygulamamızın kök dizinine index bileşeninden bir sayfa ekliyoruz. Ayrıca sayfa önizlemesinde/tarayıcı sekmesinde görünecek bir başlık ekliyoruz. ```python app.add_page(index, title="DALL-E") ``` Daha fazla sayfa ekleyerek çok sayfalı bir uygulama oluşturabilirsiniz. ## 📑 Kaynaklar
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Component Library](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Durum Reflex, Aralık 2022'de Pynecone adıyla piyasaya sürüldü. 2025'in başından itibaren, Reflex uygulamaları için en iyi barındırma deneyimini sunmak amacıyla [Reflex Cloud](https://cloud.reflex.dev) hizmete girmiştir. Bunu geliştirmeye ve daha fazla özellik eklemeye devam edeceğiz. Reflex'in her hafta yeni sürümleri ve özellikleri geliyor! Güncel kalmak için bu depoyu :star: yıldızlamayı ve :eyes: izlediğinizden emin olun. ## Katkı Her boyuttaki katkıları memnuniyetle karşılıyoruz! Aşağıda Reflex topluluğuna adım atmanın bazı yolları mevcut. - **Discord Kanalımıza Katılın**: [Discord'umuz](https://discord.gg/T5WSbC2YtQ), Reflex projeniz hakkında yardım almak ve nasıl katkıda bulunabileceğinizi tartışmak için en iyi yerdir. - **GitHub Discussions**: Eklemek istediğiniz özellikler veya kafa karıştırıcı, açıklığa kavuşturulması gereken şeyler hakkında konuşmanın harika bir yolu. - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) hataları bildirmenin mükemmel bir yoludur. Ayrıca mevcut bir sorunu deneyip çözebilir ve bir PR (Pull Requests) gönderebilirsiniz. Beceri düzeyiniz veya deneyiminiz ne olursa olsun aktif olarak katkıda bulunacak kişiler arıyoruz. Katkı sağlamak için katkı sağlama rehberimize bakabilirsiniz: [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## Hepsi Katkıda Bulunanlar Sayesinde: ## Lisans Reflex açık kaynaklıdır ve [Apache License 2.0](/LICENSE) altında lisanslıdır. ================================================ FILE: docs/vi/README.md ================================================
Reflex Logo
### **✨ Ứng dụng web hiệu suất cao, tùy chỉnh bằng Python thuần. Deploy trong vài giây. ✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex là một thư viện để xây dựng ứng dụng web toàn bộ bằng Python thuần. Các tính năng chính: - **Python thuần tuý** - Viết toàn bộ ứng dụng cả backend và frontend hoàn toàn bằng Python, không cần học JavaScript. - **Full Flexibility** - Reflex dễ dàng để bắt đầu, nhưng cũng có thể mở rộng lên các ứng dụng phức tạp. - **Deploy Instantly** - Sau khi xây dựng ứng dụng, bạn có thể triển khai bằng [một dòng lệnh](https://reflex.dev/docs/hosting/deploy-quick-start/) hoặc triển khai trên server của riêng bạn. Đọc [bài viết về kiến trúc hệ thống](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture) để hiểu rõ các hoạt động của Reflex. ## ⚙️ Cài đặt Mở cửa sổ lệnh và chạy (Yêu cầu Python phiên bản 3.10+): ```bash pip install reflex ``` ## 🥳 Tạo ứng dụng đầu tiên Cài đặt `reflex` cũng như cài đặt công cụ dòng lệnh `reflex`. Kiểm tra việc cài đặt đã thành công hay chưa bằng cách tạo mới một ứng dụng. (Thay `my_app_name` bằng tên ứng dụng của bạn): ```bash mkdir my_app_name cd my_app_name reflex init ``` Lệnh này tạo ra một ứng dụng mẫu trong một thư mục mới. Bạn có thể chạy ứng dụng ở chế độ phát triển. ```bash reflex run ``` Bạn có thể xem ứng dụng của bạn ở địa chỉ http://localhost:3000. Bạn có thể thay đổi mã nguồn ở `my_app_name/my_app_name.py`. Reflex nhanh chóng làm mới và bạn có thể thấy thay đổi trên ứng dụng của bạn ngay lập tức khi bạn lưu file. ## 🫧 Ứng dụng ví dụ Bắt đầu với ví dụ: tạo một ứng dụng tạo ảnh bằng [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node). Để cho đơn giản, chúng ta sẽ sử dụng [OpenAI API](https://platform.openai.com/docs/api-reference/authentication), nhưng bạn có thể sử dụng model của chính bạn được triển khai trên local.  
A frontend wrapper for DALL·E, shown in the process of generating an image.
  Đây là toàn bộ đoạn mã để xây dựng ứng dụng trên. Nó được viết hoàn toàn trong một file Python! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## Hãy phân tích chi tiết.
Explaining the differences between backend and frontend parts of the DALL-E app.
### **Reflex UI** Bắt đầu với giao diện chính. ```python def index(): return rx.center( ... ) ``` Hàm `index` định nghĩa phần giao diện chính của ứng dụng. Chúng tôi sử dụng các component (thành phần) khác nhau như `center`, `vstack`, `input` và `button` để xây dựng giao diện phía trước. Các component có thể được lồng vào nhau để tạo ra các bố cục phức tạp. Và bạn cũng có thể sử dụng từ khoá `args` để tận dụng đầy đủ sức mạnh của CSS. Reflex có đến hơn [60 component được xây dựng sẵn](https://reflex.dev/docs/library) để giúp bạn bắt đầu. Chúng ta có thể tạo ra một component mới khá dễ dàng, thao khảo: [xây dựng component của riêng bạn](https://reflex.dev/docs/wrapping-react/overview/). ### **State** Reflex biểu diễn giao diện bằng các hàm của state (trạng thái). ```python class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False ``` Một state định nghĩa các biến (được gọi là vars) có thể thay đổi trong một ứng dụng và cho phép các hàm có thể thay đổi chúng. Tại đây state được cấu thành từ một `prompt` và `image_url`. Có cũng những biến boolean `processing` và `complete` để chỉ ra khi nào tắt nút (trong quá trình tạo hình ảnh) và khi nào hiển thị hình ảnh kết quả. ### **Event Handlers** ```python def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` Với các state, chúng ta định nghĩa các hàm có thể thay đổi state vars được gọi là event handlers. Event handler là cách chúng ta có thể thay đổi state trong Reflex. Chúng có thể là phản hồi khi người dùng thao tác, chằng hạn khi nhấn vào nút hoặc khi đang nhập trong text box. Các hành động này được gọi là event. Ứng dụng DALL·E. của chúng ta có một event handler, `get_image` để lấy hình ảnh từ OpenAI API. Sử dụng từ khoá `yield` in ở giữa event handler để cập nhật giao diện. Hoặc giao diện có thể cập nhật ở cuối event handler. ### **Routing** Cuối cùng, chúng ta định nghĩa một ứng dụng. ```python app = rx.App() ``` Chúng ta thêm một trang ở đầu ứng dụng bằng index component. Chúng ta cũng thêm tiêu đề của ứng dụng để hiển thị lên trình duyệt. ```python app.add_page(index, title="DALL-E") ``` Bạn có thể tạo một ứng dụng nhiều trang bằng cách thêm trang. ## 📑 Tài liệu
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Component Library](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Status Reflex phát hành vào tháng 12/2022 với tên là Pynecone. Từ năm 2025, [Reflex Cloud](https://cloud.reflex.dev) đã ra mắt để cung cấp trải nghiệm lưu trữ tốt nhất cho các ứng dụng Reflex. Chúng tôi sẽ tiếp tục phát triển và triển khai thêm nhiều tính năng mới. Reflex ra phiên bản mới với các tính năng mới hàng tuần! Hãy :star: star và :eyes: watch repo này để thấy các cập nhật mới nhất. ## Contributing Chúng tôi chào đón mọi đóng góp dù lớn hay nhỏ. Dưới đây là các cách để bắt đầu với cộng đồng Reflex. - **Discord**: [Discord](https://discord.gg/T5WSbC2YtQ) của chúng tôi là nơi tốt nhất để nhờ sự giúp đỡ và thảo luận các bạn có thể đóng góp. - **GitHub Discussions**: Là cách tốt nhất để thảo luận về các tính năng mà bạn có thể đóng góp hoặc những điều bạn chưa rõ. - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) là nơi tốt nhất để thông báo. Ngoài ra bạn có thể sửa chữa các vấn đề bằng cách tạo PR. Chúng tôi luôn sẵn sàng tìm kiếm các contributor, bất kể kinh nghiệm. Để tham gia đóng góp, xin mời xem [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## Xin cảm ơn các Contributors: ## License Reflex là mã nguồn mở và sử dụng giấy phép [Apache License 2.0](/LICENSE). ================================================ FILE: docs/zh/zh_cn/README.md ================================================
Reflex Logo
### **✨ 使用 Python 创建高效且可自定义的网页应用程序,几秒钟内即可部署.✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex 是一个使用纯 Python 构建全栈 web 应用的库。 关键特性: - **纯 Python** - 前端、后端开发全都使用 Python,不需要学习 Javascript。 - **完整的灵活性** - Reflex 很容易上手, 并且也可以扩展到复杂的应用程序。 - **立即部署** - 构建后,使用[单个命令](https://reflex.dev/docs/hosting/deploy-quick-start/)就能部署应用程序;或者也可以将其托管在您自己的服务器上。 请参阅我们的[架构页](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture)了解 Reflex 如何工作。 ## ⚙️ 安装 打开一个终端并且运行(要求 Python3.10+): ```bash pip install reflex ``` ## 🥳 创建您的第一个应用程序 安装 Reflex 的同时也会安装 `reflex` 命令行工具. 通过创建一个新项目来测试是否安装成功(请把 my_app_name 替代为您的项目名字): ```bash mkdir my_app_name cd my_app_name reflex init ``` 这段命令会在新文件夹初始化一个应用程序模板. 您可以在开发者模式下运行这个应用程序: ```bash reflex run ``` 您可以看到您的应用程序运行在 http://localhost:3000. 现在您可以在以下位置修改代码 `my_app_name/my_app_name.py`,Reflex 拥有快速刷新(fast refresh),所以您可以在保存代码后马上看到更改. ## 🫧 范例 让我们来看一个例子: 创建一个使用 [DALL·E](https://platform.openai.com/docs/guides/images/image-generation?context=node) 进行图像生成的图形界面.为了保持范例简单,我们只使用 OpenAI API,但是您可以将其替换成本地端的 ML 模型.  
DALL·E的前端界面, 展示了图片生成的进程
  这是这个范例的完整代码,只需要一个 Python 文件就可以完成! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # Add state and page to the app. app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## 让我们分解以上步骤.
解释 DALL-E app 的前端和后端部分的区别。
### **Reflex UI** 让我们从 UI 开始. ```python def index(): return rx.center( ... ) ``` 这个 `index` 函数定义了应用程序的前端. 我们用不同的组件比如 `center`, `vstack`, `input`, 和 `button` 来创建前端, 组件之间可以相互嵌入,来创建复杂的布局. 并且您可以使用关键字参数来使用 CSS 的全部功能. Reflex 拥有 [60+ 个内置组件](https://reflex.dev/docs/library) 来帮助您开始创建应用程序. 我们正在积极添加组件, 但是您也可以容易的 [创建自己的组件](https://reflex.dev/docs/wrapping-react/overview/). ### **State** Reflex 用 State 来渲染您的 UI. ```python class State(rx.State): """The app state.""" prompt = "" image_url = "" processing = False complete = False ``` State 定义了所有可能会发生变化的变量(称为 vars)以及能够改变这些变量的函数. 在这个范例中,State 由 `prompt` 和 `image_url` 组成.此外,State 还包含有两个布尔值 `processing` 和 `complete`,用于指示何时显示循环进度指示器和图像. ### **Event Handlers** ```python def get_image(self): """Get the image from the prompt.""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` 在 State 中,我们定义了称为事件处理器(event handlers)的函数,用于改变状态变量(state vars).在 Reflex 中,事件处理器是我们可以修改状态的方式.它们可以作为对用户操作的响应而被调用,例如点击一个按钮或在文本框中输入.这些操作被称为事件. 我们的 DALL·E 应用有一个事件处理器,名为 `get_image`,它用于从 OpenAI API 获取图像.在事件处理器中使用 `yield` 将导致 UI 进行更新.否则,UI 将在事件处理器结束时进行更新. ### **Routing** 最后,定义我们的应用程序. ```python app = rx.App() ``` 我们添加从应用程序根目录到 index 组件的路由.我们还添加了一个在页面预览或浏览器标签中显示的标题. ```python app.add_page(index, title="DALL-E") ``` 您可以通过增加更多页面来创建一个多页面的应用. ## 📑 资源
📑 [文档](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [日志](https://reflex.dev/blog)   |   📱 [组件库](https://reflex.dev/docs/library)   |   🖼️ [模板](https://reflex.dev/templates/)   |   🛸 [部署](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ Reflex 的状态 Reflex 于 2022 年 12 月以 Pynecone 的名称推出. 从 2025 年开始,[Reflex Cloud](https://cloud.reflex.dev)已经推出,为 Reflex 应用提供最佳的托管体验。我们将继续开发并实现更多功能。 Reflex 每周都有新功能和发布新版本! 确保您按下 :star: 收藏和 :eyes: 关注 这个 仓库来确保知道最新信息. ## 贡献 我们欢迎任何大小的贡献,以下是几个好的方法来加入 Reflex 社群. - **加入我们的 Discord**: 我们的 [Discord](https://discord.gg/T5WSbC2YtQ) 是帮助您加入 Reflex 项目和讨论或贡献最棒的地方. - **GitHub Discussions**: 一个来讨论您想要添加的功能或是需要澄清的事情的好地方. - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues)是报告错误的绝佳地方,另外您可以试着解决一些现有 issue 并提交 PR. 我们正在积极寻找贡献者,无关您的技能或经验水平. 若要贡献,请查看[CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## 感谢我们所有的贡献者: ## 授权 Reflex 是一个开源项目,使用 [Apache License 2.0](/LICENSE) 授权. ================================================ FILE: docs/zh/zh_tw/README.md ================================================
Reflex Logo
**✨ 使用 Python 建立高效且可自訂的網頁應用程式,幾秒鐘內即可部署。✨** [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex) ![versions](https://img.shields.io/pypi/pyversions/reflex.svg) [![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction) [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex) [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
--- [English](https://github.com/reflex-dev/reflex/blob/main/README.md) | [简体中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_cn/README.md) | [繁體中文](https://github.com/reflex-dev/reflex/blob/main/docs/zh/zh_tw/README.md) | [Türkçe](https://github.com/reflex-dev/reflex/blob/main/docs/tr/README.md) | [हिंदी](https://github.com/reflex-dev/reflex/blob/main/docs/in/README.md) | [Português (Brasil)](https://github.com/reflex-dev/reflex/blob/main/docs/pt/pt_br/README.md) | [Italiano](https://github.com/reflex-dev/reflex/blob/main/docs/it/README.md) | [Español](https://github.com/reflex-dev/reflex/blob/main/docs/es/README.md) | [한국어](https://github.com/reflex-dev/reflex/blob/main/docs/kr/README.md) | [日本語](https://github.com/reflex-dev/reflex/blob/main/docs/ja/README.md) | [Deutsch](https://github.com/reflex-dev/reflex/blob/main/docs/de/README.md) | [Persian (پارسی)](https://github.com/reflex-dev/reflex/blob/main/docs/pe/README.md) | [Tiếng Việt](https://github.com/reflex-dev/reflex/blob/main/docs/vi/README.md) --- # Reflex Reflex 是一個可以用純 Python 構建全端網頁應用程式的函式庫。 主要特色: - **純 Python** - 您可以用 Python 撰寫應用程式的前端和後端,無需學習 Javascript。 - **完全靈活性** - Reflex 易於上手,但也可以擴展到複雜的應用程式。 - **立即部署** - 構建後,只需使用[單一指令](https://reflex.dev/docs/hosting/deploy-quick-start/)即可部署您的應用程式,或在您自己的伺服器上託管。 請參閱我們的[架構頁面](https://reflex.dev/blog/2024-03-21-reflex-architecture/#the-reflex-architecture)了解 Reflex 如何在底層運作。 ## ⚙️ 安裝 開啟一個終端機並且執行 (需要 Python 3.10+): ```bash pip install reflex ``` ## 🥳 建立你的第一個應用程式 安裝 Reflex 的同時也會安裝 `reflex` 命令行工具。 通過創建一個新專案來測試是否安裝成功。(把 my_app_name 作為新專案名稱): ```bash mkdir my_app_name cd my_app_name reflex init ``` 此命令會初始化一個應用程式模板在你的新資料夾中。 你可以在開發者模式運行這個應用程式: ```bash reflex run ``` 你可以看到你的應用程式運行在 http://localhost:3000。 現在在以下位置修改原始碼 `my_app_name/my_app_name.py`,Reflex 擁有快速刷新功能,存儲程式碼後便可立即看到改變。 ## 🫧 範例應用程式 讓我們來看一個例子: 建立一個使用 DALL·E 的圖形使用者介面,為了保持範例簡單,我們只呼叫 OpenAI API,而這部份可以置換掉,改為執行成本地端的 ML 模型。  
A frontend wrapper for DALL·E, shown in the process of generating an image.
  下方為該應用之完整程式碼,這一切都只需要一個 Python 檔案就能作到! ```python import reflex as rx import openai openai_client = openai.OpenAI() class State(rx.State): """應用程式狀態""" prompt = "" image_url = "" processing = False complete = False def get_image(self): """透過提示詞取得圖片""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True def index(): return rx.center( rx.vstack( rx.heading("DALL-E", font_size="1.5em"), rx.input( placeholder="Enter a prompt..", on_blur=State.set_prompt, width="25em", ), rx.button( "Generate Image", on_click=State.get_image, width="25em", loading=State.processing ), rx.cond( State.complete, rx.image(src=State.image_url, width="20em"), ), align="center", ), width="100%", height="100vh", ) # 把狀態跟頁面添加到應用程式。 app = rx.App() app.add_page(index, title="Reflex:DALL-E") ``` ## 讓我們來拆解一下。
解釋 DALL-E app 的前端和後端部分的區別。
### **Reflex 使用者介面** 讓我們從使用介面開始。 ```python def index(): return rx.center( ... ) ``` 這個 `index` 函式定義了應用程式的前端. 我們用不同的元件像是 `center`, `vstack`, `input`, 和 `button` 來建立前端,元件之間可互相套入以建立出複雜的版面配置。並且您可使用關鍵字引數 _keyword args_ 運行 CSS 全部功能來設計這些元件們的樣式。 Reflex 擁有 [60+ 內建元件](https://reflex.dev/docs/library) 來幫助你開始建立應用程式。我們正積極添加元件,你也可以簡單地 [創建自己所屬的元件](https://reflex.dev/docs/wrapping-react/overview/)。 ### **應用程式狀態** Reflex 使用應用程式狀態中的函式來渲染你的 UI。 ```python class State(rx.State): """應用程式狀態""" prompt = "" image_url = "" processing = False complete = False ``` 應用程式狀態定義了應用程式中所有可以更改的變數及變更他們的函式 (稱為 vars)。 這裡的狀態由 `prompt` 和 `image_url`組成, 以及布林變數 `processing` 和 `complete` 來指示何時顯示進度條及圖片。 ### **事件處理程序** ```python def get_image(self): """透過提示詞取得圖片""" if self.prompt == "": return rx.window_alert("Prompt Empty") self.processing, self.complete = True, False yield response = openai_client.images.generate( prompt=self.prompt, n=1, size="1024x1024" ) self.image_url = response.data[0].url self.processing, self.complete = False, True ``` 在應用程式狀態中,我們定義稱之為事件處理程序的函式來改變其 vars. 事件處理程序是我們用來改變 Reflex 應用程式狀態的方法。 當使用者動作被響應時,對應的事件處理程序就會被呼叫。點擊按鈕或是文字框輸入都是使用者動作,它們被稱之為事件。 我們的 DALL·E. 應用程式有一個事件處理程序 `get_image`,它透過 Open AI API 取得圖片。在事件處理程序中使用 `yield` 將讓使用者介面中途更新,若不使用的話,使用介面只能在事件處理程序結束時才更新。 ### **路由** 最後,我們定義我們的應用程式 app。 ```python app = rx.App() ``` 添加從應用程式根目錄(root of the app) 到 index 元件的路由。 我們也添加了一個標題將會顯示在 預覽/瀏覽 分頁。 ```python app.add_page(index, title="DALL-E") ``` 你可以添加更多頁面至路由藉此來建立多頁面應用程式(multi-page app) ## 📑 資源
📑 [Docs](https://reflex.dev/docs/getting-started/introduction)   |   🗞️ [Blog](https://reflex.dev/blog)   |   📱 [Component Library](https://reflex.dev/docs/library)   |   🖼️ [Templates](https://reflex.dev/templates/)   |   🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)  
## ✅ 產品狀態 Reflex 在 2022 年 12 月以 Pynecone 的名字推出。 自 2025 年起,[Reflex Cloud](https://cloud.reflex.dev) 已推出,為 Reflex 應用程式提供最佳的託管體驗。我們將繼續開發並實施更多功能。 Reflex 每周都有新功能和釋出新版本! 確保你按下 :star: 和 :eyes: watch 這個 repository 來確保知道最新資訊。 ## 貢獻 我們歡迎任何大小的貢獻,以下是一些加入 Reflex 社群的好方法。 - **加入我們的 Discord**: 我們的 [Discord](https://discord.gg/T5WSbC2YtQ) 是獲取 Reflex 專案幫助和討論如何貢獻的最佳地方。 - **GitHub Discussions**: 這是一個討論您想新增的功能或對於一些困惑/需要澄清事項的好方法。 - **GitHub Issues**: 在 [Issues](https://github.com/reflex-dev/reflex/issues) 頁面報告錯誤是一個絕佳的方式。此外,您也可以嘗試解決現有 Issue 並提交 PR。 我們積極尋找貢獻者,不論您的技能水平或經驗如何。要貢獻,請查看 [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) ## 感謝所有貢獻者: ## 授權 Reflex 是一個開源專案且使用 [Apache License 2.0](/LICENSE) 授權。 ================================================ FILE: pyi_hashes.json ================================================ { "reflex/__init__.pyi": "0a3ae880e256b9fd3b960e12a2cb51a7", "reflex/components/__init__.pyi": "ac05995852baa81062ba3d18fbc489fb", "reflex/components/base/__init__.pyi": "16e47bf19e0d62835a605baa3d039c5a", "reflex/components/base/app_wrap.pyi": "22e94feaa9fe675bcae51c412f5b67f1", "reflex/components/base/body.pyi": "e8ab029a730824bab6d4211203609e6a", "reflex/components/base/document.pyi": "311c53c90a60587a82e760103758a3cf", "reflex/components/base/error_boundary.pyi": "a678cceea014cb16048647257cd24ba6", "reflex/components/base/fragment.pyi": "745f1be02c23a0b25d7c52d7423ec76a", "reflex/components/base/link.pyi": "0bc1d26ee29d8864aed14a12991bd47d", "reflex/components/base/meta.pyi": "129aecf65ab53f756c4d1cbe1d0b188d", "reflex/components/base/script.pyi": "e5f506d1d0d6712cb9e597a781eb3941", "reflex/components/base/strict_mode.pyi": "6b72e16caadf7158ab744a0ab751b010", "reflex/components/core/__init__.pyi": "007170b97e58bdf28b2aee381d91c0c7", "reflex/components/core/auto_scroll.pyi": "18068d22aca7244a08cd0c5a897c0950", "reflex/components/core/banner.pyi": "fd93e7a92961de8524718ad32135c37c", "reflex/components/core/clipboard.pyi": "a844eb927d9bc2a43f5e88161b258539", "reflex/components/core/debounce.pyi": "055da7aa890f44fb4d48bd5978f1a874", "reflex/components/core/helmet.pyi": "43f8497c8fafe51e29dca1dd535d143a", "reflex/components/core/html.pyi": "86eb9d4c1bb4807547b2950d9a32e9fd", "reflex/components/core/sticky.pyi": "cb763b986a9b0654d1a3f33440dfcf60", "reflex/components/core/upload.pyi": "6dc28804a6dddf903e31162e87c1b023", "reflex/components/core/window_events.pyi": "af33ccec866b9540ee7fbec6dbfbd151", "reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e", "reflex/components/datadisplay/code.pyi": "b86769987ef4d1cbdddb461be88539fd", "reflex/components/datadisplay/dataeditor.pyi": "9b85f3cf6156293cd9961eb17a0ea684", "reflex/components/datadisplay/shiki_code_block.pyi": "1d53e75b6be0d3385a342e7b3011babd", "reflex/components/el/__init__.pyi": "0adfd001a926a2a40aee94f6fa725ecc", "reflex/components/el/element.pyi": "c5974a92fbc310e42d0f6cfdd13472f4", "reflex/components/el/elements/__init__.pyi": "29512d7a6b29c6dc5ff68d3b31f26528", "reflex/components/el/elements/base.pyi": "3f74c7ea573ea29b055b0cd48b040d2c", "reflex/components/el/elements/forms.pyi": "8b6bb2fbaf4bad828b076e2f7c8444d0", "reflex/components/el/elements/inline.pyi": "3549cd6ad45217aa6387800911b641c3", "reflex/components/el/elements/media.pyi": "9b97220aa99783d402b6e278c4069043", "reflex/components/el/elements/metadata.pyi": "24448004b7aa07f1225028a85bd49fef", "reflex/components/el/elements/other.pyi": "0c4d5d0b955d8596bf6cf4a48d7decdb", "reflex/components/el/elements/scripts.pyi": "d33df9f21f7e838376b2b5024beef7c9", "reflex/components/el/elements/sectioning.pyi": "3c5a7e4caa9c25da0ae788f02466eac4", "reflex/components/el/elements/tables.pyi": "686eb70ea7d8c4dafb0cc5c284e76184", "reflex/components/el/elements/typography.pyi": "684e83dde887dba12badd0fb75c87c04", "reflex/components/gridjs/datatable.pyi": "98a7e1b3f3b60cafcdfcd8879750ee42", "reflex/components/lucide/icon.pyi": "1db10f2b544908dd20c56ca4bc33d5e0", "reflex/components/markdown/markdown.pyi": "4c4bca6fb0643abb90aca9c0bb87f722", "reflex/components/moment/moment.pyi": "e1952f1c2c82cef85d91e970d1be64ab", "reflex/components/plotly/plotly.pyi": "4311a0aae2abcc9226abb6a273f96372", "reflex/components/radix/__init__.pyi": "5d8e3579912473e563676bfc71f29191", "reflex/components/radix/primitives/__init__.pyi": "01c388fe7a1f5426a16676404344edf6", "reflex/components/radix/primitives/accordion.pyi": "19484eca0ad53f538f5db04c09921738", "reflex/components/radix/primitives/base.pyi": "9ef34884fb6028dc017df5e2db639c81", "reflex/components/radix/primitives/dialog.pyi": "9ee73362bb59619c482b6b0d07033f37", "reflex/components/radix/primitives/drawer.pyi": "921e45dfaf5b9131ef27c561c3acca2e", "reflex/components/radix/primitives/form.pyi": "17002a3e9d7f52b3207614f6c1c9a24a", "reflex/components/radix/primitives/progress.pyi": "c917952d57ddb3e138a40c4005120d5e", "reflex/components/radix/primitives/slider.pyi": "4ff06f0025d47f166132909b09ab96f8", "reflex/components/radix/themes/__init__.pyi": "582b4a7ead62b2ae8605e17fa084c063", "reflex/components/radix/themes/base.pyi": "3e1ccd5ce5fef0b2898025193ee3d069", "reflex/components/radix/themes/color_mode.pyi": "dda570583355d8c0d8f607be457ba7a1", "reflex/components/radix/themes/components/__init__.pyi": "efa279ee05479d7bb8a64d49da808d03", "reflex/components/radix/themes/components/alert_dialog.pyi": "eed422fcc1ff5ccf3dbf6934699bd0b1", "reflex/components/radix/themes/components/aspect_ratio.pyi": "71de4160d79840561c48b570197a4152", "reflex/components/radix/themes/components/avatar.pyi": "e40c2f0fda6d2c028d83681a27f3fb96", "reflex/components/radix/themes/components/badge.pyi": "58fd1a9c5d2f8762e2a0370311731ff5", "reflex/components/radix/themes/components/button.pyi": "50f0b08ad5d1d1054ab537152f0f5c43", "reflex/components/radix/themes/components/callout.pyi": "547f2570ffbd10db36b745566e9f1b17", "reflex/components/radix/themes/components/card.pyi": "f7adb83f7b001a11bdd7fd6791fb3ffb", "reflex/components/radix/themes/components/checkbox.pyi": "8eabb6887a5d0849a43e086a284814c2", "reflex/components/radix/themes/components/checkbox_cards.pyi": "1d567fd04b4425abd5cc5aad10108aa9", "reflex/components/radix/themes/components/checkbox_group.pyi": "8638582a623036f8893a3fa6080f2672", "reflex/components/radix/themes/components/context_menu.pyi": "b9499d8bdd2c5565621fea5fe7d7a25a", "reflex/components/radix/themes/components/data_list.pyi": "6f8d9c582e084c23966b992158193b72", "reflex/components/radix/themes/components/dialog.pyi": "d2615f1a68c80ff930444d054b598c13", "reflex/components/radix/themes/components/dropdown_menu.pyi": "43f8770c9adf93c73398d68f79048424", "reflex/components/radix/themes/components/hover_card.pyi": "a96f4433237f9994decf935deff9f269", "reflex/components/radix/themes/components/icon_button.pyi": "f12a874bad243a81e5c8740a1d86c6bc", "reflex/components/radix/themes/components/inset.pyi": "bd7a2186b553bd4c86d83ff50c784066", "reflex/components/radix/themes/components/popover.pyi": "91f8edefeb232cc6d48690b1838144c2", "reflex/components/radix/themes/components/progress.pyi": "0e59587d5b3c8fe0d0067587f144e5b0", "reflex/components/radix/themes/components/radio.pyi": "f375aa5ac746679618ea7dad257e3224", "reflex/components/radix/themes/components/radio_cards.pyi": "9dc34a1ce2a1924eb1f41438ef84e80b", "reflex/components/radix/themes/components/radio_group.pyi": "173254cf91908bcf6aa4fa21a747e2cf", "reflex/components/radix/themes/components/scroll_area.pyi": "2e3539b0f6895dda127ee96e9864dbf9", "reflex/components/radix/themes/components/segmented_control.pyi": "1776f1ad936bae402007802b1ee98906", "reflex/components/radix/themes/components/select.pyi": "2c7aee592972ff5f05da08154aa981c8", "reflex/components/radix/themes/components/separator.pyi": "79e550cc10ee455f35d75d0e236fedd2", "reflex/components/radix/themes/components/skeleton.pyi": "a25d3ceb56f99f736ea463579845c454", "reflex/components/radix/themes/components/slider.pyi": "305a34c14ca8656ca9267e4c31aaa388", "reflex/components/radix/themes/components/spinner.pyi": "b7e689e7d75635e379242fd113a1ea9a", "reflex/components/radix/themes/components/switch.pyi": "f1ba948750a74126cda990e89a3ec7ef", "reflex/components/radix/themes/components/table.pyi": "eefbbd1904deae3d166fcad28b20fd4a", "reflex/components/radix/themes/components/tabs.pyi": "a533d2509a6798fe0ab7275b0152519d", "reflex/components/radix/themes/components/text_area.pyi": "4af55e5d18a5b9d56717bf31b23ea543", "reflex/components/radix/themes/components/text_field.pyi": "232618b744076db98d861ea1b9eb3192", "reflex/components/radix/themes/components/tooltip.pyi": "2b8366200ce92ec4784ca3ec4152e676", "reflex/components/radix/themes/layout/__init__.pyi": "73eefc509a49215b1797b5b5d28d035e", "reflex/components/radix/themes/layout/base.pyi": "5be31d7dadd23ab544e53762423d123e", "reflex/components/radix/themes/layout/box.pyi": "dbaed1c50c668805fc7b71d22f878254", "reflex/components/radix/themes/layout/center.pyi": "17323694217e8ad7611adb683f8d96ce", "reflex/components/radix/themes/layout/container.pyi": "24222fd7ffa2dc05f709eab6c7b9643c", "reflex/components/radix/themes/layout/flex.pyi": "0307e9dbe6a5784140121d77c8f67a86", "reflex/components/radix/themes/layout/grid.pyi": "95c9edb8bdd4e39dc1bd6bc2a8ca0933", "reflex/components/radix/themes/layout/list.pyi": "049ecf827ef0ba8de2d76dbf7b1c562c", "reflex/components/radix/themes/layout/section.pyi": "a51952b9b5c8227aa3024373dedcad5d", "reflex/components/radix/themes/layout/spacer.pyi": "c35accf0f2f742c90a23675ff1fb960d", "reflex/components/radix/themes/layout/stack.pyi": "271d3315c6196356d3ced759520d4e7d", "reflex/components/radix/themes/typography/__init__.pyi": "b8ef970530397e9984004961f3aaee62", "reflex/components/radix/themes/typography/blockquote.pyi": "080c71899532f5dbf4cf143e7a5ad3bf", "reflex/components/radix/themes/typography/code.pyi": "7ffe785d55979cf8ff97ea040f3e2b64", "reflex/components/radix/themes/typography/heading.pyi": "0ebb38915cd0521fd59c569e04d288bb", "reflex/components/radix/themes/typography/link.pyi": "64878125a37d47d676c9adf8156d8c41", "reflex/components/radix/themes/typography/text.pyi": "50f9ca15a941e4b77ddd12e77aa3c03e", "reflex/components/react_player/audio.pyi": "0e1690ff1f1f39bc748278d292238350", "reflex/components/react_player/react_player.pyi": "5ccd373b94ed1d3934ae6afc46bd6fe4", "reflex/components/react_player/video.pyi": "998671c06103d797c554d9278eb3b2a0", "reflex/components/react_router/dom.pyi": "3042fa630b7e26a7378fe045d7fbf4af", "reflex/components/recharts/__init__.pyi": "6ee7f1ca2c0912f389ba6f3251a74d99", "reflex/components/recharts/cartesian.pyi": "d138261ab8259d5208c2f028b9f708bd", "reflex/components/recharts/charts.pyi": "013036b9c00ad85a570efdb813c1bc40", "reflex/components/recharts/general.pyi": "d87ff9b85b2a204be01753690df4fb11", "reflex/components/recharts/polar.pyi": "b8b1a3e996e066facdf4f8c9eb363137", "reflex/components/recharts/recharts.pyi": "d5c9fc57a03b419748f0408c23319eee", "reflex/components/sonner/toast.pyi": "dca44901640cda9d58c62ff8434faa3e" } ================================================ FILE: pyproject.toml ================================================ [project] name = "reflex" version = "0.9.0dev1" description = "Web apps in pure Python." license.text = "Apache-2.0" authors = [ { name = "Nikhil Rao" }, { name = "Alek Petuskey" }, { name = "Masen Furer" }, { name = "Elijah Ahianyo" }, { name = "Thomas Brandeho" }, { name = "Khaleel Al-Adhami" }, ] maintainers = [ { name = "Masen Furer" }, { name = "Khaleel Al-Adhami" }, { email = "maintainers@reflex.dev" }, ] readme = "README.md" keywords = ["web", "framework"] requires-python = ">=3.10,<4.0" dependencies = [ "alembic >=1.15.2,<2.0", "click >=8.2", "granian[reload] >=2.5.5", "httpx >=0.23.3,<1.0", "packaging >=24.2,<27", "platformdirs >=4.3.7,<5.0", "psutil >=7.0.0,<8.0; sys_platform == 'win32'", "pydantic >=1.10.21,<3.0", "python-multipart >=0.0.20,<1.0", "python-socketio >=5.12.0,<6.0", "redis >=5.2.1,<8.0", "reflex-hosting-cli >=0.1.61", "rich >=13,<15", "sqlmodel >=0.0.27,<0.1", "starlette >=0.47.0", "typing_extensions >=4.13.0", "wrapt >=1.17.0,<3.0", ] classifiers = [ "Development Status :: 4 - Beta", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", ] [project.optional-dependencies] db = [ "alembic >=1.15.2,<2.0", "pydantic >=1.10.21,<3.0", "sqlmodel >=0.0.24,<0.1", ] monitoring = ["pyleak >=0.1.14,<1.0"] [project.urls] homepage = "https://reflex.dev" repository = "https://github.com/reflex-dev/reflex" documentation = "https://reflex.dev/docs/getting-started/introduction" [project.scripts] reflex = "reflex.reflex:cli" [dependency-groups] dev = [ "asynctest", "darglint", "dill", "fastapi", "hatchling", "libsass", "numpy", "pandas", "pillow", "playwright", "plotly", "pre-commit", "psutil", "psycopg[binary]", "pyleak >=0.1.14,<1.0", "pyright", "pytest-asyncio", "pytest-benchmark", "pytest-codspeed", "pytest-cov", "pytest-mock", "pytest-playwright", "pytest-rerunfailures", "pytest-split", "pytest", "python-dotenv", "ruff", "selenium", "starlette-admin", "toml", "uvicorn", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build] include = ["reflex", "scripts/hatch_build.py"] targets.sdist.artifacts = ["*.pyi"] targets.wheel.artifacts = ["*.pyi"] [tool.hatch.build.hooks.custom] path = "scripts/hatch_build.py" dependencies = ["plotly", "ruff", "pre_commit", "toml"] require-runtime-dependencies = true [tool.pyright] reportIncompatibleMethodOverride = false [tool.ruff] target-version = "py310" output-format = "concise" lint.isort.split-on-trailing-comma = false preview = true lint.select = ["ALL"] lint.ignore = [ "A", "ANN002", "ANN003", "ANN2", "ANN4", "ARG", "BLE", "C901", "COM", "CPY001", "D205", "DOC202", "DOC501", "DOC502", "DTZ", "E501", "F403", "FBT", "FIX", "FURB189", "FURB140", "G004", "ISC003", "PLC", "PLR", "PLW", "PT011", "PT012", "PYI", "RUF012", "RUF067", "S", "SLF", "SLOT", "TC", "TD", "TRY0", ] lint.pydocstyle.convention = "google" lint.flake8-bugbear.extend-immutable-calls = [ "reflex.utils.types.Unset", "reflex.vars.base.Var.create", ] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] "tests/*.py" = [ "ANN001", "D100", "D103", "D104", "INP", "B018", "PERF", "T", "N", ] "benchmarks/*.py" = ["ANN001", "D100", "D103", "D104", "B018", "PERF", "T", "N"] "reflex/.templates/*.py" = ["D100", "D103", "D104"] "*.pyi" = ["D301", "D415", "D417", "D418", "E742", "N", "PGH"] "pyi_generator.py" = ["N802"] "reflex/constants/*.py" = ["N"] "reflex/.templates/apps/blank/code/*" = ["INP001"] "*/blank.py" = ["I001"] [tool.pytest.ini_options] filterwarnings = "ignore:fields may not start with an underscore:RuntimeWarning" asyncio_default_fixture_loop_scope = "function" asyncio_mode = "auto" [tool.codespell] skip = "docs/*,*.html,examples/*, *.pyi, poetry.lock, uv.lock" ignore-words-list = "te, TreeE" [tool.coverage.run] source = ["reflex"] branch = true omit = [ "*/pyi_generator.py", "reflex/__main__.py", "reflex/app_module_for_backend.py", "reflex/components/chakra/*", "reflex/experimental/*", ] [tool.coverage.report] show_missing = true # TODO bump back to 79 fail_under = 70 precision = 2 ignore_errors = true exclude_also = [ "def __repr__", # Don't complain about missing debug-only code: "if self.debug", # Don't complain if tests don't hit defensive assertion code: "raise AssertionError", "raise NotImplementedError", # Regexes for lines to exclude from consideration "if 0:", # Don't complain if non-runnable code isn't run: "if __name__ == .__main__.:", # Don't complain about abstract methods, they aren't run: "@(abc.)?abstractmethod", # Don't complain about overloaded methods: "@overload", ] [tool.coverage.html] directory = "coverage_html_report" [tool.pre-commit] fail_fast = true [[tool.pre-commit.repos]] repo = "https://github.com/astral-sh/ruff-pre-commit" rev = "v0.15.6" hooks = [ { id = "ruff-format", args = [ "reflex", "tests", ] }, { id = "ruff-check", args = [ "--fix", "--exit-non-zero-on-fix", ], exclude = "^integration/benchmarks/" }, ] [[tool.pre-commit.repos]] repo = "https://github.com/codespell-project/codespell" rev = "v2.4.1" hooks = [ { id = "codespell", args = [ "reflex", ], additional_dependencies = [ "tomli", ] }, ] # Run pyi check before pyright because pyright can fail if pyi files are wrong. [[tool.pre-commit.repos]] repo = "local" hooks = [ { id = "update-pyi-files", name = "update-pyi-files", always_run = true, language = "system", require_serial = true, description = "Update pyi files as needed", entry = "python3 scripts/make_pyi.py" }, ] [[tool.pre-commit.repos]] repo = "https://github.com/RobertCraigie/pyright-python" rev = "v1.1.408" hooks = [{ id = "pyright", args = ["reflex", "tests"], language = "system" }] [[tool.pre-commit.repos]] repo = "https://github.com/pre-commit/mirrors-prettier" rev = "f62a70a3a7114896b062de517d72829ea1c884b6" hooks = [{ id = "prettier", require_serial = true }] [tool.uv] required-version = ">=0.7.0" ================================================ FILE: reflex/.templates/apps/blank/code/__init__.py ================================================ ================================================ FILE: reflex/.templates/apps/blank/code/blank.py ================================================ """Welcome to Reflex! This file outlines the steps to create a basic app.""" import reflex as rx from rxconfig import config class State(rx.State): """The app state.""" def index() -> rx.Component: # Welcome Page (Index) return rx.container( rx.color_mode.button(position="top-right"), rx.vstack( rx.heading("Welcome to Reflex!", size="9"), rx.text( "Get started by editing ", rx.code(f"{config.app_name}/{config.app_name}.py"), size="5", ), rx.link( rx.button("Check out our docs!"), href="https://reflex.dev/docs/getting-started/introduction/", is_external=True, ), spacing="5", justify="center", min_height="85vh", ), ) app = rx.App() app.add_page(index) ================================================ FILE: reflex/.templates/web/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. /_static # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env.local .env.development.local .env.test.local .env.production.local # vercel .vercel # DS_Store .DS_Store ================================================ FILE: reflex/.templates/web/app/entry.client.js ================================================ import { startTransition } from "react"; import { hydrateRoot } from "react-dom/client"; import { HydratedRouter } from "react-router/dom"; import { createElement } from "react"; startTransition(() => { hydrateRoot(document, createElement(HydratedRouter)); }); ================================================ FILE: reflex/.templates/web/app/routes.js ================================================ import { route } from "@react-router/dev/routes"; import { flatRoutes } from "@react-router/fs-routes"; export default [ ...(await flatRoutes({ ignoredRouteFiles: ["routes/\\[404\\]._index.jsx"], })), route("*", "routes/[404]._index.jsx"), ]; ================================================ FILE: reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js ================================================ import { useTheme } from "$/utils/react-theme"; import { createElement, useEffect } from "react"; import { ColorModeContext, defaultColorMode } from "$/utils/context"; export default function RadixThemesColorModeProvider({ children }) { const { theme, resolvedTheme, setTheme } = useTheme(); const toggleColorMode = () => { setTheme(resolvedTheme === "light" ? "dark" : "light"); }; const setColorMode = (mode) => { const allowedModes = ["light", "dark", "system"]; if (!allowedModes.includes(mode)) { console.error( `Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`, ); mode = defaultColorMode; } setTheme(mode); }; useEffect(() => { const radixRoot = document.querySelector( '.radix-themes[data-is-root-theme="true"]', ); if (radixRoot) { radixRoot.classList.remove("light", "dark"); radixRoot.classList.add(resolvedTheme); } }, [resolvedTheme]); return createElement( ColorModeContext.Provider, { value: { rawColorMode: theme, resolvedColorMode: resolvedTheme, toggleColorMode, setColorMode, }, }, children, ); } ================================================ FILE: reflex/.templates/web/components/shiki/code.js ================================================ import { useEffect, useState, createElement } from "react"; import { codeToHtml } from "shiki"; /** * Code component that uses Shiki to convert code to HTML and render it. * * @param code - The code to be highlighted. * @param theme - The theme to be used for highlighting. * @param language - The language of the code. * @param transformers - The transformers to be applied to the code. * @param decorations - The decorations to be applied to the code. * @param divProps - Additional properties to be passed to the div element. * @returns The rendered code block. */ export function Code({ code, theme, language, transformers, decorations, ...divProps }) { const [codeResult, setCodeResult] = useState(""); useEffect(() => { async function fetchCode() { const result = await codeToHtml(code, { lang: language, theme, transformers, decorations, }); setCodeResult(result); } fetchCode(); }, [code, language, theme, transformers, decorations]); return createElement("div", { dangerouslySetInnerHTML: { __html: codeResult }, ...divProps, }); } ================================================ FILE: reflex/.templates/web/jsconfig.json ================================================ { "compilerOptions": { "baseUrl": ".", "paths": { "$/*": ["*"], "@/*": ["public/*"] } } } ================================================ FILE: reflex/.templates/web/postcss.config.js ================================================ export default { plugins: { "postcss-import": {}, autoprefixer: {}, }, }; ================================================ FILE: reflex/.templates/web/react-router.config.js ================================================ export default { future: { unstable_optimizeDeps: true, }, ssr: false, }; ================================================ FILE: reflex/.templates/web/styles/__reflex_style_reset.css ================================================ @layer __reflex_base { /* 1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) 2. Remove default margins and padding 3. Reset all borders. */ *, ::after, ::before, ::backdrop, ::file-selector-button { box-sizing: border-box; /* 1 */ margin: 0; /* 2 */ padding: 0; /* 2 */ border: 0 solid; /* 3 */ } /* 1. Use a consistent sensible line-height in all browsers. 2. Prevent adjustments of font size after orientation changes in iOS. 3. Use a more readable tab size. 4. Use the user's configured `sans` font-family by default. 5. Use the user's configured `sans` font-feature-settings by default. 6. Use the user's configured `sans` font-variation-settings by default. 7. Disable tap highlights on iOS. */ html, :host { line-height: 1.5; /* 1 */ -webkit-text-size-adjust: 100%; /* 2 */ tab-size: 4; /* 3 */ font-family: --default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */ font-feature-settings: --default-font-feature-settings, normal; /* 5 */ font-variation-settings: --default-font-variation-settings, normal; /* 6 */ -webkit-tap-highlight-color: transparent; /* 7 */ } /* 1. Add the correct height in Firefox. 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 3. Reset the default border style to a 1px solid border. */ hr { height: 0; /* 1 */ color: inherit; /* 2 */ border-top-width: 1px; /* 3 */ } /* Add the correct text decoration in Chrome, Edge, and Safari. */ abbr:where([title]) { -webkit-text-decoration: underline dotted; text-decoration: underline dotted; } /* Remove the default font size and weight for headings. */ h1, h2, h3, h4, h5, h6 { font-size: inherit; font-weight: inherit; } /* Reset links to optimize for opt-in styling instead of opt-out. */ a { color: inherit; -webkit-text-decoration: inherit; text-decoration: inherit; } /* Add the correct font weight in Edge and Safari. */ b, strong { font-weight: bolder; } /* 1. Use the user's configured `mono` font-family by default. 2. Use the user's configured `mono` font-feature-settings by default. 3. Use the user's configured `mono` font-variation-settings by default. 4. Correct the odd `em` font sizing in all browsers. */ code, kbd, samp, pre { font-family: --default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */ font-feature-settings: --default-mono-font-feature-settings, normal; /* 2 */ font-variation-settings: --default-mono-font-variation-settings, normal; /* 3 */ font-size: 1em; /* 4 */ } /* Add the correct font size in all browsers. */ small { font-size: 80%; } /* Prevent `sub` and `sup` elements from affecting the line height in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } /* 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 3. Remove gaps between table borders by default. */ table { text-indent: 0; /* 1 */ border-color: inherit; /* 2 */ border-collapse: collapse; /* 3 */ } /* Use the modern Firefox focus style for all focusable elements. */ :-moz-focusring { outline: auto; } /* Add the correct vertical alignment in Chrome and Firefox. */ progress { vertical-align: baseline; } /* Add the correct display in Chrome and Safari. */ summary { display: list-item; } /* Make lists unstyled by default. */ ol, ul, menu { list-style: none; } /* 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) This can trigger a poorly considered lint error in some tools but is included by design. */ img, svg, video, canvas, audio, iframe, embed, object { display: block; /* 1 */ vertical-align: middle; /* 2 */ } /* Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) */ img, video { max-width: 100%; height: auto; } /* 1. Inherit font styles in all browsers. 2. Remove border radius in all browsers. 3. Remove background color in all browsers. 4. Ensure consistent opacity for disabled states in all browsers. */ button, input, select, optgroup, textarea, ::file-selector-button { font: inherit; /* 1 */ font-feature-settings: inherit; /* 1 */ font-variation-settings: inherit; /* 1 */ letter-spacing: inherit; /* 1 */ color: inherit; /* 1 */ border-radius: 0; /* 2 */ background-color: transparent; /* 3 */ opacity: 1; /* 4 */ } /* Restore default font weight. */ :where(select:is([multiple], [size])) optgroup { font-weight: bolder; } /* Restore indentation. */ :where(select:is([multiple], [size])) optgroup option { padding-inline-start: 20px; } /* Restore space after button. */ ::file-selector-button { margin-inline-end: 4px; } /* Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) */ ::placeholder { opacity: 1; } /* Set the default placeholder color to a semi-transparent version of the current text color in browsers that do not crash when using `color-mix(…)` with `currentcolor`. (https://github.com/tailwindlabs/tailwindcss/issues/17194) */ @supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or (contain-intrinsic-size: 1px) /* Safari 17+ */ { ::placeholder { color: color-mix(in oklab, currentcolor 50%, transparent); } } /* Prevent resizing textareas horizontally by default. */ textarea { resize: vertical; } /* Remove the inner padding in Chrome and Safari on macOS. */ ::-webkit-search-decoration { -webkit-appearance: none; } /* 1. Ensure date/time inputs have the same height when empty in iOS Safari. 2. Ensure text alignment can be changed on date/time inputs in iOS Safari. */ ::-webkit-date-and-time-value { min-height: 1lh; /* 1 */ text-align: inherit; /* 2 */ } /* Prevent height from changing on date/time inputs in macOS Safari when the input is set to `display: block`. */ ::-webkit-datetime-edit { display: inline-flex; } /* Remove excess padding from pseudo-elements in date/time inputs to ensure consistent height across browsers. */ ::-webkit-datetime-edit-fields-wrapper { padding: 0; } ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { padding-block: 0; } /* Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) */ :-moz-ui-invalid { box-shadow: none; } /* Correct the inability to style the border radius in iOS Safari. */ button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { appearance: button; } /* Correct the cursor style of increment and decrement buttons in Safari. */ ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { height: auto; } /* Make elements with the HTML hidden attribute stay hidden by default. */ [hidden]:where(:not([hidden="until-found"])) { display: none !important; } } ================================================ FILE: reflex/.templates/web/utils/helpers/dataeditor.js ================================================ import { GridCellKind } from "@glideapps/glide-data-grid"; export function getDEColumn(columns, col) { let c = columns[col]; c.pos = col; return c; } export function getDERow(data, row) { return data[row]; } export function locateCell(row, column) { if (Array.isArray(row)) { return row[column.pos]; } else { return row[column.id]; } } export function formatCell(value, column) { const editable = column.editable ?? true; switch (column.type) { case "int": case "float": return { kind: GridCellKind.Number, data: value, displayData: value + "", readonly: !editable, allowOverlay: editable, }; case "datetime": // value = moment format? case "str": return { kind: GridCellKind.Text, data: value, displayData: value, readonly: !editable, allowOverlay: editable, }; case "bool": return { kind: GridCellKind.Boolean, data: value, readonly: !editable, }; default: console.log( "Warning: column.type is undefined for column.title=" + column.title, ); return { kind: GridCellKind.Text, data: value, displayData: column.type, }; } } export function formatDataEditorCells(col, row, columns, data) { if (row < data.length && col < columns.length) { const column = getDEColumn(columns, col); const rowData = getDERow(data, row); const cellData = locateCell(rowData, column); return formatCell(cellData, column); } return { kind: GridCellKind.Loading }; } ================================================ FILE: reflex/.templates/web/utils/helpers/debounce.js ================================================ const debounce_timeout_id = {}; /** * Generic debounce helper * * @param {string} name - the name of the event to debounce * @param {function} func - the function to call after debouncing * @param {number} delay - the time in milliseconds to wait before calling the function */ export default function debounce(name, func, delay) { const key = `${name}__${delay}`; clearTimeout(debounce_timeout_id[key]); debounce_timeout_id[key] = setTimeout(() => { func(); delete debounce_timeout_id[key]; }, delay); } ================================================ FILE: reflex/.templates/web/utils/helpers/paste.js ================================================ import { useEffect, useRef } from "react"; const handle_paste_data = (clipboardData) => new Promise((resolve, reject) => { const pasted_data = []; const n_items = clipboardData.items.length; const extract_data = (item) => { const type = item.type; if (item.kind === "string") { item.getAsString((data) => { pasted_data.push([type, data]); if (pasted_data.length === n_items) { resolve(pasted_data); } }); } else if (item.kind === "file") { const file = item.getAsFile(); const reader = new FileReader(); reader.onload = (e) => { pasted_data.push([type, e.target.result]); if (pasted_data.length === n_items) { resolve(pasted_data); } }; if (type.indexOf("text/") === 0) { reader.readAsText(file); } else { reader.readAsDataURL(file); } } }; for (const item of clipboardData.items) { extract_data(item); } }); export default function usePasteHandler(target_ids, event_actions, on_paste) { const onPasteRef = useRef(on_paste); const eventActionsRef = useRef(event_actions); useEffect(() => { onPasteRef.current = on_paste; }, [on_paste]); useEffect(() => { eventActionsRef.current = event_actions; }, [event_actions]); useEffect(() => { const handle_paste = (_ev) => { eventActionsRef.current?.preventDefault && _ev.preventDefault(); eventActionsRef.current?.stopPropagation && _ev.stopPropagation(); handle_paste_data(_ev.clipboardData).then(onPasteRef.current); }; let cleanupListeners = null; let observer = null; const attachListeners = (targets) => { targets.forEach((target) => target.addEventListener("paste", handle_paste, false), ); return () => { targets.forEach((target) => target.removeEventListener("paste", handle_paste, false), ); }; }; const tryAttach = () => { if (target_ids.length === 0) { cleanupListeners = attachListeners([document]); return true; } const targets = target_ids .map((id) => document.getElementById(id)) .filter((element) => !!element); if (targets.length === target_ids.length) { cleanupListeners = attachListeners(targets); return true; } return false; }; if (!tryAttach()) { observer = new MutationObserver(() => { if (tryAttach()) { observer.disconnect(); observer = null; } }); observer.observe(document.body, { childList: true, subtree: true }); } return () => { if (observer) { observer.disconnect(); } if (cleanupListeners) { cleanupListeners(); } }; }, [target_ids]); } ================================================ FILE: reflex/.templates/web/utils/helpers/range.js ================================================ /** * Simulate the python range() builtin function. * inspired by https://dev.to/guyariely/using-python-range-in-javascript-337p * * If needed outside of an iterator context, use `Array.from(range(10))` or * spread syntax `[...range(10)]` to get an array. * * @param {number} start: the start or end of the range. * @param {number} stop: the end of the range. * @param {number} step: the step of the range. * @returns {object} an object with a Symbol.iterator method over the range */ export default function range(start, stop, step) { return { [Symbol.iterator]() { if (stop === undefined) { stop = start; start = 0; } if (step === undefined) { step = 1; } let i = start - step; return { next() { i += step; if ((step > 0 && i < stop) || (step < 0 && i > stop)) { return { value: i, done: false, }; } return { value: undefined, done: true, }; }, }; }, }; } ================================================ FILE: reflex/.templates/web/utils/helpers/throttle.js ================================================ const in_throttle = {}; /** * Generic throttle helper * * @param {string} name - the name of the event to throttle * @param {number} limit - time in milliseconds between events * @returns true if the event is allowed to execute, false if it is throttled */ export default function throttle(name, limit) { const key = `${name}__${limit}`; if (!in_throttle[key]) { in_throttle[key] = true; setTimeout(() => { delete in_throttle[key]; }, limit); // function was not throttled, so allow execution return true; } return false; } ================================================ FILE: reflex/.templates/web/utils/helpers/upload.js ================================================ import JSON5 from "json5"; import env from "$/env.json"; /** * Upload files to the server. * * @param state The state to apply the delta to. * @param handler The handler to use. * @param upload_id The upload id to use. * @param on_upload_progress The function to call on upload progress. * @param socket the websocket connection * @param extra_headers Extra headers to send with the request. * @param refs The refs object to store the abort controller in. * @param getBackendURL Function to get the backend URL. * @param getToken Function to get the Reflex token. * * @returns The response from posting to the UPLOADURL endpoint. */ export const uploadFiles = async ( handler, files, upload_id, on_upload_progress, extra_headers, socket, refs, getBackendURL, getToken, ) => { // return if there's no file to upload if (files === undefined || files.length === 0) { return false; } const upload_ref_name = `__upload_controllers_${upload_id}`; if (refs[upload_ref_name]) { console.log("Upload already in progress for ", upload_id); return false; } // Track how many partial updates have been processed for this upload. let resp_idx = 0; const eventHandler = (progressEvent) => { const event_callbacks = socket._callbacks.$event; // Whenever called, responseText will contain the entire response so far. const chunks = progressEvent.event.target.responseText.trim().split("\n"); // So only process _new_ chunks beyond resp_idx. chunks.slice(resp_idx).map((chunk_json) => { try { const chunk = JSON5.parse(chunk_json); event_callbacks.map((f, ix) => { f(chunk) .then(() => { if (ix === event_callbacks.length - 1) { // Mark this chunk as processed. resp_idx += 1; } }) .catch((e) => { if (progressEvent.progress === 1) { // Chunk may be incomplete, so only report errors when full response is available. console.log("Error processing chunk", chunk, e); } return; }); }); } catch (e) { if (progressEvent.progress === 1) { console.log("Error parsing chunk", chunk_json, e); } return; } }); }; const controller = new AbortController(); const formdata = new FormData(); // Add the token and handler to the file name. files.forEach((file) => { formdata.append("files", file, file.path || file.name); }); // Send the file to the server. refs[upload_ref_name] = controller; return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); // Set up event handlers xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) { resolve({ data: xhr.responseText, status: xhr.status, statusText: xhr.statusText, headers: { get: (name) => xhr.getResponseHeader(name), }, }); } else { reject(new Error(`HTTP error! status: ${xhr.status}`)); } }; xhr.onerror = function () { reject(new Error("Network error")); }; xhr.onabort = function () { reject(new Error("Upload aborted")); }; // Handle upload progress if (on_upload_progress) { xhr.upload.onprogress = function (event) { if (event.lengthComputable) { const progressEvent = { loaded: event.loaded, total: event.total, progress: event.loaded / event.total, }; on_upload_progress(progressEvent); } }; } // Handle download progress with streaming response parsing xhr.onprogress = function (event) { if (eventHandler) { const progressEvent = { event: { target: { responseText: xhr.responseText, }, }, progress: event.lengthComputable ? event.loaded / event.total : 0, }; eventHandler(progressEvent); } }; // Handle abort controller controller.signal.addEventListener("abort", () => { xhr.abort(); }); // Configure and send request xhr.open("POST", getBackendURL(env.UPLOAD)); xhr.setRequestHeader("Reflex-Client-Token", getToken()); xhr.setRequestHeader("Reflex-Event-Handler", handler); for (const [key, value] of Object.entries(extra_headers || {})) { xhr.setRequestHeader(key, value); } try { xhr.send(formdata); } catch (error) { reject(error); } }) .catch((error) => { console.log("Upload error:", error.message); return false; }) .finally(() => { delete refs[upload_ref_name]; }); }; ================================================ FILE: reflex/.templates/web/utils/react-theme.js ================================================ import { createContext, useContext, useState, useEffect, createElement, useRef, useMemo, } from "react"; import { isDevMode, defaultColorMode } from "$/utils/context"; const ThemeContext = createContext({ theme: defaultColorMode, resolvedTheme: defaultColorMode !== "system" ? defaultColorMode : "light", setTheme: () => {}, }); export function ThemeProvider({ children, defaultTheme = "system" }) { const [theme, setTheme] = useState(defaultTheme); const [systemTheme, setSystemTheme] = useState( defaultTheme !== "system" ? defaultTheme : "light", ); const [isInitialized, setIsInitialized] = useState(false); const firstRender = useRef(true); useEffect(() => { if (!firstRender.current) { return; } firstRender.current = false; if (isDevMode) { const lastCompiledTheme = localStorage.getItem("last_compiled_theme"); if (lastCompiledTheme !== defaultColorMode) { // on app startup, make sure the application color mode is persisted correctly. localStorage.setItem("last_compiled_theme", defaultColorMode); setIsInitialized(true); return; } } // Load saved theme from localStorage const savedTheme = localStorage.getItem("theme") || defaultTheme; setTheme(savedTheme); setIsInitialized(true); }); const resolvedTheme = useMemo( () => (theme === "system" ? systemTheme : theme), [theme, systemTheme], ); useEffect(() => { // Set up media query for system preference detection const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); // Listen for system preference changes const handleChange = () => { setSystemTheme(mediaQuery.matches ? "dark" : "light"); }; handleChange(); mediaQuery.addEventListener("change", handleChange); return () => { mediaQuery.removeEventListener("change", handleChange); }; }); // Save theme to localStorage whenever it changes // Skip saving only if theme key already exists and we haven't initialized yet useEffect(() => { const existingTheme = localStorage.getItem("theme"); if (!isInitialized && existingTheme !== null) return; localStorage.setItem("theme", theme); }, [theme]); useEffect(() => { if (!isInitialized) return; const root = window.document.documentElement; root.classList.remove("light", "dark"); root.classList.add(resolvedTheme); root.style.colorScheme = resolvedTheme; }, [resolvedTheme, isInitialized]); return createElement( ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme } }, children, ); } export function useTheme() { return useContext(ThemeContext); } ================================================ FILE: reflex/.templates/web/utils/state.js ================================================ // State management for Reflex web apps. import io from "socket.io-client"; import JSON5 from "json5"; import env from "$/env.json"; import reflexEnvironment from "$/reflex.json"; import Cookies from "universal-cookie"; import { useCallback, useEffect, useRef, useState } from "react"; import { useLocation, useNavigate, useSearchParams, useParams, } from "react-router"; import { initialEvents, initialState, onLoadInternalEvent, state_name, exception_state_name, } from "$/utils/context"; import debounce from "$/utils/helpers/debounce"; import throttle from "$/utils/helpers/throttle"; import { uploadFiles } from "$/utils/helpers/upload"; // Endpoint URLs. const EVENTURL = env.EVENT; // These hostnames indicate that the backend and frontend are reachable via the same domain. const SAME_DOMAIN_HOSTNAMES = ["localhost", "0.0.0.0", "::", "0:0:0:0:0:0:0:0"]; // Global variable to hold the token. let token; // Key for the token in the session storage. const TOKEN_KEY = "token"; // create cookie instance const cookies = new Cookies(); // Dictionary holding component references. export const refs = {}; // Flag ensures that only one event is processing on the backend concurrently. let event_processing = false; // Array holding pending events to be processed. const event_queue = []; /** * Generate a UUID (Used for session tokens). * Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid * @returns A UUID. */ export const generateUUID = () => { let d = new Date().getTime(), d2 = (performance && performance.now && performance.now() * 1000) || 0; return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { let r = Math.random() * 16; if (d > 0) { r = ((d + r) % 16) | 0; d = Math.floor(d / 16); } else { r = ((d2 + r) % 16) | 0; d2 = Math.floor(d2 / 16); } return (c == "x" ? r : (r & 0x7) | 0x8).toString(16); }); }; /** * Get the token for the current session. * @returns The token. */ export const getToken = () => { if (token) { return token; } if (typeof window !== "undefined") { if (!window.sessionStorage.getItem(TOKEN_KEY)) { window.sessionStorage.setItem(TOKEN_KEY, generateUUID()); } token = window.sessionStorage.getItem(TOKEN_KEY); } return token; }; /** * Get the URL for the backend server * @param url_str The URL string to parse. * @returns The given URL modified to point to the actual backend server. */ export const getBackendURL = (url_str) => { if ((url_str ?? undefined) === undefined) { url_str = env.PING; } // Get backend URL object from the endpoint. const endpoint = new URL(url_str); if ( typeof window !== "undefined" && SAME_DOMAIN_HOSTNAMES.includes(endpoint.hostname) ) { // Use the frontend domain to access the backend const frontend_hostname = window.location.hostname; endpoint.hostname = frontend_hostname; if (window.location.protocol === "https:") { if (endpoint.protocol === "ws:") { endpoint.protocol = "wss:"; } else if (endpoint.protocol === "http:") { endpoint.protocol = "https:"; } endpoint.port = ""; // Assume websocket is on https port via load balancer. } } return endpoint; }; /** * Check if the backend is disabled. * * @returns True if the backend is disabled, false otherwise. */ export const isBackendDisabled = () => { const cookie = document.cookie .split("; ") .find((row) => row.startsWith("backend-enabled=")); return cookie !== undefined && cookie.split("=")[1] == "false"; }; /** * Determine if any event in the event queue is stateful. * * @returns True if there's any event that requires state and False if none of them do. */ export const isStateful = () => { if (event_queue.length === 0) { return false; } return event_queue.some((event) => event.name.startsWith("reflex___state")); }; /** * Apply a delta to the state. * @param state The state to apply the delta to. * @param delta The delta to apply. */ export const applyDelta = (state, delta) => { return { ...state, ...delta }; }; /** * Evaluate a dynamic component. * @param component The component to evaluate. * @returns The evaluated component. */ export const evalReactComponent = async (component) => { if (!window.React && window.__reflex) { window.React = window.__reflex.react; } const encodedJs = encodeURIComponent(component); const dataUri = "data:text/javascript;charset=utf-8," + encodedJs; const module = await eval(`import(dataUri)`); return module.default; }; /** * Only Queue and process events when websocket connection exists. * @param event The event to queue. * @param socket The socket object to send the event on. * @param navigate The navigate function from React Router * @param params The params object from React Router * * @returns Adds event to queue and processes it if websocket exits, does nothing otherwise. */ export const queueEventIfSocketExists = async ( events, socket, navigate, params, ) => { if (!socket) { return; } await queueEvents(events, socket, false, navigate, params); }; /** * Check if a string is a valid HTTP URL. * @param string The string to check. * * @returns The URL object if valid, undefined otherwise. * */ function urlFrom(string) { try { return new URL(string); } catch { return undefined; } return undefined; } /** * Handle frontend event or send the event to the backend via Websocket. * @param event The event to send. * @param socket The socket object to send the event on. * @param navigate The navigate function from useNavigate * @param params The params object from useParams * * @returns True if the event was sent, false if it was handled locally. */ export const applyEvent = async (event, socket, navigate, params) => { // Handle special events if (event.name == "_redirect") { if ((event.payload.path ?? undefined) === undefined) { return false; } if (event.payload.external) { window.open( event.payload.path, "_blank", "noopener" + (event.payload.popup ? ",popup" : ""), ); return false; } const url = urlFrom(event.payload.path); let pathname = event.payload.path; if (url) { if (url.host !== window.location.host) { // External URL window.location.assign(event.payload.path); return false; } else { pathname = url.pathname + url.search + url.hash; } } if (event.payload.replace) { navigate(pathname, { replace: true }); } else { navigate(pathname); } return false; } if (event.name == "_remove_cookie") { cookies.remove(event.payload.key, { ...event.payload.options }); queueEventIfSocketExists(initialEvents(), socket, navigate, params); return false; } if (event.name == "_clear_local_storage") { localStorage.clear(); queueEventIfSocketExists(initialEvents(), socket, navigate, params); return false; } if (event.name == "_remove_local_storage") { localStorage.removeItem(event.payload.key); queueEventIfSocketExists(initialEvents(), socket, navigate, params); return false; } if (event.name == "_clear_session_storage") { sessionStorage.clear(); queueEventIfSocketExists(initialEvents(), socket, navigate, params); return false; } if (event.name == "_remove_session_storage") { sessionStorage.removeItem(event.payload.key); queueEventIfSocketExists(initialEvents(), socket, navigate, params); return false; } if (event.name == "_download") { const a = document.createElement("a"); a.hidden = true; a.href = event.payload.url; // Special case when linking to uploaded files if (a.href.includes("getBackendURL(env.UPLOAD)")) { a.href = eval?.( event.payload.url.replace( "getBackendURL(env.UPLOAD)", `"${getBackendURL(env.UPLOAD)}"`, ), ); } a.download = event.payload.filename; a.click(); a.remove(); return false; } if (event.name == "_set_focus") { const ref = event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref; const current = ref?.current; if (current === undefined || current?.focus === undefined) { console.error( `No element found for ref ${event.payload.ref} in _set_focus`, ); } else { current.focus(); } return false; } if (event.name == "_blur_focus") { const ref = event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref; const current = ref?.current; if (current === undefined || current?.blur === undefined) { console.error( `No element found for ref ${event.payload.ref} in _blur_focus`, ); } else { current.blur(); } return false; } if (event.name == "_set_value") { const ref = event.payload.ref in refs ? refs[event.payload.ref] : event.payload.ref; if (ref.current) { ref.current.value = event.payload.value; } return false; } if ( event.name == "_call_function" && typeof event.payload.function !== "string" ) { try { const eval_result = event.payload.function(); if (event.payload.callback) { const final_result = !!eval_result && typeof eval_result.then === "function" ? await eval_result : eval_result; const callback = typeof event.payload.callback === "string" ? eval(event.payload.callback) : event.payload.callback; callback(final_result); } } catch (e) { console.log("_call_function", e); if (window && window?.onerror) { window.onerror(e.message, null, null, null, e); } } return false; } if (event.name == "_call_script" || event.name == "_call_function") { try { const eval_result = event.name == "_call_script" ? eval(event.payload.javascript_code) : eval(event.payload.function)(); if (event.payload.callback) { const final_result = !!eval_result && typeof eval_result.then === "function" ? await eval_result : eval_result; const callback = typeof event.payload.callback === "string" ? eval(event.payload.callback) : event.payload.callback; callback(final_result); } } catch (e) { console.log("_call_script", e); if (window && window?.onerror) { window.onerror(e.message, null, null, null, e); } } return false; } // Update token and router data (if missing). event.token = getToken(); if ( event.router_data === undefined || Object.keys(event.router_data).length === 0 ) { // Since we don't have router directly, we need to get info from our hooks event.router_data = { pathname: window.location.pathname, query: { ...Object.fromEntries(new URLSearchParams(window.location.search)), ...params(), }, asPath: window.location.pathname + window.location.search + window.location.hash, }; } // Send the event to the server. if (socket) { socket.emit("event", event); return true; } return false; }; /** * Send an event to the server via REST. * @param event The current event. * @param socket The socket object to send the response event(s) on. * @param navigate The navigate function from React Router * @param params The params object from React Router * * @returns Whether the event was sent. */ export const applyRestEvent = async (event, socket, navigate, params) => { let eventSent = false; if (event.handler === "uploadFiles") { if (event.payload.files === undefined || event.payload.files.length === 0) { // Submit the event over the websocket to trigger the event handler. return await applyEvent( ReflexEvent(event.name, { files: [] }), socket, navigate, params, ); } // Start upload, but do not wait for it, which would block other events. uploadFiles( event.name, event.payload.files, event.payload.upload_id, event.payload.on_upload_progress, event.payload.extra_headers, socket, refs, getBackendURL, getToken, ); return false; } return eventSent; }; /** * Resolve a socket reference to the actual socket object. * Handles both ref objects ({ current: Socket }) and raw sockets. * @param socket Either a ref object or raw socket. * @returns The actual socket object. */ const resolveSocket = (socket) => { return socket?.current ?? socket; }; /** * Queue events to be processed and trigger processing of queue. * @param events Array of events to queue. * @param socket The socket object to send the event on. * @param prepend Whether to place the events at the beginning of the queue. * @param navigate The navigate function from React Router * @param params The params object from React Router */ export const queueEvents = async ( events, socket, prepend, navigate, params, ) => { if (prepend) { // Drain the existing queue and place it after the given events. events = [ ...events, ...Array.from({ length: event_queue.length }).map(() => event_queue.shift(), ), ]; } event_queue.push(...events.filter((e) => e !== undefined && e !== null)); await processEvent(resolveSocket(socket), navigate, params); }; /** * Process an event off the event queue. * @param socket The socket object to send the event on. * @param navigate The navigate function from React Router * @param params The params object from React Router */ export const processEvent = async (socket, navigate, params) => { // Only proceed if the socket is up or no event in the queue uses state, otherwise we throw the event into the void if (isStateful() && !(socket && socket.connected)) { return; } // Only proceed if we're not already processing an event. if (event_queue.length === 0 || event_processing) { return; } // Set processing to true to block other events from being processed. event_processing = true; // Apply the next event in the queue. const event = event_queue.shift(); let eventSent = false; // Process events with handlers via REST and all others via websockets. if (event.handler) { eventSent = await applyRestEvent(event, socket, navigate, params); } else { eventSent = await applyEvent(event, socket, navigate, params); } // If no event was sent, set processing to false. if (!eventSent) { event_processing = false; // recursively call processEvent to drain the queue, since there is // no state update to trigger the useEffect event loop. await processEvent(socket, navigate, params); } }; /** * Connect to a websocket and set the handlers. * @param socket The socket object to connect. * @param dispatch The function to queue state update * @param transports The transports to use. * @param setConnectErrors The function to update connection error value. * @param client_storage The client storage object from context.js * @param navigate The navigate function from React Router * @param params The params object from React Router */ export const connect = async ( socket, dispatch, transports, setConnectErrors, client_storage = {}, navigate, params, ) => { // Socket already allocated, just reconnect it if needed. if (socket.current) { if (!socket.current.connected) { socket.current.reconnect(); } return; } // Get backend URL object from the endpoint. const endpoint = getBackendURL(EVENTURL); const on_hydrated_queue = []; // Create the socket. socket.current = io(endpoint.href, { path: endpoint["pathname"], transports: transports, protocols: [reflexEnvironment.version], autoUnref: false, query: { token: getToken() }, reconnection: false, // Reconnection will be handled manually. }); socket.current.wait_connect = !socket.current.connected; // Ensure undefined fields in events are sent as null instead of removed socket.current.io.encoder.replacer = (k, v) => (v === undefined ? null : v); socket.current.io.decoder.tryParse = (str) => { try { return JSON5.parse(str); } catch (e) { return false; } }; // Set up a reconnect helper function socket.current.reconnect = () => { if ( socket.current && !socket.current.connected && !socket.current.wait_connect ) { socket.current.wait_connect = true; socket.current.rehydrate = true; socket.current.io.opts.query = { token: getToken() }; // Update token for reconnect. socket.current.connect(); } }; function checkVisibility() { if (document.visibilityState === "visible") { if (!socket.current) { connect( socket, dispatch, transports, setConnectErrors, client_storage, navigate, params, ); } else if (!socket.current.connected) { console.log("Socket is disconnected, attempting to reconnect "); socket.current.reconnect(); } else { console.log("Socket is reconnected "); } } } const disconnectTrigger = (event) => { if (socket.current?.connected) { console.log("Disconnect websocket on unload"); socket.current.disconnect(); } }; const pagehideHandler = (event) => { if (event.persisted && socket.current?.connected) { console.log("Disconnect backend before bfcache on navigation"); socket.current.disconnect(); } }; // Once the socket is open, hydrate the page. socket.current.on("connect", async () => { socket.current.wait_connect = false; setConnectErrors([]); window.addEventListener("pagehide", pagehideHandler); window.addEventListener("beforeunload", disconnectTrigger); window.addEventListener("unload", disconnectTrigger); if (socket.current.rehydrate) { socket.current.rehydrate = false; queueEvents( initialEvents(), socket, true, navigate, () => params.current, ); } // Drain any initial events from the queue. while (event_queue.length > 0 && !event_processing) { await processEvent(socket.current, navigate, () => params.current); } }); socket.current.on("connect_error", (error) => { socket.current.wait_connect = false; let n_connect_errors = 0; setConnectErrors((connectErrors) => { const new_errors = [...connectErrors.slice(-9), error]; n_connect_errors = new_errors.length; return new_errors; }); window.setTimeout(() => { if (socket.current && !socket.current.connected) { socket.current.reconnect(); } }, 200 * n_connect_errors); // Incremental backoff }); // When the socket disconnects reset the event_processing flag socket.current.on("disconnect", (reason, details) => { socket.current.wait_connect = false; const try_reconnect = reason !== "io server disconnect" && reason !== "io client disconnect"; event_processing = false; window.removeEventListener("unload", disconnectTrigger); window.removeEventListener("beforeunload", disconnectTrigger); window.removeEventListener("pagehide", pagehideHandler); if (try_reconnect) { // Attempt to reconnect transient non-intentional disconnects. socket.current.reconnect(); } }); // On each received message, queue the updates and events. socket.current.on("event", async (update) => { for (const substate in update.delta) { dispatch[substate](update.delta[substate]); // handle events waiting for `is_hydrated` if ( substate === state_name && update.delta[substate]?.is_hydrated_rx_state_ ) { queueEvents(on_hydrated_queue, socket, false, navigate, params); on_hydrated_queue.length = 0; } } applyClientStorageDelta(client_storage, update.delta); if (update.final !== null) { event_processing = !update.final; } if (update.events) { queueEvents(update.events, socket, false, navigate, params); } }); socket.current.on("reload", async (event) => { event_processing = false; on_hydrated_queue.push(event); queueEvents(initialEvents(), socket, true, navigate, params); }); socket.current.on("new_token", async (new_token) => { token = new_token; window.sessionStorage.setItem(TOKEN_KEY, new_token); }); document.addEventListener("visibilitychange", checkVisibility); }; /** * Create an event object. * @param {string} name The name of the event. * @param {Object.} payload The payload of the event. * @param {Object.} event_actions The actions to take on the event. * @param {string} handler The client handler to process event. * @returns The event object. */ export const ReflexEvent = ( name, payload = {}, event_actions = {}, handler = null, ) => { return { name, payload, handler, event_actions }; }; /** * Package client-side storage values as payload to send to the * backend with the hydrate event * @param client_storage The client storage object from context.js * @returns payload dict of client storage values */ export const hydrateClientStorage = (client_storage) => { const client_storage_values = {}; if (client_storage.cookies) { for (const state_key in client_storage.cookies) { const cookie_options = client_storage.cookies[state_key]; const cookie_name = cookie_options.name || state_key; const cookie_value = cookies.get(cookie_name, { doNotParse: true }); if (cookie_value !== undefined) { client_storage_values[state_key] = cookie_value; } } } if (client_storage.local_storage && typeof window !== "undefined") { for (const state_key in client_storage.local_storage) { const options = client_storage.local_storage[state_key]; const local_storage_value = localStorage.getItem( options.name || state_key, ); if (local_storage_value !== null) { client_storage_values[state_key] = local_storage_value; } } } if (client_storage.session_storage && typeof window != "undefined") { for (const state_key in client_storage.session_storage) { const session_options = client_storage.session_storage[state_key]; const session_storage_value = sessionStorage.getItem( session_options.name || state_key, ); if (session_storage_value != null) { client_storage_values[state_key] = session_storage_value; } } } if ( client_storage.cookies || client_storage.local_storage || client_storage.session_storage ) { return client_storage_values; } return {}; }; /** * Update client storage values based on backend state delta. * @param client_storage The client storage object from context.js * @param delta The state update from the backend */ const applyClientStorageDelta = (client_storage, delta) => { // find the main state and check for is_hydrated const unqualified_states = Object.keys(delta).filter( (key) => key.split(".").length === 1, ); if (unqualified_states.length === 1) { const main_state = delta[unqualified_states[0]]; if ( main_state.is_hydrated_rx_state_ !== undefined && !main_state.is_hydrated_rx_state_ ) { // skip if the state is not hydrated yet, since all client storage // values are sent in the hydrate event return; } } // Save known client storage values to cookies and localStorage. for (const substate in delta) { for (const key in delta[substate]) { const state_key = `${substate}.${key}`; if (client_storage.cookies && state_key in client_storage.cookies) { const cookie_options = { ...client_storage.cookies[state_key] }; const cookie_name = cookie_options.name || state_key; delete cookie_options.name; // name is not a valid cookie option cookies.set(cookie_name, delta[substate][key], cookie_options); } else if ( client_storage.local_storage && state_key in client_storage.local_storage && typeof window !== "undefined" ) { const options = client_storage.local_storage[state_key]; localStorage.setItem(options.name || state_key, delta[substate][key]); } else if ( client_storage.session_storage && state_key in client_storage.session_storage && typeof window !== "undefined" ) { const session_options = client_storage.session_storage[state_key]; sessionStorage.setItem( session_options.name || state_key, delta[substate][key], ); } } } }; /** * Establish websocket event loop for a React Router page. * @param dispatch The reducer dispatch function to update state. * @param initial_events The initial app events. * @param client_storage The client storage object from context.js * * @returns [addEvents, connectErrors] - * addEvents is used to queue an event, and * connectErrors is an array of reactive js error from the websocket connection (or null if connected). */ export const useEventLoop = ( dispatch, initial_events = () => [], client_storage = {}, ) => { const socket = useRef(null); const location = useLocation(); const navigate = useNavigate(); const paramsR = useParams(); const prevLocationRef = useRef(location); const [searchParams] = useSearchParams(); const [connectErrors, setConnectErrors] = useState([]); const params = useRef(paramsR); const mounted = useRef(false); useEffect(() => { const { "*": splat, ...remainingParams } = paramsR; if (splat) { params.current = { ...remainingParams, splat: splat.split("/") }; } else { params.current = remainingParams; } }, [paramsR]); const ensureSocketConnected = useCallback(async () => { if (!mounted.current) { // During hot reload, some components may still have a reference to // addEvents, so avoid reconnecting the socket of an unmounted event loop. return; } // only use websockets if state is present and backend is not disabled (reflex cloud). if ( Object.keys(initialState).length > 1 && !isBackendDisabled() && !socket.current?.connected ) { // Initialize the websocket connection. await connect( socket, dispatch, [env.TRANSPORT], setConnectErrors, client_storage, navigate, () => params.current, ); } }, [ socket, dispatch, setConnectErrors, client_storage, navigate, params, mounted, ]); // Function to add new events to the event queue. const addEvents = useCallback((events, args, event_actions) => { const _events = events.filter((e) => e !== undefined && e !== null); if (!event_actions?.temporal) { // Reconnect socket if needed for non-temporal events. ensureSocketConnected(); } if (!(args instanceof Array)) { args = [args]; } event_actions = _events.reduce( (acc, e) => ({ ...acc, ...e.event_actions }), event_actions ?? {}, ); const _e = args.filter((o) => o?.preventDefault !== undefined)[0]; if (event_actions?.preventDefault && _e?.preventDefault) { _e.preventDefault(); } if (event_actions?.stopPropagation && _e?.stopPropagation) { _e.stopPropagation(); } const combined_name = _events.map((e) => e.name).join("+++"); if (event_actions?.temporal) { if (!socket.current || !socket.current.connected) { return; // don't queue when the backend is not connected } } if (event_actions?.throttle) { // If throttle returns false, the events are not added to the queue. if (!throttle(combined_name, event_actions.throttle)) { return; } } if (event_actions?.debounce) { // If debounce is used, queue the events after some delay debounce( combined_name, () => queueEvents(_events, socket, false, navigate, () => params.current), event_actions.debounce, ); } else { queueEvents(_events, socket, false, navigate, () => params.current); } }, []); const sentHydrate = useRef(false); // Avoid double-hydrate due to React strict-mode useEffect(() => { if (!sentHydrate.current) { queueEvents( initial_events(), socket, true, navigate, () => params.current, ); sentHydrate.current = true; } }, []); // Handle frontend errors and send them to the backend via websocket. useEffect(() => { if (typeof window === "undefined") { return; } window.onerror = function (msg, url, lineNo, columnNo, error) { addEvents([ ReflexEvent(`${exception_state_name}.handle_frontend_exception`, { info: error.name + ": " + error.message + "\n" + error.stack, component_stack: "", }), ]); return false; }; //NOTE: Only works in Chrome v49+ //https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events window.onunhandledrejection = function (event) { addEvents([ ReflexEvent(`${exception_state_name}.handle_frontend_exception`, { info: event.reason?.name + ": " + event.reason?.message + "\n" + event.reason?.stack, component_stack: "", }), ]); return false; }; }, []); // Handle socket connect/disconnect. useEffect(() => { // Initialize the websocket connection. mounted.current = true; ensureSocketConnected(); // Cleanup function. return () => { mounted.current = false; if (socket.current) { socket.current.disconnect(); socket.current.off(); socket.current = null; } }; }, []); // Main event loop. useEffect(() => { // Skip if the backend is disabled if (isBackendDisabled() || !socket.current || !socket.current.connected) { return; } (async () => { // Process all outstanding events. while (event_queue.length > 0 && !event_processing) { await ensureSocketConnected(); await processEvent(socket.current, navigate, () => params.current); } })(); }); // localStorage event handling useEffect(() => { const storage_to_state_map = {}; if (client_storage.local_storage && typeof window !== "undefined") { for (const state_key in client_storage.local_storage) { const options = client_storage.local_storage[state_key]; if (options.sync) { const local_storage_value_key = options.name || state_key; storage_to_state_map[local_storage_value_key] = state_key; } } } // e is StorageEvent const handleStorage = (e) => { if (storage_to_state_map[e.key]) { const vars = {}; vars[storage_to_state_map[e.key]] = e.newValue; const event = ReflexEvent( `${state_name}.reflex___state____update_vars_internal_state.update_vars_internal`, { vars: vars }, ); addEvents([event], e); } }; window.addEventListener("storage", handleStorage); return () => window.removeEventListener("storage", handleStorage); }); const handleNavigationEvents = useRef(false); // Route after the initial page hydration useEffect(() => { // The first time this effect runs is initial load, so don't handle // any navigation events. if (!handleNavigationEvents.current) { handleNavigationEvents.current = true; return; } if (location.state?.fromNotFound) { // If the redirect is from a 404 page, we skip onLoadInternalEvent, // since it was already run when the 404 page was first rendered. return; } // This will run when the location changes if ( location.pathname + location.search === prevLocationRef.current.pathname + prevLocationRef.current.search ) { if (location.hash) { // If the hash is the same, we don't need to do anything. return; } } // Equivalent to routeChangeStart - runs when navigation begins const main_state_dispatch = dispatch["reflex___state____state"]; if (main_state_dispatch !== undefined) { main_state_dispatch({ is_hydrated_rx_state_: false }); } // Equivalent to routeChangeComplete - runs after navigation completes addEvents(onLoadInternalEvent()); // Update the ref prevLocationRef.current = location; }, [location, dispatch, onLoadInternalEvent, addEvents]); return [addEvents, connectErrors]; }; /*** * Check if a value is truthy in python. * @param val The value to check. * @returns True if the value is truthy, false otherwise. */ export const isTrue = (val) => { if (Array.isArray(val)) return val.length > 0; if (val === Object(val)) return Object.keys(val).length > 0; return Boolean(val); }; /*** * Check if a value is not null or undefined. * @param val The value to check. * @returns True if the value is not null or undefined, false otherwise. */ export const isNotNullOrUndefined = (val) => { return (val ?? undefined) !== undefined; }; /** * Get the value from a ref. * @param ref The ref to get the value from. * @returns The value. */ export const getRefValue = (ref) => { if (!ref || !ref.current) { return; } if (ref.current.type == "checkbox") { return ref.current.checked; // chakra } else if ( ref.current.className?.includes("rt-CheckboxRoot") || ref.current.className?.includes("rt-SwitchRoot") ) { return ref.current.ariaChecked == "true"; // radix } else if (ref.current.className?.includes("rt-SliderRoot")) { // find the actual slider return ref.current.querySelector(".rt-SliderThumb")?.ariaValueNow; } else { //querySelector(":checked") is needed to get value from radio_group return ( ref.current.value || (ref.current.querySelector && ref.current.querySelector(":checked") && ref.current.querySelector(":checked")?.value) ); } }; /** * Get the values from a ref array. * @param refs The refs to get the values from. * @returns The values array. */ export const getRefValues = (refs) => { if (!refs) { return; } // getAttribute is used by RangeSlider because it doesn't assign value return refs.map((ref) => ref.current ? ref.current.value || ref.current.getAttribute("aria-valuenow") : null, ); }; /** * Spread two arrays or two objects. * @param first The first array or object. * @param second The second array or object. * @returns The final merged array or object. */ export const spreadArraysOrObjects = (first, second) => { if (Array.isArray(first) && Array.isArray(second)) { return [...first, ...second]; } else if (typeof first === "object" && typeof second === "object") { return { ...first, ...second }; } else { throw new Error("Both parameters must be either arrays or objects."); } }; ================================================ FILE: reflex/.templates/web/vite-plugin-safari-cachebust.js ================================================ /* vite-plugin-safari-cachebust.js * * Rewrite modulepreload tags and ESM imports to include a cache-busting * query parameter for Safari browser. * * https://github.com/remix-run/react-router/issues/12761 * * The issue seems to be Safari over-aggressive caching of ESM imports (and modulepreload) * which does not respect the cache-control headers sent by the server. This approach * allows hot reload to work in Safari when adding routes or changing dependencies. * * No equivalent transformation is needed for production builds, as the * output already contains the file hash in the name. */ /** * @typedef {import('vite').Plugin} Plugin * @typedef {import('vite').ViteDevServer} ViteDevServer * @typedef {import('http').IncomingMessage} IncomingMessage * @typedef {import('http').ServerResponse} ServerResponse * @typedef {import('connect').NextHandleFunction} NextHandleFunction */ const pluginName = "vite-plugin-safari-cachebust"; /** * Creates a Vite plugin that adds cache-busting for Safari browsers * @returns {Plugin} The Vite plugin */ export default function safariCacheBustPlugin() { return { name: pluginName, /** * Configure the dev server with the Safari middleware * @param {ViteDevServer} server - The Vite dev server instance */ configureServer(server) { server.middlewares.use(createSafariMiddleware()); }, }; } /** * Determines if the user agent is Safari * @param {string} ua - The user agent string * @returns {boolean} True if the browser is Safari */ function isSafari(ua) { return /Safari/.test(ua) && !/Chrome/.test(ua); } /** * Creates a middleware that adds cache-busting for Safari browsers * @returns {NextHandleFunction} The middleware function */ function createSafariMiddleware() { // Set when a log message for rewriting n links has been emitted. let _have_logged_n = -1; /** * Rewrites module import links in HTML content with cache-busting parameters * @param {string} html - The HTML content to process * @returns {string} The processed HTML content */ function rewriteModuleImports(html) { const currentTimestamp = new Date().getTime(); const parts = html.split(/(]*>)/g); /** @type {[string, string][]} */ const replacements = parts .map((chunk) => { const match = chunk.match( //, ); if (!match) return; const [fullMatch, href, rest] = match; if (/^(https?:)?\/\//.test(href)) return; try { const newHref = href.includes("?") ? `${href}&__reflex_ts=${currentTimestamp}` : `${href}?__reflex_ts=${currentTimestamp}`; return [href, newHref]; } catch { // no worries; } }) .filter(Boolean); if (replacements.length && _have_logged_n !== replacements.length) { _have_logged_n = replacements.length; console.debug( `[${pluginName}] Rewrote ${replacements.length} modulepreload links with __reflex_ts param.`, ); } return replacements.reduce((accumulator, [target, replacement]) => { return accumulator.split(target).join(replacement); }, html); } /** * Middleware function to handle Safari cache busting * @param {IncomingMessage} req - The incoming request * @param {ServerResponse} res - The server response * @param {(err?: any) => void} next - The next middleware function * @returns {void} */ return function safariCacheBustMiddleware(req, res, next) { const ua = req.headers["user-agent"] || ""; // Remove our special cache bust query param to avoid affecting lower middleware layers. if ( req.url && (req.url.includes("?__reflex_ts=") || req.url.includes("&__reflex_ts=")) ) { req.url = req.url.replace(/(\?|&)__reflex_ts=\d+/, ""); return next(); } // Only apply this middleware for Safari browsers. if (!isSafari(ua)) return next(); // Only transform requests that want HTML. const header_accept = req.headers["accept"] || ""; if ( typeof header_accept !== "string" || !header_accept.includes("text/html") ) { return next(); } let buffer = ""; const _end = res.end.bind(res); res.setHeader("x-modified-by", "vite-plugin-safari-cachebust"); /** * Overridden write method to collect chunks * @param {any} chunk - The chunk to write * @param {...any} args - Additional arguments * @returns {boolean} Result of the write operation */ res.write = function (chunk, ...args) { buffer += chunk instanceof Buffer ? chunk.toString("utf-8") : chunk; return true; }; /** * Overridden end method to process and send the final response * @param {any} chunk - The final chunk to write * @param {...any} args - Additional arguments * @returns {ServerResponse} The server response */ res.end = function (chunk, ...args) { if (chunk) { buffer += chunk instanceof Buffer ? chunk.toString("utf-8") : chunk; } buffer = rewriteModuleImports(buffer); return _end(buffer, ...args); }; return next(); }; } ================================================ FILE: reflex/__init__.py ================================================ """Import all classes and functions the end user will need to make an app. Anything imported here will be available in the default Reflex import as `rx.*`. Dynamic Imports --------------- Reflex utilizes dynamic imports, or lazy loading, to reduce startup/import times. With this approach, imports are delayed until they are actually needed. We use the `lazy_loader` library(https://github.com/scientific-python/lazy_loader) to achieve this. How it works -------------- `lazy_loader.attach` takes two optional arguments: `submodules` and `submod_attrs`. - `submodules` typically points to directories or files to be accessed. - `submod_attrs` defines a mapping of directory or file names as keys with a list of attributes or modules to access. Example directory structure: reflex/ |_ components/ |_ radix/ |_ themes/ |_ components/ |_ box.py To add `box` under the `rx` namespace (`rx.box`), add the relative path to `submod_attrs` in `reflex/__init__.py` (this file): ```python lazy_loader.attach( submodules={"components"}, submod_attrs={ "components.radix.themes.components.box": ["box"] } ) ``` This implies that `box` will be imported from `reflex/components/radix/themes/components/box.py`. To add box under the `rx.radix` namespace (`rx.radix.box`), add the relative path to the submod_attrs argument in `reflex/components/radix/__init__.py`: ```python lazy_loader.attach( submodules = {"themes"}, submod_attrs = { "themes.components.box": ["box"] } ) ``` Note: It is important to specify the immediate submodules of a directory in the submodules argument to ensure they are registered at runtime. For example, 'components' for reflex, 'radix' for components, 'themes' for radix, etc. Pyi_generator -------------- To generate `.pyi` files for `__init__.py` files, we read the `_SUBMODULES` and `_SUBMOD_ATTRS` attributes to generate the import statements. It is highly recommended to define these with the provided annotations to facilitate their generation. Aliases ------------ This is a special case to specify an alias for a component. As an example, we use this typically for `rx.list` where defining `list` attribute in the list.py overshadows python's list object which messes up the pyi generation for `list.pyi`. As a result, aliases should be used for similar cases like this. Note that this logic is employed to fix the pyi generation and alias should still be defined or accessible. Check out the __getattr__ logic in `reflex/components/radix/themes/layouts/list.py` ```python lazy_loader.attach( submodules={"components"}, submod_attrs={ "components.radix.themes.layouts": [("list_ns", "list")] } ) ``` In the example above, you will be able to do `rx.list` """ from __future__ import annotations import sys from reflex.utils import lazy_loader if sys.version_info < (3, 11): from reflex.utils import console console.warn( "Reflex support for Python 3.10 is deprecated and will be removed in a future release. Please upgrade to Python 3.11 or higher for continued support." ) del console del sys RADIX_THEMES_MAPPING: dict = { "components.radix.themes.base": ["color_mode", "theme", "theme_panel"], "components.radix.themes.color_mode": ["color_mode"], } RADIX_THEMES_COMPONENTS_MAPPING: dict = { **{ f"components.radix.themes.components.{mod}": [mod] for mod in [ "alert_dialog", "aspect_ratio", "avatar", "badge", "button", "callout", "card", "checkbox", "context_menu", "data_list", "dialog", "hover_card", "icon_button", "input", "inset", "popover", "scroll_area", "select", "skeleton", "slider", "spinner", "switch", "table", "tabs", "text_area", "tooltip", "segmented_control", "radio_cards", "checkbox_cards", "checkbox_group", ] }, "components.radix.themes.components.text_field": ["text_field", "input"], "components.radix.themes.components.radio_group": ["radio", "radio_group"], "components.radix.themes.components.dropdown_menu": ["menu", "dropdown_menu"], "components.radix.themes.components.separator": ["divider", "separator"], "components.radix.themes.components.progress": ["progress"], } RADIX_THEMES_LAYOUT_MAPPING: dict = { "components.radix.themes.layout.box": [ "box", ], "components.radix.themes.layout.center": [ "center", ], "components.radix.themes.layout.container": [ "container", ], "components.radix.themes.layout.flex": [ "flex", ], "components.radix.themes.layout.grid": [ "grid", ], "components.radix.themes.layout.section": [ "section", ], "components.radix.themes.layout.spacer": [ "spacer", ], "components.radix.themes.layout.stack": [ "stack", "hstack", "vstack", ], "components.radix.themes.layout.list": [ ("list_ns", "list"), "list_item", "ordered_list", "unordered_list", ], } RADIX_THEMES_TYPOGRAPHY_MAPPING: dict = { "components.radix.themes.typography.blockquote": [ "blockquote", ], "components.radix.themes.typography.code": [ "code", ], "components.radix.themes.typography.heading": [ "heading", ], "components.radix.themes.typography.link": [ "link", ], "components.radix.themes.typography.text": [ "text", ], } RADIX_PRIMITIVES_MAPPING: dict = { "components.radix.primitives.accordion": [ "accordion", ], "components.radix.primitives.drawer": [ "drawer", ], "components.radix.primitives.form": [ "form", ], "components.radix.primitives.progress": [ "progress", ], } RADIX_PRIMITIVES_SHORTCUT_MAPPING: dict = { k: v for k, v in RADIX_PRIMITIVES_MAPPING.items() if "progress" not in k } COMPONENTS_CORE_MAPPING: dict = { "components.core.banner": [ "connection_banner", "connection_modal", ], "components.core.cond": ["cond", "color_mode_cond"], "components.core.foreach": ["foreach"], "components.core.debounce": ["debounce_input"], "components.core.html": ["html"], "components.core.match": ["match"], "components.core.clipboard": ["clipboard"], "components.core.colors": ["color"], "components.core.breakpoints": ["breakpoints"], "components.core.responsive": [ "desktop_only", "mobile_and_tablet", "mobile_only", "tablet_and_desktop", "tablet_only", ], "components.core.upload": [ "cancel_upload", "clear_selected_files", "get_upload_dir", "get_upload_url", "selected_files", "upload", ], "components.core.auto_scroll": ["auto_scroll"], "components.core.window_events": ["window_event_listener"], } COMPONENTS_BASE_MAPPING: dict = { "components.base.fragment": ["fragment", "Fragment"], "components.base.script": ["script", "Script"], } RADIX_MAPPING: dict = { **RADIX_THEMES_MAPPING, **RADIX_THEMES_COMPONENTS_MAPPING, **RADIX_THEMES_TYPOGRAPHY_MAPPING, **RADIX_THEMES_LAYOUT_MAPPING, **RADIX_PRIMITIVES_SHORTCUT_MAPPING, } _MAPPING: dict = { "experimental": ["_x"], "admin": ["AdminDash"], "app": ["App", "UploadFile"], "assets": ["asset"], "base": ["Base"], "components.component": [ "Component", "NoSSRComponent", "memo", "ComponentNamespace", ], "components.el.elements.media": ["image"], "components.lucide": ["icon"], **COMPONENTS_BASE_MAPPING, "components": ["el", "radix", "lucide", "recharts"], "components.markdown": ["markdown"], **RADIX_MAPPING, "components.plotly": ["plotly"], "components.react_player": ["audio", "video"], **COMPONENTS_CORE_MAPPING, "components.datadisplay.code": [ "code_block", ], "components.datadisplay.dataeditor": [ "data_editor", "data_editor_theme", ], "components.sonner.toast": ["toast"], "components.props": ["PropsBase"], "components.datadisplay.logo": ["logo"], "components.gridjs": ["data_table"], "components.moment": ["MomentDelta", "moment"], "config": ["Config", "DBConfig"], "constants": ["Env"], "constants.colors": ["Color"], "event": [ "event", "EventChain", "EventHandler", "call_script", "call_function", "run_script", "clear_local_storage", "clear_session_storage", "console_log", "download", "noop", "prevent_default", "redirect", "remove_cookie", "remove_local_storage", "remove_session_storage", "set_clipboard", "set_focus", "scroll_to", "set_value", "stop_propagation", "upload_files", "window_alert", ], "istate.storage": [ "Cookie", "LocalStorage", "SessionStorage", ], "middleware": ["middleware", "Middleware"], "model": ["asession", "session", "Model", "ModelRegistry"], "page": ["page"], "state": [ "var", "ComponentState", "State", "dynamic", ], "istate.shared": ["SharedState"], "istate.wrappers": ["get_state"], "style": ["Style", "toggle_color_mode"], "utils.imports": ["ImportDict", "ImportVar"], "utils.misc": ["run_in_thread"], "utils.serializers": ["serializer"], "vars": ["Var", "field", "Field"], } _SUBMODULES: set[str] = { "components", "app", "style", "admin", "base", "model", "testing", "utils", "vars", "config", "compiler", "plugins", } _SUBMOD_ATTRS: dict = _MAPPING getattr, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, ) def __getattr__(name: str): return getattr(name) ================================================ FILE: reflex/__main__.py ================================================ """reflex package invocation entry point.""" from .reflex import cli if __name__ == "__main__": cli() ================================================ FILE: reflex/admin.py ================================================ """The Reflex Admin Dashboard.""" from __future__ import annotations from dataclasses import dataclass, field from typing import TYPE_CHECKING if TYPE_CHECKING: from starlette_admin.base import BaseAdmin as Admin @dataclass class AdminDash: """Data used to build the admin dashboard.""" models: list = field(default_factory=list) view_overrides: dict = field(default_factory=dict) admin: Admin | None = None ================================================ FILE: reflex/app.py ================================================ """The main Reflex app.""" from __future__ import annotations import asyncio import concurrent.futures import contextlib import copy import dataclasses import functools import inspect import json import operator import sys import time import traceback import urllib.parse from collections.abc import ( AsyncGenerator, AsyncIterator, Awaitable, Callable, Coroutine, Mapping, Sequence, ) from datetime import datetime from itertools import chain from pathlib import Path from timeit import default_timer as timer from types import SimpleNamespace from typing import TYPE_CHECKING, Any, BinaryIO, ParamSpec, get_args, get_type_hints from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn from socketio import ASGIApp as EngineIOApp from socketio import AsyncNamespace, AsyncServer from starlette.applications import Starlette from starlette.datastructures import Headers from starlette.datastructures import UploadFile as StarletteUploadFile from starlette.exceptions import HTTPException from starlette.middleware import cors from starlette.requests import ClientDisconnect, Request from starlette.responses import JSONResponse, Response, StreamingResponse from starlette.staticfiles import StaticFiles from typing_extensions import Unpack from reflex import constants from reflex.admin import AdminDash from reflex.app_mixins import AppMixin, LifespanMixin, MiddlewareMixin from reflex.compiler import compiler from reflex.compiler import utils as compiler_utils from reflex.compiler.compiler import ( ExecutorSafeFunctions, compile_theme, readable_name_from_component, ) from reflex.components.base.app_wrap import AppWrap from reflex.components.base.error_boundary import ErrorBoundary from reflex.components.base.fragment import Fragment from reflex.components.base.strict_mode import StrictMode from reflex.components.component import ( CUSTOM_COMPONENTS, Component, ComponentStyle, evaluate_style_namespaces, ) from reflex.components.core.banner import ( backend_disabled, connection_pulser, connection_toaster, ) from reflex.components.core.breakpoints import set_breakpoints from reflex.components.core.sticky import sticky from reflex.components.radix import themes from reflex.components.sonner.toast import toast from reflex.config import get_config from reflex.environment import ExecutorType, environment from reflex.event import ( _EVENT_FIELDS, Event, EventSpec, EventType, IndividualEventType, get_hydrate_event, noop, ) from reflex.istate.manager import StateModificationContext from reflex.istate.proxy import StateProxy from reflex.page import DECORATED_PAGES from reflex.route import ( get_route_args, replace_brackets_with_keywords, verify_route_validity, ) from reflex.state import ( BaseState, RouterData, State, StateManager, StateUpdate, _split_substate_key, _substate_key, all_base_state_classes, code_uses_state_contexts, ) from reflex.utils import ( codespaces, console, exceptions, format, frontend_skeleton, js_runtimes, path_ops, prerequisites, types, ) from reflex.utils.exec import ( get_compile_context, is_prod_mode, is_testing_env, should_prerender_routes, ) from reflex.utils.imports import ImportVar from reflex.utils.misc import run_in_thread from reflex.utils.token_manager import RedisTokenManager, TokenManager from reflex.utils.types import ASGIApp, Message, Receive, Scope, Send if TYPE_CHECKING: from reflex.vars import Var # Define custom types. ComponentCallable = Callable[[], Component | tuple[Component, ...] | str | Var] else: ComponentCallable = Callable[[], Component | tuple[Component, ...] | str] Reducer = Callable[[Event], Coroutine[Any, Any, StateUpdate]] def default_frontend_exception_handler(exception: Exception) -> None: """Default frontend exception handler function. Args: exception: The exception. """ console.error(f"[Reflex Frontend Exception]\n {exception}\n") def default_backend_exception_handler(exception: Exception) -> EventSpec: """Default backend exception handler function. Args: exception: The exception. Returns: EventSpec: The window alert event. """ from reflex.components.sonner.toast import toast error = traceback.format_exc() console.error(f"[Reflex Backend Exception]\n {error}\n") error_message = ( ["Contact the website administrator."] if is_prod_mode() else [f"{type(exception).__name__}: {exception}.", "See logs for details."] ) return toast( "An error occurred.", level="error", fallback_to_alert=True, description="
".join(error_message), position="top-center", id="backend_error", style={"width": "500px"}, ) def extra_overlay_function() -> Component | None: """Extra overlay function to add to the overlay component. Returns: The extra overlay function. """ config = get_config() extra_config = config.extra_overlay_function config_overlay = None if extra_config: module, _, function_name = extra_config.rpartition(".") try: module = __import__(module) config_overlay = Fragment.create(getattr(module, function_name)()) config_overlay._get_all_imports() except Exception as e: from reflex.compiler.utils import save_error log_path = save_error(e) console.error( f"Error loading extra_overlay_function {extra_config}. Error saved to {log_path}" ) return config_overlay def default_overlay_component() -> Component: """Default overlay_component attribute for App. Returns: The default overlay_component, which is a connection_modal. """ from reflex.components.component import memo def default_overlay_components(): return Fragment.create( connection_pulser(), connection_toaster(), *( [backend_disabled()] if get_compile_context() == constants.CompileContext.DEPLOY else [] ), *codespaces.codespaces_auto_redirect(), ) return Fragment.create(memo(default_overlay_components)()) def default_error_boundary(*children: Component, **props) -> Component: """Default error_boundary attribute for App. Args: *children: The children to render in the error boundary. **props: The props to pass to the error boundary. Returns: The default error_boundary, which is an ErrorBoundary. """ return ErrorBoundary.create( *children, **props, ) @dataclasses.dataclass(frozen=True) class UploadFile(StarletteUploadFile): """A file uploaded to the server. Args: file: The standard Python file object (non-async). filename: The original file name. size: The size of the file in bytes. headers: The headers of the request. """ file: BinaryIO path: Path | None = dataclasses.field(default=None) size: int | None = dataclasses.field(default=None) headers: Headers = dataclasses.field(default_factory=Headers) @property def filename(self) -> str | None: """Get the name of the uploaded file. Returns: The name of the uploaded file. """ return self.name @property def name(self) -> str | None: """Get the name of the uploaded file. Returns: The name of the uploaded file. """ if self.path: return self.path.name return None @dataclasses.dataclass( frozen=True, ) class UnevaluatedPage: """An uncompiled page.""" component: Component | ComponentCallable route: str title: Var | str | None description: Var | str | None image: str on_load: EventType[()] | None meta: Sequence[Mapping[str, Any] | Component] context: Mapping[str, Any] def merged_with(self, other: UnevaluatedPage) -> UnevaluatedPage: """Merge the other page into this one. Args: other: The other page to merge with. Returns: The merged page. """ return dataclasses.replace( self, title=self.title if self.title is not None else other.title, description=self.description if self.description is not None else other.description, on_load=self.on_load if self.on_load is not None else other.on_load, context=self.context if self.context is not None else other.context, ) P = ParamSpec("P") @dataclasses.dataclass() class App(MiddlewareMixin, LifespanMixin): """The main Reflex app that encapsulates the backend and frontend. Every Reflex app needs an app defined in its main module. ```python # app.py import reflex as rx # Define state and pages ... app = rx.App( # Set global level style. style={...}, # Set the top level theme. theme=rx.theme(accent_color="blue"), ) ``` """ # The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app. theme: Component | None = dataclasses.field( default_factory=lambda: themes.theme(accent_color="blue") ) # The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app. style: ComponentStyle = dataclasses.field(default_factory=dict) # A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app. stylesheets: list[str] = dataclasses.field(default_factory=list) # Whether to include CSS reset for margin and padding (defaults to True). reset_style: bool = dataclasses.field(default=True) # A component that is present on every page (defaults to the Connection Error banner). overlay_component: Component | ComponentCallable | None = dataclasses.field( default=None ) # App wraps to be applied to the whole app. Expected to be a dictionary of (order, name) to a function that takes whether the state is enabled and optionally returns a component. app_wraps: dict[tuple[int, str], Callable[[bool], Component | None]] = ( dataclasses.field( default_factory=lambda: { (55, "ErrorBoundary"): ( lambda stateful: default_error_boundary( **({"on_error": noop()} if not stateful else {}) ) ), (5, "Overlay"): ( lambda stateful: default_overlay_component() if stateful else None ), (4, "ExtraOverlay"): lambda stateful: extra_overlay_function(), } ) ) # Extra app wraps to be applied to the whole app. extra_app_wraps: dict[tuple[int, str], Callable[[bool], Component | None]] = ( dataclasses.field(default_factory=dict) ) # Components to add to the head of every page. head_components: list[Component] = dataclasses.field(default_factory=list) # The Socket.IO AsyncServer instance. sio: AsyncServer | None = None # The language to add to the html root tag of every page. html_lang: str | None = None # Attributes to add to the html root tag of every page. html_custom_attrs: dict[str, str] | None = None # A map from a route to an unevaluated page. _unevaluated_pages: dict[str, UnevaluatedPage] = dataclasses.field( default_factory=dict ) # A map from a page route to the component to render. Users should use `add_page`. _pages: dict[str, Component] = dataclasses.field(default_factory=dict) # A mapping of pages which created states as they were being evaluated. _stateful_pages: dict[str, None] = dataclasses.field(default_factory=dict) # The backend API object. _api: Starlette | None = None # The state class to use for the app. _state: type[BaseState] | None = None # Whether to enable state for the app. If False, the app will not use state. enable_state: bool = True # Class to manage many client states. _state_manager: StateManager | None = None # Mapping from a route to event handlers to trigger when the page loads. _load_events: dict[str, list[IndividualEventType[()]]] = dataclasses.field( default_factory=dict ) # Admin dashboard to view and manage the database. admin_dash: AdminDash | None = None # The async server name space. _event_namespace: EventNamespace | None = None # Background tasks that are currently running. _background_tasks: set[asyncio.Task] = dataclasses.field(default_factory=set) # Frontend Error Handler Function frontend_exception_handler: Callable[[Exception], None] = ( default_frontend_exception_handler ) # Backend Error Handler Function backend_exception_handler: Callable[ [Exception], EventSpec | list[EventSpec] | None ] = default_backend_exception_handler # Put the toast provider in the app wrap. toaster: Component | None = dataclasses.field(default_factory=toast.provider) # Transform the ASGI app before running it. api_transformer: ( Sequence[Callable[[ASGIApp], ASGIApp] | Starlette] | Callable[[ASGIApp], ASGIApp] | Starlette | None ) = None @property def event_namespace(self) -> EventNamespace | None: """Get the event namespace. Returns: The event namespace. """ return self._event_namespace def __post_init__(self): """Initialize the app. Raises: ValueError: If the event namespace is not provided in the config. Also, if there are multiple client subclasses of rx.BaseState(Subclasses of rx.BaseState should consist of the DefaultState and the client app state). """ # Special case to allow test cases have multiple subclasses of rx.BaseState. if not is_testing_env() and BaseState.__subclasses__() != [State]: # Only rx.State is allowed as Base State subclass. msg = "rx.BaseState cannot be subclassed directly. Use rx.State instead" raise ValueError(msg) get_config(reload=True) if "breakpoints" in self.style: set_breakpoints(self.style.pop("breakpoints")) # Set up the API. self._api = Starlette() App._add_cors(self._api) self._add_default_endpoints() for clz in App.__mro__: if clz == App: continue if issubclass(clz, AppMixin): clz._init_mixin(self) if self.enable_state: self._enable_state() # Set up the admin dash. self._setup_admin_dash() if sys.platform == "win32" and not is_prod_mode(): # Hack to fix Windows hot reload issue. from reflex.utils.compat import windows_hot_reload_lifespan_hack self.register_lifespan_task(windows_hot_reload_lifespan_hack) def _enable_state(self) -> None: """Enable state for the app.""" if not self._state: self._state = State self._setup_state() def _setup_state(self) -> None: """Set up the state for the app. Raises: RuntimeError: If the socket server is invalid. """ if not self._state: return config = get_config() # Set up the state manager. self._state_manager = StateManager.create(state=self._state) # Set up the Socket.IO AsyncServer. if not self.sio: self.sio = AsyncServer( async_mode="asgi", cors_allowed_origins=( ( "*" if config.cors_allowed_origins == ("*",) else list(config.cors_allowed_origins) ) if config.transport == "websocket" else [] ), cors_credentials=config.transport == "websocket", max_http_buffer_size=environment.REFLEX_SOCKET_MAX_HTTP_BUFFER_SIZE.get(), ping_interval=environment.REFLEX_SOCKET_INTERVAL.get(), ping_timeout=environment.REFLEX_SOCKET_TIMEOUT.get(), json=SimpleNamespace( dumps=staticmethod(format.json_dumps), loads=staticmethod(json.loads), ), allow_upgrades=False, transports=[config.transport], ) elif getattr(self.sio, "async_mode", "") != "asgi": msg = f"Custom `sio` must use `async_mode='asgi'`, not '{self.sio.async_mode}'." raise RuntimeError(msg) # Create the socket app. Note event endpoint constant replaces the default 'socket.io' path. socket_app = EngineIOApp(self.sio, socketio_path="") namespace = config.get_event_namespace() # Create the event namespace and attach the main app. Not related to any paths. self._event_namespace = EventNamespace(namespace, self) # Register the event namespace with the socket. self.sio.register_namespace(self.event_namespace) # Mount the socket app with the API. if self._api: class HeaderMiddleware: def __init__(self, app: ASGIApp): self.app = app async def __call__(self, scope: Scope, receive: Receive, send: Send): original_send = send async def modified_send(message: Message): if message["type"] == "websocket.accept": if scope.get("subprotocols"): # The following *does* say "subprotocol" instead of "subprotocols", intentionally. message["subprotocol"] = scope["subprotocols"][0] headers = dict(message.get("headers", [])) header_key = b"sec-websocket-protocol" if subprotocol := headers.get(header_key): message["headers"] = [ *message.get("headers", []), (header_key, subprotocol), ] return await original_send(message) return await self.app(scope, receive, modified_send) socket_app_with_headers = HeaderMiddleware(socket_app) self._api.mount(str(constants.Endpoint.EVENT), socket_app_with_headers) # Check the exception handlers self._validate_exception_handlers() def __repr__(self) -> str: """Get the string representation of the app. Returns: The string representation of the app. """ return f"" def __call__(self) -> ASGIApp: """Run the backend api instance. Returns: The backend api. Raises: ValueError: If the app has not been initialized. """ from reflex.assets import remove_stale_external_asset_symlinks from reflex.vars.base import GLOBAL_CACHE # Clean up stale symlinks in assets/external/ before compiling, so that # rx.asset(shared=True) symlink re-creation doesn't trigger further reloads. remove_stale_external_asset_symlinks() self._compile(prerender_routes=should_prerender_routes()) config = get_config() for plugin in config.plugins: plugin.post_compile(app=self) # We will not be making more vars, so we can clear the global cache to free up memory. GLOBAL_CACHE.clear() if not self._api: msg = "The app has not been initialized." raise ValueError(msg) asgi_app = self._api if environment.REFLEX_MOUNT_FRONTEND_COMPILED_APP.get(): asgi_app.mount( "/" + config.frontend_path.strip("/"), StaticFiles( directory=prerequisites.get_web_dir() / constants.Dirs.STATIC / config.frontend_path.strip("/"), html=True, ), name="frontend", ) if self.api_transformer is not None: api_transformers: Sequence[Starlette | Callable[[ASGIApp], ASGIApp]] = ( [self.api_transformer] if not isinstance(self.api_transformer, Sequence) else self.api_transformer ) for api_transformer in api_transformers: if isinstance(api_transformer, Starlette): # Mount the api to the starlette app. App._add_cors(api_transformer) api_transformer.mount("", asgi_app) asgi_app = api_transformer else: # Transform the asgi app. asgi_app = api_transformer(asgi_app) top_asgi_app = Starlette(lifespan=self._run_lifespan_tasks) top_asgi_app.mount("", asgi_app) App._add_cors(top_asgi_app) return top_asgi_app def _add_default_endpoints(self): """Add default api endpoints (ping).""" # To test the server. if not self._api: return self._api.add_route( str(constants.Endpoint.PING), ping, methods=["GET"], ) self._api.add_route( str(constants.Endpoint.HEALTH), health, methods=["GET"], ) def _add_optional_endpoints(self): """Add optional api endpoints (_upload).""" from reflex.components.core.upload import Upload, get_upload_dir if not self._api: return upload_is_used_marker = ( prerequisites.get_backend_dir() / constants.Dirs.UPLOAD_IS_USED ) if Upload.is_used or upload_is_used_marker.exists(): # To upload files. self._api.add_route( str(constants.Endpoint.UPLOAD), upload(self), methods=["POST"], ) # To access uploaded files. self._api.mount( str(constants.Endpoint.UPLOAD), StaticFiles(directory=get_upload_dir()), name="uploaded_files", ) upload_is_used_marker.parent.mkdir(parents=True, exist_ok=True) upload_is_used_marker.touch() if codespaces.is_running_in_codespaces(): self._api.add_route( str(constants.Endpoint.AUTH_CODESPACE), codespaces.auth_codespace, methods=["GET"], ) if environment.REFLEX_ADD_ALL_ROUTES_ENDPOINT.get(): self.add_all_routes_endpoint() @staticmethod def _add_cors(api: Starlette): """Add CORS middleware to the app. Args: api: The Starlette app to add CORS middleware to. """ api.add_middleware( cors.CORSMiddleware, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], allow_origins=get_config().cors_allowed_origins, ) @property def state_manager(self) -> StateManager: """Get the state manager. Returns: The initialized state manager. Raises: ValueError: if the state has not been initialized. """ if self._state_manager is None: msg = "The state manager has not been initialized." raise ValueError(msg) return self._state_manager @staticmethod def _generate_component(component: Component | ComponentCallable) -> Component: """Generate a component from a callable. Args: component: The component function to call or Component to return as-is. Returns: The generated component. """ from reflex.compiler.compiler import into_component return into_component(component) def add_page( self, component: Component | ComponentCallable | None = None, route: str | None = None, title: str | Var | None = None, description: str | Var | None = None, image: str = constants.DefaultPage.IMAGE, on_load: EventType[()] | None = None, meta: Sequence[Mapping[str, Any] | Component] = constants.DefaultPage.META_LIST, context: dict[str, Any] | None = None, ): """Add a page to the app. If the component is a callable, by default the route is the name of the function. Otherwise, a route must be provided. Args: component: The component to display at the page. route: The route to display the component at. title: The title of the page. description: The description of the page. image: The image to display on the page. on_load: The event handler(s) that will be called each time the page load. meta: The metadata of the page. context: Values passed to page for custom page-specific logic. Raises: PageValueError: When the component is not set for a non-404 page. RouteValueError: When the specified route name already exists. """ # If the route is not set, get it from the callable. if route is None: if not isinstance(component, Callable): msg = "Route must be set if component is not a callable." raise exceptions.RouteValueError(msg) # Format the route. route = format.format_route(format.to_kebab_case(component.__name__)) else: route = format.format_route(route) if route == constants.Page404.SLUG: if component is None: from reflex.components.el.elements import span component = span("404: Page not found") component = self._generate_component(component) title = title or constants.Page404.TITLE description = description or constants.Page404.DESCRIPTION image = image or constants.Page404.IMAGE else: if component is None: msg = "Component must be set for a non-404 page." raise exceptions.PageValueError(msg) # Check if the route given is valid verify_route_validity(route) unevaluated_page = UnevaluatedPage( component=component, route=route, title=title, description=description, image=image, on_load=on_load, meta=meta, context=context or {}, ) if route in self._unevaluated_pages: if self._unevaluated_pages[route].component is component: unevaluated_page = unevaluated_page.merged_with( self._unevaluated_pages[route] ) console.warn( f"Page {route} is being redefined with the same component." ) else: route_name = ( f"`{route}` or `/`" if route == constants.PageNames.INDEX_ROUTE else f"`{route}`" ) existing_component = self._unevaluated_pages[route].component msg = ( f"Tried to add page {readable_name_from_component(component)} with route {route_name} but " f"page {readable_name_from_component(existing_component)} with the same route already exists. " "Make sure you do not have two pages with the same route." ) raise exceptions.RouteValueError(msg) # Setup dynamic args for the route. # this state assignment is only required for tests using the deprecated state kwarg for App state = self._state or State state.setup_dynamic_args(get_route_args(route)) self._load_events[route] = ( (on_load if isinstance(on_load, list) else [on_load]) if on_load is not None else [] ) self._unevaluated_pages[route] = unevaluated_page def _compile_page(self, route: str, save_page: bool = True): """Compile a page. Args: route: The route of the page to compile. save_page: If True, the compiled page is saved to self._pages. """ n_states_before = len(all_base_state_classes) component = compiler.compile_unevaluated_page( route, self._unevaluated_pages[route], self.style, self.theme ) # Indicate that evaluating this page creates one or more state classes. if len(all_base_state_classes) > n_states_before: self._stateful_pages[route] = None # Add the page. self._check_routes_conflict(route) if save_page: self._pages[route] = component @functools.cached_property def router(self) -> Callable[[str], str | None]: """Get the route computer function. Returns: The route computer function. """ from reflex.route import get_router return get_router(list(dict.fromkeys([*self._unevaluated_pages, *self._pages]))) def get_load_events(self, path: str) -> list[IndividualEventType[()]]: """Get the load events for a route. Args: path: The route to get the load events for. Returns: The load events for the route. """ four_oh_four_load_events = self._load_events.get("404", []) route = self.router(path) if not route: # If the path is not a valid route, return the 404 page load events. return four_oh_four_load_events return self._load_events.get( route, four_oh_four_load_events, ) def _check_routes_conflict(self, new_route: str): """Verify if there is any conflict between the new route and any existing route. Based on conflicts that React Router would throw if not intercepted. Args: new_route: the route being newly added. Raises: RouteValueError: exception showing which conflict exist with the route to be added """ from reflex.utils.exceptions import RouteValueError if "[" not in new_route: return segments = ( constants.RouteRegex.SINGLE_SEGMENT, constants.RouteRegex.DOUBLE_SEGMENT, constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT, ) for route in self._pages: replaced_route = replace_brackets_with_keywords(route) for rw, r, nr in zip( replaced_route.split("/"), route.split("/"), new_route.split("/"), strict=False, ): if rw in segments and r != nr: # If the slugs in the segments of both routes are not the same, then the route is invalid msg = f"You cannot use different slug names for the same dynamic path in {route} and {new_route} ('{r}' != '{nr}')" raise RouteValueError(msg) if rw not in segments and r != nr: # if the section being compared in both routes is not a dynamic segment(i.e not wrapped in brackets) # then we are guaranteed that the route is valid and there's no need checking the rest. # eg. /posts/[id]/info/[slug1] and /posts/[id]/info1/[slug1] is always going to be valid since # info1 will break away into its own tree. break def _setup_admin_dash(self): """Setup the admin dash.""" try: from starlette_admin.contrib.sqla.admin import Admin from starlette_admin.contrib.sqla.view import ModelView from reflex.model import Model except ImportError: return # Get the admin dash. if not self._api: return admin_dash = self.admin_dash if admin_dash and admin_dash.models: # Build the admin dashboard admin = admin_dash.admin or Admin( engine=Model.get_db_engine(), title="Reflex Admin Dashboard", logo_url="https://reflex.dev/Reflex.svg", ) for model in admin_dash.models: view = admin_dash.view_overrides.get(model, ModelView) admin.add_view(view(model)) admin.mount_to(self._api) def _get_frontend_packages(self, imports: dict[str, set[ImportVar]]): """Gets the frontend packages to be installed and filters out the unnecessary ones. Args: imports: A dictionary containing the imports used in the current page. Example: >>> _get_frontend_packages({"react": "16.14.0", "react-dom": "16.14.0"}) """ dependencies = constants.PackageJson.DEPENDENCIES dev_dependencies = constants.PackageJson.DEV_DEPENDENCIES page_imports = { i for i, tags in imports.items() if i not in dependencies and i not in dev_dependencies and not any(i.startswith(prefix) for prefix in ["/", "$/", "."]) and i != "" and any(tag.install for tag in tags) } pinned = {i.rpartition("@")[0] for i in page_imports if "@" in i} page_imports = {i for i in page_imports if i not in pinned} frontend_packages = get_config().frontend_packages filtered_frontend_packages = [] for package in frontend_packages: if package in page_imports: console.warn( f"React packages and their dependencies are inferred from Component.library and Component.lib_dependencies, remove `{package}` from `frontend_packages`" ) continue filtered_frontend_packages.append(package) page_imports.update(filtered_frontend_packages) js_runtimes.install_frontend_packages(page_imports, get_config()) def _app_root(self, app_wrappers: dict[tuple[int, str], Component]) -> Component: for component in tuple(app_wrappers.values()): app_wrappers.update(component._get_all_app_wrap_components()) order = sorted(app_wrappers, key=operator.itemgetter(0), reverse=True) root = copy.deepcopy(app_wrappers[order[0]]) def reducer(parent: Component, key: tuple[int, str]) -> Component: child = copy.deepcopy(app_wrappers[key]) parent.children.append(child) return child functools.reduce( lambda parent, key: reducer(parent, key), order[1:], root, ) return root def _should_compile(self) -> bool: """Check if the app should be compiled. Returns: Whether the app should be compiled. """ # Check the environment variable. if environment.REFLEX_SKIP_COMPILE.get(): return False nocompile = prerequisites.get_web_dir() / constants.NOCOMPILE_FILE # Check the nocompile file. if nocompile.exists(): # Delete the nocompile file nocompile.unlink(missing_ok=True) return False # By default, compile the app. return True def _add_overlay_to_component( self, component: Component, overlay_component: Component ) -> Component: children = component.children if children[0] == overlay_component: return component return Fragment.create(overlay_component, *children) def _setup_overlay_component(self): """If a State is not used and no overlay_component is specified, do not render the connection modal.""" if self.overlay_component is None: return console.deprecate( feature_name="overlay_component", reason="Use `extra_app_wraps` to add the overlay component instead.", deprecation_version="0.8.2", removal_version="0.9.0", ) overlay_component = self._generate_component(self.overlay_component) for k, component in self._pages.items(): self._pages[k] = self._add_overlay_to_component( component, overlay_component ) def _setup_sticky_badge(self): """Add the sticky badge to the app.""" from reflex.components.component import memo @memo def memoized_badge(): sticky_badge = sticky() sticky_badge._add_style_recursive({}) return sticky_badge self.app_wraps[0, "StickyBadge"] = lambda _: memoized_badge() def _apply_decorated_pages(self): """Add @rx.page decorated pages to the app.""" app_name = get_config().app_name for render, kwargs in DECORATED_PAGES[app_name]: self.add_page(render, **kwargs) def _validate_var_dependencies(self, state: type[BaseState] | None = None) -> None: """Validate the dependencies of the vars in the app. Args: state: The state to validate the dependencies for. Raises: VarDependencyError: When a computed var has an invalid dependency. """ if not self._state: return if not state: state = self._state for var in state.computed_vars.values(): if not var._cache: continue deps = var._deps(objclass=state) for state_name, dep_set in deps.items(): state_cls = ( state.get_root_state().get_class_substate(state_name) if state_name != state.get_full_name() else state ) for dep in dep_set: if dep not in state_cls.vars and dep not in state_cls.backend_vars: msg = f"ComputedVar {var._name} on state {state.__name__} has an invalid dependency {state_name}.{dep}" raise exceptions.VarDependencyError(msg) for substate in state.class_subclasses: self._validate_var_dependencies(substate) def _compile( self, prerender_routes: bool = False, dry_run: bool = False, use_rich: bool = True, ): """Compile the app and output it to the pages folder. Args: prerender_routes: Whether to prerender the routes. dry_run: Whether to compile the app without saving it. use_rich: Whether to use rich progress bars. Raises: ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined. FileNotFoundError: When a plugin requires a file that does not exist. """ from reflex.utils.exceptions import ReflexRuntimeError self._apply_decorated_pages() self._pages = {} def get_compilation_time() -> str: return str(datetime.now().time()).split(".")[0] should_compile = self._should_compile() backend_dir = prerequisites.get_backend_dir() if not dry_run and not should_compile and backend_dir.exists(): stateful_pages_marker = backend_dir / constants.Dirs.STATEFUL_PAGES if stateful_pages_marker.exists(): with stateful_pages_marker.open("r") as f: stateful_pages = json.load(f) for route in stateful_pages: console.debug(f"BE Evaluating stateful page: {route}") self._compile_page(route, save_page=False) self._add_optional_endpoints() return # Render a default 404 page if the user didn't supply one if constants.Page404.SLUG not in self._unevaluated_pages: self.add_page(route=constants.Page404.SLUG) # Fix up the style. self.style = evaluate_style_namespaces(self.style) # Add the app wrappers. app_wrappers: dict[tuple[int, str], Component] = { # Default app wrap component renders {children} (0, "AppWrap"): AppWrap.create() } if self.theme is not None: # If a theme component was provided, wrap the app with it app_wrappers[20, "Theme"] = self.theme # Get the env mode. config = get_config() if config.react_strict_mode: app_wrappers[200, "StrictMode"] = StrictMode.create() if not should_compile and not dry_run: with console.timing("Evaluate Pages (Backend)"): for route in self._unevaluated_pages: console.debug(f"Evaluating page: {route}") self._compile_page(route, save_page=should_compile) # Save the pages which created new states at eval time. self._write_stateful_pages_marker() # Add the optional endpoints (_upload) self._add_optional_endpoints() return # Create a progress bar. progress = ( Progress( *Progress.get_default_columns()[:-1], MofNCompleteColumn(), TimeElapsedColumn(), ) if use_rich else console.PoorProgress() ) # try to be somewhat accurate - but still not 100% adhoc_steps_without_executor = 7 fixed_pages_within_executor = 4 plugin_count = len(config.plugins) progress.start() task = progress.add_task( f"[{get_compilation_time()}] Compiling:", total=len(self._unevaluated_pages) + ((len(self._unevaluated_pages) + len(self._pages)) * 3) + fixed_pages_within_executor + adhoc_steps_without_executor + plugin_count, ) with console.timing("Evaluate Pages (Frontend)"): performance_metrics: list[tuple[str, float]] = [] for route in self._unevaluated_pages: console.debug(f"Evaluating page: {route}") start = timer() self._compile_page(route, save_page=should_compile) end = timer() performance_metrics.append((route, end - start)) progress.advance(task) console.debug( "Slowest pages:\n" + "\n".join( f"{route}: {time * 1000:.1f}ms" for route, time in sorted( performance_metrics, key=operator.itemgetter(1), reverse=True )[:10] ) ) # Save the pages which created new states at eval time. self._write_stateful_pages_marker() # Add the optional endpoints (_upload) self._add_optional_endpoints() self._validate_var_dependencies() self._setup_overlay_component() if config.show_built_with_reflex is None: if ( get_compile_context() == constants.CompileContext.DEPLOY and prerequisites.get_user_tier() in ["pro", "team", "enterprise"] ): config.show_built_with_reflex = False else: config.show_built_with_reflex = True if is_prod_mode() and config.show_built_with_reflex: self._setup_sticky_badge() progress.advance(task) # Store the compile results. compile_results: list[tuple[str, str]] = [] progress.advance(task) # Track imports found. all_imports = {} if (toaster := self.toaster) is not None: from reflex.components.component import memo @memo def memoized_toast_provider(): return toaster toast_provider = Fragment.create(memoized_toast_provider()) app_wrappers[44, "ToasterProvider"] = toast_provider # Add the app wraps to the app. for key, app_wrap in chain( self.app_wraps.items(), self.extra_app_wraps.items() ): # If the app wrap is a callable, generate the component component = app_wrap(self._state is not None) if component is not None: app_wrappers[key] = component # Compile custom components. ( memo_components_output, memo_components_result, memo_components_imports, ) = compiler.compile_memo_components(dict.fromkeys(CUSTOM_COMPONENTS.values())) compile_results.append((memo_components_output, memo_components_result)) all_imports.update(memo_components_imports) progress.advance(task) with console.timing("Collect all imports and app wraps"): # This has to happen before compiling stateful components as that # prevents recursive functions from reaching all components. for component in self._pages.values(): # Add component._get_all_imports() to all_imports. all_imports.update(component._get_all_imports()) # Add the app wrappers from this component. app_wrappers.update(component._get_all_app_wrap_components()) progress.advance(task) # Perform auto-memoization of stateful components. with console.timing("Auto-memoize StatefulComponents"): ( stateful_components_path, stateful_components_code, page_components, ) = compiler.compile_stateful_components( self._pages.values(), progress_function=lambda task=task: progress.advance(task), ) progress.advance(task) # Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State. if code_uses_state_contexts(stateful_components_code) and self._state is None: msg = ( "To access rx.State in frontend components, at least one " "subclass of rx.State must be defined in the app." ) raise ReflexRuntimeError(msg) compile_results.append((stateful_components_path, stateful_components_code)) progress.advance(task) # Compile the root document before fork. compile_results.append( compiler.compile_document_root( self.head_components, html_lang=self.html_lang, html_custom_attrs=( {"suppressHydrationWarning": True, **self.html_custom_attrs} if self.html_custom_attrs else {"suppressHydrationWarning": True} ), ) ) progress.advance(task) # Copy the assets. assets_src = Path.cwd() / constants.Dirs.APP_ASSETS if assets_src.is_dir() and not dry_run: with console.timing("Copy assets"): path_ops.update_directory_tree( src=assets_src, dest=( Path.cwd() / prerequisites.get_web_dir() / constants.Dirs.PUBLIC ), ) executor = ExecutorType.get_executor_from_environment() for route, component in zip(self._pages, page_components, strict=True): ExecutorSafeFunctions.COMPONENTS[route] = component modify_files_tasks: list[tuple[str, str, Callable[[str], str]]] = [] with console.timing("Compile to Javascript"), executor as executor: result_futures: list[ concurrent.futures.Future[ list[tuple[str, str]] | tuple[str, str] | None ] ] = [] def _submit_work( fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None], *args: P.args, **kwargs: P.kwargs, ): f = executor.submit(fn, *args, **kwargs) f.add_done_callback(lambda _: progress.advance(task)) result_futures.append(f) # Compile the pre-compiled pages. for route in self._pages: _submit_work( ExecutorSafeFunctions.compile_page, route, ) # Compile the root stylesheet with base styles. _submit_work( compiler.compile_root_stylesheet, self.stylesheets, self.reset_style ) # Compile the theme. _submit_work(compile_theme, self.style) def _submit_work_without_advancing( fn: Callable[P, list[tuple[str, str]] | tuple[str, str] | None], *args: P.args, **kwargs: P.kwargs, ): f = executor.submit(fn, *args, **kwargs) result_futures.append(f) for plugin in config.plugins: plugin.pre_compile( add_save_task=_submit_work_without_advancing, add_modify_task=( lambda *args, plugin=plugin: modify_files_tasks.append(( plugin.__class__.__module__ + plugin.__class__.__name__, *args, )) ), unevaluated_pages=list(self._unevaluated_pages.values()), ) # Wait for all compilation tasks to complete. for future in concurrent.futures.as_completed(result_futures): if (result := future.result()) is not None: if isinstance(result, list): compile_results.extend(result) else: compile_results.append(result) progress.advance(task, advance=len(config.plugins)) app_root = self._app_root(app_wrappers=app_wrappers) # Get imports from AppWrap components. all_imports.update(app_root._get_all_imports()) progress.advance(task) # Compile the contexts. compile_results.append( compiler.compile_contexts(self._state, self.theme), ) if self.theme is not None: # Fix #2992 by removing the top-level appearance prop self.theme.appearance = None # pyright: ignore[reportAttributeAccessIssue] progress.advance(task) # Compile the app root. compile_results.append( compiler.compile_app(app_root), ) progress.advance(task) progress.stop() if dry_run: return # Install frontend packages. with console.timing("Install Frontend Packages"): self._get_frontend_packages(all_imports) # Setup the react-router.config.js frontend_skeleton.update_react_router_config( prerender_routes=prerender_routes, ) if is_prod_mode(): # Empty the .web pages directory. compiler.purge_web_pages_dir() else: # In dev mode, delete removed pages and update existing pages. keep_files = [Path(output_path) for output_path, _ in compile_results] for p in Path( prerequisites.get_web_dir() / constants.Dirs.PAGES / constants.Dirs.ROUTES ).rglob("*"): if p.is_file() and p not in keep_files: # Remove pages that are no longer in the app. p.unlink() output_mapping: dict[Path, str] = {} for output_path, code in compile_results: path = compiler_utils.resolve_path_of_web_dir(output_path) if path in output_mapping: console.warn( f"Path {path} has two different outputs. The first one will be used." ) else: output_mapping[path] = code for plugin in config.plugins: for static_file_path, content in plugin.get_static_assets(): path = compiler_utils.resolve_path_of_web_dir(static_file_path) if path in output_mapping: console.warn( f"Plugin {plugin.__class__.__name__} is trying to write to {path} but it already exists. The plugin file will be ignored." ) else: output_mapping[path] = ( content.decode("utf-8") if isinstance(content, bytes) else content ) for plugin_name, file_path, modify_fn in modify_files_tasks: path = compiler_utils.resolve_path_of_web_dir(file_path) file_content = output_mapping.get(path) if file_content is None: if path.exists(): file_content = path.read_text() else: msg = f"Plugin {plugin_name} is trying to modify {path} but it does not exist." raise FileNotFoundError(msg) output_mapping[path] = modify_fn(file_content) with console.timing("Write to Disk"): for output_path, code in output_mapping.items(): compiler_utils.write_file(output_path, code) def _write_stateful_pages_marker(self): """Write list of routes that create dynamic states for the backend to use later.""" if self._state is not None: stateful_pages_marker = ( prerequisites.get_backend_dir() / constants.Dirs.STATEFUL_PAGES ) stateful_pages_marker.parent.mkdir(parents=True, exist_ok=True) with stateful_pages_marker.open("w") as f: json.dump(list(self._stateful_pages), f) def add_all_routes_endpoint(self): """Add an endpoint to the app that returns all the routes.""" if not self._api: return def all_routes(_request: Request) -> Response: return JSONResponse(list(self._unevaluated_pages.keys())) self._api.add_route( str(constants.Endpoint.ALL_ROUTES), all_routes, methods=["GET"] ) @contextlib.asynccontextmanager async def modify_state( self, token: str, background: bool = False, previous_dirty_vars: dict[str, set[str]] | None = None, **context: Unpack[StateModificationContext], ) -> AsyncIterator[BaseState]: """Modify the state out of band. Args: token: The token to modify the state for. background: Whether the modification is happening in a background task. previous_dirty_vars: Vars that are considered dirty from a previous operation. Yields: The state to modify. Raises: RuntimeError: If the app has not been initialized yet. """ if self.event_namespace is None: msg = "App has not been initialized yet." raise RuntimeError(msg) # Get exclusive access to the state. async with self.state_manager.modify_state_with_links( token, previous_dirty_vars=previous_dirty_vars, **context ) as state: # No other event handler can modify the state while in this context. yield state delta = await state._get_resolved_delta() state._clean() if delta: # When the frontend vars are modified emit the delta to the frontend. await self.event_namespace.emit_update( update=StateUpdate( delta=delta, final=True if not background else None, ), token=token, ) def _process_background( self, state: BaseState, event: Event ) -> asyncio.Task | None: """Process an event in the background and emit updates as they arrive. Args: state: The state to process the event for. event: The event to process. Returns: Task if the event was backgroundable, otherwise None """ substate, handler = state._get_event_handler(event) if not handler.is_background: return None substate = StateProxy(substate, event) async def _coro(): """Coroutine to process the event and emit updates inside an asyncio.Task. Raises: RuntimeError: If the app has not been initialized yet. """ if self.event_namespace is None: msg = "App has not been initialized yet." raise RuntimeError(msg) # Process the event. async for update in state._process_event( handler=handler, state=substate, payload=event.payload ): # Postprocess the event. update = await self._postprocess(state, event, update) # Send the update to the client. await self.event_namespace.emit_update( update=update, token=event.token, ) task = asyncio.create_task( _coro(), name=f"reflex_background_task|{event.name}|{time.time()}|{event.token}", ) self._background_tasks.add(task) # Clean up task from background_tasks set when complete. task.add_done_callback(self._background_tasks.discard) return task def _validate_exception_handlers(self): """Validate the custom event exception handlers for front- and backend. Raises: ValueError: If the custom exception handlers are invalid. """ frontend_arg_spec = { "exception": Exception, } backend_arg_spec = { "exception": Exception, } for handler_domain, handler_fn, handler_spec in zip( ["frontend", "backend"], [self.frontend_exception_handler, self.backend_exception_handler], [ frontend_arg_spec, backend_arg_spec, ], strict=True, ): if hasattr(handler_fn, "__name__"): fn_name_ = handler_fn.__name__ else: fn_name_ = type(handler_fn).__name__ if isinstance(handler_fn, functools.partial): msg = f"Provided custom {handler_domain} exception handler `{fn_name_}` is a partial function. Please provide a named function instead." raise ValueError(msg) if not callable(handler_fn): msg = f"Provided custom {handler_domain} exception handler `{fn_name_}` is not a function." raise ValueError(msg) # Allow named functions only as lambda functions cannot be introspected if fn_name_ == "": msg = f"Provided custom {handler_domain} exception handler `{fn_name_}` is a lambda function. Please use a named function instead." raise ValueError(msg) # Check if the function has the necessary annotations and types in the right order argspec = inspect.getfullargspec(handler_fn) arg_annotations = { k: eval(v) if isinstance(v, str) else v for k, v in argspec.annotations.items() if k not in ["args", "kwargs", "return"] } for required_arg_index, required_arg in enumerate(handler_spec): if required_arg not in arg_annotations: msg = f"Provided custom {handler_domain} exception handler `{fn_name_}` does not take the required argument `{required_arg}`" raise ValueError(msg) if list(arg_annotations.keys())[required_arg_index] != required_arg: msg = ( f"Provided custom {handler_domain} exception handler `{fn_name_}` has the wrong argument order." f"Expected `{required_arg}` as the {required_arg_index + 1} argument but got `{list(arg_annotations.keys())[required_arg_index]}`" ) raise ValueError(msg) if not issubclass(arg_annotations[required_arg], Exception): msg = ( f"Provided custom {handler_domain} exception handler `{fn_name_}` has the wrong type for {required_arg} argument." f"Expected to be `Exception` but got `{arg_annotations[required_arg]}`" ) raise ValueError(msg) # Check if the return type is valid for backend exception handler if handler_domain == "backend": sig = inspect.signature(self.backend_exception_handler) return_type = ( eval(sig.return_annotation) if isinstance(sig.return_annotation, str) else sig.return_annotation ) valid = bool( return_type == EventSpec or return_type == EventSpec | None or return_type == list[EventSpec] or return_type == inspect.Signature.empty or return_type is None ) if not valid: msg = ( f"Provided custom {handler_domain} exception handler `{fn_name_}` has the wrong return type." f"Expected `EventSpec | list[EventSpec] | None` but got `{return_type}`" ) raise ValueError(msg) async def process( app: App, event: Event, sid: str, headers: dict, client_ip: str ) -> AsyncGenerator[StateUpdate]: """Process an event. Args: app: The app to process the event for. event: The event to process. sid: The Socket.IO session id. headers: The client headers. client_ip: The client_ip. Yields: The state updates after processing the event. Raises: Exception: If a reflex specific error occurs during processing the event. """ from reflex.utils import telemetry try: # Add request data to the state. router_data = event.router_data router_data.update({ constants.RouteVar.QUERY: format.format_query_params(event.router_data), constants.RouteVar.CLIENT_TOKEN: event.token, constants.RouteVar.SESSION_ID: sid, constants.RouteVar.HEADERS: headers, constants.RouteVar.CLIENT_IP: client_ip, }) # Get the state for the session exclusively. async with app.state_manager.modify_state_with_links( event.substate_token, event=event ) as state: # When this is a brand new instance of the state, signal the # frontend to reload before processing it. if ( not state.router_data and event.name != get_hydrate_event(state) and app.event_namespace is not None ): await asyncio.create_task( app.event_namespace.emit( "reload", data=event, to=sid, ), name=f"reflex_emit_reload|{event.name}|{time.time()}|{event.token}", ) return router_data[constants.RouteVar.PATH] = "/" + ( app.router(path) or "404" if (path := router_data.get(constants.RouteVar.PATH)) else "404" ).removeprefix("/") # re-assign only when the value is different if state.router_data != router_data: # assignment will recurse into substates and force recalculation of # dependent ComputedVar (dynamic route variables) state.router_data = router_data state.router = RouterData.from_router_data(router_data) # Preprocess the event. update = await app._preprocess(state, event) # If there was an update, yield it. if update is not None: yield update # Only process the event if there is no update. else: if app._process_background(state, event) is not None: # `final=True` allows the frontend send more events immediately. yield StateUpdate(final=True) else: # Process the event synchronously. async for update in state._process(event): # Postprocess the event. update = await app._postprocess(state, event, update) # Yield the update. yield update except Exception as ex: telemetry.send_error(ex, context="backend") app.backend_exception_handler(ex) raise def ping(_request: Request) -> Response: """Test API endpoint. Args: _request: The Starlette request object. Returns: The response. """ return JSONResponse("pong") async def health(_request: Request) -> JSONResponse: """Health check endpoint to assess the status of the database and Redis services. Args: _request: The Starlette request object. Returns: JSONResponse: A JSON object with the health status: - "status" (bool): Overall health, True if all checks pass. - "db" (bool or str): Database status - True, False, or "NA". - "redis" (bool or str): Redis status - True, False, or "NA". """ health_status = {"status": True} status_code = 200 tasks = [] if prerequisites.check_db_used(): from reflex.model import get_db_status tasks.append(run_in_thread(get_db_status)) if prerequisites.check_redis_used(): tasks.append(prerequisites.get_redis_status()) results = await asyncio.gather(*tasks) for result in results: health_status |= result if "redis" in health_status and health_status["redis"] is None: health_status["redis"] = False if not all(health_status.values()): health_status["status"] = False status_code = 503 return JSONResponse(content=health_status, status_code=status_code) class _UploadStreamingResponse(StreamingResponse): """Streaming response that always releases upload form resources.""" _on_finish: Callable[[], Awaitable[None]] def __init__( self, *args: Any, on_finish: Callable[[], Awaitable[None]], **kwargs: Any, ) -> None: super().__init__(*args, **kwargs) self._on_finish = on_finish async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: try: await super().__call__(scope, receive, send) finally: await self._on_finish() def upload(app: App): """Upload a file. Args: app: The app to upload the file for. Returns: The upload function. """ async def upload_file(request: Request): """Upload a file. Args: request: The Starlette request object. Returns: StreamingResponse yielding newline-delimited JSON of StateUpdate emitted by the upload handler. Raises: UploadValueError: if there are no args with supported annotation. UploadTypeError: if a background task is used as the handler. HTTPException: when the request does not include token / handler headers. """ from reflex.utils.exceptions import UploadTypeError, UploadValueError # Get the files from the request. try: form_data = await request.form() except ClientDisconnect: return Response() # user cancelled form_data_closed = False async def _close_form_data() -> None: """Close the parsed form data exactly once.""" nonlocal form_data_closed if form_data_closed: return form_data_closed = True await form_data.close() async def _create_upload_event() -> Event: """Create an upload event using the live Starlette temp files. Returns: The upload event backed by the original temp files. """ files = form_data.getlist("files") if not files: msg = "No files were uploaded." raise UploadValueError(msg) token = request.headers.get("reflex-client-token") handler = request.headers.get("reflex-event-handler") if not token or not handler: raise HTTPException( status_code=400, detail="Missing reflex-client-token or reflex-event-handler header.", ) # Get the state for the session. substate_token = _substate_key(token, handler.rpartition(".")[0]) state = await app.state_manager.get_state(substate_token) handler_upload_param = () _current_state, event_handler = state._get_event_handler(handler) if event_handler.is_background: msg = f"@rx.event(background=True) is not supported for upload handler `{handler}`." raise UploadTypeError(msg) func = event_handler.fn if isinstance(func, functools.partial): func = func.func for k, v in get_type_hints(func).items(): if types.is_generic_alias(v) and types._issubclass( get_args(v)[0], UploadFile, ): handler_upload_param = (k, v) break if not handler_upload_param: msg = ( f"`{handler}` handler should have a parameter annotated as " "list[rx.UploadFile]" ) raise UploadValueError(msg) # Keep the parsed form data alive until the upload event finishes so # the underlying Starlette temp files remain available to the handler. file_uploads = [] for file in files: if not isinstance(file, StarletteUploadFile): raise UploadValueError( "Uploaded file is not an UploadFile." + str(file) ) file_uploads.append( UploadFile( file=file.file, path=Path(file.filename.lstrip("/")) if file.filename else None, size=file.size, headers=file.headers, ) ) return Event( token=token, name=handler, payload={handler_upload_param[0]: file_uploads}, ) event: Event | None = None try: event = await _create_upload_event() finally: if event is None: await _close_form_data() async def _ndjson_updates(): """Process the upload event, generating ndjson updates. Yields: Each state update as JSON followed by a new line. """ # Process the event. async with app.state_manager.modify_state_with_links( event.substate_token, event=event ) as state: async for update in state._process(event): # Postprocess the event. update = await app._postprocess(state, event, update) yield update.json() + "\n" # Stream updates to client return _UploadStreamingResponse( _ndjson_updates(), media_type="application/x-ndjson", on_finish=_close_form_data, ) return upload_file class EventNamespace(AsyncNamespace): """The event namespace.""" # The application object. app: App def __init__(self, namespace: str, app: App): """Initialize the event namespace. Args: namespace: The namespace. app: The application object. """ super().__init__(namespace) self.app = app # Use TokenManager for distributed duplicate tab prevention self._token_manager = TokenManager.create() @property def token_to_sid(self) -> Mapping[str, str]: """Get token to SID mapping for backward compatibility. Note: this mapping is read-only. Returns: The token to SID mapping. """ # For backward compatibility, expose the underlying dict return self._token_manager.token_to_sid @property def sid_to_token(self) -> dict[str, str]: """Get SID to token mapping for backward compatibility. Returns: The SID to token mapping dict. """ # For backward compatibility, expose the underlying dict return self._token_manager.sid_to_token async def on_connect(self, sid: str, environ: dict): """Event for when the websocket is connected. Args: sid: The Socket.IO session id. environ: The request information, including HTTP headers. """ if isinstance(self._token_manager, RedisTokenManager): # Make sure this instance is watching for updates from other instances. self._token_manager.ensure_lost_and_found_task(self.emit_update) query_params = urllib.parse.parse_qs(environ.get("QUERY_STRING", "")) token_list = query_params.get("token", []) if token_list: await self.link_token_to_sid(sid, token_list[0]) else: console.warn(f"No token provided in connection for session {sid}") subprotocol = environ.get("HTTP_SEC_WEBSOCKET_PROTOCOL") if subprotocol and subprotocol != constants.Reflex.VERSION: console.warn( f"Frontend version {subprotocol} for session {sid} does not match the backend version {constants.Reflex.VERSION}." ) def on_disconnect(self, sid: str) -> asyncio.Task | None: """Event for when the websocket disconnects. Args: sid: The Socket.IO session id. Returns: An asyncio Task for cleaning up the token, or None. """ # Get token before cleaning up disconnect_token = self.sid_to_token.get(sid) if disconnect_token: # Use async cleanup through token manager task = asyncio.create_task( self._token_manager.disconnect_token(disconnect_token, sid), name=f"reflex_disconnect_token|{disconnect_token}|{time.time()}", ) # Don't await to avoid blocking disconnect, but handle potential errors task.add_done_callback( lambda t: ( t.exception() and console.error(f"Token cleanup error: {t.exception()}") ) ) return task return None async def emit_update(self, update: StateUpdate, token: str) -> None: """Emit an update to the client. Args: update: The state update to send. token: The client token (tab) associated with the event. """ client_token, _ = _split_substate_key(token) socket_record = self._token_manager.token_to_socket.get(client_token) if ( socket_record is None or socket_record.instance_id != self._token_manager.instance_id ): if isinstance(self._token_manager, RedisTokenManager): # The socket belongs to another instance of the app, send it to the lost and found. if not await self._token_manager.emit_lost_and_found( client_token, update ): console.warn( f"Failed to send delta to lost and found for client {token!r}" ) else: # If the socket record is None, we are not connected to a client. Prevent sending # updates to all clients. console.warn( f"Attempting to send delta to disconnected client {token!r}" ) return # Creating a task prevents the update from being blocked behind other coroutines. await asyncio.create_task( self.emit(str(constants.SocketEvent.EVENT), update, to=socket_record.sid), name=f"reflex_emit_event|{token}|{socket_record.sid}|{time.time()}", ) async def on_event(self, sid: str, data: Any): """Event for receiving front-end websocket events. Args: sid: The Socket.IO session id. data: The event data. Raises: RuntimeError: If the Socket.IO is badly initialized. EventDeserializationError: If the event data is not a dictionary. """ fields = data if isinstance(fields, str): console.warn( "Received event data as a string. This generally should not happen and may indicate a bug." f" Event data: {fields}" ) try: fields = json.loads(fields) except json.JSONDecodeError as ex: msg = f"Failed to deserialize event data: {fields}." raise exceptions.EventDeserializationError(msg) from ex if not isinstance(fields, dict): msg = f"Event data must be a dictionary, but received {fields} of type {type(fields)}." raise exceptions.EventDeserializationError(msg) try: # Get the event. event = Event(**{k: v for k, v in fields.items() if k in _EVENT_FIELDS}) except (TypeError, ValueError) as ex: msg = f"Failed to deserialize event data: {fields}." raise exceptions.EventDeserializationError(msg) from ex # Correct the token if it doesn't match what we expect for this SID expected_token = self.sid_to_token.get(sid) if expected_token and event.token != expected_token: # Create new event with corrected token since Event is frozen from dataclasses import replace event = replace(event, token=expected_token) # Get the event environment. if self.app.sio is None: msg = "Socket.IO is not initialized." raise RuntimeError(msg) environ = self.app.sio.get_environ(sid, self.namespace) if environ is None: msg = "Socket.IO environ is not initialized." raise RuntimeError(msg) # Get the client headers. headers = { k.decode("utf-8"): v.decode("utf-8") for (k, v) in environ["asgi.scope"]["headers"] } # Get the client IP try: client_ip = environ["asgi.scope"]["client"][0] headers["asgi-scope-client"] = client_ip except (KeyError, IndexError): client_ip = environ.get("REMOTE_ADDR", "0.0.0.0") # Unroll reverse proxy forwarded headers. client_ip = ( headers .get( "x-forwarded-for", client_ip, ) .partition(",")[0] .strip() ) async with contextlib.aclosing( process(self.app, event, sid, headers, client_ip) ) as updates_gen: # Process the events. async for update in updates_gen: # Emit the update from processing the event. await self.emit_update(update=update, token=event.token) async def on_ping(self, sid: str): """Event for testing the API endpoint. Args: sid: The Socket.IO session id. """ # Emit the test event. await self.emit(str(constants.SocketEvent.PING), "pong", to=sid) async def link_token_to_sid(self, sid: str, token: str): """Link a token to a session id. Args: sid: The Socket.IO session id. token: The client token. """ # Use TokenManager for duplicate detection and Redis support new_token = await self._token_manager.link_token_to_sid(token, sid) if new_token: # Duplicate detected, emit new token to client await self.emit("new_token", new_token, to=sid) # Update client state to apply new sid/token for running background tasks. async with self.app.state_manager.modify_state( _substate_key(new_token or token, self.app.state_manager.state) ) as state: state.router_data[constants.RouteVar.SESSION_ID] = sid state.router = RouterData.from_router_data(state.router_data) ================================================ FILE: reflex/app_mixins/__init__.py ================================================ """Reflex AppMixins package.""" from .lifespan import LifespanMixin from .middleware import MiddlewareMixin from .mixin import AppMixin ================================================ FILE: reflex/app_mixins/lifespan.py ================================================ """Mixin that allow tasks to run during the whole app lifespan.""" from __future__ import annotations import asyncio import contextlib import dataclasses import functools import inspect import time from collections.abc import Callable, Coroutine from starlette.applications import Starlette from reflex.utils import console from reflex.utils.exceptions import InvalidLifespanTaskTypeError from .mixin import AppMixin @dataclasses.dataclass class LifespanMixin(AppMixin): """A Mixin that allow tasks to run during the whole app lifespan.""" # Lifespan tasks that are planned to run. lifespan_tasks: set[asyncio.Task | Callable] = dataclasses.field( default_factory=set ) @contextlib.asynccontextmanager async def _run_lifespan_tasks(self, app: Starlette): running_tasks = [] try: async with contextlib.AsyncExitStack() as stack: for task in self.lifespan_tasks: run_msg = f"Started lifespan task: {task.__name__} as {{type}}" # pyright: ignore [reportAttributeAccessIssue] if isinstance(task, asyncio.Task): running_tasks.append(task) else: task_name = task.__name__ signature = inspect.signature(task) if "app" in signature.parameters: task = functools.partial(task, app=app) t_ = task() if isinstance(t_, contextlib._AsyncGeneratorContextManager): await stack.enter_async_context(t_) console.debug(run_msg.format(type="asynccontextmanager")) elif isinstance(t_, Coroutine): task_ = asyncio.create_task( t_, name=f"reflex_lifespan_task|{task_name}|{time.time()}", ) task_.add_done_callback(lambda t: t.result()) running_tasks.append(task_) console.debug(run_msg.format(type="coroutine")) else: console.debug(run_msg.format(type="function")) yield finally: for task in running_tasks: console.debug(f"Canceling lifespan task: {task}") task.cancel(msg="lifespan_cleanup") # Disassociate sid / token pairings so they can be reconnected properly. try: event_namespace = self.event_namespace # pyright: ignore[reportAttributeAccessIssue] except AttributeError: pass else: try: if event_namespace: await event_namespace._token_manager.disconnect_all() except Exception as e: console.error(f"Error during lifespan cleanup: {e}") # Flush any pending writes from the state manager. try: state_manager = self.state_manager # pyright: ignore[reportAttributeAccessIssue] except AttributeError: pass else: await state_manager.close() def register_lifespan_task(self, task: Callable | asyncio.Task, **task_kwargs): """Register a task to run during the lifespan of the app. Args: task: The task to register. **task_kwargs: The kwargs of the task. Raises: InvalidLifespanTaskTypeError: If the task is a generator function. """ if inspect.isgeneratorfunction(task) or inspect.isasyncgenfunction(task): msg = f"Task {task.__name__} of type generator must be decorated with contextlib.asynccontextmanager." raise InvalidLifespanTaskTypeError(msg) task_name = task.__name__ # pyright: ignore [reportAttributeAccessIssue] if task_kwargs: original_task = task task = functools.partial(task, **task_kwargs) # pyright: ignore [reportArgumentType] functools.update_wrapper(task, original_task) # pyright: ignore [reportArgumentType] self.lifespan_tasks.add(task) console.debug(f"Registered lifespan task: {task_name}") ================================================ FILE: reflex/app_mixins/middleware.py ================================================ """Middleware Mixin that allow to add middleware to the app.""" from __future__ import annotations import dataclasses import inspect from reflex.event import Event from reflex.middleware import HydrateMiddleware, Middleware from reflex.state import BaseState, StateUpdate from .mixin import AppMixin @dataclasses.dataclass class MiddlewareMixin(AppMixin): """Middleware Mixin that allow to add middleware to the app.""" # Middleware to add to the app. Users should use `add_middleware`. _middlewares: list[Middleware] = dataclasses.field(default_factory=list) def _init_mixin(self): self._middlewares.append(HydrateMiddleware()) def add_middleware(self, middleware: Middleware, index: int | None = None): """Add middleware to the app. Args: middleware: The middleware to add. index: The index to add the middleware at. """ if index is None: self._middlewares.append(middleware) else: self._middlewares.insert(index, middleware) async def _preprocess(self, state: BaseState, event: Event) -> StateUpdate | None: """Preprocess the event. This is where middleware can modify the event before it is processed. Each middleware is called in the order it was added to the app. If a middleware returns an update, the event is not processed and the update is returned. Args: state: The state to preprocess. event: The event to preprocess. Returns: An optional state to return. """ for middleware in self._middlewares: out = middleware.preprocess(app=self, state=state, event=event) # pyright: ignore [reportArgumentType] if inspect.isawaitable(out): out = await out if out is not None: return out return None async def _postprocess( self, state: BaseState, event: Event, update: StateUpdate ) -> StateUpdate: """Postprocess the event. This is where middleware can modify the delta after it is processed. Each middleware is called in the order it was added to the app. Args: state: The state to postprocess. event: The event to postprocess. update: The current state update. Returns: The state update to return. """ out = update for middleware in self._middlewares: out = middleware.postprocess( app=self, # pyright: ignore [reportArgumentType] state=state, event=event, update=update, ) if inspect.isawaitable(out): out = await out return out # pyright: ignore[reportReturnType] ================================================ FILE: reflex/app_mixins/mixin.py ================================================ """Default mixin for all app mixins.""" import dataclasses @dataclasses.dataclass class AppMixin: """Define the base class for all app mixins.""" def _init_mixin(self): """Initialize the mixin. Any App mixin can override this method to perform any initialization. """ ================================================ FILE: reflex/assets.py ================================================ """Helper functions for adding assets to the app.""" import inspect from pathlib import Path from reflex import constants from reflex.environment import EnvironmentVariables def remove_stale_external_asset_symlinks(): """Remove broken symlinks and empty directories in assets/external/. When a Python module directory that uses rx.asset(shared=True) is renamed or deleted, stale symlinks remain in assets/external/ pointing to the old path. This cleanup prevents issues with file watchers detecting symlink re-creation during import. """ external_dir = ( Path.cwd() / constants.Dirs.APP_ASSETS / constants.Dirs.EXTERNAL_APP_ASSETS ) if not external_dir.exists(): return # Remove broken symlinks. broken = [ p for p in external_dir.rglob("*") if p.is_symlink() and not p.resolve().exists() ] for path in broken: path.unlink() # Remove empty directories left behind (deepest first). for dirpath in sorted(external_dir.rglob("*"), reverse=True): if dirpath.is_dir() and not dirpath.is_symlink() and not any(dirpath.iterdir()): dirpath.rmdir() def asset( path: str, shared: bool = False, subfolder: str | None = None, _stack_level: int = 1, ) -> str: """Add an asset to the app, either shared as a symlink or local. Shared/External/Library assets: Place the file next to your including python file. Links the file to the app's external assets directory. Example: ```python # my_custom_javascript.js is a shared asset located next to the including python file. rx.script(src=rx.asset(path="my_custom_javascript.js", shared=True)) rx.image(src=rx.asset(path="test_image.png", shared=True, subfolder="subfolder")) ``` Local/Internal assets: Place the file in the app's assets/ directory. Example: ```python # local_image.png is an asset located in the app's assets/ directory. It cannot be shared when developing a library. rx.image(src=rx.asset(path="local_image.png")) ``` Args: path: The relative path of the asset. subfolder: The directory to place the shared asset in. shared: Whether to expose the asset to other apps. _stack_level: The stack level to determine the calling file, defaults to the immediate caller 1. When using rx.asset via a helper function, increase this number for each helper function in the stack. Returns: The relative URL to the asset. Raises: FileNotFoundError: If the file does not exist. ValueError: If subfolder is provided for local assets. """ assets = constants.Dirs.APP_ASSETS backend_only = EnvironmentVariables.REFLEX_BACKEND_ONLY.get() # Local asset handling if not shared: cwd = Path.cwd() src_file_local = cwd / assets / path if subfolder is not None: msg = "Subfolder is not supported for local assets." raise ValueError(msg) if not backend_only and not src_file_local.exists(): msg = f"File not found: {src_file_local}" raise FileNotFoundError(msg) return f"/{path}" # Shared asset handling # Determine the file by which the asset is exposed. frame = inspect.stack()[_stack_level] calling_file = frame.filename module = inspect.getmodule(frame[0]) assert module is not None external = constants.Dirs.EXTERNAL_APP_ASSETS src_file_shared = Path(calling_file).parent / path if not src_file_shared.exists(): msg = f"File not found: {src_file_shared}" raise FileNotFoundError(msg) caller_module_path = module.__name__.replace(".", "/") subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path # Symlink the asset to the app's external assets directory if running frontend. if not backend_only: # Create the asset folder in the currently compiling app. asset_folder = Path.cwd() / assets / external / subfolder asset_folder.mkdir(parents=True, exist_ok=True) dst_file = asset_folder / path if not dst_file.exists() and ( not dst_file.is_symlink() or dst_file.resolve() != src_file_shared.resolve() ): try: dst_file.symlink_to(src_file_shared) except FileExistsError: # This happens when Simon builds the app on a bind mount in a docker container. dst_file.unlink() dst_file.symlink_to(src_file_shared) return f"/{external}/{subfolder}/{path}" ================================================ FILE: reflex/base.py ================================================ """Define the base Reflex class.""" from importlib.util import find_spec if find_spec("pydantic") and find_spec("pydantic.v1"): from pydantic.v1 import BaseModel from reflex.utils.compat import ModelMetaclassLazyAnnotations class Base(BaseModel, metaclass=ModelMetaclassLazyAnnotations): """The base class subclassed by all Reflex classes. This class wraps Pydantic and provides common methods such as serialization and setting fields. Any data structure that needs to be transferred between the frontend and backend should subclass this class. """ class Config: """Pydantic config.""" arbitrary_types_allowed = True use_enum_values = True extra = "allow" def __init_subclass__(cls): """Warn that rx.Base is deprecated.""" from reflex.utils import console console.deprecate( feature_name="rx.Base", reason=f"{cls!r} is subclassing rx.Base. You can subclass from `pydantic.BaseModel` directly instead or use dataclasses if possible.", deprecation_version="0.8.15", removal_version="0.9.0", ) super().__init_subclass__() def json(self) -> str: """Convert the object to a json string. Returns: The object as a json string. """ from reflex.utils.serializers import serialize return self.__config__.json_dumps( self.dict(), default=serialize, ) else: class PydanticNotFoundFallback: """Fallback base class for environments without Pydantic.""" def __init__(self, *args, **kwargs): """Initialize the base class. Args: *args: Positional arguments. **kwargs: Keyword arguments. Raises: ImportError: As Pydantic is not installed. """ msg = ( "Pydantic is not installed. Please install it to use rx.Base." "You can install it with `pip install pydantic`." ) raise ImportError(msg) Base = PydanticNotFoundFallback # pyright: ignore[reportAssignmentType] ================================================ FILE: reflex/compiler/__init__.py ================================================ """The Reflex compiler.""" ================================================ FILE: reflex/compiler/compiler.py ================================================ """Compiler for the reflex apps.""" from __future__ import annotations import sys from collections.abc import Callable, Iterable, Sequence from inspect import getmodule from pathlib import Path from typing import TYPE_CHECKING, Any from reflex import constants from reflex.compiler import templates, utils from reflex.components.base.fragment import Fragment from reflex.components.component import ( BaseComponent, Component, ComponentStyle, CustomComponent, StatefulComponent, ) from reflex.config import get_config from reflex.constants.compiler import PageNames, ResetStylesheet from reflex.constants.state import FIELD_MARKER from reflex.environment import environment from reflex.state import BaseState from reflex.style import SYSTEM_COLOR_MODE from reflex.utils import console, path_ops from reflex.utils.exceptions import ReflexError from reflex.utils.exec import is_prod_mode from reflex.utils.format import to_title_case from reflex.utils.imports import ImportVar, ParsedImportDict from reflex.utils.prerequisites import get_web_dir from reflex.vars.base import LiteralVar, Var def _apply_common_imports( imports: dict[str, list[ImportVar]], ): imports.setdefault("@emotion/react", []).append(ImportVar("jsx")) imports.setdefault("react", []).extend( [ImportVar("Fragment"), ImportVar("useEffect")], ) def _compile_document_root(root: Component) -> str: """Compile the document root. Args: root: The document root to compile. Returns: The compiled document root. """ document_root_imports = root._get_all_imports() _apply_common_imports(document_root_imports) return templates.document_root_template( imports=utils.compile_imports(document_root_imports), document=root.render(), ) def _normalize_library_name(lib: str) -> str: """Normalize the library name. Args: lib: The library name to normalize. Returns: The normalized library name. """ if lib == "react": return "React" return lib.replace("$/", "").replace("@", "").replace("/", "_").replace("-", "_") def _compile_app(app_root: Component) -> str: """Compile the app template component. Args: app_root: The app root to compile. Returns: The compiled app. """ from reflex.components.dynamic import bundled_libraries window_libraries = [ (_normalize_library_name(name), name) for name in bundled_libraries ] window_libraries_deduped = list(dict.fromkeys(window_libraries)) app_root_imports = app_root._get_all_imports() _apply_common_imports(app_root_imports) return templates.app_root_template( imports=utils.compile_imports(app_root_imports), custom_codes=app_root._get_all_custom_code(), hooks=app_root._get_all_hooks(), window_libraries=window_libraries_deduped, render=app_root.render(), dynamic_imports=app_root._get_all_dynamic_imports(), ) def _compile_theme(theme: str) -> str: """Compile the theme. Args: theme: The theme to compile. Returns: The compiled theme. """ return templates.theme_template(theme=theme) def _compile_contexts(state: type[BaseState] | None, theme: Component | None) -> str: """Compile the initial state and contexts. Args: state: The app state. theme: The top-level app theme. Returns: The compiled context file. """ appearance = getattr(theme, "appearance", None) if appearance is None or str(LiteralVar.create(appearance)) == '"inherit"': appearance = LiteralVar.create(SYSTEM_COLOR_MODE) return ( templates.context_template( initial_state=utils.compile_state(state), state_name=state.get_name(), client_storage=utils.compile_client_storage(state), is_dev_mode=not is_prod_mode(), default_color_mode=str(appearance), ) if state else templates.context_template( is_dev_mode=not is_prod_mode(), default_color_mode=str(appearance), ) ) def _compile_page(component: BaseComponent) -> str: """Compile the component. Args: component: The component to compile. Returns: The compiled component. """ imports = component._get_all_imports() _apply_common_imports(imports) imports = utils.compile_imports(imports) # Compile the code to render the component. return templates.page_template( imports=imports, dynamic_imports=sorted(component._get_all_dynamic_imports()), custom_codes=component._get_all_custom_code(), hooks=component._get_all_hooks(), render=component.render(), ) def compile_root_stylesheet( stylesheets: list[str], reset_style: bool = True ) -> tuple[str, str]: """Compile the root stylesheet. Args: stylesheets: The stylesheets to include in the root stylesheet. reset_style: Whether to include CSS reset for margin and padding. Returns: The path and code of the compiled root stylesheet. """ output_path = utils.get_root_stylesheet_path() code = _compile_root_stylesheet(stylesheets, reset_style) return output_path, code def _validate_stylesheet(stylesheet_full_path: Path, assets_app_path: Path) -> None: """Validate the stylesheet. Args: stylesheet_full_path: The stylesheet to validate. assets_app_path: The path to the assets directory. Raises: ValueError: If the stylesheet is not supported. FileNotFoundError: If the stylesheet is not found. """ suffix = stylesheet_full_path.suffix[1:] if stylesheet_full_path.suffix else "" if suffix not in constants.Reflex.STYLESHEETS_SUPPORTED: msg = f"Stylesheet file {stylesheet_full_path} is not supported." raise ValueError(msg) if not stylesheet_full_path.absolute().is_relative_to(assets_app_path.absolute()): msg = f"Cannot include stylesheets from outside the assets directory: {stylesheet_full_path}" raise FileNotFoundError(msg) if not stylesheet_full_path.name: msg = f"Stylesheet file name cannot be empty: {stylesheet_full_path}" raise ValueError(msg) if ( len( stylesheet_full_path .absolute() .relative_to(assets_app_path.absolute()) .parts ) == 1 and stylesheet_full_path.stem == PageNames.STYLESHEET_ROOT ): msg = f"Stylesheet file name cannot be '{PageNames.STYLESHEET_ROOT}': {stylesheet_full_path}" raise ValueError(msg) RADIX_THEMES_STYLESHEET = "@radix-ui/themes/styles.css" def _compile_root_stylesheet(stylesheets: list[str], reset_style: bool = True) -> str: """Compile the root stylesheet. Args: stylesheets: The stylesheets to include in the root stylesheet. reset_style: Whether to include CSS reset for margin and padding. Returns: The compiled root stylesheet. Raises: FileNotFoundError: If a specified stylesheet in assets directory does not exist. """ # Add stylesheets from plugins. sheets = [] # Add CSS reset if enabled if reset_style: # Reference the vendored style reset file (automatically copied from .templates/web) sheets.append(f"./{ResetStylesheet.FILENAME}") sheets.extend( [RADIX_THEMES_STYLESHEET] + [ sheet for plugin in get_config().plugins for sheet in plugin.get_stylesheet_paths() ] ) failed_to_import_sass = False assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS stylesheets_files: list[Path] = [] stylesheets_urls = [] for stylesheet in stylesheets: if not utils.is_valid_url(stylesheet): # check if stylesheet provided exists. stylesheet_full_path = assets_app_path / stylesheet.strip("/") if not stylesheet_full_path.exists(): msg = f"The stylesheet file {stylesheet_full_path} does not exist." raise FileNotFoundError(msg) if stylesheet_full_path.is_dir(): all_files = ( file for ext in constants.Reflex.STYLESHEETS_SUPPORTED for file in stylesheet_full_path.rglob("*." + ext) ) for file in all_files: if file.is_dir(): continue # Validate the stylesheet. _validate_stylesheet(file, assets_app_path) stylesheets_files.append(file) else: # Validate the stylesheet. _validate_stylesheet(stylesheet_full_path, assets_app_path) stylesheets_files.append(stylesheet_full_path) else: stylesheets_urls.append(stylesheet) sheets.extend(dict.fromkeys(stylesheets_urls)) for stylesheet in stylesheets_files: target_path = stylesheet.relative_to(assets_app_path).with_suffix(".css") target = get_web_dir() / constants.Dirs.STYLES / target_path target.parent.mkdir(parents=True, exist_ok=True) if stylesheet.suffix == ".css": path_ops.cp(src=stylesheet, dest=target, overwrite=True) else: try: from sass import compile as sass_compile target.write_text( data=sass_compile( filename=str(stylesheet), output_style="compressed", ), encoding="utf8", ) except ImportError: failed_to_import_sass = True str_target_path = "./" + str(target_path) sheets.append(str_target_path) if str_target_path not in sheets else None if failed_to_import_sass: console.error( 'The `libsass` package is required to compile sass/scss stylesheet files. Run `pip install "libsass>=0.23.0"`.' ) return templates.styles_template(stylesheets=sheets) def _compile_component(component: Component | StatefulComponent) -> str: """Compile a single component. Args: component: The component to compile. Returns: The compiled component. """ return templates.component_template(component=component) def _compile_memo_components( components: Iterable[CustomComponent], ) -> tuple[str, dict[str, list[ImportVar]]]: """Compile the components. Args: components: The components to compile. Returns: The compiled components. """ imports = { "react": [ImportVar(tag="memo")], f"$/{constants.Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } component_renders = [] # Compile each component. for component in components: component_render, component_imports = utils.compile_custom_component(component) component_renders.append(component_render) imports = utils.merge_imports(imports, component_imports) _apply_common_imports(imports) dynamic_imports = { comp_import: None for comp_render in component_renders if "dynamic_imports" in comp_render for comp_import in comp_render["dynamic_imports"] } custom_codes = { comp_custom_code: None for comp_render in component_renders for comp_custom_code in comp_render.get("custom_code", []) } # Compile the components page. return ( templates.memo_components_template( imports=utils.compile_imports(imports), components=component_renders, dynamic_imports=sorted(dynamic_imports), custom_codes=custom_codes, ), imports, ) def _get_shared_components_recursive( component: BaseComponent, rendered_components: dict[str, None], all_import_dicts: list[ParsedImportDict], ): """Get the shared components for a component and its children. A shared component is a StatefulComponent that appears in 2 or more pages and is a candidate for writing to a common file and importing into each page where it is used. Args: component: The component to collect shared StatefulComponents for. rendered_components: A dict to store the rendered shared components in. all_import_dicts: A list to store the imports of all shared components in. """ for child in component.children: # Depth-first traversal. _get_shared_components_recursive(child, rendered_components, all_import_dicts) # When the component is referenced by more than one page, render it # to be included in the STATEFUL_COMPONENTS module. # Skip this step in dev mode, thereby avoiding potential hot reload errors for larger apps if isinstance(component, StatefulComponent) and component.references > 1: # Reset this flag to render the actual component. component.rendered_as_shared = False # Include dynamic imports in the shared component. if dynamic_imports := component._get_all_dynamic_imports(): rendered_components.update(dict.fromkeys(dynamic_imports)) # Include custom code in the shared component. rendered_components.update(component._get_all_custom_code(export=True)) # Include all imports in the shared component. all_import_dicts.append(component._get_all_imports()) # Indicate that this component now imports from the shared file. component.rendered_as_shared = True def _compile_stateful_components( page_components: list[BaseComponent], ) -> str: """Walk the page components and extract shared stateful components. Any StatefulComponent that is shared by more than one page will be rendered to a separate module and marked rendered_as_shared so subsequent renderings will import the component from the shared module instead of directly including the code for it. Args: page_components: The Components or StatefulComponents to compile. Returns: The rendered stateful components code. """ all_import_dicts = [] rendered_components = {} for page_component in page_components: _get_shared_components_recursive( page_component, rendered_components, all_import_dicts ) # Don't import from the file that we're about to create. all_imports = utils.merge_imports(*all_import_dicts) all_imports.pop( f"$/{constants.Dirs.UTILS}/{constants.PageNames.STATEFUL_COMPONENTS}", None ) if rendered_components: _apply_common_imports(all_imports) return templates.stateful_components_template( imports=utils.compile_imports(all_imports), memoized_code="\n".join(rendered_components), ) def compile_document_root( head_components: list[Component], html_lang: str | None = None, html_custom_attrs: dict[str, Var | Any] | None = None, ) -> tuple[str, str]: """Compile the document root. Args: head_components: The components to include in the head. html_lang: The language of the document, will be added to the html root element. html_custom_attrs: custom attributes added to the html root element. Returns: The path and code of the compiled document root. """ # Get the path for the output file. output_path = str( get_web_dir() / constants.Dirs.PAGES / constants.PageNames.DOCUMENT_ROOT ) # Create the document root. document_root = utils.create_document_root( head_components, html_lang=html_lang, html_custom_attrs=html_custom_attrs ) # Compile the document root. code = _compile_document_root(document_root) return output_path, code def compile_app(app_root: Component) -> tuple[str, str]: """Compile the app root. Args: app_root: The app root component to compile. Returns: The path and code of the compiled app wrapper. """ # Get the path for the output file. output_path = str( get_web_dir() / constants.Dirs.PAGES / constants.PageNames.APP_ROOT ) # Compile the document root. code = _compile_app(app_root) return output_path, code def compile_theme(style: ComponentStyle) -> tuple[str, str]: """Compile the theme. Args: style: The style to compile. Returns: The path and code of the compiled theme. """ output_path = utils.get_theme_path() # Create the theme. theme = utils.create_theme(style) # Compile the theme. code = _compile_theme(str(LiteralVar.create(theme))) return output_path, code def compile_contexts( state: type[BaseState] | None, theme: Component | None, ) -> tuple[str, str]: """Compile the initial state / context. Args: state: The app state. theme: The top-level app theme. Returns: The path and code of the compiled context. """ # Get the path for the output file. output_path = utils.get_context_path() return output_path, _compile_contexts(state, theme) def compile_page(path: str, component: BaseComponent) -> tuple[str, str]: """Compile a single page. Args: path: The path to compile the page to. component: The component to compile. Returns: The path and code of the compiled page. """ # Get the path for the output file. output_path = utils.get_page_path(path) # Add the style to the component. code = _compile_page(component) return output_path, code def compile_memo_components( components: Iterable[CustomComponent], ) -> tuple[str, str, dict[str, list[ImportVar]]]: """Compile the custom components. Args: components: The custom components to compile. Returns: The path and code of the compiled components. """ # Get the path for the output file. output_path = utils.get_components_path() # Compile the components. code, imports = _compile_memo_components(components) return output_path, code, imports def compile_stateful_components( pages: Iterable[Component], progress_function: Callable[[], None], ) -> tuple[str, str, list[BaseComponent]]: """Separately compile components that depend on State vars. StatefulComponents are compiled as their own component functions with their own useContext declarations, which allows page components to be stateless and avoid re-rendering along with parts of the page that actually depend on state. Args: pages: The pages to extract stateful components from. progress_function: A function to call to indicate progress, called once per page. Returns: The path and code of the compiled stateful components. """ output_path = utils.get_stateful_components_path() page_components = [] for page in pages: # Compile the stateful components page_component = StatefulComponent.compile_from(page) or page progress_function() page_components.append(page_component) code = _compile_stateful_components(page_components) if is_prod_mode() else "" return output_path, code, page_components def purge_web_pages_dir(): """Empty out .web/pages directory.""" if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR.get(): # Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set. return # Empty out the web pages directory. utils.empty_dir( get_web_dir() / constants.Dirs.PAGES, keep_files=["routes.js", "entry.client.js"], ) if TYPE_CHECKING: from reflex.app import ComponentCallable, UnevaluatedPage def _into_component_once( component: Component | ComponentCallable | tuple[Component, ...] | str | Var | int | float, ) -> Component | None: """Convert a component to a Component. Args: component: The component to convert. Returns: The converted component. """ if isinstance(component, Component): return component if isinstance(component, (Var, int, float, str)): return Fragment.create(component) if isinstance(component, Sequence): return Fragment.create(*component) return None def readable_name_from_component( component: Component | ComponentCallable, ) -> str | None: """Get the readable name of a component. Args: component: The component to get the name of. Returns: The readable name of the component. """ if isinstance(component, Component): return type(component).__name__ if isinstance(component, (Var, int, float, str)): return str(component) if isinstance(component, Sequence): return ", ".join(str(c) for c in component) if callable(component): module_name = getattr(component, "__module__", None) if module_name is not None: module = getmodule(component) if module is not None: module_name = module.__name__ if module_name is not None: return f"{module_name}.{component.__name__}" return component.__name__ return None def _modify_exception(e: Exception) -> None: """Modify the exception to make it more readable. Args: e: The exception to modify. """ if len(e.args) == 1 and isinstance((msg := e.args[0]), str): while (state_index := msg.find("reflex___")) != -1: dot_index = msg.find(".", state_index) if dot_index == -1: break state_name = msg[state_index:dot_index] module_dot_state_name = state_name.replace("___", ".").rsplit("__", 1)[-1] module_path, _, state_snake_case = module_dot_state_name.rpartition(".") if not state_snake_case: break actual_state_name = to_title_case(state_snake_case) msg = ( f"{msg[:state_index]}{module_path}.{actual_state_name}{msg[dot_index:]}" ) msg = msg.replace(FIELD_MARKER, "") e.args = (msg,) def into_component(component: Component | ComponentCallable) -> Component: """Convert a component to a Component. Args: component: The component to convert. Returns: The converted component. Raises: TypeError: If the component is not a Component. # noqa: DAR401 """ if (converted := _into_component_once(component)) is not None: return converted if not callable(component): msg = f"Expected a Component or callable, got {component!r} of type {type(component)}" raise TypeError(msg) try: component_called = component() except KeyError as e: if isinstance(e, ReflexError): _modify_exception(e) raise key = e.args[0] if e.args else None if key is not None and isinstance(key, Var): raise TypeError( "Cannot access a primitive map with a Var. Consider calling rx.Var.create() on the map." ).with_traceback(e.__traceback__) from None raise except TypeError as e: if isinstance(e, ReflexError): _modify_exception(e) raise message = e.args[0] if e.args else None if message and isinstance(message, str): if message.endswith("has no len()") and ( "ArrayCastedVar" in message or "ObjectCastedVar" in message or "StringCastedVar" in message ): raise TypeError( "Cannot pass a Var to a built-in function. Consider using .length() for accessing the length of an iterable Var." ).with_traceback(e.__traceback__) from None if message.endswith(( "indices must be integers or slices, not NumberCastedVar", "indices must be integers or slices, not BooleanCastedVar", )): raise TypeError( "Cannot index into a primitive sequence with a Var. Consider calling rx.Var.create() on the sequence." ).with_traceback(e.__traceback__) from None if "CastedVar" in str(e): raise TypeError( "Cannot pass a Var to a built-in function. Consider moving the operation to the backend, using existing Var operations, or defining a custom Var operation." ).with_traceback(e.__traceback__) from None raise except ReflexError as e: _modify_exception(e) raise if (converted := _into_component_once(component_called)) is not None: return converted msg = f"Expected a Component, got {component_called!r} of type {type(component_called)}" raise TypeError(msg) def compile_unevaluated_page( route: str, page: UnevaluatedPage, style: ComponentStyle | None = None, theme: Component | None = None, ) -> Component: """Compiles an uncompiled page into a component and adds meta information. Args: route: The route of the page. page: The uncompiled page object. style: The style of the page. theme: The theme of the page. Returns: The compiled component and whether state should be enabled. Raises: Exception: If an error occurs while evaluating the page. """ try: # Generate the component if it is a callable. component = into_component(page.component) component._add_style_recursive(style or {}, theme) from reflex.utils.format import make_default_page_title component = Fragment.create(component) meta_args = { "title": ( page.title if page.title is not None else make_default_page_title(get_config().app_name, route) ), "image": page.image, "meta": page.meta, } if page.description is not None: meta_args["description"] = page.description # Add meta information to the component. utils.add_meta( component, **meta_args, ) except Exception as e: if sys.version_info >= (3, 11): e.add_note(f"Happened while evaluating page {route!r}") raise else: return component class ExecutorSafeFunctions: """Helper class to allow parallelisation of parts of the compilation process. This class (and its class attributes) are available at global scope. In a multiprocessing context (like when using a ProcessPoolExecutor), the content of this global class is logically replicated to any FORKED process. How it works: * Before the child process is forked, ensure that we stash any input data required by any future function call in the child process. * After the child process is forked, the child process will have a copy of the global class, which includes the previously stashed input data. * Any task submitted to the child process simply needs a way to communicate which input data the requested function call requires. Why do we need this? Passing input data directly to child process often not possible because the input data is not picklable. The mechanic described here removes the need to pickle the input data at all. Limitations: * This can never support returning unpicklable OUTPUT data. * Any object mutations done by the child process will not propagate back to the parent process (fork goes one way!). """ COMPONENTS: dict[str, BaseComponent] = {} UNCOMPILED_PAGES: dict[str, UnevaluatedPage] = {} @classmethod def compile_page(cls, route: str) -> tuple[str, str]: """Compile a page. Args: route: The route of the page to compile. Returns: The path and code of the compiled page. """ return compile_page(route, cls.COMPONENTS[route]) @classmethod def compile_unevaluated_page( cls, route: str, style: ComponentStyle, theme: Component | None, ) -> tuple[str, Component, tuple[str, str]]: """Compile an unevaluated page. Args: route: The route of the page to compile. style: The style of the page. theme: The theme of the page. Returns: The route, compiled component, and compiled page. """ component = compile_unevaluated_page( route, cls.UNCOMPILED_PAGES[route], style, theme ) return route, component, compile_page(route, component) @classmethod def compile_theme(cls, style: ComponentStyle | None) -> tuple[str, str]: """Compile the theme. Args: style: The style to compile. Returns: The path and code of the compiled theme. Raises: ValueError: If the style is not set. """ if style is None: msg = "STYLE should be set" raise ValueError(msg) return compile_theme(style) ================================================ FILE: reflex/compiler/templates.py ================================================ """Templates to use in the reflex compiler.""" from __future__ import annotations import json from collections.abc import Iterable, Mapping from typing import TYPE_CHECKING, Any, Literal from reflex import constants from reflex.constants import Hooks from reflex.constants.state import CAMEL_CASE_MEMO_MARKER from reflex.utils.format import format_state_name, json_dumps from reflex.vars.base import VarData if TYPE_CHECKING: from reflex.compiler.utils import _ImportDict from reflex.components.component import Component, StatefulComponent def _sort_hooks( hooks: dict[str, VarData | None], ) -> tuple[list[str], list[str], list[str]]: """Sort the hooks by their position. Args: hooks: The hooks to sort. Returns: The sorted hooks. """ internal_hooks = [] pre_trigger_hooks = [] post_trigger_hooks = [] for hook, data in hooks.items(): if data and data.position and data.position == Hooks.HookPosition.INTERNAL: internal_hooks.append(hook) elif not data or ( not data.position or data.position == constants.Hooks.HookPosition.PRE_TRIGGER ): pre_trigger_hooks.append(hook) elif ( data and data.position and data.position == constants.Hooks.HookPosition.POST_TRIGGER ): post_trigger_hooks.append(hook) return internal_hooks, pre_trigger_hooks, post_trigger_hooks class _RenderUtils: @staticmethod def render(component: Mapping[str, Any] | str) -> str: if isinstance(component, str): return component or "null" if "iterable" in component: return _RenderUtils.render_iterable_tag(component) if "match_cases" in component: return _RenderUtils.render_match_tag(component) if "cond_state" in component: return _RenderUtils.render_condition_tag(component) if (contents := component.get("contents")) is not None: return contents or "null" return _RenderUtils.render_tag(component) @staticmethod def render_tag(component: Mapping[str, Any]) -> str: name = component.get("name") or "Fragment" props = f"{{{','.join(component['props'])}}}" rendered_children = [ _RenderUtils.render(child) for child in component.get("children", []) if child ] return f"jsx({name},{props},{','.join(rendered_children)})" @staticmethod def render_condition_tag(component: Any) -> str: return f"({component['cond_state']}?({_RenderUtils.render(component['true_value'])}):({_RenderUtils.render(component['false_value'])}))" @staticmethod def render_iterable_tag(component: Any) -> str: children_rendered = "".join([ _RenderUtils.render(child) for child in component.get("children", []) ]) return f"Array.prototype.map.call({component['iterable_state']} ?? [],(({component['arg_name']},{component['arg_index']})=>({children_rendered})))" @staticmethod def render_match_tag(component: Any) -> str: cases_code = "" for conditions, return_value in component["match_cases"]: for condition in conditions: cases_code += f" case JSON.stringify({condition}):\n" cases_code += f""" return {_RenderUtils.render(return_value)}; break; """ return f"""(() => {{ switch (JSON.stringify({component["cond"]})) {{ {cases_code} default: return {_RenderUtils.render(component["default"])}; break; }} }})()""" @staticmethod def get_import(module: _ImportDict) -> str: default_import = module["default"] rest_imports = module["rest"] if default_import and rest_imports: rest_imports_str = ",".join(sorted(rest_imports)) return f'import {default_import}, {{{rest_imports_str}}} from "{module["lib"]}"' if default_import: return f'import {default_import} from "{module["lib"]}"' if rest_imports: rest_imports_str = ",".join(sorted(rest_imports)) return f'import {{{rest_imports_str}}} from "{module["lib"]}"' return f'import "{module["lib"]}"' def rxconfig_template(app_name: str): """Template for the Reflex config file. Args: app_name: The name of the application. Returns: Rendered Reflex config file content as string. """ return f"""import reflex as rx config = rx.Config( app_name="{app_name}", plugins=[ rx.plugins.SitemapPlugin(), rx.plugins.TailwindV4Plugin(), ] )""" def document_root_template(*, imports: list[_ImportDict], document: dict[str, Any]): """Template for the document root. Args: imports: List of import statements. document: Document root component. Returns: Rendered document root code as string. """ imports_rendered = "\n".join([_RenderUtils.get_import(mod) for mod in imports]) return f"""{imports_rendered} export function Layout({{children}}) {{ return ( {_RenderUtils.render(document)} ) }}""" def app_root_template( *, imports: list[_ImportDict], custom_codes: Iterable[str], hooks: dict[str, VarData | None], window_libraries: list[tuple[str, str]], render: dict[str, Any], dynamic_imports: set[str], ): """Template for the App root. Args: imports: The list of import statements. custom_codes: The set of custom code snippets. hooks: The dictionary of hooks. window_libraries: The list of window libraries. render: The dictionary of render functions. dynamic_imports: The set of dynamic imports. Returns: Rendered App root component as string. """ imports_str = "\n".join([_RenderUtils.get_import(mod) for mod in imports]) dynamic_imports_str = "\n".join(dynamic_imports) custom_code_str = "\n".join(custom_codes) import_window_libraries = "\n".join([ f'import * as {lib_alias} from "{lib_path}";' for lib_alias, lib_path in window_libraries ]) window_imports_str = "\n".join([ f' "{lib_path}": {lib_alias},' for lib_alias, lib_path in window_libraries ]) return f""" {imports_str} {dynamic_imports_str} import {{ EventLoopProvider, StateProvider, defaultColorMode }} from "$/utils/context"; import {{ ThemeProvider }} from '$/utils/react-theme'; import {{ Layout as AppLayout }} from './_document'; import {{ Outlet }} from 'react-router'; {import_window_libraries} {custom_code_str} function AppWrap({{children}}) {{ {_render_hooks(hooks)} return ({_RenderUtils.render(render)}) }} export function Layout({{children}}) {{ useEffect(() => {{ // Make contexts and state objects available globally for dynamic eval'd components let windowImports = {{ {window_imports_str} }}; window["__reflex"] = windowImports; }}, []); return jsx(AppLayout, {{}}, jsx(ThemeProvider, {{defaultTheme: defaultColorMode, attribute: "class"}}, jsx(StateProvider, {{}}, jsx(EventLoopProvider, {{}}, jsx(AppWrap, {{}}, children) ) ) ) ); }} export default function App() {{ return jsx(Outlet, {{}}); }} """ def theme_template(theme: str): """Template for the theme file. Args: theme: The theme to render. Returns: Rendered theme file content as string. """ return f"""export default {theme}""" def context_template( *, is_dev_mode: bool, default_color_mode: str, initial_state: dict[str, Any] | None = None, state_name: str | None = None, client_storage: dict[str, dict[str, dict[str, Any]]] | None = None, ): """Template for the context file. Args: initial_state: The initial state for the context. state_name: The name of the state. client_storage: The client storage for the context. is_dev_mode: Whether the app is in development mode. default_color_mode: The default color mode for the context. Returns: Rendered context file content as string. """ initial_state = initial_state or {} state_contexts_str = "".join([ f"{format_state_name(state_name)}: createContext(null)," for state_name in initial_state ]) state_str = ( rf""" export const state_name = "{state_name}" export const exception_state_name = "{constants.CompileVars.FRONTEND_EXCEPTION_STATE_FULL}" // These events are triggered on initial load and each page navigation. export const onLoadInternalEvent = () => {{ const internal_events = []; // Get tracked cookie and local storage vars to send to the backend. const client_storage_vars = hydrateClientStorage(clientStorage); // But only send the vars if any are actually set in the browser. if (client_storage_vars && Object.keys(client_storage_vars).length !== 0) {{ internal_events.push( ReflexEvent( '{state_name}.{constants.CompileVars.UPDATE_VARS_INTERNAL}', {{vars: client_storage_vars}}, ), ); }} // `on_load_internal` triggers the correct on_load event(s) for the current page. // If the page does not define any on_load event, this will just set `is_hydrated = true`. internal_events.push(ReflexEvent('{state_name}.{constants.CompileVars.ON_LOAD_INTERNAL}')); return internal_events; }} // The following events are sent when the websocket connects or reconnects. export const initialEvents = () => [ ReflexEvent('{state_name}.{constants.CompileVars.HYDRATE}'), ...onLoadInternalEvent() ] """ if state_name else """ export const state_name = undefined export const exception_state_name = undefined export const onLoadInternalEvent = () => [] export const initialEvents = () => [] """ ) state_reducer_str = "\n".join( rf'const [{format_state_name(state_name)}, dispatch_{format_state_name(state_name)}] = useReducer(applyDelta, initialState["{state_name}"])' for state_name in initial_state ) create_state_contexts_str = "\n".join( rf"createElement(StateContexts.{format_state_name(state_name)},{{value: {format_state_name(state_name)}}}," for state_name in initial_state ) dispatchers_str = "\n".join( f'"{state_name}": dispatch_{format_state_name(state_name)},' for state_name in initial_state ) return rf"""import {{ createContext, useContext, useMemo, useReducer, useState, createElement, useEffect }} from "react" import {{ applyDelta, ReflexEvent, hydrateClientStorage, useEventLoop, refs }} from "$/utils/state" import {{ jsx }} from "@emotion/react"; export const initialState = {"{}" if not initial_state else json_dumps(initial_state)} export const defaultColorMode = {default_color_mode} export const ColorModeContext = createContext(null); export const UploadFilesContext = createContext(null); export const DispatchContext = createContext(null); export const StateContexts = {{{state_contexts_str}}}; export const EventLoopContext = createContext(null); export const clientStorage = {"{}" if client_storage is None else json.dumps(client_storage)} {state_str} export const isDevMode = {json.dumps(is_dev_mode)}; export function UploadFilesProvider({{ children }}) {{ const [filesById, setFilesById] = useState({{}}) refs["__clear_selected_files"] = (id) => setFilesById(filesById => {{ const newFilesById = {{...filesById}} delete newFilesById[id] return newFilesById }}) return createElement( UploadFilesContext.Provider, {{ value: [filesById, setFilesById] }}, children ); }} export function ClientSide(component) {{ return ({{ children, ...props }}) => {{ const [Component, setComponent] = useState(null); useEffect(() => {{ async function load() {{ const comp = await component(); setComponent(() => comp); }} load(); }}, []); return Component ? jsx(Component, props, children) : null; }}; }} export function EventLoopProvider({{ children }}) {{ const dispatch = useContext(DispatchContext) const [addEvents, connectErrors] = useEventLoop( dispatch, initialEvents, clientStorage, ) return createElement( EventLoopContext.Provider, {{ value: [addEvents, connectErrors] }}, children ); }} export function StateProvider({{ children }}) {{ {state_reducer_str} const dispatchers = useMemo(() => {{ return {{ {dispatchers_str} }} }}, []) return ( {create_state_contexts_str} createElement(DispatchContext, {{value: dispatchers}}, children) {")" * len(initial_state)} ) }}""" def component_template(component: Component | StatefulComponent): """Template to render a component tag. Args: component: The component to render. Returns: Rendered component as string. """ return _RenderUtils.render(component.render()) def page_template( imports: Iterable[_ImportDict], dynamic_imports: Iterable[str], custom_codes: Iterable[str], hooks: dict[str, VarData | None], render: dict[str, Any], ): """Template for a single react page. Args: imports: List of import statements. dynamic_imports: List of dynamic import statements. custom_codes: List of custom code snippets. hooks: Dictionary of hooks. render: Render function for the component. Returns: Rendered React page component as string. """ imports_str = "\n".join([_RenderUtils.get_import(imp) for imp in imports]) custom_code_str = "\n".join(custom_codes) dynamic_imports_str = "\n".join(dynamic_imports) hooks_str = _render_hooks(hooks) return f"""{imports_str} {dynamic_imports_str} {custom_code_str} export default function Component() {{ {hooks_str} return ( {_RenderUtils.render(render)} ) }}""" def package_json_template( scripts: dict[str, str], dependencies: dict[str, str], dev_dependencies: dict[str, str], overrides: dict[str, str], ): """Template for package.json. Args: scripts: The scripts to include in the package.json file. dependencies: The dependencies to include in the package.json file. dev_dependencies: The devDependencies to include in the package.json file. overrides: The overrides to include in the package.json file. Returns: Rendered package.json content as string. """ return json.dumps({ "name": "reflex", "type": "module", "scripts": scripts, "dependencies": dependencies, "devDependencies": dev_dependencies, "overrides": overrides, }) def vite_config_template( base: str, hmr: bool, force_full_reload: bool, experimental_hmr: bool, sourcemap: bool | Literal["inline", "hidden"], allowed_hosts: bool | list[str] = False, ): """Template for vite.config.js. Args: base: The base path for the Vite config. hmr: Whether to enable hot module replacement. force_full_reload: Whether to force a full reload on changes. experimental_hmr: Whether to enable experimental HMR features. sourcemap: The sourcemap configuration. allowed_hosts: Allow all hosts (True), specific hosts (list of strings), or only localhost (False). Returns: Rendered vite.config.js content as string. """ if allowed_hosts is True: allowed_hosts_line = "\n allowedHosts: true," elif isinstance(allowed_hosts, list) and allowed_hosts: allowed_hosts_line = f"\n allowedHosts: {json.dumps(allowed_hosts)}," else: allowed_hosts_line = "" return rf"""import {{ fileURLToPath, URL }} from "url"; import {{ reactRouter }} from "@react-router/dev/vite"; import {{ defineConfig }} from "vite"; import safariCacheBustPlugin from "./vite-plugin-safari-cachebust"; // Ensure that bun always uses the react-dom/server.node functions. function alwaysUseReactDomServerNode() {{ return {{ name: "vite-plugin-always-use-react-dom-server-node", enforce: "pre", resolveId(source, importer) {{ if ( typeof importer === "string" && importer.endsWith("/entry.server.node.tsx") && source.includes("react-dom/server") ) {{ return this.resolve("react-dom/server.node", importer, {{ skipSelf: true, }}); }} return null; }}, }}; }} function fullReload() {{ return {{ name: "full-reload", enforce: "pre", handleHotUpdate({{ server }}) {{ server.ws.send({{ type: "full-reload", }}); return []; }} }}; }} export default defineConfig((config) => ({{ plugins: [ alwaysUseReactDomServerNode(), reactRouter(), safariCacheBustPlugin(), ].concat({"[fullReload()]" if force_full_reload else "[]"}), build: {{ assetsDir: "{base}assets".slice(1), sourcemap: {"true" if sourcemap is True else "false" if sourcemap is False else repr(sourcemap)}, rollupOptions: {{ onwarn(warning, warn) {{ if (warning.code === "EVAL" && warning.id && warning.id.endsWith("state.js")) return; warn(warning); }}, jsx: {{}}, output: {{ advancedChunks: {{ groups: [ {{ test: /env.json/, name: "reflex-env", }}, ], }}, }}, }}, }}, experimental: {{ enableNativePlugin: false, hmr: {"true" if experimental_hmr else "false"}, }}, server: {{ port: process.env.PORT,{allowed_hosts_line} hmr: {"true" if hmr else "false"}, watch: {{ ignored: [ "**/.web/backend/**", "**/.web/reflex.install_frontend_packages.cached", ], }}, }}, resolve: {{ mainFields: ["browser", "module", "jsnext"], alias: [ {{ find: "$", replacement: fileURLToPath(new URL("./", import.meta.url)), }}, {{ find: "@", replacement: fileURLToPath(new URL("./public", import.meta.url)), }}, ], }}, }}));""" def stateful_component_template( tag_name: str, memo_trigger_hooks: list[str], component: Component, export: bool ): """Template for stateful component. Args: tag_name: The tag name for the component. memo_trigger_hooks: The memo trigger hooks for the component. component: The component to render. export: Whether to export the component. Returns: Rendered stateful component code as string. """ all_hooks = component._get_all_hooks() return f""" {"export " if export else ""}function {tag_name} () {{ {_render_hooks(all_hooks, memo_trigger_hooks)} return ( {_RenderUtils.render(component.render())} ) }} """ def stateful_components_template(imports: list[_ImportDict], memoized_code: str) -> str: """Template for stateful components. Args: imports: List of import statements. memoized_code: Memoized code for stateful components. Returns: Rendered stateful components code as string. """ imports_str = "\n".join([_RenderUtils.get_import(imp) for imp in imports]) return f"{imports_str}\n{memoized_code}" def memo_components_template( imports: list[_ImportDict], components: list[dict[str, Any]], dynamic_imports: Iterable[str], custom_codes: Iterable[str], ) -> str: """Template for custom component. Args: imports: List of import statements. components: List of component definitions. dynamic_imports: List of dynamic import statements. custom_codes: List of custom code snippets. Returns: Rendered custom component code as string. """ imports_str = "\n".join([_RenderUtils.get_import(imp) for imp in imports]) dynamic_imports_str = "\n".join(dynamic_imports) custom_code_str = "\n".join(custom_codes) components_code = "" for component in components: components_code += f""" export const {component["name"]} = memo(({{ {",".join([f"{prop}:{prop}{CAMEL_CASE_MEMO_MARKER}" for prop in component.get("props", [])])} }}) => {{ {_render_hooks(component.get("hooks", {}))} return( {_RenderUtils.render(component["render"])} ) }}); """ return f""" {imports_str} {dynamic_imports_str} {custom_code_str} {components_code}""" def styles_template(stylesheets: list[str]) -> str: """Template for styles.css. Args: stylesheets: List of stylesheets to include. Returns: Rendered styles.css content as string. """ return "@layer __reflex_base;\n" + "\n".join([ f"@import url('{sheet_name}');" for sheet_name in stylesheets ]) def _render_hooks(hooks: dict[str, VarData | None], memo: list | None = None) -> str: """Render hooks for macros. Args: hooks: Dictionary of hooks to render. memo: Optional list of memo hooks. Returns: Rendered hooks code as string. """ internal, pre_trigger, post_trigger = _sort_hooks(hooks) internal_str = "\n".join(internal) pre_trigger_str = "\n".join(pre_trigger) post_trigger_str = "\n".join(post_trigger) memo_str = "\n".join(memo) if memo is not None else "" return f"{internal_str}\n{pre_trigger_str}\n{memo_str}\n{post_trigger_str}" ================================================ FILE: reflex/compiler/utils.py ================================================ """Common utility functions used in the compiler.""" from __future__ import annotations import asyncio import concurrent.futures import operator import traceback from collections.abc import Mapping, Sequence from datetime import datetime from pathlib import Path from typing import Any, TypedDict from urllib.parse import urlparse from reflex import constants from reflex.components.base import Description, Image, Scripts from reflex.components.base.document import Links, ScrollRestoration from reflex.components.base.document import Meta as ReactMeta from reflex.components.component import Component, ComponentStyle, CustomComponent from reflex.components.el.elements.metadata import Head, Link, Meta, Title from reflex.components.el.elements.other import Html from reflex.components.el.elements.sectioning import Body from reflex.constants.state import FIELD_MARKER from reflex.istate.storage import Cookie, LocalStorage, SessionStorage from reflex.state import BaseState, _resolve_delta from reflex.style import Style from reflex.utils import format, imports, path_ops from reflex.utils.imports import ImportVar, ParsedImportDict from reflex.utils.prerequisites import get_web_dir from reflex.vars.base import Field, Var, VarData # To re-export this function. merge_imports = imports.merge_imports def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]: """Compile an import statement. Args: fields: The set of fields to import from the library. Returns: The libraries for default and rest. default: default library. When install "import def from library". rest: rest of libraries. When install "import {rest1, rest2} from library" Raises: ValueError: If there is more than one default import. """ # ignore the ImportVar fields with render=False during compilation fields_set = {field for field in fields if field.render} # Check for default imports. defaults = {field for field in fields_set if field.is_default} if len(defaults) >= 2: msg = "Only one default import is allowed." raise ValueError(msg) # Get the default import, and the specific imports. default = next(iter({field.name for field in defaults}), "") rest = {field.name for field in fields_set - defaults} return default, sorted(rest) def validate_imports(import_dict: ParsedImportDict): """Verify that the same Tag is not used in multiple import. Args: import_dict: The dict of imports to validate Raises: ValueError: if a conflict on "tag/alias" is detected for an import. """ used_tags = {} for lib, imported_items in import_dict.items(): for imported_item in imported_items: import_name = ( f"{imported_item.tag}/{imported_item.alias}" if imported_item.alias else imported_item.tag ) if import_name in used_tags: already_imported = used_tags[import_name] if (already_imported[0] == "$" and already_imported[1:] == lib) or ( lib[0] == "$" and lib[1:] == already_imported ): used_tags[import_name] = lib if lib[0] == "$" else already_imported continue msg = f"Can not compile, the tag {import_name} is used multiple time from {lib} and {used_tags[import_name]}" raise ValueError(msg) if import_name is not None: used_tags[import_name] = lib class _ImportDict(TypedDict): lib: str default: str rest: list[str] def compile_imports(import_dict: ParsedImportDict) -> list[_ImportDict]: """Compile an import dict. Args: import_dict: The import dict to compile. Returns: The list of import dict. Raises: ValueError: If an import in the dict is invalid. """ collapsed_import_dict: ParsedImportDict = imports.collapse_imports(import_dict) validate_imports(collapsed_import_dict) import_dicts: list[_ImportDict] = [] for lib, fields in collapsed_import_dict.items(): # prevent lib from being rendered on the page if all imports are non rendered kind if not any(f.render for f in fields): continue lib_paths: dict[str, list[ImportVar]] = {} for field in fields: lib_paths.setdefault(field.package_path, []).append(field) compiled = { path: compile_import_statement(fields) for path, fields in lib_paths.items() } for path, (default, rest) in compiled.items(): if not lib: if default: msg = "No default field allowed for empty library." raise ValueError(msg) if rest is None or len(rest) == 0: msg = "No fields to import." raise ValueError(msg) import_dicts.extend(get_import_dict(module) for module in sorted(rest)) continue # remove the version before rendering the package imports formatted_lib = format.format_library_name(lib) + ( path if path != "/" else "" ) import_dicts.append(get_import_dict(formatted_lib, default, rest)) return import_dicts def get_import_dict( lib: str, default: str = "", rest: list[str] | None = None ) -> _ImportDict: """Get dictionary for import template. Args: lib: The importing react library. default: The default module to import. rest: The rest module to import. Returns: A dictionary for import template. """ return _ImportDict( lib=lib, default=default, rest=rest or [], ) def save_error(error: Exception) -> str: """Save the error to a file. Args: error: The error to save. Returns: The path of the saved error. """ timestamp = datetime.now().strftime("%Y-%m-%d__%H-%M-%S") constants.Reflex.LOGS_DIR.mkdir(parents=True, exist_ok=True) log_path = constants.Reflex.LOGS_DIR / f"error_{timestamp}.log" traceback.TracebackException.from_exception(error).print(file=log_path.open("w+")) return str(log_path) def _sorted_keys(d: Mapping[str, Any]) -> dict[str, Any]: """Sort the keys of a dictionary. Args: d: The dictionary to sort. Returns: A new dictionary with sorted keys. """ return dict(sorted(d.items(), key=operator.itemgetter(0))) def compile_state(state: type[BaseState]) -> dict: """Compile the state of the app. Args: state: The app state object. Returns: A dictionary of the compiled state. """ initial_state = state(_reflex_internal_init=True).dict(initial=True) try: _ = asyncio.get_running_loop() except RuntimeError: pass else: with concurrent.futures.ThreadPoolExecutor() as pool: resolved_initial_state = pool.submit( asyncio.run, _resolve_delta(initial_state) ).result() return _sorted_keys(resolved_initial_state) # Normally the compile runs before any event loop starts, we asyncio.run is available for calling. return _sorted_keys(asyncio.run(_resolve_delta(initial_state))) def _compile_client_storage_field( field: Field, ) -> ( tuple[ type[Cookie] | type[LocalStorage] | type[SessionStorage], dict[str, Any], ] | tuple[None, None] ): """Compile the given cookie, local_storage or session_storage field. Args: field: The possible cookie field to compile. Returns: A dictionary of the compiled cookie or None if the field is not cookie-like. """ for field_type in (Cookie, LocalStorage, SessionStorage): if isinstance(field.default, field_type): cs_obj = field.default elif isinstance(field.type_, type) and issubclass(field.type_, field_type): cs_obj = field.type_() else: continue return field_type, cs_obj.options() return None, None def _compile_client_storage_recursive( state: type[BaseState], ) -> tuple[ dict[str, dict[str, Any]], dict[str, dict[str, Any]], dict[str, dict[str, Any]] ]: """Compile the client-side storage for the given state recursively. Args: state: The app state object. Returns: A tuple of the compiled client-side storage info: (cookies, local_storage, session_storage). """ cookies: dict[str, dict[str, Any]] = {} local_storage: dict[str, dict[str, Any]] = {} session_storage: dict[str, dict[str, Any]] = {} state_name = state.get_full_name() for name, field in state.__fields__.items(): if name in state.inherited_vars: # only include vars defined in this state continue state_key = f"{state_name}.{name}" + FIELD_MARKER field_type, options = _compile_client_storage_field(field) if field_type is None or options is None: continue if field_type is Cookie: cookies[state_key] = options elif field_type is LocalStorage: local_storage[state_key] = options elif field_type is SessionStorage: session_storage[state_key] = options else: continue for substate in state.get_substates(): ( substate_cookies, substate_local_storage, substate_session_storage, ) = _compile_client_storage_recursive(substate) cookies.update(substate_cookies) local_storage.update(substate_local_storage) session_storage.update(substate_session_storage) return cookies, local_storage, session_storage def compile_client_storage( state: type[BaseState], ) -> dict[str, dict[str, dict[str, Any]]]: """Compile the client-side storage for the given state. Args: state: The app state object. Returns: A dictionary of the compiled client-side storage info. """ cookies, local_storage, session_storage = _compile_client_storage_recursive(state) return { constants.COOKIES: cookies, constants.LOCAL_STORAGE: local_storage, constants.SESSION_STORAGE: session_storage, } def compile_custom_component( component: CustomComponent, ) -> tuple[dict, ParsedImportDict]: """Compile a custom component. Args: component: The custom component to compile. Returns: A tuple of the compiled component and the imports required by the component. """ # Render the component. render = component.get_component() # Get the imports. imports: ParsedImportDict = { lib: fields for lib, fields in render._get_all_imports().items() if lib != component.library } imports.setdefault("@emotion/react", []).append(ImportVar("jsx")) # Concatenate the props. props = list(component.props) # Compile the component. return ( { "name": component.tag, "props": props, "render": render.render(), "hooks": render._get_all_hooks(), "custom_code": render._get_all_custom_code(), "dynamic_imports": render._get_all_dynamic_imports(), }, imports, ) def create_document_root( head_components: Sequence[Component] | None = None, html_lang: str | None = None, html_custom_attrs: dict[str, Var | Any] | None = None, ) -> Component: """Create the document root. Args: head_components: The components to add to the head. html_lang: The language of the document, will be added to the html root element. html_custom_attrs: custom attributes added to the html root element. Returns: The document root. """ from reflex.utils.misc import preload_color_theme existing_meta_types = set() for component in head_components or []: if isinstance(component, Meta): if component.char_set is not None: # pyright: ignore[reportAttributeAccessIssue] existing_meta_types.add("char_set") if ( (name := component.name) is not None # pyright: ignore[reportAttributeAccessIssue] and name.equals(Var.create("viewport")) ): existing_meta_types.add("viewport") # Always include the framework meta and link tags. always_head_components = [ ReactMeta.create(), Link.create( rel="stylesheet", type="text/css", href=Var( "reflexGlobalStyles", _var_data=VarData( imports={ "$/styles/__reflex_global_styles.css?url": [ ImportVar(tag="reflexGlobalStyles", is_default=True) ] } ), ), ), Links.create(), ] maybe_head_components = [] # Only include these if the user has not specified them. if "char_set" not in existing_meta_types: maybe_head_components.append(Meta.create(char_set="utf-8")) if "viewport" not in existing_meta_types: maybe_head_components.append( Meta.create(name="viewport", content="width=device-width, initial-scale=1") ) # Add theme preload script as the very first component to prevent FOUC theme_preload_components = [preload_color_theme()] head_components = [ *theme_preload_components, *(head_components or []), *maybe_head_components, *always_head_components, ] html_component = Html.create( Head.create(*head_components), Body.create( Var("children"), ScrollRestoration.create(), Scripts.create(), ), lang=html_lang or "en", custom_attrs=html_custom_attrs or {}, ) hooks = html_component._get_all_hooks() if hooks: msg = "You cannot use stateful components or hooks in the document root. Check your head components." raise ValueError(msg) return html_component def create_theme(style: ComponentStyle) -> dict: """Create the base style for the app. Args: style: The style dict for the app. Returns: The base style for the app. """ # Get the global style from the style dict. style_rules = Style({k: v for k, v in style.items() if isinstance(k, str)}) root_style = { # Root styles. ":root": Style({ f"*{k}": v for k, v in style_rules.items() if k.startswith(":") }), # Body styles. "body": Style( {k: v for k, v in style_rules.items() if not k.startswith(":")}, ), } # Return the theme. return {"styles": {"global": root_style}} def _format_route_part(part: str) -> str: if part.startswith("[") and part.endswith("]"): if part.startswith(("[...", "[[...")): return "$" if part.startswith("[["): return "($" + part.removeprefix("[[").removesuffix("]]") + ")" # We don't add [] here since we are reusing them from the input return "$" + part return "[" + part + "]" def _path_to_file_stem(path: str) -> str: if path == "index": return "_index" path = path if path != "index" else "/" name = ".".join([_format_route_part(part) for part in path.split("/")]).lstrip(".") return name + "._index" if not name.endswith("$") else name def get_page_path(path: str) -> str: """Get the path of the compiled JS file for the given page. Args: path: The path of the page. Returns: The path of the compiled JS file. """ return str( get_web_dir() / constants.Dirs.PAGES / constants.Dirs.ROUTES / (_path_to_file_stem(path) + constants.Ext.JSX) ) def get_theme_path() -> str: """Get the path of the base theme style. Returns: The path of the theme style. """ return str( get_web_dir() / constants.Dirs.UTILS / (constants.PageNames.THEME + constants.Ext.JS) ) def get_root_stylesheet_path() -> str: """Get the path of the app root file. Returns: The path of the app root file. """ return str( get_web_dir() / constants.Dirs.STYLES / (constants.PageNames.STYLESHEET_ROOT + constants.Ext.CSS) ) def get_context_path() -> str: """Get the path of the context / initial state file. Returns: The path of the context module. """ return str(get_web_dir() / (constants.Dirs.CONTEXTS_PATH + constants.Ext.JS)) def get_components_path() -> str: """Get the path of the compiled components. Returns: The path of the compiled components. """ return str( get_web_dir() / constants.Dirs.UTILS / (constants.PageNames.COMPONENTS + constants.Ext.JSX), ) def get_stateful_components_path() -> str: """Get the path of the compiled stateful components. Returns: The path of the compiled stateful components. """ return str( get_web_dir() / constants.Dirs.UTILS / (constants.PageNames.STATEFUL_COMPONENTS + constants.Ext.JSX) ) def add_meta( page: Component, title: str, image: str, meta: Sequence[Mapping[str, Any] | Component], description: str | None = None, ) -> Component: """Add metadata to a page. Args: page: The component for the page. title: The title of the page. image: The image for the page. meta: The metadata list. description: The description of the page. Returns: The component with the metadata added. """ meta_tags = [ item if isinstance(item, Component) else Meta.create(**item) for item in meta ] children: list[Any] = [Title.create(title)] if description: children.append(Description.create(content=description)) children.append(Image.create(content=image)) page.children.extend(children) page.children.extend(meta_tags) return page def resolve_path_of_web_dir(path: str | Path) -> Path: """Get the path under the web directory. Args: path: The path to get. It can be a relative or absolute path. Returns: The path under the web directory. """ path = Path(path) web_dir = get_web_dir() if path.is_relative_to(web_dir): return path.absolute() return (web_dir / path).absolute() def write_file(path: str | Path, code: str): """Write the given code to the given path. Args: path: The path to write the code to. code: The code to write. """ path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) if path.exists() and path.read_text(encoding="utf-8") == code: return path.write_text(code, encoding="utf-8") def empty_dir(path: str | Path, keep_files: list[str] | None = None): """Remove all files and folders in a directory except for the keep_files. Args: path: The path to the directory that will be emptied keep_files: List of filenames or foldernames that will not be deleted. """ path = Path(path) # If the directory does not exist, return. if not path.exists(): return # Remove all files and folders in the directory. keep_files = keep_files or [] for element in path.iterdir(): if element.name not in keep_files: path_ops.rm(element) def is_valid_url(url: str) -> bool: """Check if a url is valid. Args: url: The Url to check. Returns: Whether url is valid. """ result = urlparse(url) return all([result.scheme, result.netloc]) ================================================ FILE: reflex/components/__init__.py ================================================ """Import all the components.""" from __future__ import annotations from reflex.utils import lazy_loader _SUBMODULES: set[str] = { "lucide", "core", "datadisplay", "gridjs", "markdown", "moment", "plotly", "radix", "react_player", "react_router", "sonner", "el", "base", "recharts", } _SUBMOD_ATTRS: dict[str, list[str]] = { "component": [ "Component", "NoSSRComponent", ], } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/base/__init__.py ================================================ """Base components.""" from __future__ import annotations from reflex.utils import lazy_loader _SUBMODULES: set[str] = {"app_wrap", "bare"} _SUBMOD_ATTRS: dict[str, list[str]] = { "body": ["Body"], "document": ["Scripts", "Outlet", "ScrollRestoration", "Links", "Meta"], "fragment": [ "Fragment", "fragment", ], "error_boundary": [ "ErrorBoundary", "error_boundary", ], "link": ["RawLink", "ScriptTag"], "meta": ["Description", "Image", "Meta", "Title"], "script": ["Script", "script"], } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/base/app_wrap.py ================================================ """Top-level component that wraps the entire app.""" from reflex.components.base.fragment import Fragment from reflex.components.component import Component from reflex.vars.base import Var class AppWrap(Fragment): """Top-level component that wraps the entire app.""" @classmethod def create(cls) -> Component: """Create a new AppWrap component. Returns: A new AppWrap component containing {children}. """ return super().create(Var(_js_expr="children")) ================================================ FILE: reflex/components/base/bare.py ================================================ """A bare component.""" from __future__ import annotations from collections.abc import Iterator, Sequence from typing import Any from reflex.components.component import BaseComponent, Component, ComponentStyle from reflex.components.tags import Tag from reflex.components.tags.tagless import Tagless from reflex.environment import PerformanceMode, environment from reflex.utils import console from reflex.utils.decorator import once from reflex.utils.imports import ParsedImportDict from reflex.vars import BooleanVar, ObjectVar, Var from reflex.vars.base import GLOBAL_CACHE, VarData from reflex.vars.sequence import LiteralStringVar @once def get_performance_mode(): """Get the performance mode. Returns: The performance mode. """ return environment.REFLEX_PERF_MODE.get() def validate_str(value: str): """Validate a string value. Args: value: The value to validate. Raises: ValueError: If the value is a Var and the performance mode is set to raise. """ perf_mode = get_performance_mode() if perf_mode != PerformanceMode.OFF and value.startswith("reflex___state"): if perf_mode == PerformanceMode.WARN: console.warn( f"Output includes {value!s} which will be displayed as a string. If you are calling `str` on a Var, consider using .to_string() instead." ) elif perf_mode == PerformanceMode.RAISE: msg = f"Output includes {value!s} which will be displayed as a string. If you are calling `str` on a Var, consider using .to_string() instead." raise ValueError(msg) def _components_from_var(var: Var) -> Sequence[BaseComponent]: var_data = var._get_all_var_data() return var_data.components if var_data else () class Bare(Component): """A component with no tag.""" contents: Var[Any] @classmethod def create(cls, contents: Any) -> Component: """Create a Bare component, with no tag. Args: contents: The contents of the component. Returns: The component. """ if isinstance(contents, Var): if isinstance(contents, LiteralStringVar): validate_str(contents._var_value) return cls._unsafe_create(children=[], contents=contents) if isinstance(contents, str): validate_str(contents) contents = Var.create(contents if contents is not None else "") return cls._unsafe_create(children=[], contents=contents) def _get_all_hooks_internal(self) -> dict[str, VarData | None]: """Include the hooks for the component. Returns: The hooks for the component. """ hooks = super()._get_all_hooks_internal() if isinstance(self.contents, Var): for component in _components_from_var(self.contents): hooks |= component._get_all_hooks_internal() return hooks def _get_all_hooks(self) -> dict[str, VarData | None]: """Include the hooks for the component. Returns: The hooks for the component. """ hooks = super()._get_all_hooks() if isinstance(self.contents, Var): for component in _components_from_var(self.contents): hooks |= component._get_all_hooks() return hooks def _get_all_imports(self, collapse: bool = False) -> ParsedImportDict: """Include the imports for the component. Args: collapse: Whether to collapse the imports. Returns: The imports for the component. """ imports = super()._get_all_imports(collapse=collapse) if isinstance(self.contents, Var): var_data = self.contents._get_all_var_data() if var_data: imports |= {k: list(v) for k, v in var_data.imports} return imports def _get_all_dynamic_imports(self) -> set[str]: """Get dynamic imports for the component. Returns: The dynamic imports. """ dynamic_imports = super()._get_all_dynamic_imports() if isinstance(self.contents, Var): for component in _components_from_var(self.contents): dynamic_imports |= component._get_all_dynamic_imports() return dynamic_imports def _get_all_custom_code(self) -> dict[str, None]: """Get custom code for the component. Returns: The custom code. """ custom_code = super()._get_all_custom_code() if isinstance(self.contents, Var): for component in _components_from_var(self.contents): custom_code |= component._get_all_custom_code() return custom_code def _get_all_app_wrap_components( self, *, ignore_ids: set[int] | None = None ) -> dict[tuple[int, str], Component]: """Get the components that should be wrapped in the app. Args: ignore_ids: The ids to ignore when collecting components. Returns: The components that should be wrapped in the app. """ ignore_ids = ignore_ids or set() app_wrap_components = super()._get_all_app_wrap_components( ignore_ids=ignore_ids ) if isinstance(self.contents, Var): for component in _components_from_var(self.contents): component_id = id(component) if isinstance(component, Component) and component_id not in ignore_ids: ignore_ids.add(component_id) app_wrap_components |= component._get_all_app_wrap_components( ignore_ids=ignore_ids ) return app_wrap_components def _get_all_refs(self) -> dict[str, None]: """Get the refs for the children of the component. Returns: The refs for the children. """ refs = super()._get_all_refs() if isinstance(self.contents, Var): for component in _components_from_var(self.contents): refs |= component._get_all_refs() return refs def _render(self) -> Tag: contents = ( Var.create(self.contents) if not isinstance(self.contents, Var) else self.contents ) if isinstance(contents, (BooleanVar, ObjectVar)): return Tagless(contents=f"{contents.to_string()!s}") return Tagless(contents=f"{contents!s}") def render(self) -> dict: """Render the component as a dictionary. This is overridden to provide a short performant path for rendering. Returns: The rendered component. """ contents = ( Var.create(self.contents) if not isinstance(self.contents, Var) else self.contents ) if isinstance(contents, (BooleanVar, ObjectVar)): return {"contents": f"{contents.to_string()!s}"} return {"contents": f"{contents!s}"} def _add_style_recursive( self, style: ComponentStyle, theme: Component | None = None ) -> Component: """Add style to the component and its children. Args: style: The style to add. theme: The theme to add. Returns: The component with the style added. """ new_self = super()._add_style_recursive(style, theme) are_components_touched = False if isinstance(self.contents, Var): for component in _components_from_var(self.contents): if isinstance(component, Component): component._add_style_recursive(style, theme) are_components_touched = True if are_components_touched: GLOBAL_CACHE.clear() return new_self def _get_vars( self, include_children: bool = False, ignore_ids: set[int] | None = None ) -> Iterator[Var]: """Walk all Vars used in this component. Args: include_children: Whether to include Vars from children. ignore_ids: The ids to ignore. Yields: The contents if it is a Var, otherwise nothing. """ yield self.contents ================================================ FILE: reflex/components/base/body.py ================================================ """Display the page body.""" from reflex.components.el import elements class Body(elements.Body): """A body component.""" ================================================ FILE: reflex/components/base/document.py ================================================ """Document components.""" from reflex.components.component import Component class ReactRouterLib(Component): """Root document components.""" library = "react-router" class Meta(ReactRouterLib): """The document meta tags.""" tag = "Meta" class Links(ReactRouterLib): """The document link tags.""" tag = "Links" class ScrollRestoration(ReactRouterLib): """The document scroll restoration.""" tag = "ScrollRestoration" class Outlet(ReactRouterLib): """The document outlet.""" tag = "Outlet" class Scripts(ReactRouterLib): """The document main scripts.""" tag = "Scripts" ================================================ FILE: reflex/components/base/error_boundary.py ================================================ """A React Error Boundary component that catches unhandled frontend exceptions.""" from __future__ import annotations from reflex.components.component import Component from reflex.components.datadisplay.logo import svg_logo from reflex.components.el import a, button, div, h2, hr, p, pre, svg from reflex.event import EventHandler, set_clipboard from reflex.state import FrontendEventExceptionState from reflex.vars.base import Var from reflex.vars.function import ArgsFunctionOperation from reflex.vars.object import ObjectVar def on_error_spec( error: ObjectVar[dict[str, str]], info: ObjectVar[dict[str, str]] ) -> tuple[Var[str], Var[str]]: """The spec for the on_error event handler. Args: error: The error message. info: Additional information about the error. Returns: The arguments for the event handler. """ return ( error.name.to(str) + ": " + error.message.to(str) + "\n" + error.stack.to(str), info.componentStack, ) _ERROR_DISPLAY: str = r"""event_args.error.name + ': ' + event_args.error.message + '\n' + event_args.error.stack""" class ErrorBoundary(Component): """A React Error Boundary component that catches unhandled frontend exceptions.""" library = "react-error-boundary@6.1.1" tag = "ErrorBoundary" # Fired when the boundary catches an error. on_error: EventHandler[on_error_spec] # Rendered instead of the children when an error is caught. fallback_render: Var[Component] @classmethod def create(cls, *children, **props): """Create an ErrorBoundary component. Args: *children: The children of the component. **props: The props of the component. Returns: The ErrorBoundary component. """ if "on_error" not in props: props["on_error"] = FrontendEventExceptionState.handle_frontend_exception if "fallback_render" not in props: props["fallback_render"] = ArgsFunctionOperation.create( ("event_args",), Var.create( div( div( div( svg( svg.circle(cx="12", cy="12", r="10"), svg.path(d="M16 16s-1.5-2-4-2-4 2-4 2"), svg.line(x1="9", x2="9.01", y1="9", y2="9"), svg.line(x1="15", x2="15.01", y1="9", y2="9"), xmlns="http://www.w3.org/2000/svg", width="25vmin", view_box="0 0 24 24", class_name="lucide lucide-frown-icon lucide-frown", custom_attrs={ "fill": "none", "stroke": "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", }, ), h2( "An error occurred while rendering this page.", font_size="5vmin", font_weight="bold", ), opacity="0.5", display="flex", gap="4vmin", align_items="center", ), p( "This is an error with the application itself. Refreshing the page might help.", opacity="0.75", margin_block="1rem", ), div( div( pre( Var(_js_expr=_ERROR_DISPLAY), word_break="break-word", white_space="pre-wrap", ), padding="0.5rem", ), width="100%", background="color-mix(in srgb, currentColor 5%, transparent)", max_height="15rem", overflow="auto", border_radius="0.4rem", ), button( "Copy", on_click=set_clipboard(Var(_js_expr=_ERROR_DISPLAY)), padding="0.35rem 1.35rem", margin_block="0.5rem", margin_inline_start="auto", background="color-mix(in srgb, currentColor 15%, transparent)", border_radius="0.4rem", width="fit-content", _hover={ "background": "color-mix(in srgb, currentColor 25%, transparent)" }, _active={ "background": "color-mix(in srgb, currentColor 35%, transparent)" }, ), hr( border_color="currentColor", opacity="0.25", ), a( div( "Built with ", svg_logo("currentColor"), display="flex", align_items="baseline", justify_content="center", font_family="monospace", gap="0.5rem", ), href="https://reflex.dev", ), display="flex", flex_direction="column", gap="0.5rem", max_width="min(80ch, 90vw)", border_radius="0.25rem", padding="1rem", ), height="100%", width="100%", position="absolute", background_color="#fff", color="#000", display="flex", align_items="center", justify_content="center", ) ), _var_type=Component, ) else: props["fallback_render"] = ArgsFunctionOperation.create( ("event_args",), props["fallback_render"], _var_type=Component, ) return super().create(*children, **props) error_boundary = ErrorBoundary.create ================================================ FILE: reflex/components/base/fragment.py ================================================ """React fragments to enable bare returns of component trees from functions.""" from reflex.components.component import Component class Fragment(Component): """A React fragment to return multiple components from a function without wrapping it in a container.""" library = "react" tag = "Fragment" fragment = Fragment.create ================================================ FILE: reflex/components/base/link.py ================================================ """Display the title of the current page.""" from reflex.components.el.elements.base import BaseHTML from reflex.vars.base import Var class RawLink(BaseHTML): """A component that displays the title of the current page.""" tag = "link" # The href. href: Var[str] # The type of link. rel: Var[str] class ScriptTag(BaseHTML): """A script tag with the specified type and source.""" tag = "script" # The type of script represented. type_: Var[str] # The URI of an external script. source: Var[str] # Metadata to verify the content of the script. integrity: Var[str] # Whether to allow cross-origin requests. crossorigin: Var[str] # Indicates which referrer to send when fetching the script. referrer_policy: Var[str] # Whether to asynchronously load the script. is_async: Var[bool] # Whether to defer loading the script. defer: Var[bool] ================================================ FILE: reflex/components/base/meta.py ================================================ """Display the title of the current page.""" from __future__ import annotations from reflex.components.base.bare import Bare from reflex.components.el import elements from reflex.components.el.elements.metadata import Meta as Meta # for compatibility from reflex.vars.base import Var class Title(elements.Title): """A component that displays the title of the current page.""" def render(self) -> dict: """Render the title component. Returns: The rendered title component. Raises: ValueError: If the title is not a single string. """ # Make sure the title is a single string. if len(self.children) != 1 or not isinstance(self.children[0], Bare): msg = "Title must be a single string." raise ValueError(msg) return super().render() class Description(elements.Meta): """A component that displays the title of the current page.""" # The type of the description. name: Var[str] = Var.create("description") class Image(elements.Meta): """A component that displays the title of the current page.""" # The type of the image. property: Var[str] = Var.create("og:image") ================================================ FILE: reflex/components/base/script.py ================================================ """Wrapper for the script element. Uses the Helmet component to manage the head.""" from __future__ import annotations from reflex.components import el as elements from reflex.components.core.helmet import helmet from reflex.utils import console class Script(elements.Script): """Wrapper for the script element.""" @classmethod def create( cls, *children, **props, ): """Display the script element. Args: *children: The children of the element. **props: The properties of the element. Returns: The script element. Raises: ValueError: If neither children nor src is specified. """ async_ = props.pop("async_", None) char_set = props.pop("char_set", None) cross_origin = props.pop("cross_origin", None) defer = props.pop("defer", None) integrity = props.pop("integrity", None) referrer_policy = props.pop("referrer_policy", None) src = props.pop("src", None) type = props.pop("type", None) key = props.pop("key", None) id = props.pop("id", None) class_name = props.pop("class_name", None) custom_attrs = props.pop("custom_attrs", None) on_mount = props.pop("on_mount", None) on_unmount = props.pop("on_unmount", None) if props: console.warn( f"rx.script does not support the following properties: {list(props.keys())}" ) if not children and not src: msg = "You must specify either children or src for the script element." raise ValueError(msg) return helmet( elements.Script.create( *children, async_=async_, char_set=char_set, cross_origin=cross_origin, defer=defer, integrity=integrity, referrer_policy=referrer_policy, src=src, type=type, key=key, id=id, class_name=class_name, custom_attrs=custom_attrs, on_mount=on_mount, on_unmount=on_unmount, ) ) script = Script.create ================================================ FILE: reflex/components/base/strict_mode.py ================================================ """Module for the StrictMode component.""" from reflex.components.component import Component class StrictMode(Component): """A React strict mode component to enable strict mode for its children.""" library = "react" tag = "StrictMode" ================================================ FILE: reflex/components/component.py ================================================ """Base component definitions.""" from __future__ import annotations import contextlib import copy import dataclasses import enum import functools import inspect import typing from abc import ABC, ABCMeta, abstractmethod from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from dataclasses import _MISSING_TYPE, MISSING from functools import wraps from hashlib import md5 from types import SimpleNamespace from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, cast, get_args, get_origin from rich.markup import escape from typing_extensions import dataclass_transform import reflex.state from reflex import constants from reflex.compiler.templates import stateful_component_template from reflex.components.core.breakpoints import Breakpoints from reflex.components.dynamic import load_dynamic_serializer from reflex.components.field import BaseField, FieldBasedMeta from reflex.components.tags import Tag from reflex.constants import ( Dirs, EventTriggers, Hooks, Imports, MemoizationDisposition, MemoizationMode, PageNames, ) from reflex.constants.compiler import SpecialAttributes from reflex.constants.state import CAMEL_CASE_MEMO_MARKER, FRONTEND_EVENT_STATE from reflex.event import ( EventCallback, EventChain, EventHandler, EventSpec, args_specs_from_fields, no_args_event_spec, parse_args_spec, pointer_event_spec, run_script, unwrap_var_annotation, ) from reflex.style import Style, format_as_emotion from reflex.utils import console, format, imports, types from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict from reflex.vars import VarData from reflex.vars.base import ( CachedVarOperation, LiteralNoneVar, LiteralVar, Var, cached_property_no_lock, ) from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar, FunctionVar from reflex.vars.number import ternary_operation from reflex.vars.object import ObjectVar from reflex.vars.sequence import LiteralArrayVar, LiteralStringVar, StringVar FIELD_TYPE = TypeVar("FIELD_TYPE") class ComponentField(BaseField[FIELD_TYPE]): """A field for a component.""" def __init__( self, default: FIELD_TYPE | _MISSING_TYPE = MISSING, default_factory: Callable[[], FIELD_TYPE] | None = None, is_javascript: bool | None = None, annotated_type: type[Any] | _MISSING_TYPE = MISSING, ) -> None: """Initialize the field. Args: default: The default value for the field. default_factory: The default factory for the field. is_javascript: Whether the field is a javascript property. annotated_type: The annotated type for the field. """ super().__init__(default, default_factory, annotated_type) self.is_javascript = is_javascript def __repr__(self) -> str: """Represent the field in a readable format. Returns: The string representation of the field. """ annotated_type_str = ( f", annotated_type={self.annotated_type!r}" if self.annotated_type is not MISSING else "" ) if self.default is not MISSING: return f"ComponentField(default={self.default!r}, is_javascript={self.is_javascript!r}{annotated_type_str})" return f"ComponentField(default_factory={self.default_factory!r}, is_javascript={self.is_javascript!r}{annotated_type_str})" def field( default: FIELD_TYPE | _MISSING_TYPE = MISSING, default_factory: Callable[[], FIELD_TYPE] | None = None, is_javascript_property: bool | None = None, ) -> FIELD_TYPE: """Create a field for a component. Args: default: The default value for the field. default_factory: The default factory for the field. is_javascript_property: Whether the field is a javascript property. Returns: The field for the component. Raises: ValueError: If both default and default_factory are specified. """ if default is not MISSING and default_factory is not None: msg = "cannot specify both default and default_factory" raise ValueError(msg) return ComponentField( # pyright: ignore [reportReturnType] default=default, default_factory=default_factory, is_javascript=is_javascript_property, ) @dataclass_transform(kw_only_default=True, field_specifiers=(field,)) class BaseComponentMeta(FieldBasedMeta, ABCMeta): """Meta class for BaseComponent.""" if TYPE_CHECKING: _inherited_fields: Mapping[str, ComponentField] _own_fields: Mapping[str, ComponentField] _fields: Mapping[str, ComponentField] _js_fields: Mapping[str, ComponentField] @classmethod def _process_annotated_fields( cls, namespace: dict[str, Any], annotations: dict[str, Any], inherited_fields: dict[str, ComponentField], ) -> dict[str, ComponentField]: own_fields: dict[str, ComponentField] = {} for key, annotation in annotations.items(): value = namespace.get(key, MISSING) if types.is_classvar(annotation): # If the annotation is a classvar, skip it. continue if value is MISSING: value = ComponentField( default=None, is_javascript=(key[0] != "_"), annotated_type=annotation, ) elif not isinstance(value, ComponentField): value = ComponentField( default=value, is_javascript=( (key[0] != "_") if (existing_field := inherited_fields.get(key)) is None else existing_field.is_javascript ), annotated_type=annotation, ) else: value = ComponentField( default=value.default, default_factory=value.default_factory, is_javascript=value.is_javascript, annotated_type=annotation, ) own_fields[key] = value return own_fields @classmethod def _create_field( cls, annotated_type: Any, default: Any = MISSING, default_factory: Callable[[], Any] | None = None, ) -> ComponentField: return ComponentField( annotated_type=annotated_type, default=default, default_factory=default_factory, is_javascript=True, # Default for components ) @classmethod def _process_field_overrides( cls, namespace: dict[str, Any], annotations: dict[str, Any], inherited_fields: dict[str, Any], ) -> dict[str, ComponentField]: own_fields: dict[str, ComponentField] = {} for key, value, inherited_field in [ (key, value, inherited_field) for key, value in namespace.items() if key not in annotations and ((inherited_field := inherited_fields.get(key)) is not None) ]: new_field = ComponentField( default=value, is_javascript=inherited_field.is_javascript, annotated_type=inherited_field.annotated_type, ) own_fields[key] = new_field return own_fields @classmethod def _finalize_fields( cls, namespace: dict[str, Any], inherited_fields: dict[str, ComponentField], own_fields: dict[str, ComponentField], ) -> None: # Call parent implementation super()._finalize_fields(namespace, inherited_fields, own_fields) # Add JavaScript fields mapping all_fields = namespace["_fields"] namespace["_js_fields"] = { key: value for key, value in all_fields.items() if value.is_javascript is True } class BaseComponent(metaclass=BaseComponentMeta): """The base class for all Reflex components. This is something that can be rendered as a Component via the Reflex compiler. """ # The children nested within the component. children: list[BaseComponent] = field( default_factory=list, is_javascript_property=False ) # The library that the component is based on. library: str | None = field(default=None, is_javascript_property=False) # List here the non-react dependency needed by `library` lib_dependencies: list[str] = field( default_factory=list, is_javascript_property=False ) # The tag to use when rendering the component. tag: str | None = field(default=None, is_javascript_property=False) def __init__( self, **kwargs, ): """Initialize the component. Args: **kwargs: The kwargs to pass to the component. """ for key, value in kwargs.items(): setattr(self, key, value) for name, value in self.get_fields().items(): if name not in kwargs: setattr(self, name, value.default_value()) def set(self, **kwargs): """Set the component props. Args: **kwargs: The kwargs to set. Returns: The component with the updated props. """ for key, value in kwargs.items(): setattr(self, key, value) return self def __eq__(self, value: Any) -> bool: """Check if the component is equal to another value. Args: value: The value to compare to. Returns: Whether the component is equal to the value. """ return type(self) is type(value) and bool( getattr(self, key) == getattr(value, key) for key in self.get_fields() ) @classmethod def get_fields(cls) -> Mapping[str, ComponentField]: """Get the fields of the component. Returns: The fields of the component. """ return cls._fields @classmethod def get_js_fields(cls) -> Mapping[str, ComponentField]: """Get the javascript fields of the component. Returns: The javascript fields of the component. """ return cls._js_fields @abstractmethod def render(self) -> dict: """Render the component. Returns: The dictionary for template of the component. """ @abstractmethod def _get_all_hooks_internal(self) -> dict[str, VarData | None]: """Get the reflex internal hooks for the component and its children. Returns: The code that should appear just before user-defined hooks. """ @abstractmethod def _get_all_hooks(self) -> dict[str, VarData | None]: """Get the React hooks for this component. Returns: The code that should appear just before returning the rendered component. """ @abstractmethod def _get_all_imports(self) -> ParsedImportDict: """Get all the libraries and fields that are used by the component. Returns: The import dict with the required imports. """ @abstractmethod def _get_all_dynamic_imports(self) -> set[str]: """Get dynamic imports for the component. Returns: The dynamic imports. """ @abstractmethod def _get_all_custom_code(self) -> dict[str, None]: """Get custom code for the component. Returns: The custom code. """ @abstractmethod def _get_all_refs(self) -> dict[str, None]: """Get the refs for the children of the component. Returns: The refs for the children. """ class ComponentNamespace(SimpleNamespace): """A namespace to manage components with subcomponents.""" def __hash__(self) -> int: # pyright: ignore [reportIncompatibleVariableOverride] """Get the hash of the namespace. Returns: The hash of the namespace. """ return hash(type(self).__name__) def evaluate_style_namespaces(style: ComponentStyle) -> dict: """Evaluate namespaces in the style. Args: style: The style to evaluate. Returns: The evaluated style. """ return { k.__call__ if isinstance(k, ComponentNamespace) else k: v for k, v in style.items() } # Map from component to styling. ComponentStyle = dict[str | type[BaseComponent] | Callable | ComponentNamespace, Any] ComponentChildTypes = (*types.PrimitiveTypes, Var, BaseComponent, type(None)) def _satisfies_type_hint(obj: Any, type_hint: Any) -> bool: return types._isinstance( obj, type_hint, nested=1, treat_var_as_type=True, treat_mutable_obj_as_immutable=( isinstance(obj, Var) and not isinstance(obj, LiteralVar) ), ) def satisfies_type_hint(obj: Any, type_hint: Any) -> bool: """Check if an object satisfies a type hint. Args: obj: The object to check. type_hint: The type hint to check against. Returns: Whether the object satisfies the type hint. """ if _satisfies_type_hint(obj, type_hint): return True if _satisfies_type_hint(obj, type_hint | None): obj = ( obj if not isinstance(obj, Var) else (obj._var_value if isinstance(obj, LiteralVar) else obj) ) console.warn( "Passing None to a Var that is not explicitly marked as Optional (| None) is deprecated. " f"Passed {obj!s} of type {escape(str(type(obj) if not isinstance(obj, Var) else obj._var_type))} to {escape(str(type_hint))}." ) return True return False def _components_from( component_or_var: BaseComponent | Var, ) -> tuple[BaseComponent, ...]: """Get the components from a component or Var. Args: component_or_var: The component or Var to get the components from. Returns: The components. """ if isinstance(component_or_var, Var): var_data = component_or_var._get_all_var_data() return var_data.components if var_data else () if isinstance(component_or_var, BaseComponent): return (component_or_var,) return () def _hash_str(value: str) -> str: return md5(f'"{value}"'.encode(), usedforsecurity=False).hexdigest() def _hash_sequence(value: Sequence) -> str: return _hash_str(str([_deterministic_hash(v) for v in value])) def _hash_dict(value: dict) -> str: return _hash_sequence( sorted([(k, _deterministic_hash(v)) for k, v in value.items()]) ) def _deterministic_hash(value: object) -> str: """Hash a rendered dictionary. Args: value: The dictionary to hash. Returns: The hash of the dictionary. Raises: TypeError: If the value is not hashable. """ if value is None: # Hash None as a special case. return "None" if isinstance(value, (int, float, enum.Enum)): # Hash numbers and booleans directly. return str(value) if isinstance(value, str): return _hash_str(value) if isinstance(value, dict): return _hash_dict(value) if isinstance(value, (tuple, list)): # Hash tuples by hashing each element. return _hash_sequence(value) if isinstance(value, Var): return _hash_str( str((value._js_expr, _deterministic_hash(value._get_all_var_data()))) ) if dataclasses.is_dataclass(value): return _hash_dict({ k.name: getattr(value, k.name) for k in dataclasses.fields(value) }) if isinstance(value, BaseComponent): # If the value is a component, hash its rendered code. return _hash_dict(value.render()) msg = ( f"Cannot hash value `{value}` of type `{type(value).__name__}`. " "Only BaseComponent, Var, VarData, dict, str, tuple, and enum.Enum are supported." ) raise TypeError(msg) DEFAULT_TRIGGERS: Mapping[str, types.ArgsSpec | Sequence[types.ArgsSpec]] = { EventTriggers.ON_FOCUS: no_args_event_spec, EventTriggers.ON_BLUR: no_args_event_spec, EventTriggers.ON_CLICK: pointer_event_spec, # pyright: ignore [reportAssignmentType] EventTriggers.ON_CONTEXT_MENU: pointer_event_spec, # pyright: ignore [reportAssignmentType] EventTriggers.ON_DOUBLE_CLICK: pointer_event_spec, # pyright: ignore [reportAssignmentType] EventTriggers.ON_MOUSE_DOWN: no_args_event_spec, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec, EventTriggers.ON_MOUSE_OUT: no_args_event_spec, EventTriggers.ON_MOUSE_OVER: no_args_event_spec, EventTriggers.ON_MOUSE_UP: no_args_event_spec, EventTriggers.ON_SCROLL: no_args_event_spec, EventTriggers.ON_SCROLL_END: no_args_event_spec, EventTriggers.ON_MOUNT: no_args_event_spec, EventTriggers.ON_UNMOUNT: no_args_event_spec, } T = TypeVar("T", bound="Component") class Component(BaseComponent, ABC): """A component with style, event trigger and other props.""" # The style of the component. style: Style = field(default_factory=Style, is_javascript_property=False) # A mapping from event triggers to event chains. event_triggers: dict[str, EventChain | Var] = field( default_factory=dict, is_javascript_property=False ) # The alias for the tag. alias: str | None = field(default=None, is_javascript_property=False) # Whether the component is a global scope tag. True for tags like `html`, `head`, `body`. _is_tag_in_global_scope: ClassVar[bool] = False # Whether the import is default or named. is_default: bool | None = field(default=False, is_javascript_property=False) # A unique key for the component. key: Any = field(default=None, is_javascript_property=False) # The id for the component. id: Any = field(default=None, is_javascript_property=False) # The Var to pass as the ref to the component. ref: Var | None = field(default=None, is_javascript_property=False) # The class name for the component. class_name: Any = field(default=None, is_javascript_property=False) # Special component props. special_props: list[Var] = field(default_factory=list, is_javascript_property=False) # components that cannot be children _invalid_children: ClassVar[list[str]] = [] # only components that are allowed as children _valid_children: ClassVar[list[str]] = [] # only components that are allowed as parent _valid_parents: ClassVar[list[str]] = [] # props to change the name of _rename_props: ClassVar[dict[str, str]] = {} # custom attribute custom_attrs: dict[str, Var | Any] = field( default_factory=dict, is_javascript_property=False ) # When to memoize this component and its children. _memoization_mode: MemoizationMode = field( default_factory=MemoizationMode, is_javascript_property=False ) # State class associated with this component instance State: type[reflex.state.State] | None = field( default=None, is_javascript_property=False ) def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the component. This method should be implemented by subclasses to add new imports for the component. Implementations do NOT need to call super(). The result of calling add_imports in each parent class will be merged internally. Returns: The additional imports for this component subclass. The format of the return value is a dictionary where the keys are the library names (with optional npm-style version specifications) mapping to a single name to be imported, or a list names to be imported. For advanced use cases, the values can be ImportVar instances (for example, to provide an alias or mark that an import is the default export from the given library). ```python return { "react": "useEffect", "react-draggable": ["DraggableCore", rx.ImportVar(tag="Draggable", is_default=True)], } ``` """ return {} def add_hooks(self) -> list[str | Var]: """Add hooks inside the component function. Hooks are pieces of literal Javascript code that is inserted inside the React component function. Each logical hook should be a separate string in the list. Common strings will be deduplicated and inserted into the component function only once, so define const variables and other identical code in their own strings to avoid defining the same const or hook multiple times. If a hook depends on specific data from the component instance, be sure to use unique values inside the string to _avoid_ deduplication. Implementations do NOT need to call super(). The result of calling add_hooks in each parent class will be merged and deduplicated internally. Returns: The additional hooks for this component subclass. ```python return [ "const [count, setCount] = useState(0);", "useEffect(() => { setCount((prev) => prev + 1); console.log(`mounted ${count} times`); }, []);", ] ``` """ return [] def add_custom_code(self) -> list[str]: """Add custom Javascript code into the page that contains this component. Custom code is inserted at module level, after any imports. Each string of custom code is deduplicated per-page, so take care to avoid defining the same const or function differently from different component instances. Custom code is useful for defining global functions or constants which can then be referenced inside hooks or used by component vars. Implementations do NOT need to call super(). The result of calling add_custom_code in each parent class will be merged and deduplicated internally. Returns: The additional custom code for this component subclass. ```python return [ "const translatePoints = (event) => { return { x: event.clientX, y: event.clientY }; };", ] ``` """ return [] @classmethod def __init_subclass__(cls, **kwargs): """Set default properties. Args: **kwargs: The kwargs to pass to the superclass. """ super().__init_subclass__(**kwargs) # Ensure renamed props from parent classes are applied to the subclass. if cls._rename_props: inherited_rename_props = {} for parent in reversed(cls.mro()): if issubclass(parent, Component) and parent._rename_props: inherited_rename_props.update(parent._rename_props) cls._rename_props = inherited_rename_props def __init__(self, **kwargs): """Initialize the custom component. Args: **kwargs: The kwargs to pass to the component. """ console.error( "Instantiating components directly is not supported." f" Use `{self.__class__.__name__}.create` method instead." ) def _post_init(self, *args, **kwargs): """Initialize the component. Args: *args: Args to initialize the component. **kwargs: Kwargs to initialize the component. Raises: TypeError: If an invalid prop is passed. ValueError: If an event trigger passed is not valid. """ # Set the id and children initially. children = kwargs.get("children", []) self._validate_component_children(children) # Get the component fields, triggers, and props. fields = self.get_fields() component_specific_triggers = self.get_event_triggers() props = self.get_props() # Add any events triggers. if "event_triggers" not in kwargs: kwargs["event_triggers"] = {} kwargs["event_triggers"] = kwargs["event_triggers"].copy() # Iterate through the kwargs and set the props. for key, value in kwargs.items(): if ( key.startswith("on_") and key not in component_specific_triggers and key not in props ): valid_triggers = sorted(component_specific_triggers.keys()) msg = ( f"The {(comp_name := type(self).__name__)} does not take in an `{key}` event trigger. " f"Valid triggers for {comp_name}: {valid_triggers}. " f"If {comp_name} is a third party component make sure to add `{key}` to the component's event triggers. " f"visit https://reflex.dev/docs/wrapping-react/guide/#event-triggers for more info." ) raise ValueError(msg) if key in component_specific_triggers: # Event triggers are bound to event chains. is_var = False elif key in props: # Set the field type. is_var = ( field.type_origin is Var if (field := fields.get(key)) else False ) else: continue # Check whether the key is a component prop. if is_var: try: kwargs[key] = LiteralVar.create(value) # Get the passed type and the var type. passed_type = kwargs[key]._var_type expected_type = types.get_args( types.get_field_type(type(self), key) )[0] except TypeError: # If it is not a valid var, check the base types. passed_type = type(value) expected_type = types.get_field_type(type(self), key) if not satisfies_type_hint(value, expected_type): value_name = value._js_expr if isinstance(value, Var) else value additional_info = ( " You can call `.bool()` on the value to convert it to a boolean." if expected_type is bool and isinstance(value, Var) else "" ) raise TypeError( f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_type}." + additional_info ) # Check if the key is an event trigger. if key in component_specific_triggers: kwargs["event_triggers"][key] = EventChain.create( value=value, args_spec=component_specific_triggers[key], key=key, ) # Remove any keys that were added as events. for key in kwargs["event_triggers"]: kwargs.pop(key, None) # Place data_ and aria_ attributes into custom_attrs special_attributes = [ key for key in kwargs if key not in fields and SpecialAttributes.is_special(key) ] if special_attributes: custom_attrs = kwargs.setdefault("custom_attrs", {}) custom_attrs.update({ format.to_kebab_case(key): kwargs.pop(key) for key in special_attributes }) # Add style props to the component. style = kwargs.get("style", {}) if isinstance(style, Sequence): if any(not isinstance(s, Mapping) for s in style): msg = "Style must be a dictionary or a list of dictionaries." raise TypeError(msg) # Merge styles, the later ones overriding keys in the earlier ones. style = { k: v for style_dict in style for k, v in cast(Mapping, style_dict).items() } if isinstance(style, (Breakpoints, Var)): style = { # Assign the Breakpoints to the self-referential selector to avoid squashing down to a regular dict. "&": style, } fields_style = self.get_fields()["style"] kwargs["style"] = Style({ **fields_style.default_value(), **style, **{attr: value for attr, value in kwargs.items() if attr not in fields}, }) # Convert class_name to str if it's list class_name = kwargs.get("class_name", "") if isinstance(class_name, (list, tuple)): has_var = False for c in class_name: if isinstance(c, str): continue if isinstance(c, Var): if not isinstance(c, StringVar) and not issubclass( c._var_type, str ): msg = f"Invalid class_name passed for prop {type(self).__name__}.class_name, expected type str, got value {c._js_expr} of type {c._var_type}." raise TypeError(msg) has_var = True else: msg = f"Invalid class_name passed for prop {type(self).__name__}.class_name, expected type str, got value {c} of type {type(c)}." raise TypeError(msg) if has_var: kwargs["class_name"] = LiteralArrayVar.create( class_name, _var_type=list[str] ).join(" ") else: kwargs["class_name"] = " ".join(class_name) elif ( isinstance(class_name, Var) and not isinstance(class_name, StringVar) and not issubclass(class_name._var_type, str) ): msg = f"Invalid class_name passed for prop {type(self).__name__}.class_name, expected type str, got value {class_name._js_expr} of type {class_name._var_type}." raise TypeError(msg) # Construct the component. for key, value in kwargs.items(): setattr(self, key, value) @classmethod def get_event_triggers(cls) -> dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]]: """Get the event triggers for the component. Returns: The event triggers. """ # Look for component specific triggers, # e.g. variable declared as EventHandler types. return DEFAULT_TRIGGERS | args_specs_from_fields(cls.get_fields()) # pyright: ignore [reportOperatorIssue] def __repr__(self) -> str: """Represent the component in React. Returns: The code to render the component. """ return format.json_dumps(self.render()) def __str__(self) -> str: """Represent the component in React. Returns: The code to render the component. """ from reflex.compiler.compiler import _compile_component return _compile_component(self) def _exclude_props(self) -> list[str]: """Props to exclude when adding the component props to the Tag. Returns: A list of component props to exclude. """ return [] def _render(self, props: dict[str, Any] | None = None) -> Tag: """Define how to render the component in React. Args: props: The props to render (if None, then use get_props). Returns: The tag to render. """ # Create the base tag. name = (self.tag if not self.alias else self.alias) or "" if self._is_tag_in_global_scope and self.library is None: name = '"' + name + '"' # Create the base tag. tag = Tag( name=name, special_props=self.special_props.copy(), ) if props is None: # Add component props to the tag. props = { attr.removesuffix("_"): getattr(self, attr) for attr in self.get_props() } # Add ref to element if `ref` is None and `id` is not None. if self.ref is not None: props["ref"] = self.ref elif (ref := self.get_ref()) is not None: props["ref"] = Var(_js_expr=ref) else: props = props.copy() props.update( **{ trigger: handler for trigger, handler in self.event_triggers.items() if trigger not in {EventTriggers.ON_MOUNT, EventTriggers.ON_UNMOUNT} }, key=self.key, id=self.id, class_name=self.class_name, ) props.update(self._get_style()) props.update(self.custom_attrs) # remove excluded props from prop dict before adding to tag. for prop_to_exclude in self._exclude_props(): props.pop(prop_to_exclude, None) return tag.add_props(**props) @classmethod @functools.cache def get_props(cls) -> Iterable[str]: """Get the unique fields for the component. Returns: The unique fields. """ return cls.get_js_fields() @classmethod @functools.cache def get_initial_props(cls) -> set[str]: """Get the initial props to set for the component. Returns: The initial props to set. """ return set() @functools.cached_property def _get_component_prop_property(self) -> Sequence[BaseComponent]: return [ component for prop in self.get_props() if (value := getattr(self, prop)) is not None and isinstance(value, (BaseComponent, Var)) for component in _components_from(value) ] def _get_components_in_props(self) -> Sequence[BaseComponent]: """Get the components in the props. Returns: The components in the props """ return self._get_component_prop_property @classmethod def _validate_children(cls, children: tuple | list): from reflex.utils.exceptions import ChildrenTypeError for child in children: if isinstance(child, (tuple, list)): cls._validate_children(child) # Make sure the child is a valid type. if isinstance(child, dict) or not isinstance(child, ComponentChildTypes): raise ChildrenTypeError(component=cls.__name__, child=child) @classmethod def create(cls: type[T], *children, **props) -> T: """Create the component. Args: *children: The children of the component. **props: The props of the component. Returns: The component. """ # Import here to avoid circular imports. from reflex.components.base.bare import Bare from reflex.components.base.fragment import Fragment # Filter out None props props = {key: value for key, value in props.items() if value is not None} # Validate all the children. cls._validate_children(children) children_normalized = [ ( child if isinstance(child, Component) else ( Fragment.create(*child) if isinstance(child, tuple) else Bare.create(contents=LiteralVar.create(child)) ) ) for child in children ] return cls._create(children_normalized, **props) @classmethod def _create(cls: type[T], children: Sequence[BaseComponent], **props: Any) -> T: """Create the component. Args: children: The children of the component. **props: The props of the component. Returns: The component. """ comp = cls.__new__(cls) super(Component, comp).__init__(id=props.get("id"), children=list(children)) comp._post_init(children=list(children), **props) return comp @classmethod def _unsafe_create( cls: type[T], children: Sequence[BaseComponent], **props: Any ) -> T: """Create the component without running post_init. Args: children: The children of the component. **props: The props of the component. Returns: The component. """ comp = cls.__new__(cls) super(Component, comp).__init__(id=props.get("id"), children=list(children)) for prop, value in props.items(): setattr(comp, prop, value) return comp def add_style(self) -> dict[str, Any] | None: """Add style to the component. Downstream components can override this method to return a style dict that will be applied to the component. Returns: The style to add. """ return None def _add_style(self) -> Style: """Call add_style for all bases in the MRO. Downstream components should NOT override. Use add_style instead. Returns: The style to add. """ styles = [] # Walk the MRO to call all `add_style` methods. for base in self._iter_parent_classes_with_method("add_style"): s = base.add_style(self) if s is not None: styles.append(s) style_ = Style() for s in reversed(styles): style_.update(s) return style_ def _get_component_style(self, styles: ComponentStyle | Style) -> Style | None: """Get the style to the component from `App.style`. Args: styles: The style to apply. Returns: The style of the component. """ component_style = None if (style := styles.get(type(self))) is not None: # pyright: ignore [reportArgumentType] component_style = Style(style) if (style := styles.get(self.create)) is not None: # pyright: ignore [reportArgumentType] component_style = Style(style) return component_style def _add_style_recursive( self, style: ComponentStyle | Style, theme: Component | None = None ) -> Component: """Add additional style to the component and its children. Apply order is as follows (with the latest overriding the earliest): 1. Default style from `_add_style`/`add_style`. 2. User-defined style from `App.style`. 3. User-defined style from `Component.style`. 4. style dict and css props passed to the component instance. Args: style: A dict from component to styling. theme: The theme to apply. (for retro-compatibility with deprecated _apply_theme API) Returns: The component with the additional style. Raises: UserWarning: If `_add_style` has been overridden. """ # 1. Default style from `_add_style`/`add_style`. if type(self)._add_style != Component._add_style: msg = "Do not override _add_style directly. Use add_style instead." raise UserWarning(msg) new_style = self._add_style() style_vars = [new_style._var_data] # 2. User-defined style from `App.style`. component_style = self._get_component_style(style) if component_style: new_style.update(component_style) style_vars.append(component_style._var_data) # 4. style dict and css props passed to the component instance. new_style.update(self.style) style_vars.append(self.style._var_data) new_style._var_data = VarData.merge(*style_vars) # Assign the new style self.style = new_style # Recursively add style to the children. for child in self.children: # Skip BaseComponent and StatefulComponent children. if not isinstance(child, Component): continue child._add_style_recursive(style, theme) return self def _get_style(self) -> dict: """Get the style for the component. Returns: The dictionary of the component style as value and the style notation as key. """ if isinstance(self.style, Var): return {"css": self.style} emotion_style = format_as_emotion(self.style) return ( {"css": LiteralVar.create(emotion_style)} if emotion_style is not None else {} ) def render(self) -> dict: """Render the component. Returns: The dictionary for template of component. """ tag = self._render() rendered_dict = dict( tag.set( children=[child.render() for child in self.children], ) ) self._replace_prop_names(rendered_dict) return rendered_dict def _replace_prop_names(self, rendered_dict: dict) -> None: """Replace the prop names in the render dictionary. Args: rendered_dict: The render dictionary with all the component props and event handlers. """ # fast path if not self._rename_props: return for ix, prop in enumerate(rendered_dict["props"]): for old_prop, new_prop in self._rename_props.items(): if prop.startswith(old_prop): rendered_dict["props"][ix] = prop.replace(old_prop, new_prop, 1) def _validate_component_children(self, children: list[Component]): """Validate the children components. Args: children: The children of the component. """ from reflex.components.base.fragment import Fragment from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach from reflex.components.core.match import Match no_valid_parents_defined = all(child._valid_parents == [] for child in children) if ( not self._invalid_children and not self._valid_children and no_valid_parents_defined ): return comp_name = type(self).__name__ allowed_components = [ comp.__name__ for comp in (Fragment, Foreach, Cond, Match) ] def validate_child(child: Any): child_name = type(child).__name__ # Iterate through the immediate children of fragment if isinstance(child, Fragment): for c in child.children: validate_child(c) if isinstance(child, Cond): validate_child(child.children[0]) validate_child(child.children[1]) if isinstance(child, Match): for cases in child.match_cases: validate_child(cases[-1]) validate_child(child.default) if self._invalid_children and child_name in self._invalid_children: msg = f"The component `{comp_name}` cannot have `{child_name}` as a child component" raise ValueError(msg) if self._valid_children and child_name not in [ *self._valid_children, *allowed_components, ]: valid_child_list = ", ".join([ f"`{v_child}`" for v_child in self._valid_children ]) msg = f"The component `{comp_name}` only allows the components: {valid_child_list} as children. Got `{child_name}` instead." raise ValueError(msg) if child._valid_parents and all( clz_name not in [*child._valid_parents, *allowed_components] for clz_name in self._iter_parent_classes_names() ): valid_parent_list = ", ".join([ f"`{v_parent}`" for v_parent in child._valid_parents ]) msg = f"The component `{child_name}` can only be a child of the components: {valid_parent_list}. Got `{comp_name}` instead." raise ValueError(msg) for child in children: validate_child(child) @staticmethod def _get_vars_from_event_triggers( event_triggers: dict[str, EventChain | Var], ) -> Iterator[tuple[str, list[Var]]]: """Get the Vars associated with each event trigger. Args: event_triggers: The event triggers from the component instance. Yields: tuple of (event_name, event_vars) """ for event_trigger, event in event_triggers.items(): if isinstance(event, Var): yield event_trigger, [event] elif isinstance(event, EventChain): event_args = [] for spec in event.events: if isinstance(spec, EventSpec): for args in spec.args: event_args.extend(args) else: event_args.append(spec) yield event_trigger, event_args def _get_vars( self, include_children: bool = False, ignore_ids: set[int] | None = None ) -> Iterator[Var]: """Walk all Vars used in this component. Args: include_children: Whether to include Vars from children. ignore_ids: The ids to ignore. Yields: Each var referenced by the component (props, styles, event handlers). """ ignore_ids = ignore_ids or set() vars: list[Var] | None = getattr(self, "__vars", None) if vars is not None: yield from vars vars = self.__vars = [] # Get Vars associated with event trigger arguments. for _, event_vars in self._get_vars_from_event_triggers(self.event_triggers): vars.extend(event_vars) # Get Vars associated with component props. for prop in self.get_props(): prop_var = getattr(self, prop) if isinstance(prop_var, Var): vars.append(prop_var) # Style keeps track of its own VarData instance, so embed in a temp Var that is yielded. if (isinstance(self.style, dict) and self.style) or isinstance(self.style, Var): vars.append( Var( _js_expr="style", _var_type=str, _var_data=VarData.merge(self.style._var_data), ) ) # Special props are always Var instances. vars.extend(self.special_props) # Get Vars associated with common Component props. for comp_prop in ( self.class_name, self.id, self.key, *self.custom_attrs.values(), ): if isinstance(comp_prop, Var): vars.append(comp_prop) elif isinstance(comp_prop, str): # Collapse VarData encoded in f-strings. var = LiteralStringVar.create(comp_prop) if var._get_all_var_data() is not None: vars.append(var) # Get Vars associated with children. if include_children: for child in self.children: if not isinstance(child, Component) or id(child) in ignore_ids: continue ignore_ids.add(id(child)) child_vars = child._get_vars( include_children=include_children, ignore_ids=ignore_ids ) vars.extend(child_vars) yield from vars def _event_trigger_values_use_state(self) -> bool: """Check if the values of a component's event trigger use state. Returns: True if any of the component's event trigger values uses State. """ for trigger in self.event_triggers.values(): if isinstance(trigger, EventChain): for event in trigger.events: if isinstance(event, EventCallback): continue if isinstance(event, EventSpec): if ( event.handler.state_full_name and event.handler.state_full_name != FRONTEND_EVENT_STATE ): return True else: if event._var_state: return True elif isinstance(trigger, Var) and trigger._var_state: return True return False def _has_stateful_event_triggers(self): """Check if component or children have any event triggers that use state. Returns: True if the component or children have any event triggers that uses state. """ if self.event_triggers and self._event_trigger_values_use_state(): return True for child in self.children: if isinstance(child, Component) and child._has_stateful_event_triggers(): return True return False @classmethod def _iter_parent_classes_names(cls) -> Iterator[str]: for clz in cls.mro(): if clz is Component: break yield clz.__name__ @classmethod def _iter_parent_classes_with_method(cls, method: str) -> Sequence[type[Component]]: """Iterate through parent classes that define a given method. Used for handling the `add_*` API functions that internally simulate a super() call chain. Args: method: The method to look for. Returns: A sequence of parent classes that define the method (differently than the base). """ current_class_method = getattr(Component, method, None) seen_methods = ( {current_class_method} if current_class_method is not None else set() ) clzs: list[type[Component]] = [] for clz in cls.mro(): if clz is Component: break if not issubclass(clz, Component): continue method_func = getattr(clz, method, None) if not callable(method_func) or method_func in seen_methods: continue seen_methods.add(method_func) clzs.append(clz) return clzs def _get_custom_code(self) -> str | None: """Get custom code for the component. Returns: The custom code. """ return None def _get_all_custom_code(self) -> dict[str, None]: """Get custom code for the component and its children. Returns: The custom code. """ # Store the code in a set to avoid duplicates. code: dict[str, None] = {} # Add the custom code for this component. custom_code = self._get_custom_code() if custom_code is not None: code[custom_code] = None for component in self._get_components_in_props(): code |= component._get_all_custom_code() # Add the custom code from add_custom_code method. for clz in self._iter_parent_classes_with_method("add_custom_code"): for item in clz.add_custom_code(self): code[item] = None # Add the custom code for the children. for child in self.children: code |= child._get_all_custom_code() # Return the code. return code def _get_dynamic_imports(self) -> str | None: """Get dynamic import for the component. Returns: The dynamic import. """ return None def _get_all_dynamic_imports(self) -> set[str]: """Get dynamic imports for the component and its children. Returns: The dynamic imports. """ # Store the import in a set to avoid duplicates. dynamic_imports: set[str] = set() # Get dynamic import for this component. dynamic_import = self._get_dynamic_imports() if dynamic_import: dynamic_imports.add(dynamic_import) # Get the dynamic imports from children for child in self.children: dynamic_imports |= child._get_all_dynamic_imports() for component in self._get_components_in_props(): dynamic_imports |= component._get_all_dynamic_imports() # Return the dynamic imports return dynamic_imports def _get_dependencies_imports(self) -> ParsedImportDict: """Get the imports from lib_dependencies for installing. Returns: The dependencies imports of the component. """ return { dep: [ImportVar(tag=None, render=False)] for dep in self.lib_dependencies } def _get_hooks_imports(self) -> ParsedImportDict: """Get the imports required by certain hooks. Returns: The imports required for all selected hooks. """ imports_ = {} if self._get_ref_hook() is not None: # Handle hooks needed for attaching react refs to DOM nodes. imports_.setdefault("react", set()).add(ImportVar(tag="useRef")) imports_.setdefault(f"$/{Dirs.STATE_PATH}", set()).add( ImportVar(tag="refs") ) if self._get_mount_lifecycle_hook(): # Handle hooks for `on_mount` / `on_unmount`. imports_.setdefault("react", set()).add(ImportVar(tag="useEffect")) other_imports = [] user_hooks = self._get_hooks() user_hooks_data = ( VarData.merge(user_hooks._get_all_var_data()) if user_hooks is not None and isinstance(user_hooks, Var) else None ) if user_hooks_data is not None: other_imports.append(user_hooks_data.imports) other_imports.extend( hook_vardata.imports for hook_vardata in self._get_added_hooks().values() if hook_vardata is not None ) return imports.merge_imports(imports_, *other_imports) def _get_imports(self) -> ParsedImportDict: """Get all the libraries and fields that are used by the component. Returns: The imports needed by the component. """ imports_ = ( {self.library: [self.import_var]} if self.library is not None and self.tag is not None else {} ) # Get static imports required for event processing. event_imports = Imports.EVENTS if self.event_triggers else {} # Collect imports from Vars used directly by this component. var_imports = [ dict(var_data.imports) for var in self._get_vars() if (var_data := var._get_all_var_data()) is not None ] added_import_dicts: list[ParsedImportDict] = [] for clz in self._iter_parent_classes_with_method("add_imports"): list_of_import_dict = clz.add_imports(self) if not isinstance(list_of_import_dict, list): added_import_dicts.append(imports.parse_imports(list_of_import_dict)) else: added_import_dicts.extend([ imports.parse_imports(item) for item in list_of_import_dict ]) return imports.merge_parsed_imports( self._get_dependencies_imports(), self._get_hooks_imports(), imports_, event_imports, *var_imports, *added_import_dicts, ) def _get_all_imports(self, collapse: bool = False) -> ParsedImportDict: """Get all the libraries and fields that are used by the component and its children. Args: collapse: Whether to collapse the imports by removing duplicates. Returns: The import dict with the required imports. """ imports_ = imports.merge_parsed_imports( self._get_imports(), *[child._get_all_imports() for child in self.children] ) return imports.collapse_imports(imports_) if collapse else imports_ def _get_mount_lifecycle_hook(self) -> str | None: """Generate the component lifecycle hook. Returns: The useEffect hook for managing `on_mount` and `on_unmount` events. """ # pop on_mount and on_unmount from event_triggers since these are handled by # hooks, not as actually props in the component on_mount = self.event_triggers.get(EventTriggers.ON_MOUNT, None) on_unmount = self.event_triggers.get(EventTriggers.ON_UNMOUNT, None) if on_mount is not None: on_mount = str(LiteralVar.create(on_mount)) + "()" if on_unmount is not None: on_unmount = str(LiteralVar.create(on_unmount)) + "()" if on_mount is not None or on_unmount is not None: return f""" useEffect(() => {{ {on_mount or ""} return () => {{ {on_unmount or ""} }} }}, []);""" return None def _get_ref_hook(self) -> Var | None: """Generate the ref hook for the component. Returns: The useRef hook for managing refs. """ ref = self.get_ref() if ref is not None: return Var( f"const {ref} = useRef(null); {Var(_js_expr=ref)._as_ref()!s} = {ref};", _var_data=VarData(position=Hooks.HookPosition.INTERNAL), ) return None def _get_vars_hooks(self) -> dict[str, VarData | None]: """Get the hooks required by vars referenced in this component. Returns: The hooks for the vars. """ vars_hooks = {} for var in self._get_vars(): var_data = var._get_all_var_data() if var_data is not None: vars_hooks.update( var_data.hooks if isinstance(var_data.hooks, dict) else { k: VarData(position=Hooks.HookPosition.INTERNAL) for k in var_data.hooks } ) for component in var_data.components: vars_hooks.update(component._get_all_hooks()) return vars_hooks def _get_events_hooks(self) -> dict[str, VarData | None]: """Get the hooks required by events referenced in this component. Returns: The hooks for the events. """ return ( {Hooks.EVENTS: VarData(position=Hooks.HookPosition.INTERNAL)} if self.event_triggers else {} ) def _get_hooks_internal(self) -> dict[str, VarData | None]: """Get the React hooks for this component managed by the framework. Downstream components should NOT override this method to avoid breaking framework functionality. Returns: The internally managed hooks. """ return { **{ str(hook): VarData(position=Hooks.HookPosition.INTERNAL) for hook in [self._get_ref_hook(), self._get_mount_lifecycle_hook()] if hook is not None }, **self._get_vars_hooks(), **self._get_events_hooks(), } def _get_added_hooks(self) -> dict[str, VarData | None]: """Get the hooks added via `add_hooks` method. Returns: The deduplicated hooks and imports added by the component and parent components. """ code = {} def extract_var_hooks(hook: Var): var_data = VarData.merge(hook._get_all_var_data()) if var_data is not None: for sub_hook in var_data.hooks: code[sub_hook] = None if str(hook) in code: code[str(hook)] = VarData.merge(var_data, code[str(hook)]) else: code[str(hook)] = var_data # Add the hook code from add_hooks for each parent class (this is reversed to preserve # the order of the hooks in the final output) for clz in reversed(self._iter_parent_classes_with_method("add_hooks")): for hook in clz.add_hooks(self): if isinstance(hook, Var): extract_var_hooks(hook) else: code[hook] = None return code def _get_hooks(self) -> str | None: """Get the React hooks for this component. Downstream components should override this method to add their own hooks. Returns: The hooks for just this component. """ return def _get_all_hooks_internal(self) -> dict[str, VarData | None]: """Get the reflex internal hooks for the component and its children. Returns: The code that should appear just before user-defined hooks. """ # Store the code in a set to avoid duplicates. code = self._get_hooks_internal() # Add the hook code for the children. for child in self.children: code.update(child._get_all_hooks_internal()) return code def _get_all_hooks(self) -> dict[str, VarData | None]: """Get the React hooks for this component and its children. Returns: The code that should appear just before returning the rendered component. """ code = {} # Add the internal hooks for this component. code.update(self._get_hooks_internal()) # Add the hook code for this component. hooks = self._get_hooks() if hooks is not None: code[hooks] = None code.update(self._get_added_hooks()) # Add the hook code for the children. for child in self.children: code.update(child._get_all_hooks()) return code def get_ref(self) -> str | None: """Get the name of the ref for the component. Returns: The ref name. """ # do not create a ref if the id is dynamic or unspecified if self.id is None or isinstance(self.id, Var): return None return format.format_ref(self.id) def _get_all_refs(self) -> dict[str, None]: """Get the refs for the children of the component. Returns: The refs for the children. """ refs = {} ref = self.get_ref() if ref is not None: refs[ref] = None for child in self.children: refs |= child._get_all_refs() for component in self._get_components_in_props(): refs |= component._get_all_refs() return refs @property def import_var(self): """The tag to import. Returns: An import var. """ # If the tag is dot-qualified, only import the left-most name. tag = self.tag.partition(".")[0] if self.tag else None alias = self.alias.partition(".")[0] if self.alias else None return ImportVar(tag=tag, is_default=self.is_default, alias=alias) @staticmethod def _get_app_wrap_components() -> dict[tuple[int, str], Component]: """Get the app wrap components for the component. Returns: The app wrap components. """ return {} def _get_all_app_wrap_components( self, *, ignore_ids: set[int] | None = None ) -> dict[tuple[int, str], Component]: """Get the app wrap components for the component and its children. Args: ignore_ids: A set of component IDs to ignore. Used to avoid duplicates. Returns: The app wrap components. """ ignore_ids = ignore_ids or set() # Store the components in a set to avoid duplicates. components = self._get_app_wrap_components() for component in tuple(components.values()): component_id = id(component) if component_id in ignore_ids: continue ignore_ids.add(component_id) components.update( component._get_all_app_wrap_components(ignore_ids=ignore_ids) ) # Add the app wrap components for the children. for child in self.children: child_id = id(child) # Skip BaseComponent and StatefulComponent children. if not isinstance(child, Component) or child_id in ignore_ids: continue ignore_ids.add(child_id) components.update(child._get_all_app_wrap_components(ignore_ids=ignore_ids)) # Return the components. return components class CustomComponent(Component): """A custom user-defined component.""" # Use the components library. library = f"$/{Dirs.COMPONENTS_PATH}" # The function that creates the component. component_fn: Callable[..., Component] = field(default=Component.create) # The props of the component. props: dict[str, Any] = field(default_factory=dict) def _post_init(self, **kwargs): """Initialize the custom component. Args: **kwargs: The kwargs to pass to the component. """ component_fn = kwargs.get("component_fn") # Set the props. props_types = typing.get_type_hints(component_fn) if component_fn else {} props = {key: value for key, value in kwargs.items() if key in props_types} kwargs = {key: value for key, value in kwargs.items() if key not in props_types} event_types = { key for key in props if ( (get_origin((annotation := props_types.get(key))) or annotation) == EventHandler ) } def get_args_spec(key: str) -> types.ArgsSpec | Sequence[types.ArgsSpec]: type_ = props_types[key] return ( args[0] if (args := get_args(type_)) else ( annotation_args[1] if get_origin( annotation := inspect.getfullargspec(component_fn).annotations[ key ] ) is typing.Annotated and (annotation_args := get_args(annotation)) else no_args_event_spec ) ) super()._post_init( event_triggers={ key: EventChain.create( value=props[key], args_spec=get_args_spec(key), key=key, ) for key in event_types }, **kwargs, ) to_camel_cased_props = { format.to_camel_case(key): None for key in props if key not in event_types } self.get_props = lambda: to_camel_cased_props # pyright: ignore [reportIncompatibleVariableOverride] # Unset the style. self.style = Style() # Set the tag to the name of the function. self.tag = format.to_title_case(self.component_fn.__name__) for key, value in props.items(): # Skip kwargs that are not props. if key not in props_types: continue camel_cased_key = format.to_camel_case(key) # Get the type based on the annotation. type_ = props_types[key] # Handle event chains. if type_ is EventHandler: inspect.getfullargspec(component_fn).annotations[key] self.props[camel_cased_key] = EventChain.create( value=value, args_spec=get_args_spec(key), key=key ) continue value = LiteralVar.create(value) self.props[camel_cased_key] = value setattr(self, camel_cased_key, value) def __eq__(self, other: Any) -> bool: """Check if the component is equal to another. Args: other: The other component. Returns: Whether the component is equal to the other. """ return isinstance(other, CustomComponent) and self.tag == other.tag def __hash__(self) -> int: """Get the hash of the component. Returns: The hash of the component. """ return hash(self.tag) @classmethod def get_props(cls) -> Iterable[str]: """Get the props for the component. Returns: The set of component props. """ return () @staticmethod def _get_event_spec_from_args_spec(name: str, event: EventChain) -> Callable: """Get the event spec from the args spec. Args: name: The name of the event event: The args spec. Returns: The event spec. """ def fn(*args): return run_script(Var(name).to(FunctionVar).call(*args)) if event.args_spec: arg_spec = ( event.args_spec if not isinstance(event.args_spec, Sequence) else event.args_spec[0] ) names = inspect.getfullargspec(arg_spec).args fn.__signature__ = inspect.Signature( # pyright: ignore[reportFunctionMemberAccess] parameters=[ inspect.Parameter( name=name, kind=inspect.Parameter.POSITIONAL_ONLY, annotation=arg._var_type, ) for name, arg in zip( names, parse_args_spec(event.args_spec)[0], strict=True ) ] ) return fn def get_prop_vars(self) -> list[Var | Callable]: """Get the prop vars. Returns: The prop vars. """ return [ Var( _js_expr=name + CAMEL_CASE_MEMO_MARKER, _var_type=(prop._var_type if isinstance(prop, Var) else type(prop)), ).guess_type() if isinstance(prop, Var) or not isinstance(prop, EventChain) else CustomComponent._get_event_spec_from_args_spec( name + CAMEL_CASE_MEMO_MARKER, prop ) for name, prop in self.props.items() ] @functools.cache # noqa: B019 def get_component(self) -> Component: """Render the component. Returns: The code to render the component. """ component = self.component_fn(*self.get_prop_vars()) try: from reflex.utils.prerequisites import get_and_validate_app style = get_and_validate_app().app.style except Exception: style = {} component._add_style_recursive(style) return component def _get_all_app_wrap_components( self, *, ignore_ids: set[int] | None = None ) -> dict[tuple[int, str], Component]: """Get the app wrap components for the custom component. Args: ignore_ids: A set of IDs to ignore to avoid infinite recursion. Returns: The app wrap components. """ ignore_ids = ignore_ids or set() component = self.get_component() if id(component) in ignore_ids: return {} ignore_ids.add(id(component)) return self.get_component()._get_all_app_wrap_components(ignore_ids=ignore_ids) CUSTOM_COMPONENTS: dict[str, CustomComponent] = {} def _register_custom_component( component_fn: Callable[..., Component], ): """Register a custom component to be compiled. Args: component_fn: The function that creates the component. Returns: The custom component. Raises: TypeError: If the tag name cannot be determined. """ dummy_props = { prop: ( Var( "", _var_type=unwrap_var_annotation(annotation), ).guess_type() if not types.safe_issubclass(annotation, EventHandler) else EventSpec(handler=EventHandler(fn=no_args_event_spec)) ) for prop, annotation in typing.get_type_hints(component_fn).items() if prop != "return" } dummy_component = CustomComponent._create( children=[], component_fn=component_fn, **dummy_props, ) if dummy_component.tag is None: msg = f"Could not determine the tag name for {component_fn!r}" raise TypeError(msg) CUSTOM_COMPONENTS[dummy_component.tag] = dummy_component return dummy_component def custom_component( component_fn: Callable[..., Component], ) -> Callable[..., CustomComponent]: """Create a custom component from a function. Args: component_fn: The function that creates the component. Returns: The decorated function. """ @wraps(component_fn) def wrapper(*children, **props) -> CustomComponent: # Remove the children from the props. props.pop("children", None) return CustomComponent._create( children=list(children), component_fn=component_fn, **props ) # Register this component so it can be compiled. dummy_component = _register_custom_component(component_fn) if tag := dummy_component.tag: object.__setattr__( wrapper, "_as_var", lambda: Var( tag, _var_type=type[Component], _var_data=VarData( imports={ f"$/{constants.Dirs.UTILS}/components": [ImportVar(tag=tag)], "@emotion/react": [ ImportVar(tag="jsx"), ], } ), ), ) return wrapper # Alias memo to custom_component. memo = custom_component class NoSSRComponent(Component): """A dynamic component that is not rendered on the server.""" def _get_import_name(self) -> str | None: if not self.library: return None return f"${self.library}" if self.library.startswith("/") else self.library def _get_imports(self) -> ParsedImportDict: """Get the imports for the component. Returns: The imports for dynamically importing the component at module load time. """ # React lazy import mechanism. dynamic_import = { f"$/{constants.Dirs.UTILS}/context": [ImportVar(tag="ClientSide")], } # The normal imports for this component. imports_ = super()._get_imports() # Do NOT import the main library/tag statically. import_name = self._get_import_name() if import_name is not None: with contextlib.suppress(ValueError): imports_[import_name].remove(self.import_var) imports_[import_name].append(ImportVar(tag=None, render=False)) return imports.merge_imports( dynamic_import, imports_, self._get_dependencies_imports(), ) def _get_dynamic_imports(self) -> str: # extract the correct import name from library name base_import_name = self._get_import_name() if base_import_name is None: msg = "Undefined library for NoSSRComponent" raise ValueError(msg) import_name = format.format_library_name(base_import_name) library_import = f"import('{import_name}')" mod_import = ( # https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-named-exports f".then((mod) => mod.{self.tag})" if not self.is_default else ".then((mod) => mod.default.default ?? mod.default)" ) return ( f"const {self.alias or self.tag} = ClientSide(() => " + library_import + mod_import + ")" ) class StatefulComponent(BaseComponent): """A component that depends on state and is rendered outside of the page component. If a StatefulComponent is used in multiple pages, it will be rendered to a common file and imported into each page that uses it. A stateful component has a tag name that includes a hash of the code that it renders to. This tag name refers to the specific component with the specific props that it was created with. """ # A lookup table to caching memoized component instances. tag_to_stateful_component: ClassVar[dict[str, StatefulComponent]] = {} # Reference to the original component that was memoized into this component. component: Component = field( default_factory=Component, is_javascript_property=False ) # How many times this component is referenced in the app. references: int = field(default=0, is_javascript_property=False) # Whether the component has already been rendered to a shared file. rendered_as_shared: bool = field(default=False, is_javascript_property=False) memo_trigger_hooks: list[str] = field( default_factory=list, is_javascript_property=False ) @classmethod def create(cls, component: Component) -> StatefulComponent | None: """Create a stateful component from a component. Args: component: The component to memoize. Returns: The stateful component or None if the component should not be memoized. """ from reflex.components.core.foreach import Foreach if component._memoization_mode.disposition == MemoizationDisposition.NEVER: # Never memoize this component. return None if component.tag is None: # Only memoize components with a tag. return None # If _var_data is found in this component, it is a candidate for auto-memoization. should_memoize = False # If the component requests to be memoized, then ignore other checks. if component._memoization_mode.disposition == MemoizationDisposition.ALWAYS: should_memoize = True if not should_memoize: # Determine if any Vars have associated data. for prop_var in component._get_vars(include_children=True): if prop_var._get_all_var_data(): should_memoize = True break if not should_memoize: # Check for special-cases in child components. for child in component.children: # Skip BaseComponent and StatefulComponent children. if not isinstance(child, Component): continue # Always consider Foreach something that must be memoized by the parent. if isinstance(child, Foreach): should_memoize = True break child = cls._child_var(child) if isinstance(child, Var) and child._get_all_var_data(): should_memoize = True break if should_memoize or component.event_triggers: # Render the component to determine tag+hash based on component code. tag_name = cls._get_tag_name(component) if tag_name is None: return None # Look up the tag in the cache stateful_component = cls.tag_to_stateful_component.get(tag_name) if stateful_component is None: memo_trigger_hooks = cls._fix_event_triggers(component) # Set the stateful component in the cache for the given tag. stateful_component = cls.tag_to_stateful_component.setdefault( tag_name, cls( children=component.children, component=component, tag=tag_name, memo_trigger_hooks=memo_trigger_hooks, ), ) # Bump the reference count -- multiple pages referencing the same component # will result in writing it to a common file. stateful_component.references += 1 return stateful_component # Return None to indicate this component should not be memoized. return None @staticmethod def _child_var(child: Component) -> Var | Component: """Get the Var from a child component. This method is used for special cases when the StatefulComponent should actually wrap the parent component of the child instead of recursing into the children and memoizing them independently. Args: child: The child component. Returns: The Var from the child component or the child itself (for regular cases). """ from reflex.components.base.bare import Bare from reflex.components.core.cond import Cond from reflex.components.core.foreach import Foreach from reflex.components.core.match import Match if isinstance(child, Bare): return child.contents if isinstance(child, Cond): return child.cond if isinstance(child, Foreach): return child.iterable if isinstance(child, Match): return child.cond return child @classmethod def _get_tag_name(cls, component: Component) -> str | None: """Get the tag based on rendering the given component. Args: component: The component to render. Returns: The tag for the stateful component. """ # Get the render dict for the component. rendered_code = component.render() if not rendered_code: # Never memoize non-visual components. return None # Compute the hash based on the rendered code. code_hash = _hash_str(_deterministic_hash(rendered_code)) # Format the tag name including the hash. return format.format_state_name( f"{component.tag or 'Comp'}_{code_hash}" ).capitalize() def _render_stateful_code( self, export: bool = False, ) -> str: if not self.tag: return "" # Render the code for this component and hooks. return stateful_component_template( tag_name=self.tag, memo_trigger_hooks=self.memo_trigger_hooks, component=self.component, export=export, ) @classmethod def _fix_event_triggers( cls, component: Component, ) -> list[str]: """Render the code for a stateful component. Args: component: The component to render. Returns: The memoized event trigger hooks for the component. """ # Memoize event triggers useCallback to avoid unnecessary re-renders. memo_event_triggers = tuple(cls._get_memoized_event_triggers(component).items()) # Trigger hooks stored separately to write after the normal hooks (see stateful_component.js.jinja2) memo_trigger_hooks: list[str] = [] if memo_event_triggers: # Copy the component to avoid mutating the original. component = copy.copy(component) for event_trigger, ( memo_trigger, memo_trigger_hook, ) in memo_event_triggers: # Replace the event trigger with the memoized version. memo_trigger_hooks.append(memo_trigger_hook) component.event_triggers[event_trigger] = memo_trigger return memo_trigger_hooks @staticmethod def _get_hook_deps(hook: str) -> list[str]: """Extract var deps from a hook. Args: hook: The hook line to extract deps from. Returns: A list of var names created by the hook declaration. """ # Ensure that the hook is a var declaration. var_decl = hook.partition("=")[0].strip() if not any(var_decl.startswith(kw) for kw in ["const ", "let ", "var "]): return [] # Extract the var name from the declaration. _, _, var_name = var_decl.partition(" ") var_name = var_name.strip() # Break up array and object destructuring if used. if var_name.startswith(("[", "{")): return [ v.strip().replace("...", "") for v in var_name.strip("[]{}").split(",") ] return [var_name] @staticmethod def _get_deps_from_event_trigger( event: EventChain | EventSpec | Var, ) -> dict[str, None]: """Get the dependencies accessed by event triggers. Args: event: The event trigger to extract deps from. Returns: The dependencies accessed by the event triggers. """ events: list = [event] deps = {} if isinstance(event, EventChain): events.extend(event.events) for ev in events: if isinstance(ev, EventSpec): for arg in ev.args: for a in arg: var_datas = VarData.merge(a._get_all_var_data()) if var_datas and var_datas.deps is not None: deps |= {str(dep): None for dep in var_datas.deps} return deps @classmethod def _get_memoized_event_triggers( cls, component: Component, ) -> dict[str, tuple[Var, str]]: """Memoize event handler functions with useCallback to avoid unnecessary re-renders. Args: component: The component with events to memoize. Returns: A dict of event trigger name to a tuple of the memoized event trigger Var and the hook code that memoizes the event handler. """ trigger_memo = {} for event_trigger, event_args in component._get_vars_from_event_triggers( component.event_triggers ): if event_trigger in { EventTriggers.ON_MOUNT, EventTriggers.ON_UNMOUNT, EventTriggers.ON_SUBMIT, }: # Do not memoize lifecycle or submit events. continue # Get the actual EventSpec and render it. event = component.event_triggers[event_trigger] rendered_chain = str(LiteralVar.create(event)) # Hash the rendered EventChain to get a deterministic function name. chain_hash = md5(str(rendered_chain).encode("utf-8")).hexdigest() memo_name = f"{event_trigger}_{chain_hash}" # Calculate Var dependencies accessed by the handler for useCallback dep array. var_deps = ["addEvents", "ReflexEvent"] # Get deps from event trigger var data. var_deps.extend(cls._get_deps_from_event_trigger(event)) # Get deps from hooks. for arg in event_args: var_data = arg._get_all_var_data() if var_data is None: continue for hook in var_data.hooks: var_deps.extend(cls._get_hook_deps(hook)) memo_var_data = VarData.merge( *[var._get_all_var_data() for var in event_args], VarData( imports={"react": [ImportVar(tag="useCallback")]}, ), ) # Store the memoized function name and hook code for this event trigger. trigger_memo[event_trigger] = ( Var(_js_expr=memo_name)._replace( _var_type=EventChain, merge_var_data=memo_var_data ), f"const {memo_name} = useCallback({rendered_chain}, [{', '.join(var_deps)}])", ) return trigger_memo def _get_all_hooks_internal(self) -> dict[str, VarData | None]: """Get the reflex internal hooks for the component and its children. Returns: The code that should appear just before user-defined hooks. """ return {} def _get_all_hooks(self) -> dict[str, VarData | None]: """Get the React hooks for this component. Returns: The code that should appear just before returning the rendered component. """ return {} def _get_all_imports(self) -> ParsedImportDict: """Get all the libraries and fields that are used by the component. Returns: The import dict with the required imports. """ if self.rendered_as_shared: return { f"$/{Dirs.UTILS}/{PageNames.STATEFUL_COMPONENTS}": [ ImportVar(tag=self.tag) ] } return self.component._get_all_imports() def _get_all_dynamic_imports(self) -> set[str]: """Get dynamic imports for the component. Returns: The dynamic imports. """ if self.rendered_as_shared: return set() return self.component._get_all_dynamic_imports() def _get_all_custom_code(self, export: bool = False) -> dict[str, None]: """Get custom code for the component. Args: export: Whether to export the component. Returns: The custom code. """ if self.rendered_as_shared: return {} return self.component._get_all_custom_code() | ({ self._render_stateful_code(export=export): None }) def _get_all_refs(self) -> dict[str, None]: """Get the refs for the children of the component. Returns: The refs for the children. """ if self.rendered_as_shared: return {} return self.component._get_all_refs() def render(self) -> dict: """Define how to render the component in React. Returns: The tag to render. """ return dict(Tag(name=self.tag or "")) def __str__(self) -> str: """Represent the component in React. Returns: The code to render the component. """ from reflex.compiler.compiler import _compile_component return _compile_component(self) @classmethod def compile_from(cls, component: BaseComponent) -> BaseComponent: """Walk through the component tree and memoize all stateful components. Args: component: The component to memoize. Returns: The memoized component tree. """ if isinstance(component, Component): if component._memoization_mode.recursive: # Recursively memoize stateful children (default). component.children = [ cls.compile_from(child) for child in component.children ] # Memoize this component if it depends on state. stateful_component = cls.create(component) if stateful_component is not None: return stateful_component return component class MemoizationLeaf(Component): """A component that does not separately memoize its children. Any component which depends on finding the exact names of children components within it, should be a memoization leaf so the compiler does not replace the provided child tags with memoized tags. During creation, a memoization leaf will mark itself as wanting to be memoized if any of its children return any hooks. """ _memoization_mode = MemoizationMode(recursive=False) @classmethod def create(cls, *children, **props) -> Component: """Create a new memoization leaf component. Args: *children: The children of the component. **props: The props of the component. Returns: The memoization leaf """ comp = super().create(*children, **props) if comp._get_all_hooks(): comp._memoization_mode = dataclasses.replace( comp._memoization_mode, disposition=MemoizationDisposition.ALWAYS ) return comp load_dynamic_serializer() class ComponentVar(Var[Component], python_types=BaseComponent): """A Var that represents a Component.""" def empty_component() -> Component: """Create an empty component. Returns: An empty component. """ from reflex.components.base.bare import Bare return Bare.create("") def render_dict_to_var(tag: dict | Component | str) -> Var: """Convert a render dict to a Var. Args: tag: The render dict. Returns: The Var. """ if not isinstance(tag, dict): if isinstance(tag, Component): return render_dict_to_var(tag.render()) return Var.create(tag) if "contents" in tag: return Var(tag["contents"]) if "iterable" in tag: function_return = LiteralArrayVar.create([ render_dict_to_var(child.render()) for child in tag["children"] ]) func = ArgsFunctionOperation.create( (tag["arg_var_name"], tag["index_var_name"]), function_return, ) return FunctionStringVar.create("Array.prototype.map.call").call( tag["iterable"] if not isinstance(tag["iterable"], ObjectVar) else tag["iterable"].items(), func, ) if "match_cases" in tag: element = Var(tag["cond"]) conditionals = render_dict_to_var(tag["default"]) for case in tag["match_cases"][::-1]: conditions, return_value = case condition = Var.create(False) for pattern in conditions: condition = condition | ( Var(pattern).to_string() == element.to_string() ) conditionals = ternary_operation( condition, render_dict_to_var(return_value), conditionals, ) return conditionals if "cond_state" in tag: return ternary_operation( Var(tag["cond_state"]), render_dict_to_var(tag["true_value"]), render_dict_to_var(tag["false_value"]) if tag["false_value"] is not None else LiteralNoneVar.create(), ) props = Var("({" + ",".join(tag["props"]) + "})") raw_tag_name = tag.get("name") tag_name = Var(raw_tag_name or "Fragment") return FunctionStringVar.create( "jsx", ).call( tag_name, props, *[render_dict_to_var(child) for child in tag["children"]], ) @dataclasses.dataclass( eq=False, frozen=True, slots=True, ) class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar): """A Var that represents a Component.""" _var_value: BaseComponent = dataclasses.field(default_factory=empty_component) @cached_property_no_lock def _cached_var_name(self) -> str: """Get the name of the var. Returns: The name of the var. """ return str(render_dict_to_var(self._var_value.render())) @cached_property_no_lock def _cached_get_all_var_data(self) -> VarData | None: """Get the VarData for the var. Returns: The VarData for the var. """ return VarData.merge( self._var_data, VarData( imports={ "@emotion/react": ["jsx"], "react": ["Fragment"], }, ), VarData( imports=self._var_value._get_all_imports(), ), ) def __hash__(self) -> int: """Get the hash of the var. Returns: The hash of the var. """ return hash((type(self).__name__, self._js_expr)) @classmethod def create( cls, value: Component, _var_data: VarData | None = None, ): """Create a var from a value. Args: value: The value of the var. _var_data: Additional hooks and imports associated with the Var. Returns: The var. """ var_datas = [ var_data for var in value._get_vars(include_children=True) if (var_data := var._get_all_var_data()) ] return LiteralComponentVar( _js_expr="", _var_type=type(value), _var_data=VarData.merge( _var_data, *var_datas, VarData( components=(value,), ), ), _var_value=value, ) ================================================ FILE: reflex/components/core/__init__.py ================================================ """Core Reflex components.""" from __future__ import annotations from reflex.utils import lazy_loader _SUBMODULES: set[str] = {"layout"} _SUBMOD_ATTRS: dict[str, list[str]] = { "banner": [ "ConnectionBanner", "ConnectionModal", "ConnectionPulser", "ConnectionToaster", "connection_banner", "connection_modal", "connection_toaster", "connection_pulser", ], "clipboard": ["Clipboard", "clipboard"], "colors": [ "color", ], "cond": ["Cond", "color_mode_cond", "cond"], "debounce": ["DebounceInput", "debounce_input"], "foreach": [ "foreach", "Foreach", ], "html": ["html", "Html"], "helmet": ["Helmet"], "match": [ "match", "Match", ], "breakpoints": ["breakpoints", "set_breakpoints"], "responsive": [ "desktop_only", "mobile_and_tablet", "mobile_only", "tablet_and_desktop", "tablet_only", ], "upload": [ "upload", "cancel_upload", "clear_selected_files", "get_upload_dir", "get_upload_url", "selected_files", ], "auto_scroll": ["auto_scroll"], "window_events": ["WindowEventListener", "window_event_listener"], } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/core/auto_scroll.py ================================================ """A component that automatically scrolls to the bottom when new content is added.""" from __future__ import annotations import dataclasses from reflex.components.el.elements.typography import Div from reflex.constants.compiler import MemoizationDisposition, MemoizationMode from reflex.utils.imports import ImportDict from reflex.vars.base import Var, get_unique_variable_name class AutoScroll(Div): """A div that automatically scrolls to the bottom when new content is added.""" _memoization_mode = MemoizationMode( disposition=MemoizationDisposition.ALWAYS, recursive=False ) @classmethod def create(cls, *children, **props): """Create an AutoScroll component. Args: *children: The children of the component. **props: The props of the component. Returns: An AutoScroll component. """ props.setdefault("overflow", "auto") props.setdefault("id", get_unique_variable_name()) component = super().create(*children, **props) if "key" in props: component._memoization_mode = dataclasses.replace( component._memoization_mode, recursive=True ) return component def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports required for the component. Returns: The imports required for the component. """ return {"react": ["useEffect", "useRef"]} def add_hooks(self) -> list[str | Var]: """Add hooks required for the component. Returns: The hooks required for the component. """ ref_name = self.get_ref() unique_id = ref_name return [ f"const wasNearBottom_{unique_id} = useRef(false);", f"const hadScrollbar_{unique_id} = useRef(false);", f""" const checkIfNearBottom_{unique_id} = () => {{ if (!{ref_name}.current) return; const container = {ref_name}.current; const nearBottomThreshold = 50; // pixels from bottom to trigger auto-scroll const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight; wasNearBottom_{unique_id}.current = distanceFromBottom <= nearBottomThreshold; // Track if container had a scrollbar hadScrollbar_{unique_id}.current = container.scrollHeight > container.clientHeight; }}; """, f""" const scrollToBottomIfNeeded_{unique_id} = () => {{ if (!{ref_name}.current) return; const container = {ref_name}.current; const hasScrollbarNow = container.scrollHeight > container.clientHeight; // Scroll if: // 1. User was near bottom, OR // 2. Container didn't have scrollbar before but does now if (wasNearBottom_{unique_id}.current || (!hadScrollbar_{unique_id}.current && hasScrollbarNow)) {{ container.scrollTop = container.scrollHeight; }} // Update scrollbar state for next check hadScrollbar_{unique_id}.current = hasScrollbarNow; }}; """, f""" useEffect(() => {{ const container = {ref_name}.current; if (!container) return; scrollToBottomIfNeeded_{unique_id}(); // Create ResizeObserver to detect height changes const resizeObserver = new ResizeObserver(() => {{ scrollToBottomIfNeeded_{unique_id}(); }}); // Track scroll position before height changes container.addEventListener('scroll', checkIfNearBottom_{unique_id}); // Initial check checkIfNearBottom_{unique_id}(); // Observe container for size changes resizeObserver.observe(container); return () => {{ container.removeEventListener('scroll', checkIfNearBottom_{unique_id}); resizeObserver.disconnect(); }}; }}); """, ] auto_scroll = AutoScroll.create ================================================ FILE: reflex/components/core/banner.py ================================================ """Banner components.""" from __future__ import annotations from reflex import constants from reflex.components.base.fragment import Fragment from reflex.components.component import Component from reflex.components.core.cond import cond from reflex.components.el.elements.typography import Div from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.components.dialog import ( DialogContent, DialogRoot, DialogTitle, ) from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.typography.text import Text from reflex.components.sonner.toast import ToastProps, toast_ref from reflex.constants import Dirs, Hooks, Imports from reflex.constants.compiler import CompileVars from reflex.environment import environment from reflex.utils.imports import ImportVar from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar from reflex.vars.number import BooleanVar from reflex.vars.sequence import LiteralArrayVar connect_error_var_data: VarData = VarData( imports=Imports.EVENTS, hooks={Hooks.EVENTS: None}, ) connect_errors = Var( _js_expr=CompileVars.CONNECT_ERROR, _var_data=connect_error_var_data ) connection_error = Var( _js_expr="((connectErrors.length > 0) ? connectErrors[connectErrors.length - 1].message : '')", _var_data=connect_error_var_data, ) connection_errors_count = Var( _js_expr="connectErrors.length", _var_data=connect_error_var_data ) has_connection_errors = Var( _js_expr="(connectErrors.length > 0)", _var_data=connect_error_var_data ).to(BooleanVar) has_too_many_connection_errors = Var( _js_expr="(connectErrors.length >= 2)", _var_data=connect_error_var_data ).to(BooleanVar) class WebsocketTargetURL(Var): """A component that renders the websocket target URL.""" @classmethod def create(cls) -> Var: """Create a websocket target URL component. Returns: The websocket target URL component. """ return Var( _js_expr="getBackendURL(env.EVENT).href", _var_data=VarData( imports={ "$/env.json": [ImportVar(tag="env", is_default=True)], f"$/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")], }, ), _var_type=WebsocketTargetURL, ) def default_connection_error() -> list[str | Var | Component]: """Get the default connection error message. Returns: The default connection error message. """ return [ "Cannot connect to server: ", connection_error, ". Check if server is reachable at ", WebsocketTargetURL.create(), ] class ConnectionToaster(Fragment): """A connection toaster component.""" def add_hooks(self) -> list[str | Var]: """Add the hooks for the connection toaster. Returns: The hooks for the connection toaster. """ toast_id = "websocket-error" target_url = WebsocketTargetURL.create() props = ToastProps( description=LiteralVar.create( f"Check if server is reachable at {target_url}", ), close_button=True, duration=120000, id=toast_id, ) # pyright: ignore [reportCallIssue] if environment.REFLEX_DOES_BACKEND_COLD_START.get(): loading_message = Var.create("Backend is starting.") backend_is_loading_toast_var = Var( f"toast?.loading({loading_message!s}, {{...toast_props, description: '', closeButton: false, onDismiss: () => setUserDismissed(true)}},)" ) backend_is_not_responding = Var.create("Backend is not responding.") backend_is_down_toast_var = Var( f"toast?.error({backend_is_not_responding!s}, {{...toast_props, description: '', onDismiss: () => setUserDismissed(true)}},)" ) toast_var = Var( f""" if (waitedForBackend) {{ {backend_is_down_toast_var!s} }} else {{ {backend_is_loading_toast_var!s}; }} setTimeout(() => {{ if ({has_too_many_connection_errors!s}) {{ setWaitedForBackend(true); }} }}, {environment.REFLEX_BACKEND_COLD_START_TIMEOUT.get() * 1000}); """ ) else: loading_message = Var.create( f"Cannot connect to server: {connection_error}." ) toast_var = Var( f"toast?.error({loading_message!s}, {{...toast_props, onDismiss: () => setUserDismissed(true)}},)" ) individual_hooks = [ Var(f"const toast = {toast_ref};"), f"const toast_props = {LiteralVar.create(props)!s};", "const [userDismissed, setUserDismissed] = useState(false);", "const [waitedForBackend, setWaitedForBackend] = useState(false);", FunctionStringVar( "useEffect", _var_data=VarData( imports={ "react": ("useEffect", "useState"), **( dict(var_data.imports) if (var_data := target_url._get_all_var_data()) is not None else {} ), } ), ).call( # TODO: This breaks the assumption that Vars are JS expressions Var( _js_expr=f""" () => {{ if ({has_too_many_connection_errors!s}) {{ if (!userDismissed) {{ {toast_var!s} }} }} else {{ toast?.dismiss("{toast_id}"); setUserDismissed(false); // after reconnection reset dismissed state }} }} """ ), LiteralArrayVar.create([connect_errors, Var("waitedForBackend")]), ), ] return [ Hooks.EVENTS, *individual_hooks, ] @classmethod def create(cls, *children, **props) -> Component: """Create a connection toaster component. Args: *children: The children of the component. **props: The properties of the component. Returns: The connection toaster component. """ return super().create(*children, **props) class ConnectionBanner(Component): """A connection banner component.""" @classmethod def create(cls, comp: Component | None = None) -> Component: """Create a connection banner component. Args: comp: The component to render when there's a server connection error. Returns: The connection banner component. """ if not comp: comp = Flex.create( Text.create( *default_connection_error(), color="black", size="4", ), justify="center", background_color="crimson", width="100vw", padding="5px", position="fixed", ) return cond(has_connection_errors, comp) class ConnectionModal(Component): """A connection status modal window.""" @classmethod def create(cls, comp: Component | None = None) -> Component: """Create a connection banner component. Args: comp: The component to render when there's a server connection error. Returns: The connection banner component. """ if not comp: comp = Text.create(*default_connection_error()) return cond( has_too_many_connection_errors, DialogRoot.create( DialogContent.create( DialogTitle.create("Connection Error"), comp, ), open=has_too_many_connection_errors, z_index=9999, ), ) class WifiOffPulse(Icon): """A wifi_off icon with an animated opacity pulse.""" @classmethod def create(cls, *children, **props) -> Icon: """Create a wifi_off icon with an animated opacity pulse. Args: *children: The children of the component. **props: The properties of the component. Returns: The icon component with default props applied. """ pulse_var = Var(r"keyframes({ from: { opacity: 0 }, to: { opacity: 1 } })").to( str ) return super().create( "wifi_off", color=props.pop("color", "crimson"), size=props.pop("size", 32), z_index=props.pop("z_index", 9999), position=props.pop("position", "fixed"), bottom=props.pop("bottom", "33px"), right=props.pop("right", "33px"), animation=LiteralVar.create(f"{pulse_var} 1s infinite"), **props, ) def add_imports(self) -> dict[str, str | ImportVar | list[str | ImportVar]]: """Add imports for the WifiOffPulse component. Returns: The import dict. """ return {"@emotion/react": [ImportVar(tag="keyframes")]} class ConnectionPulser(Div): """A connection pulser component.""" @classmethod def create(cls, **props) -> Component: """Create a connection pulser component. Args: **props: The properties of the component. Returns: The connection pulser component. """ return super().create( cond( has_connection_errors, WifiOffPulse.create(**props), ), title=f"Connection Error: {connection_error}", position="fixed", width="100vw", height="0", ) class BackendDisabled(Div): """A component that displays a message when the backend is disabled.""" @classmethod def create(cls, **props) -> Component: """Create a backend disabled component. Args: **props: The properties of the component. Returns: The backend disabled component. """ import reflex as rx is_backend_disabled = Var( "backendDisabled", _var_type=bool, _var_data=VarData( hooks={ "const [backendDisabled, setBackendDisabled] = useState(false);": None, "useEffect(() => { setBackendDisabled(isBackendDisabled()); }, []);": None, }, imports={ f"$/{constants.Dirs.STATE_PATH}": [ ImportVar(tag="isBackendDisabled") ], }, ), ) return super().create( rx.cond( is_backend_disabled, rx.box( rx.el.link( rel="preconnect", href="https://fonts.googleapis.com", ), rx.el.link( rel="preconnect", href="https://fonts.gstatic.com", crossorigin="", ), rx.el.link( href="https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,500;0,600&display=swap", rel="stylesheet", ), rx.box( rx.vstack( rx.text( "This app is paused", font_size="1.5rem", font_weight="600", line_height="1.25rem", letter_spacing="-0.0375rem", ), rx.hstack( rx.el.svg( rx.el.path( d="M6.90816 1.34341C7.61776 1.10786 8.38256 1.10786 9.09216 1.34341C9.7989 1.57799 10.3538 2.13435 10.9112 2.91605C11.4668 3.69515 12.0807 4.78145 12.872 6.18175L12.9031 6.23672C13.6946 7.63721 14.3085 8.72348 14.6911 9.60441C15.0755 10.4896 15.267 11.2539 15.1142 11.9881C14.9604 12.7275 14.5811 13.3997 14.0287 13.9079C13.4776 14.4147 12.7273 14.6286 11.7826 14.7313C10.8432 14.8334 9.6143 14.8334 8.0327 14.8334H7.9677C6.38604 14.8334 5.15719 14.8334 4.21778 14.7313C3.27301 14.6286 2.52269 14.4147 1.97164 13.9079C1.41924 13.3997 1.03995 12.7275 0.88613 11.9881C0.733363 11.2539 0.92483 10.4896 1.30926 9.60441C1.69184 8.72348 2.30573 7.63721 3.09722 6.23671L3.12828 6.18175C3.91964 4.78146 4.53355 3.69515 5.08914 2.91605C5.64658 2.13435 6.20146 1.57799 6.90816 1.34341ZM7.3335 11.3334C7.3335 10.9652 7.63063 10.6667 7.99716 10.6667H8.00316C8.3697 10.6667 8.66683 10.9652 8.66683 11.3334C8.66683 11.7016 8.3697 12.0001 8.00316 12.0001H7.99716C7.63063 12.0001 7.3335 11.7016 7.3335 11.3334ZM7.3335 8.66675C7.3335 9.03495 7.63196 9.33341 8.00016 9.33341C8.36836 9.33341 8.66683 9.03495 8.66683 8.66675V6.00008C8.66683 5.63189 8.36836 5.33341 8.00016 5.33341C7.63196 5.33341 7.3335 5.63189 7.3335 6.00008V8.66675Z", fill_rule="evenodd", clip_rule="evenodd", fill=rx.color("amber", 11), ), width="16", height="16", viewBox="0 0 16 16", fill="none", xmlns="http://www.w3.org/2000/svg", margin_top="0.125rem", flex_shrink="0", ), rx.text( "If you are the owner of this app, visit ", rx.link( "Reflex Cloud", color=rx.color("amber", 11), underline="always", _hover={ "color": rx.color("amber", 11), "text_decoration_color": rx.color( "amber", 11 ), }, text_decoration_color=rx.color("amber", 10), href="https://cloud.reflex.dev/", font_weight="600", is_external=True, ), " for more information on how to resume your app.", font_size="0.875rem", font_weight="500", line_height="1.25rem", letter_spacing="-0.01094rem", color=rx.color("amber", 11), ), align="start", gap="0.625rem", border_radius="0.75rem", border_width="1px", border_color=rx.color("amber", 5), background_color=rx.color("amber", 3), padding="0.625rem", ), rx.link( rx.el.button( "Resume app", color="rgba(252, 252, 253, 1)", font_size="0.875rem", font_weight="600", line_height="1.25rem", letter_spacing="-0.01094rem", height="2.5rem", padding="0rem 0.75rem", width="100%", border_radius="0.75rem", background=f"linear-gradient(180deg, {rx.color('violet', 9)} 0%, {rx.color('violet', 10)} 100%)", _hover={ "background": f"linear-gradient(180deg, {rx.color('violet', 10)} 0%, {rx.color('violet', 10)} 100%)", }, ), width="100%", underline="none", href="https://cloud.reflex.dev/", is_external=True, ), gap="1rem", ), font_family='"Instrument Sans", "Helvetica", "Arial", sans-serif', position="fixed", top="50%", left="50%", transform="translate(-50%, -50%)", width="60ch", max_width="90vw", border_radius="0.75rem", border_width="1px", border_color=rx.color("slate", 4), padding="1.5rem", background_color=rx.color("slate", 1), box_shadow="0px 2px 5px 0px light-dark(rgba(28, 32, 36, 0.03), rgba(0, 0, 0, 0.00))", ), position="fixed", z_index=9999, backdrop_filter="grayscale(1) blur(5px)", width="100dvw", height="100dvh", ), ) ) connection_banner = ConnectionBanner.create connection_modal = ConnectionModal.create connection_toaster = ConnectionToaster.create connection_pulser = ConnectionPulser.create backend_disabled = BackendDisabled.create ================================================ FILE: reflex/components/core/breakpoints.py ================================================ """Breakpoints utility.""" from __future__ import annotations from typing import TypeVar breakpoints_values = ["30em", "48em", "62em", "80em", "96em"] breakpoint_names = ["xs", "sm", "md", "lg", "xl"] def set_breakpoints(values: tuple[str, str, str, str, str]): """Overwrite default breakpoint values. Args: values: CSS values in order defining the breakpoints of responsive layouts """ breakpoints_values.clear() breakpoints_values.extend(values) K = TypeVar("K", bound=str) V = TypeVar("V") class Breakpoints(dict[K, V]): """A responsive styling helper.""" def factorize(self): """Removes references to breakpoints names and instead replaces them with their corresponding values. Returns: The factorized breakpoints. """ return Breakpoints({ ( breakpoints_values[breakpoint_names.index(k)] if k in breakpoint_names else ("0px" if k == "initial" else k) ): v for k, v in self.items() if v is not None }) @classmethod def create( cls, custom: dict[K, V] | None = None, initial: V | None = None, xs: V | None = None, sm: V | None = None, md: V | None = None, lg: V | None = None, xl: V | None = None, ): """Create a new instance of the helper. Only provide a custom component OR use named props. Args: custom: Custom mapping using CSS values or variables. initial: Styling when in the initial width xs: Styling when in the extra-small width sm: Styling when in the small width md: Styling when in the medium width lg: Styling when in the large width xl: Styling when in the extra-large width Returns: The responsive mapping. Raises: ValueError: If both custom and any other named parameters are provided. """ thresholds = [initial, xs, sm, md, lg, xl] if custom is not None: if any(threshold is not None for threshold in thresholds): msg = "Named props cannot be used with custom thresholds" raise ValueError(msg) return Breakpoints(custom) return Breakpoints({ k: v for k, v in zip(["initial", *breakpoint_names], thresholds, strict=True) if v is not None }) breakpoints = Breakpoints.create T = TypeVar("T") Responsive = T | Breakpoints[str, T] ================================================ FILE: reflex/components/core/clipboard.py ================================================ """Global on_paste handling for Reflex app.""" from __future__ import annotations from collections.abc import Sequence from reflex.components.base.fragment import Fragment from reflex.components.tags.tag import Tag from reflex.constants.compiler import Hooks from reflex.event import EventChain, EventHandler, passthrough_event_spec from reflex.utils.format import format_prop, wrap from reflex.utils.imports import ImportVar from reflex.vars import get_unique_variable_name from reflex.vars.base import Var, VarData class Clipboard(Fragment): """Clipboard component.""" # The element ids to attach the event listener to. Defaults to all child components or the document. targets: Var[Sequence[str]] # Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri. on_paste: EventHandler[passthrough_event_spec(list[tuple[str, str]])] # Save the original event actions for the on_paste event. on_paste_event_actions: Var[dict[str, bool | int]] @classmethod def create(cls, *children, **props): """Create a Clipboard component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Clipboard Component. """ if "targets" not in props: # Add all children as targets if not specified. targets = props.setdefault("targets", []) for c in children: if c.id is None: c.id = f"clipboard_{get_unique_variable_name()}" targets.append(c.id) if "on_paste" in props: # Capture the event actions for the on_paste handler if not specified. props.setdefault("on_paste_event_actions", props["on_paste"].event_actions) return super().create(*children, **props) def _exclude_props(self) -> list[str]: return [*super()._exclude_props(), "on_paste", "on_paste_event_actions"] def _render(self) -> Tag: tag = super()._render() # Ensure a different Fragment component is created whenever targets differ return tag.remove_props("targets").add_props(key=self.targets) def add_imports(self) -> dict[str, ImportVar]: """Add the imports for the Clipboard component. Returns: The import dict for the component. """ return { "$/utils/helpers/paste.js": ImportVar( tag="usePasteHandler", is_default=True ), } def add_hooks(self) -> list[str | Var[str]]: """Add hook to register paste event listener. Returns: The hooks to add to the component. """ on_paste = self.event_triggers["on_paste"] if on_paste is None: return [] if isinstance(on_paste, EventChain): on_paste = wrap(str(format_prop(on_paste)).strip("{}"), "(") hook_expr = f"usePasteHandler({self.targets!s}, {self.on_paste_event_actions!s}, {on_paste!s})" return [ Var( hook_expr, _var_type="str", _var_data=VarData(position=Hooks.HookPosition.POST_TRIGGER), ), ] clipboard = Clipboard.create ================================================ FILE: reflex/components/core/colors.py ================================================ """The colors used in Reflex are a wrapper around https://www.radix-ui.com/colors.""" from reflex.constants.base import REFLEX_VAR_OPENING_TAG from reflex.constants.colors import ( COLORS, MAX_SHADE_VALUE, MIN_SHADE_VALUE, Color, ColorType, ShadeType, ) from reflex.vars.base import Var def color( color: ColorType | Var[str], shade: ShadeType | Var[int] = 7, alpha: bool | Var[bool] = False, ) -> Color: """Create a color object. Args: color: The color to use. shade: The shade of the color to use. alpha: Whether to use the alpha variant of the color. Returns: The color object. Raises: ValueError: If the color, shade, or alpha are not valid. """ if isinstance(color, str): if color not in COLORS and REFLEX_VAR_OPENING_TAG not in color: msg = f"Color must be one of {COLORS}, received {color}" raise ValueError(msg) elif not isinstance(color, Var): msg = "Color must be a string or a Var" raise ValueError(msg) if isinstance(shade, int): if shade < MIN_SHADE_VALUE or shade > MAX_SHADE_VALUE: msg = f"Shade must be between {MIN_SHADE_VALUE} and {MAX_SHADE_VALUE}" raise ValueError(msg) elif not isinstance(shade, Var): msg = "Shade must be an integer or a Var" raise ValueError(msg) if not isinstance(alpha, (bool, Var)): msg = "Alpha must be a boolean or a Var" raise ValueError(msg) return Color(color, shade, alpha) ================================================ FILE: reflex/components/core/cond.py ================================================ """Create a list of components from an iterable.""" from __future__ import annotations from typing import Any, overload from reflex.components.base.fragment import Fragment from reflex.components.component import BaseComponent, Component from reflex.components.tags import CondTag, Tag from reflex.constants import Dirs from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode from reflex.utils import types from reflex.utils.imports import ImportDict, ImportVar from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var from reflex.vars.number import ternary_operation _IS_TRUE_IMPORT: ImportDict = { f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")], } class Cond(Component): """Render one of two components based on a condition.""" # The cond to determine which component to render. cond: Var[Any] @classmethod def create( cls, cond: Var, comp1: BaseComponent, comp2: BaseComponent | types.Unset = types.Unset(), ) -> Component: """Create a conditional component. Args: cond: The cond to determine which component to render. comp1: The component to render if the cond is true. comp2: The component to render if the cond is false. Returns: The conditional component. """ # Wrap everything in fragments. if type(comp1) is not Fragment: comp1 = Fragment.create(comp1) if isinstance(comp2, types.Unset) or type(comp2) is not Fragment: comp2 = ( Fragment.create(comp2) if not isinstance(comp2, types.Unset) else Fragment.create() ) return Fragment.create( cls._create( children=[comp1, comp2], cond=cond, ) ) def _render(self) -> Tag: return CondTag( cond_state=str(self.cond), true_value=self.children[0].render(), false_value=self.children[1].render(), ) def render(self) -> dict: """Render the component. Returns: The dictionary for template of component. """ return { "cond_state": str(self.cond), "true_value": self.children[0].render(), "false_value": self.children[1].render(), } def add_imports(self) -> ImportDict: """Add imports for the Cond component. Returns: The import dict for the component. """ var_data = VarData.merge(self.cond._get_all_var_data()) imports = var_data.old_school_imports() if var_data else {} return {**imports, **_IS_TRUE_IMPORT} @overload def cond(condition: Any, c1: Component, c2: Any, /) -> Component: ... # pyright: ignore [reportOverlappingOverload] @overload def cond(condition: Any, c1: Component, /) -> Component: ... @overload def cond(condition: Any, c1: Any, c2: Component, /) -> Component: ... # pyright: ignore [reportOverlappingOverload] @overload def cond(condition: Any, c1: Any, c2: Any, /) -> Var: ... def cond(condition: Any, c1: Any, c2: Any = types.Unset(), /) -> Component | Var: """Create a conditional component or Prop. Args: condition: The cond to determine which component to render. c1: The component or prop to render if the cond_var is true. c2: The component or prop to render if the cond_var is false. Returns: The conditional component. Raises: ValueError: If the arguments are invalid. """ # Convert the condition to a Var. cond_var = LiteralVar.create(condition) if cond_var is None: msg = "The condition must be set." raise ValueError(msg) # If the first component is a component, create a Cond component. if isinstance(c1, BaseComponent): if not isinstance(c2, types.Unset) and not isinstance(c2, BaseComponent): return Cond.create(cond_var.bool(), c1, Fragment.create(c2)) return Cond.create(cond_var.bool(), c1, c2) # Otherwise, create a conditional Var. # Check that the second argument is valid. if isinstance(c2, BaseComponent): return Cond.create(cond_var.bool(), Fragment.create(c1), c2) if isinstance(c2, types.Unset): msg = "For conditional vars, the second argument must be set." raise ValueError(msg) # convert the truth and false cond parts into vars so the _var_data can be obtained. c1_var = Var.create(c1) c2_var = Var.create(c2) if c1_var is cond_var or c1_var.equals(cond_var): c1_var = c1_var.to(types.value_inside_optional(c1_var._var_type)) # Create the conditional var. return ternary_operation( cond_var.bool(), c1_var, c2_var, ) @overload def color_mode_cond(light: Component, dark: Component | None = None) -> Component: ... # pyright: ignore [reportOverlappingOverload] @overload def color_mode_cond(light: Any, dark: Any = None) -> Var: ... def color_mode_cond(light: Any, dark: Any = None) -> Var | Component: """Create a component or Prop based on color_mode. Args: light: The component or prop to render if color_mode is default dark: The component or prop to render if color_mode is non-default Returns: The conditional component or prop. """ return cond( resolved_color_mode == LiteralVar.create(LIGHT_COLOR_MODE), light, dark, ) ================================================ FILE: reflex/components/core/debounce.py ================================================ """Wrapper around react-debounce-input.""" from __future__ import annotations from typing import Any from reflex.components.component import Component from reflex.constants import EventTriggers from reflex.event import EventHandler, no_args_event_spec from reflex.vars import VarData from reflex.vars.base import Var DEFAULT_DEBOUNCE_TIMEOUT = 300 class DebounceInput(Component): """The DebounceInput component is used to buffer input events on the client side. It is intended to wrap various form controls and should be used whenever a fully-controlled input is needed to prevent lost input data when the backend is experiencing high latency. """ library = "react-debounce-input@3.3.0" tag = "DebounceInput" is_default = True # Minimum input characters before triggering the on_change event min_length: Var[int] # Time to wait between end of input and triggering on_change debounce_timeout: Var[int] = Var.create(DEFAULT_DEBOUNCE_TIMEOUT) # If true, notify when Enter key is pressed force_notify_by_enter: Var[bool] # If true, notify when form control loses focus force_notify_on_blur: Var[bool] # If provided, create a fully-controlled input value: Var[str | int | float] # The ref to attach to the created input input_ref: Var[str] # The element to wrap element: Var[type[Component]] # Fired when the input value changes on_change: EventHandler[no_args_event_spec] @classmethod def create(cls, *children: Component, **props: Any) -> Component: """Create a DebounceInput component. Carry first child props directly on this tag. Since react-debounce-input wants to create and manage the underlying input component itself, we carry all props, events, and styles from the child, and then neuter the child's render method so it produces no output. Args: children: The child component to wrap. props: The component props. Returns: The DebounceInput component. Raises: RuntimeError: unless exactly one child element is provided. ValueError: if the child element does not have an on_change handler. """ if len(children) != 1: msg = ( "Provide a single child for DebounceInput, such as rx.input() or " "rx.text_area()" ) raise RuntimeError(msg) child = children[0] if "on_change" not in child.event_triggers: msg = "DebounceInput child requires an on_change handler" raise ValueError(msg) # Carry known props and event_triggers from the child. props_from_child = { p: getattr(child, p) for p in cls.get_props() if getattr(child, p, None) is not None } props[EventTriggers.ON_CHANGE] = child.event_triggers.pop( EventTriggers.ON_CHANGE ) props = {**props_from_child, **props} # Carry all other child props directly via custom_attrs other_props = { p: getattr(child, p) for p in child.get_props() if p not in props_from_child and getattr(child, p) is not None } props.setdefault("custom_attrs", {}).update(other_props, **child.custom_attrs) # Carry base Component props. props.setdefault("style", {}).update(child.style) if child.class_name is not None: props["class_name"] = f"{props.get('class_name', '')} {child.class_name}" for field in ("key", "special_props"): if getattr(child, field) is not None: props[field] = getattr(child, field) child_ref = child.get_ref() if props.get("input_ref") is None and child_ref: props["input_ref"] = Var(_js_expr=child_ref, _var_type=str) if child.id is not None: props["id"] = child.id # Set the child element to wrap, including any imports/hooks from the child. props.setdefault( "element", Var( _js_expr=str(child.alias or child.tag), _var_type=type[Component], _var_data=VarData( imports=child._get_imports(), hooks=child._get_all_hooks(), ), ), ) component = super().create(**props) component._get_style = child._get_style component.event_triggers.update(child.event_triggers) component.children = child.children component._rename_props = child._rename_props # pyright: ignore[reportAttributeAccessIssue] outer_get_all_custom_code = component._get_all_custom_code component._get_all_custom_code = lambda: ( outer_get_all_custom_code() | (child._get_all_custom_code()) ) return component def _render(self): return super()._render().remove_props("ref") debounce_input = DebounceInput.create ================================================ FILE: reflex/components/core/foreach.py ================================================ """Create a list of components from an iterable.""" from __future__ import annotations import functools import inspect from collections.abc import Callable, Iterable from hashlib import md5 from typing import Any from reflex.components.base.fragment import Fragment from reflex.components.component import Component, field from reflex.components.core.cond import cond from reflex.components.tags import IterTag from reflex.constants import MemoizationMode from reflex.constants.state import FIELD_MARKER from reflex.state import ComponentState from reflex.utils import types from reflex.utils.exceptions import UntypedVarError from reflex.vars.base import LiteralVar, Var class ForeachVarError(TypeError): """Raised when the iterable type is Any.""" class ForeachRenderError(TypeError): """Raised when there is an error with the foreach render function.""" class Foreach(Component): """A component that takes in an iterable and a render function and renders a list of components.""" _memoization_mode = MemoizationMode(recursive=False) # The iterable to create components from. iterable: Var[Iterable] # A function from the render args to the component. render_fn: Callable = field(default=Fragment.create, is_javascript_property=False) @classmethod def create( cls, iterable: Var[Iterable] | Iterable, render_fn: Callable, ) -> Foreach: """Create a foreach component. Args: iterable: The iterable to create components from. render_fn: A function from the render args to the component. Returns: The foreach component. Raises: ForeachVarError: If the iterable is of type Any. TypeError: If the render function is a ComponentState. UntypedVarError: If the iterable is of type Any without a type annotation. # noqa: DAR401 with_traceback # noqa: DAR402 UntypedVarError """ from reflex.vars import ArrayVar, ObjectVar, StringVar iterable = ( LiteralVar.create(iterable).guess_type() if not isinstance(iterable, Var) else iterable.guess_type() ) if iterable._var_type == Any: msg = ( f"Could not foreach over var `{iterable!s}` of type Any. " "(If you are trying to foreach over a state var, add a type annotation to the var). " "See https://reflex.dev/docs/library/dynamic-rendering/foreach/" ) raise ForeachVarError(msg) if ( hasattr(render_fn, "__qualname__") and render_fn.__qualname__ == ComponentState.create.__qualname__ ): msg = "Using a ComponentState as `render_fn` inside `rx.foreach` is not supported yet." raise TypeError(msg) if isinstance(iterable, ObjectVar): iterable = iterable.entries() if isinstance(iterable, StringVar): iterable = iterable.split() if not isinstance(iterable, ArrayVar): msg = ( f"Could not foreach over var `{iterable!s}` of type {iterable._var_type}. " "See https://reflex.dev/docs/library/dynamic-rendering/foreach/" ) raise ForeachVarError(msg) if types.is_optional(iterable._var_type): iterable = cond(iterable, iterable, []) component = cls._create( children=[], iterable=iterable, render_fn=render_fn, ) try: # Keep a ref to a rendered component to determine correct imports/hooks/styles. component.children = [component._render().render_component()] except UntypedVarError as e: raise UntypedVarError( iterable, "foreach", "https://reflex.dev/docs/library/dynamic-rendering/foreach/", ).with_traceback(e.__traceback__) from None return component def _render(self) -> IterTag: props = {} render_sig = inspect.signature(self.render_fn) params = list(render_sig.parameters.values()) # Validate the render function signature. if len(params) == 0 or len(params) > 2: msg = ( "Expected 1 or 2 parameters in foreach render function, got " f"{[p.name for p in params]}. See " "https://reflex.dev/docs/library/dynamic-rendering/foreach/" ) raise ForeachRenderError(msg) if len(params) >= 1: # Determine the arg var name based on the params accepted by render_fn. props["arg_var_name"] = params[0].name + FIELD_MARKER if len(params) == 2: # Determine the index var name based on the params accepted by render_fn. props["index_var_name"] = params[1].name + FIELD_MARKER else: render_fn = self.render_fn # Otherwise, use a deterministic index, based on the render function bytecode. if (render_fn_code := getattr(render_fn, "__code__", None)) is not None: code_hash = md5(render_fn_code.co_code).hexdigest() elif isinstance(render_fn, functools.partial): code_hash = md5(render_fn.func.__code__.co_code).hexdigest() else: code_hash = md5(repr(render_fn).encode()).hexdigest() props["index_var_name"] = f"index_{code_hash}" return IterTag( iterable=self.iterable, render_fn=self.render_fn, children=self.children, **props, ) def render(self): """Render the component. Returns: The dictionary for template of component. """ tag = self._render() return dict( tag, iterable_state=str(tag.iterable), arg_name=tag.arg_var_name, arg_index=tag.index_var_name, ) foreach = Foreach.create ================================================ FILE: reflex/components/core/helmet.py ================================================ """Helmet component module.""" from reflex.components.component import Component class Helmet(Component): """A helmet component.""" library = "react-helmet@6.1.0" tag = "Helmet" helmet = Helmet.create ================================================ FILE: reflex/components/core/html.py ================================================ """A html component.""" from reflex.components.el.elements.typography import Div from reflex.vars.base import Var class Html(Div): """Render the html. Returns: The code to render the html component. """ # The HTML to render. dangerouslySetInnerHTML: Var[dict[str, str]] # noqa: N815 @classmethod def create(cls, *children, **props): """Create a html component. Args: *children: The children of the component. **props: The props to pass to the component. Returns: The html component. Raises: ValueError: If children are not provided or more than one child is provided. """ # If children are not provided, throw an error. if len(children) != 1: msg = "Must provide children to the html component." raise ValueError(msg) props["dangerouslySetInnerHTML"] = {"__html": children[0]} # Apply the default classname given_class_name = props.pop("class_name", []) if isinstance(given_class_name, str): given_class_name = [given_class_name] props["class_name"] = ["rx-Html", *given_class_name] # Create the component. return super().create(**props) html = Html.create ================================================ FILE: reflex/components/core/layout/__init__.py ================================================ """Core layout components.""" ================================================ FILE: reflex/components/core/match.py ================================================ """rx.match.""" import textwrap from typing import Any, cast from reflex.components.base import Fragment from reflex.components.component import BaseComponent, Component, MemoizationLeaf, field from reflex.components.tags import Tag from reflex.components.tags.match_tag import MatchTag from reflex.style import Style from reflex.utils import format from reflex.utils.exceptions import MatchTypeError from reflex.utils.imports import ImportDict from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var class Match(MemoizationLeaf): """Match cases based on a condition.""" # The condition to determine which case to match. cond: Var[Any] # The list of match cases to be matched. match_cases: list[tuple[list[Var], BaseComponent]] = field( default_factory=list, is_javascript_property=False ) # The catchall case to match. default: BaseComponent = field( default_factory=Fragment.create, is_javascript_property=False ) @classmethod def create(cls, cond: Any, *cases) -> Component | Var: """Create a Match Component. Args: cond: The condition to determine which case to match. cases: This list of cases to match. Returns: The match component. Raises: ValueError: When a default case is not provided for cases with Var return types. """ match_cond_var = cls._create_condition_var(cond) cases, default = cls._process_cases(list(cases)) match_cases = cls._process_match_cases(cases) match_cases = cls._validate_return_types(match_cases) if default is None and isinstance(match_cases[0][1], Var): msg = "For cases with return types as Vars, a default case must be provided" raise ValueError(msg) return cls._create_match_cond_var_or_component( match_cond_var, match_cases, default ) @classmethod def _create_condition_var(cls, cond: Any) -> Var: """Convert the condition to a Var. Args: cond: The condition. Returns: The condition as a base var Raises: ValueError: If the condition is not provided. """ match_cond_var = LiteralVar.create(cond) if match_cond_var is None: msg = "The condition must be set" raise ValueError(msg) return match_cond_var @classmethod def _process_cases( cls, cases: list ) -> tuple[list[tuple], Var | BaseComponent | None]: """Process the list of match cases and the catchall default case. Args: cases: The list of match cases. Returns: The default case and the list of match case tuples. Raises: ValueError: If there are multiple default cases. """ if not cases: msg = "rx.match should have at least one case." raise ValueError(msg) if not isinstance(cases[-1], tuple): *cases, default_return = cases default_return = ( cls._create_case_var_with_var_data(default_return) if not isinstance(default_return, BaseComponent) else default_return ) else: default_return = None if any(case for case in cases if not isinstance(case, tuple)): msg = "rx.match should have tuples of cases and one default case as the last argument." raise ValueError(msg) if not cases: msg = "rx.match should have at least one case." raise ValueError(msg) return cases, default_return @classmethod def _create_case_var_with_var_data(cls, case_element: Any) -> Var: """Convert a case element into a Var.If the case is a Style type, we extract the var data and merge it with the newly created Var. Args: case_element: The case element. Returns: The case element Var. """ var_data = case_element._var_data if isinstance(case_element, Style) else None return LiteralVar.create(case_element, _var_data=var_data) @classmethod def _process_match_cases( cls, cases: list[tuple] ) -> list[tuple[list[Var], BaseComponent | Var]]: """Process the individual match cases. Args: cases: The match cases. Returns: The processed match cases. Raises: ValueError: If the default case is not the last case or the tuple elements are less than 2. """ match_cases: list[tuple[list[Var], BaseComponent | Var]] = [] for case_index, case in enumerate(cases): # There should be at least two elements in a case tuple(a condition and return value) if len(case) < 2: msg = "A case tuple should have at least a match case element and a return value." raise ValueError(msg) *conditions, return_value = case conditions_vars: list[Var] = [] for condition_index, condition in enumerate(conditions): if isinstance(condition, BaseComponent): msg = f"Match condition {condition_index} of case {case_index} cannot be a component." raise ValueError(msg) conditions_vars.append(cls._create_case_var_with_var_data(condition)) return_value = ( cls._create_case_var_with_var_data(return_value) if not isinstance(return_value, BaseComponent) else return_value ) if not isinstance(return_value, (Var, BaseComponent)): msg = "Return value must be a var or component" raise ValueError(msg) match_cases.append((conditions_vars, return_value)) return match_cases @classmethod def _validate_return_types( cls, match_cases: list[tuple[list[Var], BaseComponent | Var]] ) -> list[tuple[list[Var], Var]] | list[tuple[list[Var], BaseComponent]]: """Validate that match cases have the same return types. Args: match_cases: The match cases. Returns: The validated match cases. Raises: MatchTypeError: If the return types of cases are different. """ first_case_return = match_cases[0][-1] return_type = type(first_case_return) if isinstance(first_case_return, BaseComponent): return_type = BaseComponent elif isinstance(first_case_return, Var): return_type = Var cases = [] for index, case in enumerate(match_cases): conditions, return_value = case if not isinstance(return_value, return_type): msg = ( f"Match cases should have the same return types. Case {index} with return " f"value `{return_value._js_expr if isinstance(return_value, Var) else textwrap.shorten(str(return_value), width=250)}`" f" of type {type(return_value)!r} is not {return_type}" ) raise MatchTypeError(msg) cases.append((conditions, return_value)) return cases @classmethod def _create_match_cond_var_or_component( cls, match_cond_var: Var, match_cases: list[tuple[list[Var], BaseComponent]] | list[tuple[list[Var], Var]], default: Var | BaseComponent | None, ) -> Component | Var: """Create and return the match condition var or component. Args: match_cond_var: The match condition. match_cases: The list of match cases. default: The default case. Returns: The match component wrapped in a fragment or the match var. """ if isinstance(match_cases[0][1], BaseComponent): if default is None: default = Fragment.create() return Fragment.create( cls._create( cond=match_cond_var, match_cases=match_cases, default=default, children=[case[1] for case in match_cases] + [default], # pyright: ignore [reportArgumentType] ) ) match_cases = cast("list[tuple[list[Var], Var]]", match_cases) default = cast("Var", default) return Var( _js_expr=format.format_match( cond=str(match_cond_var), match_cases=match_cases, default=default, ), _var_type=default._var_type, _var_data=VarData.merge( match_cond_var._get_all_var_data(), *[el._get_all_var_data() for case in match_cases for el in case[0]], *[case[1]._get_all_var_data() for case in match_cases], default._get_all_var_data(), ), ) def _render(self) -> Tag: return MatchTag( cond=str(self.cond), match_cases=[ ([str(cond) for cond in conditions], return_value.render()) for conditions, return_value in self.match_cases ], default=self.default.render(), ) def render(self) -> dict: """Render the component. Returns: The dictionary for template of component. """ return dict(self._render()) def add_imports(self) -> ImportDict: """Add imports for the Match component. Returns: The import dict. """ var_data = VarData.merge(self.cond._get_all_var_data()) return var_data.old_school_imports() if var_data else {} match = Match.create ================================================ FILE: reflex/components/core/responsive.py ================================================ """Responsive components.""" from reflex.components.radix.themes.layout.box import Box # Add responsive styles shortcuts. def mobile_only(*children, **props): """Create a component that is only visible on mobile. Args: *children: The children to pass to the component. **props: The props to pass to the component. Returns: The component. """ return Box.create(*children, **props, display=["block", "none", "none", "none"]) def tablet_only(*children, **props): """Create a component that is only visible on tablet. Args: *children: The children to pass to the component. **props: The props to pass to the component. Returns: The component. """ return Box.create(*children, **props, display=["none", "block", "block", "none"]) def desktop_only(*children, **props): """Create a component that is only visible on desktop. Args: *children: The children to pass to the component. **props: The props to pass to the component. Returns: The component. """ return Box.create(*children, **props, display=["none", "none", "none", "block"]) def tablet_and_desktop(*children, **props): """Create a component that is only visible on tablet and desktop. Args: *children: The children to pass to the component. **props: The props to pass to the component. Returns: The component. """ return Box.create(*children, **props, display=["none", "block", "block", "block"]) def mobile_and_tablet(*children, **props): """Create a component that is only visible on mobile and tablet. Args: *children: The children to pass to the component. **props: The props to pass to the component. Returns: The component. """ return Box.create(*children, **props, display=["block", "block", "block", "none"]) ================================================ FILE: reflex/components/core/sticky.py ================================================ """Components for displaying the Reflex sticky logo.""" from reflex.components.component import ComponentNamespace from reflex.components.core.colors import color from reflex.components.core.cond import color_mode_cond from reflex.components.core.responsive import desktop_only from reflex.components.el.elements.inline import A from reflex.components.el.elements.media import Path, Rect, Svg from reflex.components.radix.themes.typography.text import Text from reflex.style import Style class StickyLogo(Svg): """A simple Reflex logo SVG with only the letter R.""" @classmethod def create(cls): """Create the simple Reflex logo SVG. Returns: The simple Reflex logo SVG. """ return super().create( Rect.create(width="16", height="16", rx="2", fill="#6E56CF"), Path.create(d="M10 9V13H12V9H10Z", fill="white"), Path.create(d="M4 3V13H6V9H10V7H6V5H10V7H12V3H4Z", fill="white"), width="16", height="16", viewBox="0 0 16 16", xmlns="http://www.w3.org/2000/svg", ) def add_style(self): """Add the style to the component. Returns: The style of the component. """ return Style({ "fill": "white", }) class StickyLabel(Text): """A label that displays the Reflex sticky.""" @classmethod def create(cls): """Create the sticky label. Returns: The sticky label. """ return super().create("Built with Reflex") def add_style(self): """Add the style to the component. Returns: The style of the component. """ return Style({ "color": color("slate", 1), "font_weight": "600", "font_family": "'Instrument Sans', sans-serif", "font_size": "0.875rem", "line_height": "1rem", "letter_spacing": "-0.00656rem", }) class StickyBadge(A): """A badge that displays the Reflex sticky logo.""" @classmethod def create(cls): """Create the sticky badge. Returns: The sticky badge. """ return super().create( StickyLogo.create(), desktop_only(StickyLabel.create()), href="https://reflex.dev", target="_blank", width="auto", padding="0.375rem", align="center", text_align="center", ) def add_style(self): """Add the style to the component. Returns: The style of the component. """ return Style( { "position": "fixed", "bottom": "1rem", "right": "1rem", "display": "flex", "flex-direction": "row", "gap": "0.375rem", "align-items": "center", "width": "auto", "border-radius": "0.5rem", "color": color_mode_cond("#E5E7EB", "#27282B"), "border": color_mode_cond("1px solid #27282B", "1px solid #E5E7EB"), "background-color": color_mode_cond("#151618", "#FCFCFD"), "padding": "0.375rem", "transition": "background-color 0.2s ease-in-out", "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", "z-index": "9998", "cursor": "pointer", }, ) class StickyNamespace(ComponentNamespace): """Sticky components namespace.""" __call__ = staticmethod(StickyBadge.create) label = staticmethod(StickyLabel.create) logo = staticmethod(StickyLogo.create) sticky = StickyNamespace() ================================================ FILE: reflex/components/core/upload.py ================================================ """A file upload component.""" from __future__ import annotations from collections.abc import Callable, Sequence from pathlib import Path from typing import Any, ClassVar from reflex.app import UploadFile from reflex.components.base.fragment import Fragment from reflex.components.component import ( Component, ComponentNamespace, MemoizationLeaf, StatefulComponent, field, ) from reflex.components.core.cond import cond from reflex.components.el.elements.forms import Input from reflex.components.radix.themes.layout.box import Box from reflex.components.sonner.toast import toast from reflex.constants import Dirs from reflex.constants.compiler import Hooks, Imports from reflex.environment import environment from reflex.event import ( CallableEventSpec, EventChain, EventHandler, EventSpec, call_event_fn, call_event_handler, parse_args_spec, passthrough_event_spec, run_script, upload_files, ) from reflex.style import Style from reflex.utils import format from reflex.utils.imports import ImportVar from reflex.vars import VarData from reflex.vars.base import Var, get_unique_variable_name from reflex.vars.function import FunctionVar from reflex.vars.object import ObjectVar from reflex.vars.sequence import ArrayVar, LiteralStringVar DEFAULT_UPLOAD_ID: str = "default" upload_files_context_var_data: VarData = VarData( imports={ "react": "useContext", f"$/{Dirs.CONTEXTS_PATH}": "UploadFilesContext", }, hooks={ "const [filesById, setFilesById] = useContext(UploadFilesContext);": None, }, ) def upload_file(id_: str | Var[str] = DEFAULT_UPLOAD_ID) -> Var: """Get the file upload drop trigger. This var is passed to the dropzone component to update the file list when a drop occurs. Args: id_: The id of the upload to get the drop trigger for. Returns: A var referencing the file upload drop trigger. """ id_var = LiteralStringVar.create(id_) if not isinstance(id_, Var) else id_ var_name = f"""e => setFilesById(filesById => {{ const updatedFilesById = Object.assign({{}}, filesById); updatedFilesById[{id_var!s}] = e; return updatedFilesById; }}) """ return Var( _js_expr=var_name, _var_type=EventChain, _var_data=VarData.merge( upload_files_context_var_data, id_var._get_all_var_data() ), ) def selected_files(id_: str | Var[str] = DEFAULT_UPLOAD_ID) -> Var: """Get the list of selected files. Args: id_: The id of the upload to get the selected files for. Returns: A var referencing the list of selected file paths. """ id_var = LiteralStringVar.create(id_) if not isinstance(id_, Var) else id_ return Var( _js_expr=f"(filesById[{id_var!s}] ? filesById[{id_var!s}].map((f) => f.name) : [])", _var_type=list[str], _var_data=VarData.merge( upload_files_context_var_data, id_var._get_all_var_data() ), ).guess_type() @CallableEventSpec def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec: """Clear the list of selected files. Args: id_: The id of the upload to clear. Returns: An event spec that clears the list of selected files when triggered. """ # UploadFilesProvider assigns a special function to clear selected files # into the shared global refs object to make it accessible outside a React # component via `run_script` (otherwise backend could never clear files). return run_script(Var("__clear_selected_files")._as_ref().to(FunctionVar).call(id_)) def cancel_upload(upload_id: str) -> EventSpec: """Cancel an upload. Args: upload_id: The id of the upload to cancel. Returns: An event spec that cancels the upload when triggered. """ controller = Var(f"__upload_controllers_{upload_id}")._as_ref() return run_script(f"{controller}?.abort()") def get_upload_dir() -> Path: """Get the directory where uploaded files are stored. Returns: The directory where uploaded files are stored. """ Upload.is_used = True uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR.get() uploaded_files_dir.mkdir(parents=True, exist_ok=True) return uploaded_files_dir uploaded_files_url_prefix = Var( _js_expr="getBackendURL(env.UPLOAD)", _var_data=VarData( imports={ f"$/{Dirs.STATE_PATH}": "getBackendURL", "$/env.json": ImportVar(tag="env", is_default=True), } ), ).to(str) def get_upload_url(file_path: str | Var[str]) -> Var[str]: """Get the URL of an uploaded file. Args: file_path: The path of the uploaded file. Returns: The URL of the uploaded file to be rendered from the frontend (as a str-encoded Var). """ Upload.is_used = True return Var.create(f"{uploaded_files_url_prefix}/{file_path}") _on_drop_spec = passthrough_event_spec(list[UploadFile]) def _default_drop_rejected(rejected_files: ArrayVar[list[dict[str, Any]]]) -> EventSpec: """Event handler for showing a toast with rejected file info. Args: rejected_files: The files that were rejected. Returns: An event spec that shows a toast with the rejected file info when triggered. """ def _format_rejected_file_record(rf: ObjectVar[dict[str, Any]]) -> str: rf = rf.to(ObjectVar, dict[str, dict[str, Any]]) file = rf["file"].to(ObjectVar, dict[str, Any]) errors = rf["errors"].to(ArrayVar, list[dict[str, Any]]) return f"{file['path']}: {errors.foreach(lambda kv: kv['message']).join(', ')}" # noqa: FURB118 return toast.error( title="Files not Accepted", description=rejected_files .to(ArrayVar) .foreach(_format_rejected_file_record) .join("\n\n"), close_button=True, style={"white_space": "pre-line"}, ) class UploadFilesProvider(Component): """AppWrap component that provides a dict of selected files by ID via useContext.""" library = f"$/{Dirs.CONTEXTS_PATH}" tag = "UploadFilesProvider" class GhostUpload(Fragment): """A ghost upload component.""" # Fired when files are dropped. on_drop: EventHandler[_on_drop_spec] # Fired when dropped files do not meet the specified criteria. on_drop_rejected: EventHandler[_on_drop_spec] class Upload(MemoizationLeaf): """A file upload component.""" library = "react-dropzone@15.0.0" tag = "" # The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as # values. # supported MIME types: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types accept: Var[dict[str, Sequence] | None] # Whether the dropzone is disabled. disabled: Var[bool] # The maximum number of files that can be uploaded. max_files: Var[int] # The maximum file size (bytes) that can be uploaded. max_size: Var[int] # The minimum file size (bytes) that can be uploaded. min_size: Var[int] # Whether to allow multiple files to be uploaded. multiple: Var[bool] # Whether to disable click to upload. no_click: Var[bool] # Whether to disable drag and drop. no_drag: Var[bool] # Whether to disable using the space/enter keys to upload. no_keyboard: Var[bool] # Marked True when any Upload component is created. is_used: ClassVar[bool] = False # Fired when files are dropped. on_drop: EventHandler[_on_drop_spec] # Fired when dropped files do not meet the specified criteria. on_drop_rejected: EventHandler[_on_drop_spec] # Style rules to apply when actively dragging. drag_active_style: Style | None = field(default=None, is_javascript_property=False) @classmethod def create(cls, *children, **props) -> Component: """Create an upload component. Args: *children: The children of the component. **props: The properties of the component. Returns: The upload component. """ # Mark the Upload component as used in the app. cls.is_used = True props.setdefault("multiple", True) # Apply the default classname given_class_name = props.pop("class_name", []) if isinstance(given_class_name, str): given_class_name = [given_class_name] props["class_name"] = ["rx-Upload", *given_class_name] # get only upload component props supported_props = set(cls.get_props()) | {"on_drop"} upload_props = { key: value for key, value in props.items() if key in supported_props } # Create the component. upload_props["id"] = upload_id = props.get("id", DEFAULT_UPLOAD_ID) if upload_props.get("on_drop") is None: # If on_drop is not provided, save files to be uploaded later. upload_props["on_drop"] = upload_file(upload_id) else: on_drop = ( [on_drop_prop] if not isinstance(on_drop_prop := upload_props["on_drop"], Sequence) else list(on_drop_prop) ) for ix, event in enumerate(on_drop): if isinstance(event, EventHandler): event = event(upload_files(upload_id)) if isinstance(event, EventSpec): # Call the lambda to get the event chain. event = call_event_handler(event, _on_drop_spec) elif isinstance(event, Callable): # Call the lambda to get the event chain. event = call_event_fn(event, _on_drop_spec) if isinstance(event, EventSpec): # Update the provided args for direct use with on_drop. event = event.with_args( args=tuple( cls._update_arg_tuple_for_on_drop(arg_value) for arg_value in event.args ), ) on_drop[ix] = event upload_props["on_drop"] = on_drop if upload_props.get("on_drop_rejected") is None: # If on_drop_rejected is not provided, show an error toast. upload_props["on_drop_rejected"] = _default_drop_rejected input_props_unique_name = get_unique_variable_name() root_props_unique_name = get_unique_variable_name() is_drag_active_unique_name = get_unique_variable_name() drag_active_css_class_unique_name = get_unique_variable_name() + "-drag-active" # Handle special style when dragging over the drop zone. if "drag_active_style" in props: props.setdefault("style", Style())[ f"&:where(.{drag_active_css_class_unique_name})" ] = props.pop("drag_active_style") props["class_name"].append( cond( Var(is_drag_active_unique_name), drag_active_css_class_unique_name, "", ), ) event_triggers = StatefulComponent._get_memoized_event_triggers( GhostUpload.create( on_drop=upload_props["on_drop"], on_drop_rejected=upload_props["on_drop_rejected"], ) ) callback_hooks = [] for trigger_name, (event_var, callback_str) in event_triggers.items(): upload_props[trigger_name] = event_var callback_hooks.append(callback_str) upload_props = { format.to_camel_case(key): value for key, value in upload_props.items() } use_dropzone_arguments = Var.create(upload_props) left_side = ( "const { " f"getRootProps: {root_props_unique_name}, " f"getInputProps: {input_props_unique_name}, " f"isDragActive: {is_drag_active_unique_name}" "}" ) right_side = f"useDropzone({use_dropzone_arguments!s})" var_data = VarData.merge( VarData( imports=Imports.EVENTS, hooks={Hooks.EVENTS: None}, ), use_dropzone_arguments._get_all_var_data(), VarData( hooks={ **dict.fromkeys(callback_hooks, None), f"{left_side} = {right_side};": None, }, imports={ "react-dropzone": "useDropzone", **Imports.EVENTS, }, ), ) # The file input to use. upload = Input.create(type="file") upload.special_props = [ Var( _js_expr=f"{input_props_unique_name}()", _var_type=None, _var_data=var_data, ) ] # The dropzone to use. zone = Box.create( upload, *children, **{k: v for k, v in props.items() if k not in supported_props}, ) zone.special_props = [ Var( _js_expr=f"{root_props_unique_name}()", _var_type=None, _var_data=var_data, ) ] return super().create( zone, ) @classmethod def _update_arg_tuple_for_on_drop(cls, arg_value: tuple[Var, Var]): """Helper to update caller-provided EventSpec args for direct use with on_drop. Args: arg_value: The arg tuple to update (if necessary). Returns: The updated arg_value tuple when arg is "files", otherwise the original arg_value. """ if arg_value[0]._js_expr == "files": placeholders, _ = parse_args_spec(_on_drop_spec) placeholder = placeholders[0] return (arg_value[0], placeholder) return arg_value @staticmethod def _get_app_wrap_components() -> dict[tuple[int, str], Component]: return { (5, "UploadFilesProvider"): UploadFilesProvider.create(), } class StyledUpload(Upload): """The styled Upload Component.""" @classmethod def create(cls, *children, **props) -> Component: """Create the styled upload component. Args: *children: The children of the component. **props: The properties of the component. Returns: The styled upload component. """ # Set default props. props.setdefault("border", "1px dashed var(--accent-12)") props.setdefault("padding", "5em") props.setdefault("textAlign", "center") # Mark the Upload component as used in the app. Upload.is_used = True return super().create( *children, **props, ) class UploadNamespace(ComponentNamespace): """Upload component namespace.""" root = Upload.create __call__ = StyledUpload.create upload = UploadNamespace() ================================================ FILE: reflex/components/core/window_events.py ================================================ """Window event listener component for Reflex.""" from __future__ import annotations from typing import Any, cast import reflex as rx from reflex.components.base.fragment import Fragment from reflex.components.component import StatefulComponent, field from reflex.constants.compiler import Hooks from reflex.event import key_event, no_args_event_spec from reflex.vars.base import Var, VarData from reflex.vars.object import ObjectVar def _on_resize_spec() -> tuple[Var[int], Var[int]]: """Args spec for the on_resize event trigger. Returns: A tuple containing window width and height variables. """ return (Var("window.innerWidth"), Var("window.innerHeight")) def _on_scroll_spec() -> tuple[Var[float], Var[float]]: """Args spec for the on_scroll event trigger. Returns: A tuple containing window scroll X and Y position variables. """ return (Var("window.scrollX"), Var("window.scrollY")) def _on_visibility_change_spec() -> tuple[Var[bool]]: """Args spec for the on_visibility_change event trigger. Returns: A tuple containing the document hidden state variable. """ return (Var("document.hidden"),) def _on_storage_spec(e: ObjectVar) -> tuple[Var[str], Var[str], Var[str], Var[str]]: """Args spec for the on_storage event trigger. Args: e: The storage event. Returns: A tuple containing key, old value, new value, and URL variables. """ return (e.key.to(str), e.oldValue.to(str), e.newValue.to(str), e.url.to(str)) class WindowEventListener(Fragment): """A component that listens for window events.""" # Event handlers on_resize: rx.EventHandler[_on_resize_spec] on_scroll: rx.EventHandler[_on_scroll_spec] on_focus: rx.EventHandler[no_args_event_spec] on_blur: rx.EventHandler[no_args_event_spec] on_visibility_change: rx.EventHandler[_on_visibility_change_spec] on_before_unload: rx.EventHandler[no_args_event_spec] on_key_down: rx.EventHandler[key_event] on_popstate: rx.EventHandler[no_args_event_spec] on_storage: rx.EventHandler[_on_storage_spec] hooks: list[str] = field(default_factory=list, is_javascript_property=False) @classmethod def create(cls, **props) -> WindowEventListener: """Create a WindowEventListener component. Args: **props: The props to set on the component. Returns: The created component. """ real_component = cast("WindowEventListener", super().create(**props)) hooks = StatefulComponent._fix_event_triggers(real_component) real_component.hooks = hooks return real_component def _exclude_props(self) -> list[str]: """Exclude event handler props from being passed to Fragment. Returns: List of prop names to exclude from the Fragment. """ return [*super()._exclude_props(), *self.event_triggers.keys()] def add_hooks(self) -> list[str | Var[Any]]: """Add hooks to register window event listeners. Returns: The hooks to add to the component. """ hooks: list[str | Var[Any]] = [*self.hooks] for prop_name, event_trigger in self.event_triggers.items(): # Get JS event name: remove on_ prefix and underscores event_name = prop_name.removeprefix("on_").replace("_", "") hook_expr = f""" useEffect(() => {{ if (typeof window === 'undefined') return; const fn = {Var.create(event_trigger)}; window.addEventListener('{event_name}', fn); return () => window.removeEventListener('{event_name}', fn); }}, []); """ hooks.append( Var( hook_expr, _var_data=VarData(position=Hooks.HookPosition.POST_TRIGGER), ) ) return hooks window_event_listener = WindowEventListener.create ================================================ FILE: reflex/components/datadisplay/__init__.py ================================================ """Data grid components.""" from __future__ import annotations from reflex.utils import lazy_loader _SUBMOD_ATTRS: dict[str, list[str]] = { "code": [ "CodeBlock", "code_block", "LiteralCodeLanguage", ], "dataeditor": ["data_editor", "data_editor_theme", "DataEditorTheme"], "logo": ["logo"], } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/datadisplay/code.py ================================================ """A code component.""" from __future__ import annotations import dataclasses from typing import ClassVar, Literal from reflex.components.component import Component, ComponentNamespace, field from reflex.components.core.cond import color_mode_cond from reflex.components.lucide.icon import Icon from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.radix.themes.components.button import Button from reflex.components.radix.themes.layout.box import Box from reflex.constants.colors import Color from reflex.event import set_clipboard from reflex.style import Style from reflex.utils import format from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var, VarData LiteralCodeLanguage = Literal[ "abap", "abnf", "actionscript", "ada", "agda", "al", "antlr4", "apacheconf", "apex", "apl", "applescript", "aql", "arduino", "arff", "asciidoc", "asm6502", "asmatmel", "aspnet", "autohotkey", "autoit", "avisynth", "avro-idl", "bash", "basic", "batch", "bbcode", "bicep", "birb", "bison", "bnf", "brainfuck", "brightscript", "bro", "bsl", "c", "cfscript", "chaiscript", "cil", "clike", "clojure", "cmake", "cobol", "coffeescript", "concurnas", "coq", "core", "cpp", "crystal", "csharp", "cshtml", "csp", "css", "css-extras", "csv", "cypher", "d", "dart", "dataweave", "dax", "dhall", "diff", "django", "dns-zone-file", "docker", "dot", "ebnf", "editorconfig", "eiffel", "ejs", "elixir", "elm", "erb", "erlang", "etlua", "excel-formula", "factor", "false", "firestore-security-rules", "flow", "fortran", "fsharp", "ftl", "gap", "gcode", "gdscript", "gedcom", "gherkin", "git", "glsl", "gml", "gn", "go", "go-module", "graphql", "groovy", "haml", "handlebars", "haskell", "haxe", "hcl", "hlsl", "hoon", "hpkp", "hsts", "http", "ichigojam", "icon", "icu-message-format", "idris", "iecst", "ignore", "index", "inform7", "ini", "io", "j", "java", "javadoc", "javadoclike", "javascript", "javastacktrace", "jexl", "jolie", "jq", "js-extras", "js-templates", "jsdoc", "json", "json5", "jsonp", "jsstacktrace", "jsx", "julia", "keepalived", "keyman", "kotlin", "kumir", "kusto", "latex", "latte", "less", "lilypond", "liquid", "lisp", "livescript", "llvm", "log", "lolcode", "lua", "magma", "makefile", "markdown", "markup", "markup-templating", "matlab", "maxscript", "mel", "mermaid", "mizar", "mongodb", "monkey", "moonscript", "n1ql", "n4js", "nand2tetris-hdl", "naniscript", "nasm", "neon", "nevod", "nginx", "nim", "nix", "nsis", "objectivec", "ocaml", "opencl", "openqasm", "oz", "parigp", "parser", "pascal", "pascaligo", "pcaxis", "peoplecode", "perl", "php", "php-extras", "phpdoc", "plsql", "powerquery", "powershell", "processing", "prolog", "promql", "properties", "protobuf", "psl", "pug", "puppet", "pure", "purebasic", "purescript", "python", "q", "qml", "qore", "qsharp", "r", "racket", "reason", "regex", "rego", "renpy", "rest", "rip", "roboconf", "robotframework", "ruby", "rust", "sas", "sass", "scala", "scheme", "scss", "shell-session", "smali", "smalltalk", "smarty", "sml", "solidity", "solution-file", "soy", "sparql", "splunk-spl", "sqf", "sql", "squirrel", "stan", "stylus", "swift", "systemd", "t4-cs", "t4-templating", "t4-vb", "tap", "tcl", "textile", "toml", "tremor", "tsx", "tt2", "turtle", "twig", "typescript", "typoscript", "unrealscript", "uorazor", "uri", "v", "vala", "vbnet", "velocity", "verilog", "vhdl", "vim", "visual-basic", "warpscript", "wasm", "web-idl", "wiki", "wolfram", "wren", "xeora", "xml-doc", "xojo", "xquery", "yaml", "yang", "zig", ] def construct_theme_var(theme: str) -> Var[Theme]: """Construct a theme var. Args: theme: The theme to construct. Returns: The constructed theme var. """ return Var( theme, _var_data=VarData( imports={ f"react-syntax-highlighter/dist/esm/styles/prism/{format.to_kebab_case(theme)}": [ ImportVar(tag=theme, is_default=True, install=False) ] } ), ) @dataclasses.dataclass(init=False) class Theme: """Themes for the CodeBlock component.""" a11y_dark: ClassVar[Var[Theme]] = construct_theme_var("a11yDark") atom_dark: ClassVar[Var[Theme]] = construct_theme_var("atomDark") cb: ClassVar[Var[Theme]] = construct_theme_var("cb") coldark_cold: ClassVar[Var[Theme]] = construct_theme_var("coldarkCold") coldark_dark: ClassVar[Var[Theme]] = construct_theme_var("coldarkDark") coy: ClassVar[Var[Theme]] = construct_theme_var("coy") coy_without_shadows: ClassVar[Var[Theme]] = construct_theme_var("coyWithoutShadows") darcula: ClassVar[Var[Theme]] = construct_theme_var("darcula") dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark") dracula: ClassVar[Var[Theme]] = construct_theme_var("dracula") duotone_dark: ClassVar[Var[Theme]] = construct_theme_var("duotoneDark") duotone_earth: ClassVar[Var[Theme]] = construct_theme_var("duotoneEarth") duotone_forest: ClassVar[Var[Theme]] = construct_theme_var("duotoneForest") duotone_light: ClassVar[Var[Theme]] = construct_theme_var("duotoneLight") duotone_sea: ClassVar[Var[Theme]] = construct_theme_var("duotoneSea") duotone_space: ClassVar[Var[Theme]] = construct_theme_var("duotoneSpace") funky: ClassVar[Var[Theme]] = construct_theme_var("funky") ghcolors: ClassVar[Var[Theme]] = construct_theme_var("ghcolors") gruvbox_dark: ClassVar[Var[Theme]] = construct_theme_var("gruvboxDark") gruvbox_light: ClassVar[Var[Theme]] = construct_theme_var("gruvboxLight") holi_theme: ClassVar[Var[Theme]] = construct_theme_var("holiTheme") hopscotch: ClassVar[Var[Theme]] = construct_theme_var("hopscotch") light: ClassVar[Var[Theme]] = construct_theme_var("oneLight") lucario: ClassVar[Var[Theme]] = construct_theme_var("lucario") material_dark: ClassVar[Var[Theme]] = construct_theme_var("materialDark") material_light: ClassVar[Var[Theme]] = construct_theme_var("materialLight") material_oceanic: ClassVar[Var[Theme]] = construct_theme_var("materialOceanic") night_owl: ClassVar[Var[Theme]] = construct_theme_var("nightOwl") nord: ClassVar[Var[Theme]] = construct_theme_var("nord") okaidia: ClassVar[Var[Theme]] = construct_theme_var("okaidia") one_dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark") one_light: ClassVar[Var[Theme]] = construct_theme_var("oneLight") pojoaque: ClassVar[Var[Theme]] = construct_theme_var("pojoaque") prism: ClassVar[Var[Theme]] = construct_theme_var("prism") shades_of_purple: ClassVar[Var[Theme]] = construct_theme_var("shadesOfPurple") solarized_dark_atom: ClassVar[Var[Theme]] = construct_theme_var("solarizedDarkAtom") solarizedlight: ClassVar[Var[Theme]] = construct_theme_var("solarizedlight") synthwave84: ClassVar[Var[Theme]] = construct_theme_var("synthwave84") tomorrow: ClassVar[Var[Theme]] = construct_theme_var("tomorrow") twilight: ClassVar[Var[Theme]] = construct_theme_var("twilight") vs: ClassVar[Var[Theme]] = construct_theme_var("vs") vs_dark: ClassVar[Var[Theme]] = construct_theme_var("vsDark") vsc_dark_plus: ClassVar[Var[Theme]] = construct_theme_var("vscDarkPlus") xonokai: ClassVar[Var[Theme]] = construct_theme_var("xonokai") z_touch: ClassVar[Var[Theme]] = construct_theme_var("zTouch") for theme_name in dir(Theme): if theme_name.startswith("_"): continue setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme)) class CodeBlock(Component, MarkdownComponentMap): """A code block.""" library = "react-syntax-highlighter@16.1.1" tag = "PrismAsyncLight" alias = "SyntaxHighlighter" # The theme to use ("light" or "dark"). theme: Var[Theme | str] = Theme.one_light # The language to use. language: Var[LiteralCodeLanguage] = Var.create("python") # The code to display. code: Var[str] # If this is enabled line numbers will be shown next to the code block. show_line_numbers: Var[bool] # The starting line number to use. starting_line_number: Var[int] # Whether to wrap long lines. wrap_long_lines: Var[bool] # A custom style for the code block. custom_style: dict[str, str | Var | Color] = field( default_factory=dict, is_javascript_property=False ) # Props passed down to the code tag. code_tag_props: Var[dict[str, str | dict[str, str]]] # Whether a copy button should appear. can_copy: bool | None = field( default=False, is_javascript_property=False, ) # A custom copy button to override the default one. copy_button: bool | Component | None = field( default=None, is_javascript_property=False, ) @classmethod def create( cls, *children, **props, ): """Create a text component. Args: *children: The children of the component. **props: The props to pass to the component. Returns: The text component. """ # This component handles style in a special prop. custom_style = props.pop("custom_style", {}) can_copy = props.pop("can_copy", False) copy_button = props.pop("copy_button", None) # react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark # themes respectively to ensure code compatibility. if "theme" not in props: # Default color scheme responds to global color mode. props["theme"] = color_mode_cond( light=Theme.one_light, dark=Theme.one_dark, ) if can_copy: code = children[0] copy_button = ( copy_button if copy_button is not None else Button.create( Icon.create(tag="copy"), on_click=set_clipboard(code), style=Style({"position": "absolute", "top": "0.5em", "right": "0"}), ) ) custom_style.update({"padding": "1em 3.2em 1em 1em"}) else: copy_button = None # Transfer style props to the custom style prop. for key, value in props.items(): if key not in cls.get_fields(): custom_style[key] = value # Carry the children (code) via props if children: props["code"] = children[0] if not isinstance(props["code"], Var): props["code"] = LiteralVar.create(props["code"]) # Create the component. code_block = super().create( **props, custom_style=Style(custom_style), ) if copy_button: return Box.create(code_block, copy_button, position="relative") return code_block def add_style(self): """Add style to the component.""" self.custom_style.update(self.style) def _render(self): out = super()._render() theme = self.theme return ( out .add_props(style=theme) .remove_props("theme", "code") .add_props( children=self.code, ) ) class CodeblockNamespace(ComponentNamespace): """Namespace for the CodeBlock component.""" themes = Theme __call__ = CodeBlock.create code_block = CodeblockNamespace() ================================================ FILE: reflex/components/datadisplay/dataeditor.py ================================================ """Data Editor component from glide-data-grid.""" from __future__ import annotations import dataclasses from collections.abc import Mapping, Sequence from enum import Enum from typing import Any, Literal, TypedDict from reflex.components.component import Component, NoSSRComponent from reflex.components.literals import LiteralRowMarker from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.utils import console, format, types from reflex.utils.imports import ImportDict, ImportVar from reflex.utils.serializers import serializer from reflex.vars import get_unique_variable_name from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar from reflex.vars.sequence import ArrayVar # TODO: Fix the serialization issue for custom types. class GridColumnIcons(Enum): """An Enum for the available icons in DataEditor.""" Array = "array" AudioUri = "audio_uri" Boolean = "boolean" HeaderCode = "code" Date = "date" Email = "email" Emoji = "emoji" GeoDistance = "geo_distance" IfThenElse = "if_then_else" Image = "image" JoinStrings = "join_strings" Lookup = "lookup" Markdown = "markdown" Math = "math" Number = "number" Phone = "phone" Reference = "reference" Rollup = "rollup" RowID = "row_id" SingleValue = "single_value" SplitString = "split_string" String = "string" TextTemplate = "text_template" Time = "time" Uri = "uri" VideoUri = "video_uri" @dataclasses.dataclass class DataEditorThemeBase: """The theme for the DataEditor component.""" accent_color: str | None = None accent_fg: str | None = None accent_light: str | None = None base_font_style: str | None = None bg_bubble: str | None = None bg_bubble_selected: str | None = None bg_cell: str | None = None bg_cell_medium: str | None = None bg_header: str | None = None bg_header_has_focus: str | None = None bg_header_hovered: str | None = None bg_icon_header: str | None = None bg_search_result: str | None = None border_color: str | None = None cell_horizontal_padding: int | None = None cell_vertical_padding: int | None = None drilldown_border: str | None = None editor_font_size: str | None = None fg_icon_header: str | None = None font_family: str | None = None header_bottom_border_color: str | None = None header_font_style: str | None = None horizontal_border_color: str | None = None line_height: int | None = None link_color: str | None = None text_bubble: str | None = None text_dark: str | None = None text_group_header: str | None = None text_header: str | None = None text_header_selected: str | None = None text_light: str | None = None text_medium: str | None = None @dataclasses.dataclass(init=False) class DataEditorTheme(DataEditorThemeBase): """The theme for the DataEditor component.""" def __init__(self, **kwargs: Any): """Initialize the DataEditorTheme. Args: **kwargs: The keyword arguments to initialize the theme. """ kwargs = {format.to_snake_case(k): v for k, v in kwargs.items()} super().__init__(**kwargs) class Bounds(TypedDict): """The bounds of the group header.""" x: int y: int width: int height: int class CompatSelection(TypedDict): """The selection.""" items: list class Rectangle(TypedDict): """The bounds of the group header.""" x: int y: int width: int height: int class GridSelectionCurrent(TypedDict): """The current selection.""" cell: tuple[int, int] range: Rectangle rangeStack: list[Rectangle] class GridSelection(TypedDict): """The grid selection.""" current: GridSelectionCurrent | None columns: CompatSelection rows: CompatSelection class GroupHeaderClickedEventArgs(TypedDict): """The arguments for the group header clicked event.""" kind: str group: str location: tuple[int, int] bounds: Bounds isEdge: bool shiftKey: bool ctrlKey: bool metaKey: bool isTouch: bool localEventX: int localEventY: int button: int buttons: int scrollEdge: tuple[int, int] class GridCell(TypedDict): """The grid cell.""" span: list[int] | None class GridColumn(TypedDict): """The grid column.""" title: str group: str | None class DataEditor(NoSSRComponent): """The DataEditor Component.""" tag = "DataEditor" is_default = True library: str | None = "@glideapps/glide-data-grid@6.0.3" lib_dependencies: list[str] = [ "lodash@4.17.23", "react-responsive-carousel@3.2.23", ] # Number of rows. rows: Var[int] # Headers of the columns for the data grid. columns: Var[Sequence[dict[str, Any]]] # The data. data: Var[Sequence[Sequence[Any]]] # The name of the callback used to find the data to display. get_cell_content: Var[str] # Allow selection for copying. get_cells_for_selection: Var[bool] # Allow paste. on_paste: Var[bool] # Controls the drawing of the focus ring. draw_focus_ring: Var[bool] # Enables or disables the overlay shadow when scrolling horizontally. fixed_shadow_x: Var[bool] # Enables or disables the overlay shadow when scrolling vertically. fixed_shadow_y: Var[bool] # Controls the presence of the fill indicator fill_handle: Var[bool] # The number of columns which should remain in place when scrolling horizontally. Doesn't include rowMarkers. freeze_columns: Var[int] # Controls the header of the group header row. group_header_height: Var[int] # Controls the height of the header row. header_height: Var[int] # Additional header icons: # header_icons: Var[Any] # (TODO: must be a map of name: svg) #noqa: ERA001 # The maximum width a column can be automatically sized to. max_column_auto_width: Var[int] # The maximum width a column can be resized to. max_column_width: Var[int] # The minimum width a column can be resized to. min_column_width: Var[int] # Determines the height of each row. row_height: Var[int] # Kind of row markers. Options are: "none", "number", "checkbox", "both", "checkbox-visible", "clickable-number". row_markers: Var[LiteralRowMarker] # Changes the starting index for row markers. row_marker_start_index: Var[int] # Sets the width of row markers in pixels, if unset row markers will automatically size. row_marker_width: Var[int] # Enable horizontal smooth scrolling. smooth_scroll_x: Var[bool] # Enable vertical smooth scrolling. smooth_scroll_y: Var[bool] # Controls the drawing of the left hand vertical border of a column. If set to a boolean value it controls all borders. vertical_border: Var[bool] # TODO: support a mapping (dict[int, bool]) # Allow columns selections. ("none", "single", "multi") column_select: Var[Literal["none", "single", "multi"]] # Allow range selections. ("none", "cell", "rect", "multi-cell", "multi-rect"). range_select: Var[Literal["none", "cell", "rect", "multi-cell", "multi-rect"]] # Allow row selections. ("none", "single", "multi"). row_select: Var[Literal["none", "single", "multi"]] # Prevent diagonal scrolling. prevent_diagonal_scrolling: Var[bool] # Allow to scroll past the limit of the actual content on the horizontal axis. overscroll_x: Var[int] # Allow to scroll past the limit of the actual content on the vertical axis. overscroll_y: Var[int] # Initial scroll offset on the horizontal axis. scroll_offset_x: Var[int] # Initial scroll offset on the vertical axis. scroll_offset_y: Var[int] # Controls which types of range selections can exist at the same time. ("exclusive", "mixed"). range_selection_blending: Var[Literal["exclusive", "mixed"]] # Controls which types of column selections can exist at the same time. ("exclusive", "mixed"). column_selection_blending: Var[Literal["exclusive", "mixed"]] # Controls which types of row selections can exist at the same time. ("exclusive", "mixed"). row_selection_blending: Var[Literal["exclusive", "mixed"]] # Controls row marker selection behavior. "auto" adapts to touch/mouse, "multi" acts as if Ctrl is pressed. ("auto", "multi"). row_selection_mode: Var[Literal["auto", "multi"]] # Controls how spans are handled in selections. ("default", "allowPartial"). span_range_behavior: Var[Literal["default", "allowPartial"]] # global theme theme: Var[DataEditorTheme | dict] # Fired when a cell is activated. on_cell_activated: EventHandler[passthrough_event_spec(tuple[int, int])] # Fired when a cell is clicked. on_cell_clicked: EventHandler[passthrough_event_spec(tuple[int, int])] # Fired when a cell is right-clicked. on_cell_context_menu: EventHandler[passthrough_event_spec(tuple[int, int])] # Fired when a cell is edited. on_cell_edited: EventHandler[passthrough_event_spec(tuple[int, int], GridCell)] # Fired when a group header is clicked. on_group_header_clicked: EventHandler[ passthrough_event_spec(tuple[int, int], GridCell) ] # Fired when a group header is right-clicked. on_group_header_context_menu: EventHandler[ passthrough_event_spec(int, GroupHeaderClickedEventArgs) ] # Fired when a group header is renamed. on_group_header_renamed: EventHandler[passthrough_event_spec(str, str)] # Fired when a header is clicked. on_header_clicked: EventHandler[passthrough_event_spec(tuple[int, int])] # Fired when a header is right-clicked. on_header_context_menu: EventHandler[passthrough_event_spec(tuple[int, int])] # Fired when a header menu item is clicked. on_header_menu_click: EventHandler[passthrough_event_spec(int, Rectangle)] # Fired when an item is hovered. on_item_hovered: EventHandler[passthrough_event_spec(tuple[int, int])] # Fired when a selection is deleted. on_delete: EventHandler[passthrough_event_spec(GridSelection)] # Fired when editing is finished. on_finished_editing: EventHandler[ passthrough_event_spec(GridCell | None, tuple[int, int]) ] # Fired when a row is appended. on_row_appended: EventHandler[no_args_event_spec] # The current grid selection state (columns, rows, and current cell/range). Must be used when on_grid_selection_change is used otherwise updates will not be reflected in the grid. grid_selection: Var[GridSelection] # Fired when the grid selection changes. Will pass the current selection, the selected columns and the selected rows. on_grid_selection_change: EventHandler[passthrough_event_spec(GridSelection)] # Fired when the selection is cleared. on_selection_cleared: EventHandler[no_args_event_spec] # Fired when a column is resized. on_column_resize: EventHandler[passthrough_event_spec(GridColumn, int)] # Shows search bar. show_search: Var[bool] # Fired when the search close button is clicked. on_search_close: EventHandler[no_args_event_spec] def add_imports(self) -> ImportDict: """Add imports for the component. Returns: The import dict. """ if self.library is None: return {} return { "": f"{format.format_library_name(self.library)}/dist/index.css", self.library: ["GridCellKind", "CompactSelection"], "$/utils/helpers/dataeditor.js": ImportVar( tag="formatDataEditorCells", is_default=False, install=False ), } def add_custom_code(self) -> list[str]: """Add custom code for reconstructing GridSelection with CompactSelection objects. Note: When using on_grid_selection_change, Glide Data Grid will not update its internal selection state automatically. Instead, the grid_selection prop must be updated with a GridSelection object that has CompactSelection objects for the columns and rows properties. This function provides the necessary JavaScript code to reconstruct the GridSelection object from a dict representation. Returns: JavaScript code to reconstruct GridSelection. """ return [ """ function reconstructGridSelection(selection) { if (!selection || typeof selection !== 'object') { return undefined; } const reconstructCompactSelection = (data) => { if (!data || !data.items || !Array.isArray(data.items)) { return CompactSelection.empty(); } const items = data.items; if (items.length === 0) { return CompactSelection.empty(); } let result = CompactSelection.empty(); // Items are stored as [start, end) ranges in CompactSelection internal format for (const item of items) { if (Array.isArray(item) && item.length === 2) { const [start, end] = item; result = result.add([start, end]); } } return result; }; return { current: selection.current || undefined, columns: reconstructCompactSelection(selection.columns), rows: reconstructCompactSelection(selection.rows) }; } """ ] def add_hooks(self) -> list[str]: """Get the hooks to render. Returns: The hooks to render. """ # Define the id of the component in case multiple are used in the same page. editor_id = get_unique_variable_name() # Define the name of the getData callback associated with this component and assign to get_cell_content. if self.get_cell_content is not None: data_callback = self.get_cell_content._js_expr else: data_callback = f"getData_{editor_id}" self.get_cell_content = Var(_js_expr=data_callback) code = [f"function {data_callback}([col, row]){{"] columns_path = str(self.columns) data_path = str(self.data) code.extend([ f" return formatDataEditorCells(col, row, {columns_path}, {data_path});", " }", ]) return ["\n".join(code)] @classmethod def create(cls, *children, **props) -> Component: """Create the DataEditor component. Args: *children: The children of the data editor. **props: The props of the data editor. Returns: The DataEditor component.& Raises: ValueError: invalid input. """ from reflex.components.el import Div columns = props.get("columns", []) data = props.get("data", []) rows = props.get("rows") # If rows is not provided, determine from data. if rows is None: if isinstance(data, Var) and not isinstance(data, ArrayVar): msg = "DataEditor data must be an ArrayVar if rows is not provided." raise ValueError(msg) props["rows"] = data.length() if isinstance(data, ArrayVar) else len(data) if not isinstance(columns, Var) and len(columns): if types.is_dataframe(type(data)) or ( isinstance(data, Var) and types.is_dataframe(data._var_type) ): msg = "Cannot pass in both a pandas dataframe and columns to the data_editor component." raise ValueError(msg) props["columns"] = [ format.format_data_editor_column(col) for col in columns ] if "theme" in props: theme = props.get("theme") if isinstance(theme, Mapping): props["theme"] = DataEditorTheme(**theme) # Allow by default to select a region of cells in the grid. props.setdefault("get_cells_for_selection", True) # Disable on_paste by default if not provided. props.setdefault("on_paste", False) if props.pop("get_cell_content", None) is not None: console.warn( "get_cell_content is not user configurable, the provided value will be discarded" ) # Apply the reconstruction function to grid_selection if it's a Var if (grid_selection := props.get("grid_selection")) is not None and isinstance( grid_selection, Var ): props["grid_selection"] = FunctionStringVar.create( "reconstructGridSelection" ).call(grid_selection) grid = super().create(*children, **props) return Div.create( grid, width=props.pop("width", "100%"), height=props.pop("height", "100%"), ) @staticmethod def _get_app_wrap_components() -> dict[tuple[int, str], Component]: """Get the app wrap components for the component. Returns: The app wrap components. """ from reflex.components.el import Div class Portal(Div): def get_ref(self): return None return { (-1, "DataEditorPortal"): Portal.create( id="portal", position="fixed", top=0, ) } @serializer def serialize_dataeditortheme(theme: DataEditorTheme): """The serializer for the data editor theme. Args: theme: The theme to serialize. Returns: The serialized theme. """ return { format.to_camel_case(k): v for k, v in theme.__dict__.items() if v is not None } data_editor = DataEditor.create data_editor_theme = DataEditorTheme ================================================ FILE: reflex/components/datadisplay/logo.py ================================================ """A Reflex logo component.""" import reflex as rx SVG_COLOR = rx.color_mode_cond("#110F1F", "white") def svg_logo(color: str | rx.Var[str] = SVG_COLOR, **props): """A Reflex logo SVG. Args: color: The color of the logo. props: Extra props to pass to the svg component. Returns: The Reflex logo SVG. """ def logo_path(d: str): return rx.el.path(d=d) paths = [ "M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z", "M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z", "M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z", "M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z", "M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z", "M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z", ] return rx.el.svg( *[logo_path(d) for d in paths], rx.el.title("Reflex"), aria_label="Reflex", role="img", width=props.pop("width", "56"), height=props.pop("height", "12"), fill=color, xmlns="http://www.w3.org/2000/svg", **props, ) def logo(**props): """A Reflex logo. Args: **props: The props to pass to the component. Returns: The logo component. """ return rx.center( rx.link( rx.hstack( "Built with ", svg_logo(), text_align="center", align="center", padding="1em", ), href="https://reflex.dev", size="3", ), width=props.pop("width", "100%"), **props, ) ================================================ FILE: reflex/components/datadisplay/shiki_code_block.py ================================================ """Shiki syntax hghlighter component.""" from __future__ import annotations import dataclasses import re from collections import defaultdict from dataclasses import dataclass from typing import Any, Literal from reflex.components.component import Component, ComponentNamespace, field from reflex.components.core.colors import color from reflex.components.core.cond import color_mode_cond from reflex.components.el.elements.forms import Button from reflex.components.lucide.icon import Icon from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.props import NoExtrasAllowedProps from reflex.components.radix.themes.layout.box import Box from reflex.event import run_script, set_clipboard from reflex.style import Style from reflex.utils.exceptions import VarTypeError from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar from reflex.vars.sequence import StringVar, string_replace_operation def copy_script() -> Any: """Copy script for the code block and modify the child SVG element. Returns: Any: The result of calling the script. """ return run_script( """ // Event listener for the parent click document.addEventListener('click', function(event) { // Find the closest button (parent element) const parent = event.target.closest('button'); // If the parent is found if (parent) { // Find the SVG element within the parent const svgIcon = parent.querySelector('svg'); // If the SVG exists, proceed with the script if (svgIcon) { const originalPath = svgIcon.innerHTML; const checkmarkPath = ''; // Checkmark SVG path function transition(element, scale, opacity) { element.style.transform = `scale(${scale})`; element.style.opacity = opacity; } // Animate the SVG transition(svgIcon, 0, '0'); setTimeout(() => { svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary transition(svgIcon, 1, '1'); setTimeout(() => { transition(svgIcon, 0, '0'); setTimeout(() => { svgIcon.innerHTML = originalPath; // Restore original SVG content transition(svgIcon, 1, '1'); }, 125); }, 600); }, 125); } else { // console.error('SVG element not found within the parent.'); } } else { // console.error('Parent element not found.'); } }) """ ) SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", "transformerNotationHighlight", "transformerNotationWordHighlight", "transformerNotationFocus", "transformerNotationErrorLevel", "transformerRenderWhitespace", "transformerMetaHighlight", "transformerMetaWordHighlight", "transformerCompactLineOptions", # TODO: this transformer when included adds a weird behavior which removes other code lines. Need to figure out why. # "transformerRemoveLineBreak", "transformerRemoveNotationEscape", } LINE_NUMBER_STYLING = { "code": { "counter-reset": "step", "counter-increment": "step 0", "display": "grid", "line-height": "1.7", "font-size": "0.875em", }, "code .line::before": { "content": "counter(step)", "counter-increment": "step", "width": "1rem", "margin-right": "1.5rem", "display": "inline-block", "text-align": "right", "color": "rgba(115,138,148,.4)", }, } BOX_PARENT_STYLING = { "pre": { "margin": "0", "padding": "24px", "background": "transparent", "overflow-x": "auto", "border-radius": "6px", }, } THEME_MAPPING = { "light": "one-light", "dark": "one-dark-pro", "a11y-dark": "github-dark", } LANGUAGE_MAPPING = {"bash": "shellscript"} LiteralCodeLanguage = Literal[ "abap", "actionscript-3", "ada", "angular-html", "angular-ts", "apache", "apex", "apl", "applescript", "ara", "asciidoc", "asm", "astro", "awk", "ballerina", "bat", "beancount", "berry", "bibtex", "bicep", "blade", "c", "cadence", "clarity", "clojure", "cmake", "cobol", "codeowners", "codeql", "coffee", "common-lisp", "coq", "cpp", "crystal", "csharp", "css", "csv", "cue", "cypher", "d", "dart", "dax", "desktop", "diff", "docker", "dotenv", "dream-maker", "edge", "elixir", "elm", "emacs-lisp", "erb", "erlang", "fennel", "fish", "fluent", "fortran-fixed-form", "fortran-free-form", "fsharp", "gdresource", "gdscript", "gdshader", "genie", "gherkin", "git-commit", "git-rebase", "gleam", "glimmer-js", "glimmer-ts", "glsl", "gnuplot", "go", "graphql", "groovy", "hack", "haml", "handlebars", "haskell", "haxe", "hcl", "hjson", "hlsl", "html", "html-derivative", "http", "hxml", "hy", "imba", "ini", "java", "javascript", "jinja", "jison", "json", "json5", "jsonc", "jsonl", "jsonnet", "jssm", "jsx", "julia", "kotlin", "kusto", "latex", "lean", "less", "liquid", "log", "logo", "lua", "luau", "make", "markdown", "marko", "matlab", "mdc", "mdx", "mermaid", "mojo", "move", "narrat", "nextflow", "nginx", "nim", "nix", "nushell", "objective-c", "objective-cpp", "ocaml", "pascal", "perl", "php", "plain", "plsql", "po", "postcss", "powerquery", "powershell", "prisma", "prolog", "proto", "pug", "puppet", "purescript", "python", "qml", "qmldir", "qss", "r", "racket", "raku", "razor", "reg", "regexp", "rel", "riscv", "rst", "ruby", "rust", "sas", "sass", "scala", "scheme", "scss", "shaderlab", "shellscript", "shellsession", "smalltalk", "solidity", "soy", "sparql", "splunk", "sql", "ssh-config", "stata", "stylus", "svelte", "swift", "system-verilog", "systemd", "tasl", "tcl", "templ", "terraform", "tex", "toml", "ts-tags", "tsv", "tsx", "turtle", "twig", "typescript", "typespec", "typst", "v", "vala", "vb", "verilog", "vhdl", "viml", "vue", "vue-html", "vyper", "wasm", "wenyan", "wgsl", "wikitext", "wolfram", "xml", "xsl", "yaml", "zenscript", "zig", ] LiteralCodeTheme = Literal[ "andromeeda", "aurora-x", "ayu-dark", "catppuccin-frappe", "catppuccin-latte", "catppuccin-macchiato", "catppuccin-mocha", "dark-plus", "dracula", "dracula-soft", "everforest-dark", "everforest-light", "github-dark", "github-dark-default", "github-dark-dimmed", "github-dark-high-contrast", "github-light", "github-light-default", "github-light-high-contrast", "houston", "laserwave", "light-plus", "material-theme", "material-theme-darker", "material-theme-lighter", "material-theme-ocean", "material-theme-palenight", "min-dark", "min-light", "monokai", "night-owl", "nord", "one-dark-pro", "one-light", "plastic", "poimandres", "red", # rose-pine themes dont work with the current version of shikijs transformers # https://github.com/shikijs/shiki/issues/730 "rose-pine", "rose-pine-dawn", "rose-pine-moon", "slack-dark", "slack-ochin", "snazzy-light", "solarized-dark", "solarized-light", "synthwave-84", "tokyo-night", "vesper", "vitesse-black", "vitesse-dark", "vitesse-light", ] class Position(NoExtrasAllowedProps): """Position of the decoration.""" line: int character: int class ShikiDecorations(NoExtrasAllowedProps): """Decorations for the code block.""" start: int | Position end: int | Position tag_name: str = "span" properties: dict[str, Any] = {} always_wrap: bool = False @dataclass(kw_only=True) class ShikiBaseTransformers: """Base for creating transformers.""" library: str = "" fns: list[FunctionStringVar] = dataclasses.field(default_factory=list) style: Style | None = dataclasses.field(default=None) @dataclass(kw_only=True) class ShikiJsTransformer(ShikiBaseTransformers): """A Wrapped shikijs transformer.""" library: str = "@shikijs/transformers@3.3.0" fns: list[FunctionStringVar] = dataclasses.field( default_factory=lambda: [ FunctionStringVar.create(fn) for fn in SHIKIJS_TRANSFORMER_FNS ] ) style: Style | None = dataclasses.field( default_factory=lambda: Style({ "code": { "line-height": "1.7", "font-size": "0.875em", "display": "grid", }, # Diffs ".diff": { "margin": "0 -24px", "padding": "0 24px", "width": "calc(100% + 48px)", "display": "inline-block", }, ".diff.add": { "background-color": "rgba(16, 185, 129, .14)", "position": "relative", }, ".diff.remove": { "background-color": "rgba(244, 63, 94, .14)", "opacity": "0.7", "position": "relative", }, ".diff.remove:after": { "position": "absolute", "left": "10px", "content": "'-'", "color": "#b34e52", }, ".diff.add:after": { "position": "absolute", "left": "10px", "content": "'+'", "color": "#18794e", }, # Highlight ".highlighted": { "background-color": "rgba(142, 150, 170, .14)", "margin": "0 -24px", "padding": "0 24px", "width": "calc(100% + 48px)", "display": "inline-block", }, ".highlighted.error": { "background-color": "rgba(244, 63, 94, .14)", }, ".highlighted.warning": { "background-color": "rgba(234, 179, 8, .14)", }, # Highlighted Word ".highlighted-word": { "background-color": color("gray", 2), "border": f"1px solid {color('gray', 5)}", "padding": "1px 3px", "margin": "-1px -3px", "border-radius": "4px", }, # Focused Lines ".has-focused .line:not(.focused)": { "opacity": "0.7", "filter": "blur(0.095rem)", "transition": "filter .35s, opacity .35s", }, ".has-focused:hover .line:not(.focused)": { "opacity": "1", "filter": "none", }, # White Space # ".tab, .space": { # "position": "relative", # noqa: ERA001 # }, # ".tab::before": { # "content": "'⇥'", # noqa: ERA001 # "position": "absolute", # noqa: ERA001 # "opacity": "0.3",# noqa: ERA001 # }, # ".space::before": { # "content": "'·'", # noqa: ERA001 # "position": "absolute", # noqa: ERA001 # "opacity": "0.3", # noqa: ERA001 # }, }) ) def __init__(self, **kwargs): """Initialize the transformer. Args: kwargs: Kwargs to initialize the props. """ fns = kwargs.pop("fns", None) style = kwargs.pop("style", None) if fns: kwargs["fns"] = [ ( FunctionStringVar.create(x) if not isinstance(x, FunctionStringVar) else x ) for x in fns ] if style: kwargs["style"] = Style(style) super().__init__(**kwargs) class ShikiCodeBlock(Component, MarkdownComponentMap): """A Code block.""" library = "/components/shiki/code" tag = "Code" alias = "ShikiCode" lib_dependencies: list[str] = ["shiki@3.3.0"] # The language to use. language: Var[LiteralCodeLanguage] = Var.create("python") # The theme to use ("light" or "dark"). theme: Var[LiteralCodeTheme] = Var.create("one-light") # The set of themes to use for different modes. themes: Var[list[dict[str, Any]] | dict[str, str]] # The code to display. code: Var[str] # The transformers to use for the syntax highlighter. transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = Var.create([]) # The decorations to use for the syntax highlighter. decorations: Var[list[ShikiDecorations]] = Var.create([]) @classmethod def create( cls, *children, **props, ) -> Component: """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). Args: *children: The children of the component. **props: The props to pass to the component. Returns: The code block component. """ # Separate props for the code block and the wrapper code_block_props = {} code_wrapper_props = {} decorations = props.pop("decorations", []) class_props = cls.get_props() # Distribute props between the code block and wrapper for key, value in props.items(): (code_block_props if key in class_props else code_wrapper_props)[key] = ( value ) # cast decorations into ShikiDecorations. decorations = [ ShikiDecorations(**decoration) if not isinstance(decoration, ShikiDecorations) else decoration for decoration in decorations ] code_block_props["decorations"] = decorations code_block_props["code"] = children[0] code_block = super().create(**code_block_props) transformer_styles = {} # Collect styles from transformers and wrapper for transformer in code_block.transformers._var_value: # pyright: ignore [reportAttributeAccessIssue] if isinstance(transformer, ShikiBaseTransformers) and transformer.style: transformer_styles.update(transformer.style) transformer_styles.update(code_wrapper_props.pop("style", {})) return Box.create( code_block, *children[1:], style=Style({**transformer_styles, **BOX_PARENT_STYLING}), **code_wrapper_props, ) def add_imports(self) -> dict[str, list[str]]: """Add the necessary imports. We add all referenced transformer functions as imports from their corresponding libraries. Returns: Imports for the component. Raises: ValueError: If the transformers are not of type LiteralVar. """ imports = defaultdict(list) if not isinstance(self.transformers, LiteralVar): msg = f"transformers should be a LiteralVar type. Got {type(self.transformers)} instead." raise ValueError(msg) for transformer in self.transformers._var_value: if isinstance(transformer, ShikiBaseTransformers): imports[transformer.library].extend([ ImportVar(tag=str(fn)) for fn in transformer.fns ]) if transformer.library not in self.lib_dependencies: self.lib_dependencies.append(transformer.library) return imports @classmethod def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers: """Create a transformer from a third party library. Args: library: The name of the library. fns: The str names of the functions/callables to invoke from the library. Returns: A transformer for the specified library. Raises: ValueError: If a supplied function name is not valid str. """ if any(not isinstance(fn_name, str) for fn_name in fns): msg = f"the function names should be str names of functions in the specified transformer: {library!r}" raise ValueError(msg) return ShikiBaseTransformers( library=library, fns=[FunctionStringVar.create(fn) for fn in fns], # pyright: ignore [reportCallIssue] ) def _render(self, props: dict[str, Any] | None = None): """Renders the component with the given properties, processing transformers if present. Args: props: Optional properties to pass to the render function. Returns: Rendered component output. """ # Ensure props is initialized from class attributes if not provided props = props or { attr.rstrip("_"): getattr(self, attr) for attr in self.get_props() } # Extract transformers and apply transformations transformers = props.get("transformers") if transformers is not None: transformed_values = self._process_transformers(transformers._var_value) props["transformers"] = LiteralVar.create(transformed_values) return super()._render(props) def _process_transformers(self, transformer_list: list) -> list: """Processes a list of transformers, applying transformations where necessary. Args: transformer_list: List of transformer objects or values. Returns: list: A list of transformed values. """ processed = [] for transformer in transformer_list: if isinstance(transformer, ShikiBaseTransformers): processed.extend(fn.call() for fn in transformer.fns) else: processed.append(transformer) return processed class ShikiHighLevelCodeBlock(ShikiCodeBlock): """High level component for the shiki syntax highlighter.""" # If this is enabled, the default transformers(shikijs transformer) will be used. use_transformers: Var[bool] # If this is enabled line numbers will be shown next to the code block. show_line_numbers: Var[bool] # Whether a copy button should appear. can_copy: bool = field(default=False, is_javascript_property=False) # copy_button: A custom copy button to override the default one. copy_button: Component | bool | None = field( default=None, is_javascript_property=False ) @classmethod def create( cls, *children, **props, ) -> Component: """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). Args: *children: The children of the component. **props: The props to pass to the component. Returns: The code block component. """ use_transformers = props.pop("use_transformers", False) show_line_numbers = props.pop("show_line_numbers", False) language = props.pop("language", None) can_copy = props.pop("can_copy", False) copy_button = props.pop("copy_button", None) if use_transformers: props["transformers"] = [ShikiJsTransformer()] if language is not None: props["language"] = cls._map_languages(language) # line numbers are generated via css if show_line_numbers: props["style"] = {**LINE_NUMBER_STYLING, **props.get("style", {})} theme = props.pop("theme", None) props["theme"] = props["theme"] = ( cls._map_themes(theme) if theme is not None else color_mode_cond( # Default color scheme responds to global color mode. light="one-light", dark="one-dark-pro", ) ) if can_copy: code = children[0] copy_button = ( copy_button if copy_button is not None else Button.create( Icon.create(tag="copy", size=16, color=color("gray", 11)), on_click=[ set_clipboard(cls._strip_transformer_triggers(code)), copy_script(), ], style=Style({ "position": "absolute", "top": "4px", "right": "4px", "background": color("gray", 3), "border": "1px solid", "border-color": color("gray", 5), "border-radius": "6px", "padding": "5px", "opacity": "1", "cursor": "pointer", "_hover": { "background": color("gray", 4), }, "transition": "background 0.250s ease-out", "&>svg": { "transition": "transform 0.250s ease-out, opacity 0.250s ease-out", }, "_active": { "background": color("gray", 5), }, }), ) ) if copy_button: return ShikiCodeBlock.create( children[0], copy_button, position="relative", **props ) return ShikiCodeBlock.create(children[0], **props) @staticmethod def _map_themes(theme: str) -> str: if isinstance(theme, str) and theme in THEME_MAPPING: return THEME_MAPPING[theme] return theme @staticmethod def _map_languages(language: str) -> str: if isinstance(language, str) and language in LANGUAGE_MAPPING: return LANGUAGE_MAPPING[language] return language @staticmethod def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: if not isinstance(code, (StringVar, str)): msg = f"code should be string literal or a StringVar type. Got {type(code)} instead." raise VarTypeError(msg) regex_pattern = r"[\/#]+ *\[!code.*?\]" if isinstance(code, Var): return string_replace_operation( code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), "" ) if isinstance(code, str): return re.sub(regex_pattern, "", code) return None class TransformerNamespace(ComponentNamespace): """Namespace for the Transformers.""" shikijs = ShikiJsTransformer class CodeblockNamespace(ComponentNamespace): """Namespace for the CodeBlock component.""" root = staticmethod(ShikiCodeBlock.create) create_transformer = staticmethod(ShikiCodeBlock.create_transformer) transformers = TransformerNamespace() __call__ = staticmethod(ShikiHighLevelCodeBlock.create) code_block = CodeblockNamespace() ================================================ FILE: reflex/components/dynamic.py ================================================ """Components that are dynamically generated on the backend.""" from typing import TYPE_CHECKING, Union from reflex import constants from reflex.utils import imports from reflex.utils.exceptions import DynamicComponentMissingLibraryError from reflex.utils.format import format_library_name from reflex.utils.serializers import serializer from reflex.vars import Var, get_unique_variable_name from reflex.vars.base import VarData, transform if TYPE_CHECKING: from reflex.components.component import Component def get_cdn_url(lib: str) -> str: """Get the CDN URL for a library. Args: lib: The library to get the CDN URL for. Returns: The CDN URL for the library. """ return f"https://cdn.jsdelivr.net/npm/{lib}" + "/+esm" bundled_libraries = [ "react", "@radix-ui/themes", "@emotion/react", f"$/{constants.Dirs.UTILS}/context", f"$/{constants.Dirs.UTILS}/state", f"$/{constants.Dirs.UTILS}/components", ] def bundle_library(component: Union["Component", str]): """Bundle a library with the component. Args: component: The component to bundle the library with. Raises: DynamicComponentMissingLibraryError: Raised when a dynamic component is missing a library. """ if isinstance(component, str): bundled_libraries.append(component) return if component.library is None: msg = "Component must have a library to bundle." raise DynamicComponentMissingLibraryError(msg) bundled_libraries.append(format_library_name(component.library)) def load_dynamic_serializer(): """Load the serializer for dynamic components.""" # Causes a circular import, so we import here. from reflex.components.component import Component @serializer def make_component(component: Component) -> str: """Generate the code for a dynamic component. Args: component: The component to generate code for. Returns: The generated code """ # Causes a circular import, so we import here. from reflex.compiler import compiler, templates, utils from reflex.components.base.bare import Bare component = Bare.create(Var.create(component)) rendered_components = {} # Include dynamic imports in the shared component. if dynamic_imports := component._get_all_dynamic_imports(): rendered_components.update(dict.fromkeys(dynamic_imports)) # Include custom code in the shared component. rendered_components.update(component._get_all_custom_code()) rendered_components[ templates.stateful_component_template( tag_name="MySSRComponent", memo_trigger_hooks=[], component=component, export=True, ) ] = None libs_in_window = bundled_libraries component_imports = component._get_all_imports() compiler._apply_common_imports(component_imports) imports = {} for lib, names in component_imports.items(): formatted_lib_name = format_library_name(lib) if ( not lib.startswith((".", "/", "$/")) and not lib.startswith("http") and formatted_lib_name not in libs_in_window ): imports[get_cdn_url(lib)] = names else: imports[lib] = names module_code_lines = templates.stateful_components_template( imports=utils.compile_imports(imports), memoized_code="\n".join(rendered_components), ).splitlines() # Rewrite imports from `/` to destructure from window for ix, line in enumerate(module_code_lines[:]): if line.startswith("import "): if 'from "$/' in line or 'from "/' in line: module_code_lines[ix] = ( line .replace("import ", "const ", 1) .replace(" as ", ": ") .replace(" from ", " = window['__reflex'][", 1) + "]" ) else: for lib in libs_in_window: if f'from "{lib}"' in line: module_code_lines[ix] = ( line .replace("import ", "const ", 1) .replace( f' from "{lib}"', f" = window.__reflex['{lib}']", 1 ) .replace(" as ", ": ") ) if line.startswith("export function"): module_code_lines[ix] = line.replace( "export function", "export default function", 1 ) line_stripped = line.strip() if line_stripped.startswith("{") and line_stripped.endswith("}"): module_code_lines[ix] = line_stripped[1:-1] module_code_lines.insert(0, "const React = window.__reflex.react;") function_line = next( index for index, line in enumerate(module_code_lines) if line.startswith("export default function") ) module_code_lines = [ line for _, line in sorted( enumerate(module_code_lines), key=lambda x: ( not (x[1].startswith("import ") and x[0] < function_line), x[0], ), ) ] return "\n".join([ "//__reflex_evaluate", *module_code_lines, ]) @transform def evaluate_component(js_string: Var[str]) -> Var[Component]: """Evaluate a component. Args: js_string: The JavaScript string to evaluate. Returns: The evaluated JavaScript string. """ unique_var_name = get_unique_variable_name() return js_string._replace( _js_expr=unique_var_name, _var_type=Component, merge_var_data=VarData.merge( VarData( imports={ f"$/{constants.Dirs.STATE_PATH}": [ imports.ImportVar(tag="evalReactComponent"), ], "react": [ imports.ImportVar(tag="useState"), imports.ImportVar(tag="useEffect"), ], }, hooks={ f"const [{unique_var_name}, set_{unique_var_name}] = useState(null);": None, "useEffect(() => {" "let isMounted = true;" f"evalReactComponent({js_string!s})" ".then((component) => {" "if (isMounted) {" f"set_{unique_var_name}(component);" "}" "});" "return () => {" "isMounted = false;" "};" "}" f", [{js_string!s}]);": None, }, ), ), ) ================================================ FILE: reflex/components/el/__init__.py ================================================ """The el package exports raw HTML elements.""" from __future__ import annotations from reflex.utils import lazy_loader from . import elements _SUBMODULES: set[str] = {"elements"} _SUBMOD_ATTRS: dict[str, list[str]] = { # rx.el.a is replaced by React Router's Link. f"elements.{k}": [attr for attr in attrs if attr != "a"] for k, attrs in elements._MAPPING.items() } _EXTRA_MAPPINGS: dict[str, str] = { "a": "reflex.components.react_router.link", } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, **_EXTRA_MAPPINGS, ) ================================================ FILE: reflex/components/el/element.py ================================================ """Base class definition for raw HTML elements.""" from typing import ClassVar from reflex.components.component import Component class Element(Component): """The base class for all raw HTML elements.""" _is_tag_in_global_scope: ClassVar[bool] = True def __eq__(self, other: object): """Two elements are equal if they have the same tag. Args: other: The other element. Returns: True if the elements have the same tag, False otherwise. """ return isinstance(other, Element) and self.tag == other.tag ================================================ FILE: reflex/components/el/elements/__init__.py ================================================ """Element classes.""" from __future__ import annotations from reflex.utils import lazy_loader _MAPPING = { "forms": [ "button", "datalist", "fieldset", "form", "input", "label", "legend", "meter", "optgroup", "option", "output", "progress", "select", "textarea", ], "inline": [ "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn", "em", "i", "kbd", "mark", "q", "rp", "rt", "ruby", "s", "samp", "small", "span", "strong", "sub", "sup", "time", "u", "wbr", ], "media": [ "area", "audio", "img", "image", "map", "track", "video", "embed", "iframe", "object", "picture", "portal", "source", "svg", "text", "line", "circle", "g", "ellipse", "rect", "polygon", "path", "stop", "linear_gradient", "radial_gradient", "defs", "marker", ], "metadata": [ "base", "head", "link", "meta", "title", "style", ], "other": ["details", "dialog", "summary", "slot", "template", "math", "html"], "scripts": ["canvas", "noscript", "script"], "sectioning": [ "address", "article", "aside", "body", "header", "footer", "h1", "h2", "h3", "h4", "h5", "h6", "main", "nav", "section", ], "tables": [ "caption", "col", "colgroup", "table", "td", "tfoot", "th", "thead", "tr", "tbody", ], "typography": [ "blockquote", "dd", "div", "dl", "dt", "figcaption", "figure", "hr", "ol", "li", "p", "pre", "ul", "ins", "del_", "Del", ], } EXCLUDE = ["del_", "Del", "image", "style"] for v in _MAPPING.values(): from reflex.utils.format import to_camel_case v.extend([ to_camel_case(mod)[0].upper() + to_camel_case(mod)[1:] for mod in v if mod not in EXCLUDE ]) _MAPPING["metadata"].extend(["StyleEl"]) _SUBMOD_ATTRS: dict[str, list[str]] = _MAPPING __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/el/elements/base.py ================================================ """Base classes.""" from typing import Literal from reflex.components.el.element import Element from reflex.vars.base import Var AutoCapitalize = Literal["off", "none", "on", "sentences", "words", "characters"] ContentEditable = Literal["inherit", "plaintext-only"] | bool EnterKeyHint = Literal["enter", "done", "go", "next", "previous", "search", "send"] InputMode = Literal[ "none", "text", "tel", "url", "email", "numeric", "decimal", "search", "search" ] AriaRole = Literal[ "alert", "alertdialog", "application", "article", "banner", "button", "cell", "checkbox", "columnheader", "combobox", "complementary", "contentinfo", "definition", "dialog", "directory", "document", "feed", "figure", "form", "grid", "gridcell", "group", "heading", "img", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "navigation", "none", "note", "option", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem", ] class BaseHTML(Element): """Base class for common attributes.""" # Provides a hint for generating a keyboard shortcut for the current element. access_key: Var[str] # Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Var[AutoCapitalize] # Indicates whether the element's content is editable. content_editable: Var[ContentEditable] # Defines the ID of a element which will serve as the element's context menu. context_menu: Var[str] # Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left) dir: Var[str] # Defines whether the element can be dragged. draggable: Var[bool] # Hints what media types the media element is able to play. enter_key_hint: Var[EnterKeyHint] # Defines whether the element is hidden. hidden: Var[bool] # Defines the type of the element. input_mode: Var[InputMode] # Defines the name of the element for metadata purposes. item_prop: Var[str] # Defines the language used in the element. lang: Var[str] # Defines the role of the element. role: Var[AriaRole] # Assigns a slot in a shadow DOM shadow tree to an element. slot: Var[str] # Defines whether the element may be checked for spelling errors. spell_check: Var[bool] # Defines the position of the current element in the tabbing order. tab_index: Var[int] # Defines a tooltip for the element. title: Var[str] ================================================ FILE: reflex/components/el/elements/forms.py ================================================ """Forms classes.""" from __future__ import annotations from collections.abc import Iterator from hashlib import md5 from typing import Any, ClassVar, Literal from reflex.components.el.element import Element from reflex.components.tags.tag import Tag from reflex.constants import Dirs, EventTriggers from reflex.event import ( FORM_DATA, EventChain, EventHandler, checked_input_event, float_input_event, input_event, int_input_event, key_event, on_submit_event, on_submit_string_event, prevent_default, ) from reflex.utils.imports import ImportDict from reflex.vars import VarData from reflex.vars.base import LiteralVar, Var from reflex.vars.number import ternary_operation from .base import BaseHTML def _handle_submit_js_template( handle_submit_unique_name: str, form_data: str, field_ref_mapping: str, on_submit_event_chain: str, reset_on_submit: str, ) -> str: """Generate handle submit JS using f-string formatting. Args: handle_submit_unique_name: Unique name for the handle submit function. form_data: Name of the form data variable. field_ref_mapping: JSON string of field reference mappings. on_submit_event_chain: Event chain for the submit handler. reset_on_submit: Boolean string indicating if form should reset after submit. Returns: JavaScript code for the form submit handler. """ return f""" const handleSubmit_{handle_submit_unique_name} = useCallback((ev) => {{ const $form = ev.target ev.preventDefault() const {form_data} = {{...Object.fromEntries(new FormData($form).entries()), ...{field_ref_mapping}}}; ({on_submit_event_chain}(ev)); if ({reset_on_submit}) {{ $form.reset() }} }}) """ ButtonType = Literal["submit", "reset", "button"] class Button(BaseHTML): """Display the button element.""" tag = "button" # Automatically focuses the button when the page loads auto_focus: Var[bool] # Disables the button disabled: Var[bool] # Associates the button with a form (by id) form: Var[str] # URL to send the form data to (for type="submit" buttons) form_action: Var[str] # How the form data should be encoded when submitting to the server (for type="submit" buttons) form_enc_type: Var[str] # HTTP method to use for sending form data (for type="submit" buttons) form_method: Var[str] # Bypasses form validation when submitting (for type="submit" buttons) form_no_validate: Var[bool] # Specifies where to display the response after submitting the form (for type="submit" buttons) form_target: Var[str] # Name of the button, used when sending form data name: Var[str] # Type of the button (submit, reset, or button) type: Var[ButtonType] # Value of the button, used when sending form data value: Var[str | int | float] _invalid_children: ClassVar[list[str]] = ["Button"] class Datalist(BaseHTML): """Display the datalist element.""" tag = "datalist" class Fieldset(Element): """Display the fieldset element.""" tag = "fieldset" # Disables all the form control descendants of the fieldset disabled: Var[bool] # Associates the fieldset with a form (by id) form: Var[str] # Name of the fieldset, used for scripting name: Var[str] class Form(BaseHTML): """Display the form element.""" tag = "form" # MIME types the server accepts for file upload accept: Var[str] # Character encodings to be used for form submission accept_charset: Var[str] # URL where the form's data should be submitted action: Var[str] # Whether the form should have autocomplete enabled auto_complete: Var[str] # Encoding type for the form data when submitted enc_type: Var[str] # HTTP method to use for form submission method: Var[str] # Name of the form name: Var[str] # Indicates that the form should not be validated on submit no_validate: Var[bool] # Where to display the response after submitting the form target: Var[str] # If true, the form will be cleared after submit. reset_on_submit: Var[bool] = Var.create(False) # The name used to make this form's submit handler function unique. handle_submit_unique_name: Var[str] # Fired when the form is submitted on_submit: EventHandler[on_submit_event, on_submit_string_event] @classmethod def create(cls, *children, **props): """Create a form component. Args: *children: The children of the form. **props: The properties of the form. Returns: The form component. """ if "on_submit" not in props: props["on_submit"] = prevent_default if "handle_submit_unique_name" in props: return super().create(*children, **props) # Render the form hooks and use the hash of the resulting code to create a unique name. props["handle_submit_unique_name"] = "" form = super().create(*children, **props) form.handle_submit_unique_name = md5( # pyright: ignore[reportAttributeAccessIssue] str(form._get_all_hooks()).encode("utf-8") ).hexdigest() return form def add_imports(self) -> ImportDict: """Add imports needed by the form component. Returns: The imports for the form component. """ return { "react": "useCallback", f"$/{Dirs.STATE_PATH}": ["getRefValue", "getRefValues"], } def add_hooks(self) -> list[str]: """Add hooks for the form. Returns: The hooks for the form. """ if EventTriggers.ON_SUBMIT not in self.event_triggers: return [] return [ _handle_submit_js_template( handle_submit_unique_name=str(self.handle_submit_unique_name), form_data=str(FORM_DATA), field_ref_mapping=str(LiteralVar.create(self._get_form_refs())), on_submit_event_chain=str( LiteralVar.create(self.event_triggers[EventTriggers.ON_SUBMIT]) ), reset_on_submit=str(self.reset_on_submit).lower(), ) ] def _render(self) -> Tag: render_tag = super()._render() if EventTriggers.ON_SUBMIT in self.event_triggers: render_tag = render_tag.add_props(**{ EventTriggers.ON_SUBMIT: Var( _js_expr=f"handleSubmit_{self.handle_submit_unique_name}", _var_type=EventChain, ) }) return render_tag def _get_form_refs(self) -> dict[str, Any]: # Send all the input refs to the handler. form_refs = {} for ref in self._get_all_refs(): # when ref start with refs_ it's an array of refs, so we need different method # to collect data if ref.startswith("refs_"): ref_var = Var(_js_expr=ref[:-3])._as_ref() form_refs[ref[len("refs_") : -3]] = Var( _js_expr=f"getRefValues({ref_var!s})", _var_data=VarData.merge(ref_var._get_all_var_data()), ) else: ref_var = Var(_js_expr=ref)._as_ref() form_refs[ref[4:]] = Var( _js_expr=f"getRefValue({ref_var!s})", _var_data=VarData.merge(ref_var._get_all_var_data()), ) return form_refs def _get_vars( self, include_children: bool = True, ignore_ids: set[int] | None = None ) -> Iterator[Var]: yield from super()._get_vars( include_children=include_children, ignore_ids=ignore_ids ) yield from self._get_form_refs().values() def _exclude_props(self) -> list[str]: return [ *super()._exclude_props(), "reset_on_submit", "handle_submit_unique_name", ] HTMLInputTypeAttribute = Literal[ "button", "checkbox", "color", "date", "datetime-local", "email", "file", "hidden", "image", "month", "number", "password", "radio", "range", "reset", "search", "submit", "tel", "text", "time", "url", "week", ] class BaseInput(BaseHTML): """A base class for input elements.""" tag = "input" # Accepted types of files when the input is file type accept: Var[str] # Alternate text for input type="image" alt: Var[str] # Whether the input should have autocomplete enabled auto_complete: Var[str] # Automatically focuses the input when the page loads auto_focus: Var[bool] # Captures media from the user (camera or microphone) capture: Var[Literal["user", "environment"] | bool] # Indicates whether the input is checked (for checkboxes and radio buttons) checked: Var[bool] # The initial value (for checkboxes and radio buttons) default_checked: Var[bool] # The initial value for a text field default_value: Var[str | int | float] # Disables the input disabled: Var[bool] # Associates the input with a form (by id) form: Var[str] # URL to send the form data to (for type="submit" buttons) form_action: Var[str] # How the form data should be encoded when submitting to the server (for type="submit" buttons) form_enc_type: Var[str] # HTTP method to use for sending form data (for type="submit" buttons) form_method: Var[str] # Bypasses form validation when submitting (for type="submit" buttons) form_no_validate: Var[bool] # Specifies where to display the response after submitting the form (for type="submit" buttons) form_target: Var[str] # References a datalist for suggested options list: Var[str] # Specifies the maximum value for the input max: Var[str | int | float] # Specifies the maximum number of characters allowed in the input max_length: Var[int | float] # Specifies the minimum number of characters required in the input min_length: Var[int | float] # Specifies the minimum value for the input min: Var[str | int | float] # Indicates whether multiple values can be entered in an input of the type email or file multiple: Var[bool] # Name of the input, used when sending form data name: Var[str] # Regex pattern the input's value must match to be valid pattern: Var[str] # Placeholder text in the input placeholder: Var[str] # Indicates whether the input is read-only read_only: Var[bool] # Indicates that the input is required required: Var[bool] # Specifies the visible width of a text control size: Var[str | int | float] # URL for image inputs src: Var[str] # Specifies the legal number intervals for an input step: Var[str | int | float] # Specifies the type of input type: Var[HTMLInputTypeAttribute] # Value of the input value: Var[str | int | float] # Fired when a key is pressed down on_key_down: EventHandler[key_event] # Fired when a key is released on_key_up: EventHandler[key_event] class CheckboxInput(BaseInput): """Display the input element.""" # Fired when the input value changes on_change: EventHandler[checked_input_event] # Fired when the input gains focus on_focus: EventHandler[checked_input_event] # Fired when the input loses focus on_blur: EventHandler[checked_input_event] class ValueNumberInput(BaseInput): """Display the input element.""" # Fired when the input value changes on_change: EventHandler[float_input_event, int_input_event, input_event] # Fired when the input gains focus on_focus: EventHandler[float_input_event, int_input_event, input_event] # Fired when the input loses focus on_blur: EventHandler[float_input_event, int_input_event, input_event] class Input(BaseInput): """Display the input element.""" # Fired when the input value changes on_change: EventHandler[input_event] # Fired when the input gains focus on_focus: EventHandler[input_event] # Fired when the input loses focus on_blur: EventHandler[input_event] @classmethod def create(cls, *children, **props): """Create an Input component. Args: *children: The children of the component. **props: The properties of the component. Returns: The component. """ value = props.get("value") # React expects an empty string(instead of null) for controlled inputs. if value is not None: value_var = Var.create(value) props["value"] = ternary_operation( value_var.is_not_none(), value_var, Var.create("") ) if cls is Input: input_type = props.get("type") if isinstance(input_type, str) and input_type == "checkbox": # Checkbox inputs should use the CheckboxInput class return CheckboxInput.create(*children, **props) if isinstance(input_type, str) and ( input_type == "number" or input_type == "range" ): # Number inputs should use the ValueNumberInput class return ValueNumberInput.create(*children, **props) return super().create(*children, **props) class Label(BaseHTML): """Display the label element.""" tag = "label" # ID of a form control with which the label is associated html_for: Var[str] # Associates the label with a form (by id) form: Var[str] class Legend(BaseHTML): """Display the legend element.""" tag = "legend" class Meter(BaseHTML): """Display the meter element.""" tag = "meter" # Associates the meter with a form (by id) form: Var[str] # High limit of range (above this is considered high value) high: Var[str | int | float] # Low limit of range (below this is considered low value) low: Var[str | int | float] # Maximum value of the range max: Var[str | int | float] # Minimum value of the range min: Var[str | int | float] # Optimum value in the range optimum: Var[str | int | float] # Current value of the meter value: Var[str | int | float] class Optgroup(BaseHTML): """Display the optgroup element.""" tag = "optgroup" # Disables the optgroup disabled: Var[bool] # Label for the optgroup label: Var[str] class Option(BaseHTML): """Display the option element.""" tag = "option" # Disables the option disabled: Var[bool] # Label for the option, if the text is not the label label: Var[str] # Indicates that the option is initially selected selected: Var[bool] # Value to be sent as form data value: Var[str | int | float] class Output(BaseHTML): """Display the output element.""" tag = "output" # Associates the output with one or more elements (by their IDs) html_for: Var[str] # Associates the output with a form (by id) form: Var[str] # Name of the output element for form submission name: Var[str] class Progress(BaseHTML): """Display the progress element.""" tag = "progress" # Associates the progress element with a form (by id) form: Var[str] # Maximum value of the progress indicator max: Var[str | int | float] # Current value of the progress indicator value: Var[str | int | float] class Select(BaseHTML): """Display the select element.""" tag = "select" # Whether the form control should have autocomplete enabled auto_complete: Var[str] # Automatically focuses the select when the page loads auto_focus: Var[bool] # Disables the select control disabled: Var[bool] # Associates the select with a form (by id) form: Var[str] # Indicates that multiple options can be selected multiple: Var[bool] # Name of the select, used when submitting the form name: Var[str] # Indicates that the select control must have a selected option required: Var[bool] # Number of visible options in a drop-down list size: Var[str | int] # Fired when the select value changes on_change: EventHandler[input_event] # The controlled value of the select, read only unless used with on_change value: Var[str] # The default value of the select when initially rendered default_value: Var[str] AUTO_HEIGHT_JS = """ const autoHeightOnInput = (e, is_enabled) => { if (is_enabled) { const el = e.target; el.style.overflowY = "scroll"; el.style.height = "auto"; el.style.height = (e.target.scrollHeight) + "px"; if (el.form && !el.form.data_resize_on_reset) { el.form.addEventListener("reset", () => window.setTimeout(() => autoHeightOnInput(e, is_enabled), 0)) el.form.data_resize_on_reset = true; } } } """ ENTER_KEY_SUBMIT_JS = """ const enterKeySubmitOnKeyDown = (e, is_enabled) => { if (is_enabled && e.which === 13 && !e.shiftKey) { e.preventDefault(); if (!e.repeat) { if (e.target.form) { e.target.form.requestSubmit(); } } } } """ class Textarea(BaseHTML): """Display the textarea element.""" tag = "textarea" # Whether the form control should have autocomplete enabled auto_complete: Var[str] # Automatically focuses the textarea when the page loads auto_focus: Var[bool] # Automatically fit the content height to the text (use min-height with this prop) auto_height: Var[bool] # Visible width of the text control, in average character widths cols: Var[str | int] # The default value of the textarea when initially rendered default_value: Var[str] # Name part of the textarea to submit in 'dir' and 'name' pair when form is submitted dirname: Var[str] # Disables the textarea disabled: Var[bool] # Enter key submits form (shift-enter adds new line) enter_key_submit: Var[bool] # Associates the textarea with a form (by id) form: Var[str] # Maximum number of characters allowed in the textarea max_length: Var[str | int] # Minimum number of characters required in the textarea min_length: Var[str | int] # Name of the textarea, used when submitting the form name: Var[str] # Placeholder text in the textarea placeholder: Var[str] # Indicates whether the textarea is read-only read_only: Var[bool] # Indicates that the textarea is required required: Var[bool] # Visible number of lines in the text control rows: Var[str | int] # The controlled value of the textarea, read only unless used with on_change value: Var[str] # How the text in the textarea is to be wrapped when submitting the form wrap: Var[str] # Fired when the input value changes on_change: EventHandler[input_event] # Fired when the input gains focus on_focus: EventHandler[input_event] # Fired when the input loses focus on_blur: EventHandler[input_event] # Fired when a key is pressed down on_key_down: EventHandler[key_event] # Fired when a key is released on_key_up: EventHandler[key_event] @classmethod def create(cls, *children, **props): """Create a textarea component. Args: *children: The children of the textarea. **props: The properties of the textarea. Returns: The textarea component. Raises: ValueError: when `enter_key_submit` is combined with `on_key_down`. """ enter_key_submit = props.get("enter_key_submit") auto_height = props.get("auto_height") custom_attrs = props.setdefault("custom_attrs", {}) if enter_key_submit is not None: enter_key_submit = Var.create(enter_key_submit) if "on_key_down" in props: msg = "Cannot combine `enter_key_submit` with `on_key_down`." raise ValueError(msg) custom_attrs["on_key_down"] = Var( _js_expr=f"(e) => enterKeySubmitOnKeyDown(e, {enter_key_submit!s})", _var_data=VarData.merge(enter_key_submit._get_all_var_data()), ) if auto_height is not None: auto_height = Var.create(auto_height) custom_attrs["on_input"] = Var( _js_expr=f"(e) => autoHeightOnInput(e, {auto_height!s})", _var_data=VarData.merge(auto_height._get_all_var_data()), ) return super().create(*children, **props) def _exclude_props(self) -> list[str]: return [ *super()._exclude_props(), "auto_height", "enter_key_submit", ] def _get_all_custom_code(self) -> dict[str, None]: """Include the custom code for auto_height and enter_key_submit functionality. Returns: The custom code for the component. """ custom_code = super()._get_all_custom_code() if self.auto_height is not None: custom_code[AUTO_HEIGHT_JS] = None if self.enter_key_submit is not None: custom_code[ENTER_KEY_SUBMIT_JS] = None return custom_code button = Button.create datalist = Datalist.create fieldset = Fieldset.create form = Form.create input = Input.create label = Label.create legend = Legend.create meter = Meter.create optgroup = Optgroup.create option = Option.create output = Output.create progress = Progress.create select = Select.create textarea = Textarea.create ================================================ FILE: reflex/components/el/elements/inline.py ================================================ """Inline classes.""" from typing import ClassVar, Literal from reflex.vars.base import Var from .base import BaseHTML ReferrerPolicy = Literal[ "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "same-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url", ] class A(BaseHTML): # Inherits common attributes from BaseMeta """Display the 'a' element.""" tag = "a" # Specifies that the target (the file specified in the href attribute) will be downloaded when a user clicks on the hyperlink. download: Var[str | bool] # Specifies the URL of the page the link goes to href: Var[str] # Specifies the language of the linked document href_lang: Var[str] # Specifies what media/device the linked document is optimized for media: Var[str] # Specifies which referrer is sent when fetching the resource ping: Var[str] # Specifies the relationship between the current document and the linked document referrer_policy: Var[ReferrerPolicy] # Specifies the relationship between the linked document and the current document rel: Var[str] # Specifies where to open the linked document target: Var[str | Literal["_self", "_blank", "_parent", "_top"]] _invalid_children: ClassVar[list[str]] = ["A"] class Abbr(BaseHTML): """Display the abbr element.""" tag = "abbr" class B(BaseHTML): """Display the b element.""" tag = "b" class Bdi(BaseHTML): """Display the bdi element.""" tag = "bdi" class Bdo(BaseHTML): """Display the bdo element.""" tag = "bdo" class Br(BaseHTML): """Display the br element.""" tag = "br" class Cite(BaseHTML): """Display the cite element.""" tag = "cite" class Code(BaseHTML): """Display the code element.""" tag = "code" class Data(BaseHTML): """Display the data element.""" tag = "data" # Specifies the machine-readable translation of the data element. value: Var[str | int | float] class Dfn(BaseHTML): """Display the dfn element.""" tag = "dfn" class Em(BaseHTML): """Display the em element.""" tag = "em" class I(BaseHTML): # noqa: E742 """Display the i element.""" tag = "i" class Kbd(BaseHTML): """Display the kbd element.""" tag = "kbd" class Mark(BaseHTML): """Display the mark element.""" tag = "mark" class Q(BaseHTML): """Display the q element.""" tag = "q" # Specifies the source URL of the quote. cite: Var[str] class Rp(BaseHTML): """Display the rp element.""" tag = "rp" class Rt(BaseHTML): """Display the rt element.""" tag = "rt" class Ruby(BaseHTML): """Display the ruby element.""" tag = "ruby" class S(BaseHTML): """Display the s element.""" tag = "s" class Samp(BaseHTML): """Display the samp element.""" tag = "samp" class Small(BaseHTML): """Display the small element.""" tag = "small" class Span(BaseHTML): """Display the span element.""" tag = "span" class Strong(BaseHTML): """Display the strong element.""" tag = "strong" class Sub(BaseHTML): """Display the sub element.""" tag = "sub" class Sup(BaseHTML): """Display the sup element.""" tag = "sup" class Time(BaseHTML): """Display the time element.""" tag = "time" # Specifies the date and/or time of the element. date_time: Var[str] class U(BaseHTML): """Display the u element.""" tag = "u" class Wbr(BaseHTML): """Display the wbr element.""" tag = "wbr" a = A.create abbr = Abbr.create b = B.create bdi = Bdi.create bdo = Bdo.create br = Br.create cite = Cite.create code = Code.create data = Data.create dfn = Dfn.create em = Em.create i = I.create kbd = Kbd.create mark = Mark.create q = Q.create rp = Rp.create rt = Rt.create ruby = Ruby.create s = S.create samp = Samp.create small = Small.create span = Span.create strong = Strong.create sub = Sub.create sup = Sup.create time = Time.create u = U.create wbr = Wbr.create ================================================ FILE: reflex/components/el/elements/media.py ================================================ """Media classes.""" from typing import Any, Literal from reflex import Component, ComponentNamespace from reflex.components.el.elements.inline import ReferrerPolicy from reflex.constants.colors import Color from reflex.vars.base import Var from .base import BaseHTML class Area(BaseHTML): """Display the area element.""" tag = "area" # Alternate text for the area, used for accessibility alt: Var[str] # Coordinates to define the shape of the area coords: Var[str] # Specifies that the target will be downloaded when clicked download: Var[str | bool] # Hyperlink reference for the area href: Var[str] # Language of the linked resource href_lang: Var[str] # Specifies what media/device the linked resource is optimized for media: Var[str] # Specifies which referrer information to send with the link referrer_policy: Var[ReferrerPolicy] # Specifies the relationship of the target object to the link object rel: Var[str] # Defines the shape of the area (rectangle, circle, polygon) shape: Var[str] # Specifies where to open the linked document target: Var[str] CrossOrigin = Literal["anonymous", "use-credentials", ""] class Audio(BaseHTML): """Display the audio element.""" tag = "audio" # Specifies that the audio will start playing as soon as it is ready auto_play: Var[bool] # Displays the standard audio controls controls: Var[bool] # Configures the CORS requests for the element cross_origin: Var[CrossOrigin] # Specifies that the audio will loop loop: Var[bool] # Indicates whether the audio is muted by default muted: Var[bool] # Specifies how the audio file should be preloaded preload: Var[str] # URL of the audio to play src: Var[str] ImageDecoding = Literal["async", "auto", "sync"] ImageLoading = Literal["eager", "lazy"] class Img(BaseHTML): """Display the img element.""" tag = "img" # Alternative text for the image alt: Var[str] # Configures the CORS requests for the image cross_origin: Var[CrossOrigin] # How the image should be decoded decoding: Var[ImageDecoding] # Specifies the loading behavior of the image loading: Var[ImageLoading] # Referrer policy for the image referrer_policy: Var[ReferrerPolicy] # Sizes of the image for different layouts sizes: Var[str] # URL of the image to display src: Var[Any] # A set of source sizes and URLs for responsive images src_set: Var[str] # The name of the map to use with the image use_map: Var[str] @classmethod def create(cls, *children, **props) -> Component: """Override create method to apply source attribute to value if user fails to pass in attribute. Args: *children: The children of the component. **props: The props of the component. Returns: The component. """ return ( super().create(src=children[0], **props) if children else super().create(*children, **props) ) class Map(BaseHTML): """Display the map element.""" tag = "map" # Name of the map, referenced by the 'usemap' attribute in 'img' and 'object' elements name: Var[str] class Track(BaseHTML): """Display the track element.""" tag = "track" # Indicates that the track should be enabled unless the user's preferences indicate otherwise default: Var[bool] # Specifies the kind of text track kind: Var[str] # Title of the text track, used by the browser when listing available text tracks label: Var[str] # URL of the track file src: Var[str] # Language of the track text data src_lang: Var[str] class Video(BaseHTML): """Display the video element.""" tag = "video" # Specifies that the video will start playing as soon as it is ready auto_play: Var[bool] # Displays the standard video controls controls: Var[bool] # Configures the CORS requests for the video cross_origin: Var[CrossOrigin] # Specifies that the video will loop loop: Var[bool] # Indicates whether the video is muted by default muted: Var[bool] # Indicates that the video should play 'inline', inside its element's playback area plays_inline: Var[bool] # URL of an image to show while the video is downloading, or until the user hits the play button poster: Var[str] # Specifies how the video file should be preloaded preload: Var[str] # URL of the video to play src: Var[str] class Embed(BaseHTML): """Display the embed element.""" tag = "embed" # URL of the embedded content src: Var[str] # Media type of the embedded content type: Var[str] class Iframe(BaseHTML): """Display the iframe element.""" tag = "iframe" # Permissions policy for the iframe allow: Var[str] # Specifies the loading behavior of the iframe loading: Var[Literal["eager", "lazy"]] # Name of the iframe, used as a target for hyperlinks and forms name: Var[str] # Referrer policy for the iframe referrer_policy: Var[ReferrerPolicy] # Security restrictions for the content in the iframe sandbox: Var[str] # URL of the document to display in the iframe src: Var[str] # HTML content to embed directly within the iframe src_doc: Var[str] class Object(BaseHTML): """Display the object element.""" tag = "object" # URL of the data to be used by the object data: Var[str] # Associates the object with a form element form: Var[str] # Name of the object, used for scripting or as a target for forms and links name: Var[str] # Media type of the data specified in the data attribute type: Var[str] # Name of an image map to use with the object use_map: Var[str] class Picture(BaseHTML): """Display the picture element.""" tag = "picture" class Portal(BaseHTML): """Display the portal element.""" tag = "portal" class Source(BaseHTML): """Display the source element.""" tag = "source" # Media query indicating what device the linked resource is optimized for media: Var[str] # Sizes of the source for different layouts sizes: Var[str] # URL of the media file or an image for the element to use src: Var[str] # A set of source sizes and URLs for responsive images src_set: Var[str] # Media type of the source type: Var[str] class Svg(BaseHTML): """Display the svg element.""" tag = "svg" # The width of the svg. width: Var[str | int] # The height of the svg. height: Var[str | int] # The XML namespace declaration. xmlns: Var[str] # The viewBox attribute defines the position and dimension, in user space, of an SVG viewport. view_box: Var[str] # Controls how the SVG scales to fit its viewport. preserve_aspect_ratio: Var[str] # Defines a list of transform definitions that are applied to an element and the element's children. transform: Var[str] class Text(BaseHTML): """The SVG text component.""" tag = "text" # The x coordinate of the starting point of the text baseline. x: Var[str | int] # The y coordinate of the starting point of the text baseline. y: Var[str | int] # Shifts the text position horizontally from a previous text element. dx: Var[str | int] # Shifts the text position vertically from a previous text element. dy: Var[str | int] # Rotates orientation of each individual glyph. rotate: Var[str | int] # How the text is stretched or compressed to fit the width defined by the text_length attribute. length_adjust: Var[str] # A width that the text should be scaled to fit. text_length: Var[str | int] class Line(BaseHTML): """The SVG line component.""" tag = "line" # The x-axis coordinate of the line starting point. x1: Var[str | int] # The x-axis coordinate of the the line ending point. x2: Var[str | int] # The y-axis coordinate of the line starting point. y1: Var[str | int] # The y-axis coordinate of the the line ending point. y2: Var[str | int] # The total path length, in user units. path_length: Var[int] class Circle(BaseHTML): """The SVG circle component.""" tag = "circle" # The x-axis coordinate of the center of the circle. cx: Var[str | int] # The y-axis coordinate of the center of the circle. cy: Var[str | int] # The radius of the circle. r: Var[str | int] # The total length for the circle's circumference, in user units. path_length: Var[int] class Ellipse(BaseHTML): """The SVG ellipse component.""" tag = "ellipse" # The x position of the center of the ellipse. cx: Var[str | int] # The y position of the center of the ellipse. cy: Var[str | int] # The radius of the ellipse on the x axis. rx: Var[str | int] # The radius of the ellipse on the y axis. ry: Var[str | int] # The total length for the ellipse's circumference, in user units. path_length: Var[int] class Rect(BaseHTML): """The SVG rect component.""" tag = "rect" # The x coordinate of the rect. x: Var[str | int] # The y coordinate of the rect. y: Var[str | int] # The width of the rect width: Var[str | int] # The height of the rect. height: Var[str | int] # The horizontal corner radius of the rect. Defaults to ry if it is specified. rx: Var[str | int] # The vertical corner radius of the rect. Defaults to rx if it is specified. ry: Var[str | int] # The total length of the rectangle's perimeter, in user units. path_length: Var[int] class Polygon(BaseHTML): """The SVG polygon component.""" tag = "polygon" # defines the list of points (pairs of x,y absolute coordinates) required to draw the polygon. points: Var[str] # This prop lets specify the total length for the path, in user units. path_length: Var[int] class Polyline(BaseHTML): """The SVG polyline component.""" tag = "polyline" # List of points (pairs of x,y coordinates) that define the polyline. points: Var[str] # The total path length, in user units. path_length: Var[int] class Defs(BaseHTML): """Display the defs element.""" tag = "defs" class LinearGradient(BaseHTML): """Display the linearGradient element.""" tag = "linearGradient" # Units for the gradient. gradient_units: Var[str | bool] # Transform applied to the gradient. gradient_transform: Var[str | bool] # Method used to spread the gradient. spread_method: Var[str | bool] # Reference to another gradient to inherit from. href: Var[str] # X coordinate of the starting point of the gradient. x1: Var[str | int | float] # X coordinate of the ending point of the gradient. x2: Var[str | int | float] # Y coordinate of the starting point of the gradient. y1: Var[str | int | float] # Y coordinate of the ending point of the gradient. y2: Var[str | int | float] class RadialGradient(BaseHTML): """Display the radialGradient element.""" tag = "radialGradient" # The x coordinate of the end circle of the radial gradient. cx: Var[str | int | float] # The y coordinate of the end circle of the radial gradient. cy: Var[str | int | float] # The radius of the start circle of the radial gradient. fr: Var[str | int | float] # The x coordinate of the start circle of the radial gradient. fx: Var[str | int | float] # The y coordinate of the start circle of the radial gradient. fy: Var[str | int | float] # Units for the gradient. gradient_units: Var[str | bool] # Transform applied to the gradient. gradient_transform: Var[str | bool] # Reference to another gradient to inherit from. href: Var[str] # The radius of the end circle of the radial gradient. r: Var[str | int | float] # Method used to spread the gradient. spread_method: Var[str | bool] class Stop(BaseHTML): """Display the stop element.""" tag = "stop" # Offset of the gradient stop. offset: Var[str | float | int] # Color of the gradient stop. stop_color: Var[str | Color | bool] # Opacity of the gradient stop. stop_opacity: Var[str | float | int | bool] class Path(BaseHTML): """Display the path element.""" tag = "path" # Defines the shape of the path. d: Var[str | int | float] # The total path length, in user units. path_length: Var[int] class Marker(BaseHTML): """Display the marker element.""" tag = "marker" # The height of the marker viewport. marker_height: Var[str | int | float] # The width of the marker viewport. marker_width: Var[str | int | float] # The coordinate system for the marker attributes. marker_units: Var[str] # The orientation of the marker relative to the shape it is attached to. orient: Var[str | int | float] # How the svg fragment must be deformed if it is embedded in a container with a different aspect ratio. preserve_aspect_ratio: Var[str] # The x coordinate for the reference point of the marker. ref_x: Var[str | int | float] # The y coordinate for the reference point of the marker. ref_y: Var[str | int | float] # The bound of the SVG viewport for the current SVG fragment. view_box: Var[str] class G(BaseHTML): """The SVG g component, used to group other SVG elements.""" tag = "g" # The fill color of the group. fill: Var[str | Color] # The fill opacity of the group. fill_opacity: Var[str | int | float] # The stroke color of the group. stroke: Var[str | Color] # The stroke opacity of the group. stroke_opacity: Var[str | int | float] # The stroke width of the group. stroke_width: Var[str | int | float] # The transform applied to the group. transform: Var[str] class SvgImage(BaseHTML): """The SVG image component.""" tag = "image" # URL of the image to display. href: Var[str] # X coordinate of the image. x: Var[str | int] # Y coordinate of the image. y: Var[str | int] # Width of the image. width: Var[str | int] # Height of the image. height: Var[str | int] # How the image should scale to fit its viewport. preserve_aspect_ratio: Var[str] # CORS settings for the image. crossorigin: Var[str] class Use(BaseHTML): """The SVG use component for reusing defined elements.""" tag = "use" # Reference to the element to reuse. href: Var[str] # X coordinate where the referenced element should be positioned. x: Var[str | int] # Y coordinate where the referenced element should be positioned. y: Var[str | int] # Width of the referenced element. width: Var[str | int] # Height of the referenced element. height: Var[str | int] class TSpan(BaseHTML): """The SVG tspan component for text spans within text elements.""" tag = "tspan" # X coordinate of the text. x: Var[str | int] # Y coordinate of the text. y: Var[str | int] # Horizontal offset from the previous text element. dx: Var[str | int] # Vertical offset from the previous text element. dy: Var[str | int] # Rotation of the text. rotate: Var[str | int] # How the text is stretched or compressed to fit the width defined by text_length. length_adjust: Var[str] # A width that the text should be scaled to fit. text_length: Var[str | int] class TextPath(BaseHTML): """The SVG textPath component for text along a path.""" tag = "textPath" # Reference to the path along which the text should be rendered. href: Var[str] # Inline path data. path: Var[str] # Distance along the path from its beginning to the initial position of the text. start_offset: Var[str | int] # Method for rendering text along the path. method: Var[str] # Spacing method for text. spacing: Var[str] # Which side of the path the text should be rendered on. side: Var[str] # How the text is stretched or compressed to fit the specified length. length_adjust: Var[str] # Target length for the text. text_length: Var[str | int] class Pattern(BaseHTML): """The SVG pattern component for defining repeating patterns.""" tag = "pattern" # X coordinate of the pattern. x: Var[str | int] # Y coordinate of the pattern. y: Var[str | int] # Width of the pattern. width: Var[str | int] # Height of the pattern. height: Var[str | int] # Units for the pattern coordinates. pattern_units: Var[str] # Units for the pattern content coordinates. pattern_content_units: Var[str] # Transform applied to the pattern. pattern_transform: Var[str] # ViewBox definition for the pattern. view_box: Var[str] # How the pattern should scale to fit its viewport. preserve_aspect_ratio: Var[str] # Reference to another pattern to inherit from. href: Var[str] class ClipPath(BaseHTML): """The SVG clipPath component for defining clipping paths.""" tag = "clipPath" # Units for the clipping path coordinates. clip_path_units: Var[str] class Symbol(BaseHTML): """The SVG symbol component for defining reusable symbols.""" tag = "symbol" # ViewBox definition for the symbol. view_box: Var[str] # How the symbol should scale to fit its viewport. preserve_aspect_ratio: Var[str] # Reference X coordinate. ref_x: Var[str | int] # Reference Y coordinate. ref_y: Var[str | int] class Mask(BaseHTML): """The SVG mask component for defining masks.""" tag = "mask" # X coordinate of the mask. x: Var[str | int] # Y coordinate of the mask. y: Var[str | int] # Width of the mask. width: Var[str | int] # Height of the mask. height: Var[str | int] # Units for the mask coordinates. mask_units: Var[str] # Units for the mask content coordinates. mask_content_units: Var[str] class ForeignObject(BaseHTML): """The SVG foreignObject component for embedding foreign content.""" tag = "foreignObject" # X coordinate of the foreign object. x: Var[str | int] # Y coordinate of the foreign object. y: Var[str | int] # Width of the foreign object. width: Var[str | int] # Height of the foreign object. height: Var[str | int] class SvgA(BaseHTML): """The SVG anchor component for creating links.""" tag = "a" # URL of the link. href: Var[str] # Where to open the linked resource. target: Var[str] # Download attribute for the link. download: Var[str] # Relationship between the current document and the linked resource. rel: Var[str] # Language of the linked resource. hreflang: Var[str] # MIME type of the linked resource. type: Var[str] # Referrer policy for the link. referrerpolicy: Var[str] class Animate(BaseHTML): """The SVG animate component for animations.""" tag = "animate" # Name of the attribute to animate. attribute_name: Var[str] # Starting value of the animation. from_: Var[str] # Ending value of the animation. to: Var[str] # Duration of the animation. dur: Var[str] # When the animation should begin. begin: Var[str] # When the animation should end. end: Var[str] # Number of times to repeat the animation. repeat_count: Var[str] # How values should be calculated during the animation. calc_mode: Var[str] # List of values for the animation. values: Var[str] # Key times for the animation values. key_times: Var[str] # Key splines for smooth transitions. key_splines: Var[str] # Whether animation values should accumulate. accumulate: Var[str] # Whether animation values should be additive. additive: Var[str] # Reference to the target element. href: Var[str] class AnimateMotion(BaseHTML): """The SVG animateMotion component for motion animations.""" tag = "animateMotion" # Path along which to animate. path: Var[str] # Duration of the animation. dur: Var[str] # When the animation should begin. begin: Var[str] # When the animation should end. end: Var[str] # Number of times to repeat the animation. repeat_count: Var[str] # Rotation behavior during motion. rotate: Var[str] # Key times for the motion. key_times: Var[str] # Key points along the path. key_points: Var[str] # Reference to the target element. href: Var[str] class AnimateTransform(BaseHTML): """The SVG animateTransform component for transform animations.""" tag = "animateTransform" # Name of the transform attribute to animate. attribute_name: Var[str] # Type of transformation. type: Var[str] # Starting value of the transformation. from_: Var[str] # Ending value of the transformation. to: Var[str] # Duration of the animation. dur: Var[str] # When the animation should begin. begin: Var[str] # When the animation should end. end: Var[str] # Number of times to repeat the animation. repeat_count: Var[str] # List of values for the transformation. values: Var[str] # Reference to the target element. href: Var[str] class Set(BaseHTML): """The SVG set component for setting attribute values.""" tag = "set" # Name of the attribute to set. attribute_name: Var[str] # Value to set the attribute to. to: Var[str] # When to set the attribute. begin: Var[str] # Duration for which to maintain the value. dur: Var[str] # When to end the setting. end: Var[str] # Reference to the target element. href: Var[str] class MPath(BaseHTML): """The SVG mpath component for motion path references.""" tag = "mpath" # Reference to a path element. href: Var[str] class Desc(BaseHTML): """The SVG desc component for descriptions.""" tag = "desc" class Title(BaseHTML): """The SVG title component for titles.""" tag = "title" class Metadata(BaseHTML): """The SVG metadata component for metadata.""" tag = "metadata" class Script(BaseHTML): """The SVG script component for scripts.""" tag = "script" # MIME type of the script. type: Var[str] # URL of external script. href: Var[str] # CORS settings for the script. crossorigin: Var[str] class SvgStyle(BaseHTML): """The SVG style component for stylesheets.""" tag = "style" # MIME type of the stylesheet. type: Var[str] # Media query for the stylesheet. media: Var[str] # Title of the stylesheet. title: Var[str] class Switch(BaseHTML): """The SVG switch component for conditional processing.""" tag = "switch" class View(BaseHTML): """The SVG view component for view definitions.""" tag = "view" # ViewBox definition for the view. view_box: Var[str] # How the view should scale to fit its viewport. preserve_aspect_ratio: Var[str] class SVG(ComponentNamespace): """SVG component namespace.""" text = staticmethod(Text.create) line = staticmethod(Line.create) circle = staticmethod(Circle.create) ellipse = staticmethod(Ellipse.create) rect = staticmethod(Rect.create) polygon = staticmethod(Polygon.create) polyline = staticmethod(Polyline.create) path = staticmethod(Path.create) stop = staticmethod(Stop.create) linear_gradient = staticmethod(LinearGradient.create) radial_gradient = staticmethod(RadialGradient.create) defs = staticmethod(Defs.create) marker = staticmethod(Marker.create) g = staticmethod(G.create) image = staticmethod(SvgImage.create) use = staticmethod(Use.create) tspan = staticmethod(TSpan.create) text_path = staticmethod(TextPath.create) pattern = staticmethod(Pattern.create) clip_path = staticmethod(ClipPath.create) symbol = staticmethod(Symbol.create) mask = staticmethod(Mask.create) foreign_object = staticmethod(ForeignObject.create) a = staticmethod(SvgA.create) animate = staticmethod(Animate.create) animate_motion = staticmethod(AnimateMotion.create) animate_transform = staticmethod(AnimateTransform.create) set = staticmethod(Set.create) mpath = staticmethod(MPath.create) desc = staticmethod(Desc.create) title = staticmethod(Title.create) metadata = staticmethod(Metadata.create) script = staticmethod(Script.create) style = staticmethod(SvgStyle.create) switch = staticmethod(Switch.create) view = staticmethod(View.create) __call__ = staticmethod(Svg.create) text = Text.create line = Line.create circle = Circle.create ellipse = Ellipse.create rect = Rect.create polygon = Polygon.create polyline = Polyline.create path = Path.create stop = Stop.create linear_gradient = LinearGradient.create radial_gradient = RadialGradient.create defs = Defs.create marker = Marker.create g = G.create use = Use.create tspan = TSpan.create text_path = TextPath.create pattern = Pattern.create clip_path = ClipPath.create symbol = Symbol.create mask = Mask.create foreign_object = ForeignObject.create animate = Animate.create animate_motion = AnimateMotion.create animate_transform = AnimateTransform.create set_svg = Set.create mpath = MPath.create desc = Desc.create metadata = Metadata.create switch = Switch.create view = View.create area = Area.create audio = Audio.create image = img = Img.create map = Map.create track = Track.create video = Video.create embed = Embed.create iframe = Iframe.create object = Object.create picture = Picture.create portal = Portal.create source = Source.create svg = SVG() ================================================ FILE: reflex/components/el/elements/metadata.py ================================================ """Metadata classes.""" from reflex.components.el.element import Element from reflex.components.el.elements.inline import ReferrerPolicy from reflex.components.el.elements.media import CrossOrigin from reflex.vars.base import Var from .base import BaseHTML class Base(BaseHTML): """Display the base element.""" tag = "base" href: Var[str] target: Var[str] class Head(BaseHTML): """Display the head element.""" tag = "head" class Link(BaseHTML): """Display the link element.""" tag = "link" # Specifies the CORS settings for the linked resource cross_origin: Var[CrossOrigin] # Specifies the URL of the linked document/resource href: Var[str] # Specifies the language of the text in the linked document href_lang: Var[str] # Allows a browser to check the fetched link for integrity integrity: Var[str] # Specifies on what device the linked document will be displayed media: Var[str] # Specifies the referrer policy of the linked document referrer_policy: Var[ReferrerPolicy] # Specifies the relationship between the current document and the linked one rel: Var[str] # Specifies the sizes of icons for visual media sizes: Var[str] # Specifies the MIME type of the linked document type: Var[str] class Meta(BaseHTML): # Inherits common attributes from BaseHTML """Display the meta element.""" tag = "meta" # The HTML tag for this element is # Specifies the character encoding for the HTML document char_set: Var[str] # Defines the content of the metadata content: Var[str] # Provides an HTTP header for the information/value of the content attribute http_equiv: Var[str] # Specifies a name for the metadata name: Var[str] # The type of metadata value. property: Var[str] class Title(Element): """Display the title element.""" tag = "title" # Had to be named with an underscore so it doesn't conflict with reflex.style Style in pyi class StyleEl(Element): """Display the style element.""" tag = "style" media: Var[str] suppress_hydration_warning: Var[bool] = Var.create(True) base = Base.create head = Head.create link = Link.create meta = Meta.create title = Title.create style = StyleEl.create ================================================ FILE: reflex/components/el/elements/other.py ================================================ """Other classes.""" from reflex.vars.base import Var from .base import BaseHTML class Details(BaseHTML): """Display the details element.""" tag = "details" # Indicates whether the details will be visible (expanded) to the user open: Var[bool] class Dialog(BaseHTML): """Display the dialog element.""" tag = "dialog" # Indicates whether the dialog is active and can be interacted with open: Var[bool] class Summary(BaseHTML): """Display the summary element. Used as a summary or caption for a
element. """ tag = "summary" class Slot(BaseHTML): """Display the slot element. Used as a placeholder inside a web component. """ tag = "slot" class Template(BaseHTML): """Display the template element. Used for declaring fragments of HTML that can be cloned and inserted in the document. """ tag = "template" class Math(BaseHTML): """Display the math element. Represents a mathematical expression. """ tag = "math" class Html(BaseHTML): """Display the html element.""" tag = "html" # Specifies the URL of the document's cache manifest (obsolete in HTML5) manifest: Var[str] details = Details.create dialog = Dialog.create summary = Summary.create slot = Slot.create template = Template.create math = Math.create html = Html.create ================================================ FILE: reflex/components/el/elements/scripts.py ================================================ """Scripts classes.""" from reflex.components.el.elements.inline import ReferrerPolicy from reflex.components.el.elements.media import CrossOrigin from reflex.vars.base import Var from .base import BaseHTML class Canvas(BaseHTML): """Display the canvas element.""" tag = "canvas" class Noscript(BaseHTML): """Display the noscript element.""" tag = "noscript" class Script(BaseHTML): """Display the script element.""" tag = "script" # Indicates that the script should be executed asynchronously async_: Var[bool] # Character encoding of the external script char_set: Var[str] # Configures the CORS requests for the script cross_origin: Var[CrossOrigin] # Indicates that the script should be executed after the page has finished parsing defer: Var[bool] # Security feature allowing browsers to verify what they fetch integrity: Var[str] # Specifies which referrer information to send when fetching the script referrer_policy: Var[ReferrerPolicy] # URL of an external script src: Var[str] # Specifies the MIME type of the script type: Var[str] canvas = Canvas.create noscript = Noscript.create script = Script.create ================================================ FILE: reflex/components/el/elements/sectioning.py ================================================ """Sectioning classes.""" from .base import BaseHTML class Body(BaseHTML): """Display the body element.""" tag = "body" class Address(BaseHTML): """Display the address element.""" tag = "address" class Article(BaseHTML): """Display the article element.""" tag = "article" class Aside(BaseHTML): """Display the aside element.""" tag = "aside" class Footer(BaseHTML): """Display the footer element.""" tag = "footer" class Header(BaseHTML): """Display the header element.""" tag = "header" class H1(BaseHTML): """Display the h1 element.""" tag = "h1" class H2(BaseHTML): """Display the h1 element.""" tag = "h2" class H3(BaseHTML): """Display the h1 element.""" tag = "h3" class H4(BaseHTML): """Display the h1 element.""" tag = "h4" class H5(BaseHTML): """Display the h1 element.""" tag = "h5" class H6(BaseHTML): """Display the h1 element.""" tag = "h6" class Main(BaseHTML): """Display the main element.""" tag = "main" class Nav(BaseHTML): """Display the nav element.""" tag = "nav" class Section(BaseHTML): """Display the section element.""" tag = "section" address = Address.create article = Article.create aside = Aside.create body = Body.create header = Header.create footer = Footer.create h1 = H1.create h2 = H2.create h3 = H3.create h4 = H4.create h5 = H5.create h6 = H6.create main = Main.create nav = Nav.create section = Section.create ================================================ FILE: reflex/components/el/elements/tables.py ================================================ """Tables classes.""" from typing import Literal from reflex.vars.base import Var from .base import BaseHTML class Caption(BaseHTML): """Display the caption element.""" tag = "caption" class Col(BaseHTML): """Display the col element.""" tag = "col" # Number of columns the col element spans span: Var[int] class Colgroup(BaseHTML): """Display the colgroup element.""" tag = "colgroup" # Number of columns the colgroup element spans span: Var[int] class Table(BaseHTML): """Display the table element.""" tag = "table" # Alignment of the table align: Var[Literal["left", "center", "right"]] # Provides a summary of the table's purpose and structure summary: Var[str] class Tbody(BaseHTML): """Display the tbody element.""" tag = "tbody" class Td(BaseHTML): """Display the td element.""" tag = "td" # Alignment of the content within the table cell align: Var[Literal["left", "center", "right", "justify", "char"]] # Number of columns a cell should span col_span: Var[int] # IDs of the headers associated with this cell headers: Var[str] # Number of rows a cell should span row_span: Var[int] class Tfoot(BaseHTML): """Display the tfoot element.""" tag = "tfoot" class Th(BaseHTML): """Display the th element.""" tag = "th" # Alignment of the content within the table header cell align: Var[Literal["left", "center", "right", "justify", "char"]] # Number of columns a header cell should span col_span: Var[int] # IDs of the headers associated with this header cell headers: Var[str] # Number of rows a header cell should span row_span: Var[int] # Scope of the header cell (row, col, rowgroup, colgroup) scope: Var[str] class Thead(BaseHTML): """Display the thead element.""" tag = "thead" class Tr(BaseHTML): """Display the tr element.""" tag = "tr" caption = Caption.create col = Col.create colgroup = Colgroup.create table = Table.create tbody = Tbody.create td = Td.create tfoot = Tfoot.create th = Th.create thead = Thead.create tr = Tr.create ================================================ FILE: reflex/components/el/elements/typography.py ================================================ """Typography classes.""" from typing import ClassVar, Literal from reflex.vars.base import Var from .base import BaseHTML class Blockquote(BaseHTML): """Display the blockquote element.""" tag = "blockquote" # Define the title of a work. cite: Var[str] class Dd(BaseHTML): """Display the dd element.""" tag = "dd" class Div(BaseHTML): """Display the div element.""" tag = "div" class Dl(BaseHTML): """Display the dl element.""" tag = "dl" class Dt(BaseHTML): """Display the dt element.""" tag = "dt" class Figcaption(BaseHTML): """Display the figcaption element.""" tag = "figcaption" class Figure(BaseHTML): """Display the figure element.""" tag = "figure" class Hr(BaseHTML): """Display the hr element.""" tag = "hr" class Li(BaseHTML): """Display the li element.""" tag = "li" class Menu(BaseHTML): """Display the menu element.""" tag = "menu" # Specifies that the menu element is a context menu. type: Var[str] class Ol(BaseHTML): """Display the ol element.""" tag = "ol" # Reverses the order of the list. reversed: Var[bool] # Specifies the start value of the first list item in an ordered list. start: Var[int] # Specifies the kind of marker to use in the list (letters or numbers). type: Var[Literal["1", "a", "A", "i", "I"]] class P(BaseHTML): """Display the p element.""" tag = "p" _invalid_children: ClassVar[list] = ["P", "Ol", "Ul", "Div"] class Pre(BaseHTML): """Display the pre element.""" tag = "pre" class Ul(BaseHTML): """Display the ul element.""" tag = "ul" class Ins(BaseHTML): """Display the ins element.""" tag = "ins" # Specifies the URL of the document that explains the reason why the text was inserted/changed. cite: Var[str] # Specifies the date and time of when the text was inserted/changed. date_time: Var[str] class Del(BaseHTML): """Display the del element.""" tag = "del" # Specifies the URL of the document that explains the reason why the text was deleted. cite: Var[str] # Specifies the date and time of when the text was deleted. date_time: Var[str] blockquote = Blockquote.create dd = Dd.create div = Div.create dl = Dl.create dt = Dt.create figcaption = Figcaption.create figure = Figure.create hr = Hr.create li = Li.create ol = Ol.create p = P.create pre = Pre.create ul = Ul.create ins = Ins.create del_ = Del.create # 'del' is a reserved keyword in Python ================================================ FILE: reflex/components/field.py ================================================ """Shared field infrastructure for components and props.""" from __future__ import annotations from collections.abc import Callable from dataclasses import _MISSING_TYPE, MISSING from typing import Annotated, Any, Generic, TypeVar, get_origin from reflex.utils import types from reflex.utils.compat import annotations_from_namespace FIELD_TYPE = TypeVar("FIELD_TYPE") class BaseField(Generic[FIELD_TYPE]): """Base field class used by internal metadata classes.""" def __init__( self, default: FIELD_TYPE | _MISSING_TYPE = MISSING, default_factory: Callable[[], FIELD_TYPE] | None = None, annotated_type: type[Any] | _MISSING_TYPE = MISSING, ) -> None: """Initialize the field. Args: default: The default value for the field. default_factory: The default factory for the field. annotated_type: The annotated type for the field. """ self.default = default self.default_factory = default_factory self.outer_type_ = self.annotated_type = annotated_type # Process type annotation type_origin = get_origin(annotated_type) or annotated_type if type_origin is Annotated: type_origin = annotated_type.__origin__ # pyright: ignore [reportAttributeAccessIssue] # For Annotated types, use the actual type inside the annotation self.type_ = annotated_type else: # For other types (including Union), preserve the original type self.type_ = annotated_type self.type_origin = type_origin def default_value(self) -> FIELD_TYPE: """Get the default value for the field. Returns: The default value for the field. Raises: ValueError: If no default value or factory is provided. """ if self.default is not MISSING: return self.default if self.default_factory is not None: return self.default_factory() msg = "No default value or factory provided." raise ValueError(msg) class FieldBasedMeta(type): """Shared metaclass for field-based classes like components and props. Provides common field inheritance and processing logic for both PropsBaseMeta and BaseComponentMeta. """ def __new__( cls, name: str, bases: tuple[type, ...], namespace: dict[str, Any] ) -> type: """Create a new field-based class. Args: name: The name of the class. bases: The base classes. namespace: The class namespace. Returns: The new class. """ # Collect inherited fields from base classes inherited_fields = cls._collect_inherited_fields(bases) # Get annotations from the namespace annotations = cls._resolve_annotations(namespace, name) # Process field overrides (fields with values but no annotations) own_fields = cls._process_field_overrides( namespace, annotations, inherited_fields ) # Process annotated fields own_fields.update( cls._process_annotated_fields(namespace, annotations, inherited_fields) ) # Finalize fields and store on class cls._finalize_fields(namespace, inherited_fields, own_fields) return super().__new__(cls, name, bases, namespace) @classmethod def _collect_inherited_fields(cls, bases: tuple[type, ...]) -> dict[str, Any]: inherited_fields: dict[str, Any] = {} # Collect inherited fields from base classes for base in bases[::-1]: if hasattr(base, "_inherited_fields"): inherited_fields.update(base._inherited_fields) for base in bases[::-1]: if hasattr(base, "_own_fields"): inherited_fields.update(base._own_fields) return inherited_fields @classmethod def _resolve_annotations( cls, namespace: dict[str, Any], name: str ) -> dict[str, Any]: return types.resolve_annotations( annotations_from_namespace(namespace), namespace["__module__"], ) @classmethod def _process_field_overrides( cls, namespace: dict[str, Any], annotations: dict[str, Any], inherited_fields: dict[str, Any], ) -> dict[str, Any]: own_fields: dict[str, Any] = {} for key, value in namespace.items(): if key not in annotations and key in inherited_fields: inherited_field = inherited_fields[key] new_field = cls._create_field( annotated_type=inherited_field.annotated_type, default=value, default_factory=None, ) own_fields[key] = new_field return own_fields @classmethod def _process_annotated_fields( cls, namespace: dict[str, Any], annotations: dict[str, Any], inherited_fields: dict[str, Any], ) -> dict[str, Any]: raise NotImplementedError @classmethod def _create_field( cls, annotated_type: Any, default: Any = MISSING, default_factory: Callable[[], Any] | None = None, ) -> Any: raise NotImplementedError @classmethod def _finalize_fields( cls, namespace: dict[str, Any], inherited_fields: dict[str, Any], own_fields: dict[str, Any], ) -> None: # Combine all fields all_fields = inherited_fields | own_fields # Set field names for compatibility for field_name, field in all_fields.items(): field._name = field_name # Store field mappings on the class namespace["_own_fields"] = own_fields namespace["_inherited_fields"] = inherited_fields namespace["_fields"] = all_fields ================================================ FILE: reflex/components/gridjs/__init__.py ================================================ """Grid components.""" from .datatable import DataTable data_table = DataTable.create ================================================ FILE: reflex/components/gridjs/datatable.py ================================================ """Table components.""" from __future__ import annotations from collections.abc import Sequence from typing import Any from reflex.components.component import NoSSRComponent from reflex.components.tags import Tag from reflex.utils import types from reflex.utils.imports import ImportDict from reflex.utils.serializers import serialize from reflex.vars.base import LiteralVar, Var, is_computed_var class Gridjs(NoSSRComponent): """A component that wraps a nivo bar component.""" library = "gridjs-react@6.1.1" lib_dependencies: list[str] = ["gridjs@6.2.0"] class DataTable(Gridjs): """A data table component.""" tag = "Grid" alias = "DataTableGrid" # The data to display. Either a list of lists or a pandas dataframe. data: Any # The list of columns to display. Required if data is a list and should not be provided # if the data field is a dataframe columns: Var[Sequence] # Enable a search bar. search: Var[bool] # Enable sorting on columns. sort: Var[bool] # Enable resizable columns. resizable: Var[bool] # Enable pagination. pagination: Var[bool | dict] @classmethod def create(cls, *children, **props): """Create a datatable component. Args: *children: The children of the component. **props: The props to pass to the component. Returns: The datatable component. Raises: ValueError: If a pandas dataframe is passed in and columns are also provided. """ data = props.get("data") columns = props.get("columns") # The annotation should be provided if data is a computed var. We need this to know how to # render pandas dataframes. if is_computed_var(data) and data._var_type == Any: msg = "Annotation of the computed var assigned to the data field should be provided." raise ValueError(msg) if ( columns is not None and is_computed_var(columns) and columns._var_type == Any ): msg = "Annotation of the computed var assigned to the column field should be provided." raise ValueError(msg) # If data is a pandas dataframe and columns are provided throw an error. if ( types.is_dataframe(type(data)) or (isinstance(data, Var) and types.is_dataframe(data._var_type)) ) and columns is not None: msg = "Cannot pass in both a pandas dataframe and columns to the data_table component." raise ValueError(msg) # If data is a list and columns are not provided, throw an error if ( (isinstance(data, Var) and types.typehint_issubclass(data._var_type, list)) or isinstance(data, list) ) and columns is None: msg = "column field should be specified when the data field is a list type" raise ValueError(msg) # Create the component. return super().create( *children, **props, ) def add_imports(self) -> ImportDict: """Add the imports for the datatable component. Returns: The import dict for the component. """ return {"": "gridjs/dist/theme/mermaid.css"} def _render(self) -> Tag: if isinstance(self.data, Var) and types.is_dataframe(self.data._var_type): self.columns = self.data._replace( _js_expr=f"{self.data._js_expr}.columns", _var_type=list[Any], ) self.data = self.data._replace( _js_expr=f"{self.data._js_expr}.data", _var_type=list[list[Any]], ) if types.is_dataframe(type(self.data)): # If given a pandas df break up the data and columns data = serialize(self.data) if not isinstance(data, dict): msg = "Serialized dataframe should be a dict." raise ValueError(msg) self.columns = LiteralVar.create(data["columns"]) self.data = LiteralVar.create(data["data"]) # Render the table. return super()._render() ================================================ FILE: reflex/components/literals.py ================================================ """Literal custom type used by Reflex.""" from typing import Literal # Base Literals LiteralInputType = Literal[ "button", "checkbox", "color", "date", "datetime-local", "email", "file", "hidden", "image", "month", "number", "password", "radio", "range", "reset", "search", "submit", "tel", "text", "time", "url", "week", ] LiteralRowMarker = Literal[ "none", "number", "checkbox", "both", "checkbox-visible", "clickable-number" ] ================================================ FILE: reflex/components/lucide/__init__.py ================================================ """Lucide Icon Component.""" from .icon import Icon icon = Icon.create ================================================ FILE: reflex/components/lucide/icon.py ================================================ """Lucide Icon component.""" from reflex.components.component import Component from reflex.utils import console, format from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var from reflex.vars.sequence import LiteralStringVar, StringVar LUCIDE_LIBRARY = "lucide-react@0.577.0" class LucideIconComponent(Component): """Lucide Icon Component.""" library = LUCIDE_LIBRARY class Icon(LucideIconComponent): """An Icon component.""" tag = "None" # The size of the icon in pixels. size: Var[int] @classmethod def create(cls, *children, **props) -> Component: """Initialize the Icon component. Run some additional checks on Icon component. Args: *children: The positional arguments **props: The keyword arguments Returns: The created component. Raises: AttributeError: The errors tied to bad usage of the Icon component. TypeError: If the icon name is not a string. """ if children: if len(children) == 1: child = Var.create(children[0]).guess_type() if not isinstance(child, StringVar): msg = f"Icon name must be a string, got {children[0]._var_type if isinstance(children[0], Var) else children[0]}" raise AttributeError(msg) props["tag"] = children[0] else: msg = f"Passing multiple children to Icon component is not allowed: remove positional arguments {children[1:]} to fix" raise AttributeError(msg) if "tag" not in props: msg = "Missing 'tag' keyword-argument for Icon" raise AttributeError(msg) tag_var: Var | LiteralVar = Var.create(props.pop("tag")) if isinstance(tag_var, LiteralVar): if isinstance(tag_var, LiteralStringVar): tag = format.to_snake_case(tag_var._var_value.lower()) else: msg = f"Icon name must be a string, got {type(tag_var)}" raise TypeError(msg) elif isinstance(tag_var, Var): tag_stringified = tag_var.guess_type() if not isinstance(tag_stringified, StringVar): msg = f"Icon name must be a string, got {tag_var._var_type}" raise TypeError(msg) return DynamicIcon.create(name=tag_stringified.replace("_", "-"), **props) if tag not in LUCIDE_ICON_LIST: icons_sorted = sorted( LUCIDE_ICON_LIST, key=lambda s, tag=tag: format.length_of_largest_common_substring( tag, s ), reverse=True, ) console.warn( f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(icons_sorted[0:10])}, ..." "\nSee full list at https://reflex.dev/docs/library/data-display/icon/#icons-list. Using 'circle_help' icon instead." ) tag = "circle_help" props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE.get(tag, format.to_title_case(tag)) props["alias"] = f"Lucide{props['tag']}" return super().create(**props) class DynamicIcon(LucideIconComponent): """A DynamicIcon component.""" tag = "DynamicIcon" name: Var[str] size: Var[int] def _get_imports(self): imports_ = super()._get_imports() if self.library: imports_.pop(self.library) imports_[LUCIDE_LIBRARY] = [ ImportVar("DynamicIcon", package_path="/dynamic.mjs") ] return imports_ LUCIDE_ICON_LIST = [ "a_arrow_down", "a_arrow_up", "a_large_small", "accessibility", "activity", "air_vent", "airplay", "alarm_clock_check", "alarm_clock_minus", "alarm_clock_off", "alarm_clock_plus", "alarm_clock", "alarm_smoke", "album", "align_center_horizontal", "align_center_vertical", "align_center", "align_end_horizontal", "align_end_vertical", "align_horizontal_distribute_center", "align_horizontal_distribute_end", "align_horizontal_distribute_start", "align_horizontal_justify_center", "align_horizontal_justify_end", "align_horizontal_justify_start", "align_horizontal_space_around", "align_horizontal_space_between", "align_justify", "align_left", "align_right", "align_start_horizontal", "align_start_vertical", "align_vertical_distribute_center", "align_vertical_distribute_end", "align_vertical_distribute_start", "align_vertical_justify_center", "align_vertical_justify_end", "align_vertical_justify_start", "align_vertical_space_around", "align_vertical_space_between", "ambulance", "ampersand", "ampersands", "amphora", "anchor", "angry", "annoyed", "antenna", "anvil", "aperture", "app_window_mac", "app_window", "apple", "archive_restore", "archive_x", "archive", "area_chart", "armchair", "arrow_big_down_dash", "arrow_big_down", "arrow_big_left_dash", "arrow_big_left", "arrow_big_right_dash", "arrow_big_right", "arrow_big_up_dash", "arrow_big_up", "arrow_down_0_1", "arrow_down_1_0", "arrow_down_a_z", "arrow_down_from_line", "arrow_down_left", "arrow_down_narrow_wide", "arrow_down_right", "arrow_down_to_dot", "arrow_down_to_line", "arrow_down_up", "arrow_down_wide_narrow", "arrow_down_z_a", "arrow_down", "arrow_left_from_line", "arrow_left_right", "arrow_left_to_line", "arrow_left", "arrow_right_from_line", "arrow_right_left", "arrow_right_to_line", "arrow_right", "arrow_up_0_1", "arrow_up_1_0", "arrow_up_a_z", "arrow_up_down", "arrow_up_from_dot", "arrow_up_from_line", "arrow_up_left", "arrow_up_narrow_wide", "arrow_up_right", "arrow_up_to_line", "arrow_up_wide_narrow", "arrow_up_z_a", "arrow_up", "arrows_up_from_line", "asterisk", "at_sign", "atom", "audio_lines", "audio_waveform", "award", "axe", "axis_3d", "baby", "backpack", "badge_alert", "badge_cent", "badge_check", "badge_dollar_sign", "badge_euro", "badge_help", "badge_indian_rupee", "badge_info", "badge_japanese_yen", "badge_minus", "badge_percent", "badge_plus", "badge_pound_sterling", "badge_russian_ruble", "badge_swiss_franc", "badge_turkish_lira", "badge_x", "badge", "baggage_claim", "balloon", "ban", "banana", "bandage", "banknote_arrow_down", "banknote_x", "banknote", "bar_chart_2", "bar_chart_3", "bar_chart_4", "bar_chart_big", "bar_chart_horizontal_big", "bar_chart_horizontal", "bar_chart", "barcode", "barrel", "baseline", "bath", "battery_charging", "battery_full", "battery_low", "battery_medium", "battery_plus", "battery_warning", "battery", "beaker", "bean_off", "bean", "bed_double", "bed_single", "bed", "beef", "beer_off", "beer", "bell_dot", "bell_electric", "bell_minus", "bell_off", "bell_plus", "bell_ring", "bell", "between_horizontal_end", "between_horizontal_start", "between_vertical_end", "between_vertical_start", "biceps_flexed", "bike", "binary", "binoculars", "biohazard", "bird", "birdhouse", "bitcoin", "blend", "blinds", "blocks", "bluetooth_connected", "bluetooth_off", "bluetooth_searching", "bluetooth", "bold", "bolt", "bomb", "bone", "book_a", "book_alert", "book_audio", "book_check", "book_copy", "book_dashed", "book_down", "book_headphones", "book_heart", "book_image", "book_key", "book_lock", "book_marked", "book_minus", "book_open_check", "book_open_text", "book_open", "book_plus", "book_search", "book_text", "book_type", "book_up_2", "book_up", "book_user", "book_x", "book", "bookmark_check", "bookmark_minus", "bookmark_plus", "bookmark_x", "bookmark", "boom_box", "bot_message_square", "bot_off", "bot", "bottle_wine", "bow_arrow", "box_select", "box", "boxes", "braces", "brackets", "brain_circuit", "brain_cog", "brain", "brick_wall_fire", "brick_wall_shield", "brick_wall", "briefcase_business", "briefcase_conveyor_belt", "briefcase_medical", "briefcase", "bring_to_front", "brush_cleaning", "brush", "bubbles", "bug_off", "bug_play", "bug", "building_2", "building", "bus_front", "bus", "cable_car", "cable", "cake_slice", "cake", "calculator", "calendar_1", "calendar_arrow_down", "calendar_arrow_up", "calendar_check_2", "calendar_check", "calendar_clock", "calendar_cog", "calendar_days", "calendar_fold", "calendar_heart", "calendar_minus_2", "calendar_minus", "calendar_off", "calendar_plus_2", "calendar_plus", "calendar_range", "calendar_search", "calendar_sync", "calendar_x_2", "calendar_x", "calendar", "calendars", "camera_off", "camera", "candlestick_chart", "candy_cane", "candy_off", "candy", "cannabis_off", "cannabis", "captions_off", "captions", "car_front", "car_taxi_front", "car", "caravan", "card_sim", "carrot", "case_lower", "case_sensitive", "case_upper", "cassette_tape", "cast", "castle", "cat", "cctv", "chart_area", "chart_bar_big", "chart_bar_decreasing", "chart_bar_increasing", "chart_bar_stacked", "chart_bar", "chart_candlestick", "chart_column_big", "chart_column_decreasing", "chart_column_increasing", "chart_column_stacked", "chart_column", "chart_gantt", "chart_line", "chart_network", "chart_no_axes_column_decreasing", "chart_no_axes_column_increasing", "chart_no_axes_column", "chart_no_axes_combined", "chart_no_axes_gantt", "chart_pie", "chart_scatter", "chart_spline", "check_check", "check_line", "check", "chef_hat", "cherry", "chess_bishop", "chess_king", "chess_knight", "chess_pawn", "chess_queen", "chess_rook", "chevron_down", "chevron_first", "chevron_last", "chevron_left", "chevron_right", "chevron_up", "chevrons_down_up", "chevrons_down", "chevrons_left_right_ellipsis", "chevrons_left_right", "chevrons_left", "chevrons_right_left", "chevrons_right", "chevrons_up_down", "chevrons_up", "chrome", "chromium", "church", "cigarette_off", "cigarette", "circle_alert", "circle_arrow_down", "circle_arrow_left", "circle_arrow_out_down_left", "circle_arrow_out_down_right", "circle_arrow_out_up_left", "circle_arrow_out_up_right", "circle_arrow_right", "circle_arrow_up", "circle_check_big", "circle_check", "circle_chevron_down", "circle_chevron_left", "circle_chevron_right", "circle_chevron_up", "circle_dashed", "circle_divide", "circle_dollar_sign", "circle_dot_dashed", "circle_dot", "circle_ellipsis", "circle_equal", "circle_fading_arrow_up", "circle_fading_plus", "circle_gauge", "circle_help", "circle_minus", "circle_off", "circle_parking_off", "circle_parking", "circle_pause", "circle_percent", "circle_pile", "circle_play", "circle_plus", "circle_pound_sterling", "circle_power", "circle_slash_2", "circle_slash", "circle_small", "circle_star", "circle_stop", "circle_user_round", "circle_user", "circle_x", "circle", "circuit_board", "citrus", "clapperboard", "clipboard_check", "clipboard_clock", "clipboard_copy", "clipboard_list", "clipboard_minus", "clipboard_paste", "clipboard_pen_line", "clipboard_pen", "clipboard_plus", "clipboard_type", "clipboard_x", "clipboard", "clock_1", "clock_10", "clock_11", "clock_12", "clock_2", "clock_3", "clock_4", "clock_5", "clock_6", "clock_7", "clock_8", "clock_9", "clock_alert", "clock_arrow_down", "clock_arrow_up", "clock_check", "clock_fading", "clock_plus", "clock", "closed_caption", "cloud_alert", "cloud_backup", "cloud_check", "cloud_cog", "cloud_download", "cloud_drizzle", "cloud_fog", "cloud_hail", "cloud_lightning", "cloud_moon_rain", "cloud_moon", "cloud_off", "cloud_rain_wind", "cloud_rain", "cloud_snow", "cloud_sun_rain", "cloud_sun", "cloud_sync", "cloud_upload", "cloud", "cloudy", "clover", "club", "code_xml", "code", "codepen", "codesandbox", "coffee", "cog", "coins", "columns_2", "columns_3_cog", "columns_3", "columns_4", "combine", "command", "compass", "component", "computer", "concierge_bell", "cone", "construction", "contact_round", "contact", "container", "contrast", "cookie", "cooking_pot", "copy_check", "copy_minus", "copy_plus", "copy_slash", "copy_x", "copy", "copyleft", "copyright", "corner_down_left", "corner_down_right", "corner_left_down", "corner_left_up", "corner_right_down", "corner_right_up", "corner_up_left", "corner_up_right", "cpu", "creative_commons", "credit_card", "croissant", "crop", "cross", "crosshair", "crown", "cuboid", "cup_soda", "currency", "cylinder", "dam", "database_backup", "database_search", "database_zap", "database", "decimals_arrow_left", "decimals_arrow_right", "delete", "dessert", "diameter", "diamond_minus", "diamond_percent", "diamond_plus", "diamond", "dice_1", "dice_2", "dice_3", "dice_4", "dice_5", "dice_6", "dices", "diff", "disc_2", "disc_3", "disc_album", "disc", "divide", "dna_off", "dna", "dock", "dog", "dollar_sign", "donut", "door_closed_locked", "door_closed", "door_open", "dot", "download", "drafting_compass", "drama", "dribbble", "drill", "drone", "droplet_off", "droplet", "droplets", "drum", "drumstick", "dumbbell", "ear_off", "ear", "earth_lock", "earth", "eclipse", "egg_fried", "egg_off", "egg", "ellipse", "ellipsis_vertical", "ellipsis", "equal_approximately", "equal_not", "equal", "eraser", "ethernet_port", "euro", "ev_charger", "expand", "external_link", "eye_closed", "eye_off", "eye", "facebook", "factory", "fan", "fast_forward", "feather", "fence", "ferris_wheel", "figma", "file_archive", "file_audio_2", "file_audio", "file_axis_3d", "file_badge_2", "file_badge", "file_bar_chart_2", "file_bar_chart", "file_box", "file_chart_column_increasing", "file_chart_column", "file_chart_line", "file_chart_pie", "file_check_2", "file_check", "file_clock", "file_code_2", "file_code", "file_cog", "file_diff", "file_digit", "file_down", "file_heart", "file_image", "file_input", "file_json_2", "file_json", "file_key_2", "file_key", "file_line_chart", "file_lock_2", "file_lock", "file_minus_2", "file_minus", "file_music", "file_output", "file_pen_line", "file_pen", "file_pie_chart", "file_plus_2", "file_plus", "file_question", "file_scan", "file_search_2", "file_search", "file_sliders", "file_spreadsheet", "file_stack", "file_symlink", "file_terminal", "file_text", "file_type_2", "file_type", "file_up", "file_user", "file_video_2", "file_video", "file_volume_2", "file_volume", "file_warning", "file_x_2", "file_x", "file", "files", "film", "filter_x", "filter", "fingerprint_pattern", "fingerprint", "fire_extinguisher", "fish_off", "fish_symbol", "fish", "fishing_hook", "fishing_rod", "flag_off", "flag_triangle_left", "flag_triangle_right", "flag", "flame_kindling", "flame", "flashlight_off", "flashlight", "flask_conical_off", "flask_conical", "flask_round", "flip_horizontal_2", "flip_horizontal", "flip_vertical_2", "flip_vertical", "flower_2", "flower", "focus", "fold_horizontal", "fold_vertical", "folder_archive", "folder_check", "folder_clock", "folder_closed", "folder_code", "folder_cog", "folder_dot", "folder_down", "folder_git_2", "folder_git", "folder_heart", "folder_input", "folder_kanban", "folder_key", "folder_lock", "folder_minus", "folder_open_dot", "folder_open", "folder_output", "folder_pen", "folder_plus", "folder_root", "folder_search_2", "folder_search", "folder_symlink", "folder_sync", "folder_tree", "folder_up", "folder_x", "folder", "folders", "footprints", "forklift", "form", "forward", "frame", "framer", "frown", "fuel", "fullscreen", "funnel_plus", "funnel_x", "funnel", "gallery_horizontal_end", "gallery_horizontal", "gallery_thumbnails", "gallery_vertical_end", "gallery_vertical", "gamepad_2", "gamepad_directional", "gamepad", "gantt_chart", "gauge", "gavel", "gem", "georgian_lari", "ghost", "gift", "git_branch_minus", "git_branch_plus", "git_branch", "git_commit_horizontal", "git_commit_vertical", "git_compare_arrows", "git_compare", "git_fork", "git_graph", "git_merge_conflict", "git_merge", "git_pull_request_arrow", "git_pull_request_closed", "git_pull_request_create_arrow", "git_pull_request_create", "git_pull_request_draft", "git_pull_request", "github", "gitlab", "glass_water", "glasses", "globe_lock", "globe_off", "globe_x", "globe", "goal", "gpu", "grab", "graduation_cap", "grape", "grid_2x_2_check", "grid_2x_2_plus", "grid_2x_2_x", "grid_2x_2", "grid_2x2", "grid_3x_3", "grid_3x2", "grid_3x3", "grip_horizontal", "grip_vertical", "grip", "group", "guitar", "ham", "hamburger", "hammer", "hand_coins", "hand_fist", "hand_heart", "hand_helping", "hand_metal", "hand_platter", "hand", "handbag", "handshake", "hard_drive_download", "hard_drive_upload", "hard_drive", "hard_hat", "hash", "hat_glasses", "haze", "hd", "hdmi_port", "heading_1", "heading_2", "heading_3", "heading_4", "heading_5", "heading_6", "heading", "headphone_off", "headphones", "headset", "heart_crack", "heart_handshake", "heart_minus", "heart_off", "heart_plus", "heart_pulse", "heart", "heater", "helicopter", "hexagon", "highlighter", "history", "home", "hop_off", "hop", "hospital", "hotel", "hourglass", "house_heart", "house_plug", "house_plus", "house_wifi", "house", "ice_cream_bowl", "ice_cream_cone", "id_card_lanyard", "id_card", "image_down", "image_minus", "image_off", "image_play", "image_plus", "image_up", "image_upscale", "image", "images", "import", "inbox", "indent_decrease", "indent_increase", "indian_rupee", "infinity", "info", "inspection_panel", "instagram", "italic", "iteration_ccw", "iteration_cw", "japanese_yen", "joystick", "kanban", "kayak", "key_round", "key_square", "key", "keyboard_music", "keyboard_off", "keyboard", "lamp_ceiling", "lamp_desk", "lamp_floor", "lamp_wall_down", "lamp_wall_up", "lamp", "land_plot", "landmark", "languages", "laptop_minimal_check", "laptop_minimal", "laptop", "lasso_select", "lasso", "laugh", "layers_2", "layers_3", "layers_plus", "layers", "layout_dashboard", "layout_grid", "layout_list", "layout_panel_left", "layout_panel_top", "layout_template", "leaf", "leafy_green", "lectern", "lens_concave", "lens_convex", "letter_text", "library_big", "library", "life_buoy", "ligature", "lightbulb_off", "lightbulb", "line_chart", "line_dot_right_horizontal", "line_squiggle", "link_2_off", "link_2", "link", "linkedin", "list_check", "list_checks", "list_chevrons_down_up", "list_collapse", "list_end", "list_filter_plus", "list_filter", "list_minus", "list_music", "list_ordered", "list_plus", "list_restart", "list_start", "list_todo", "list_tree", "list_video", "list_x", "list", "loader_circle", "loader_pinwheel", "loader", "locate_fixed", "locate_off", "locate", "location_edit", "lock_keyhole_open", "lock_keyhole", "lock_open", "lock", "log_in", "log_out", "logs", "lollipop", "luggage", "magnet", "mail_check", "mail_minus", "mail_open", "mail_plus", "mail_question", "mail_search", "mail_warning", "mail_x", "mail", "mailbox", "mails", "map_minus", "map_pin_check_inside", "map_pin_check", "map_pin_house", "map_pin_minus_inside", "map_pin_minus", "map_pin_off", "map_pin_plus_inside", "map_pin_plus", "map_pin_x_inside", "map_pin_x", "map_pin", "map_pinned", "map_plus", "map", "mars_stroke", "martini", "maximize_2", "maximize", "medal", "megaphone_off", "megaphone", "meh", "memory_stick", "menu", "merge", "message_circle_check", "message_circle_code", "message_circle_dashed", "message_circle_heart", "message_circle_more", "message_circle_off", "message_circle_plus", "message_circle_question", "message_circle_reply", "message_circle_warning", "message_circle_x", "message_circle", "message_square_check", "message_square_code", "message_square_dashed", "message_square_diff", "message_square_dot", "message_square_heart", "message_square_lock", "message_square_more", "message_square_off", "message_square_plus", "message_square_quote", "message_square_reply", "message_square_share", "message_square_text", "message_square_warning", "message_square_x", "message_square", "messages_square", "metronome", "mic_off", "mic_vocal", "mic", "microchip", "microscope", "microwave", "milestone", "milk_off", "milk", "minimize_2", "minimize", "minus", "mirror_rectangular", "mirror_round", "monitor_check", "monitor_cloud", "monitor_cog", "monitor_dot", "monitor_down", "monitor_off", "monitor_pause", "monitor_play", "monitor_smartphone", "monitor_speaker", "monitor_stop", "monitor_up", "monitor_x", "monitor", "moon_star", "moon", "motorbike", "mountain_snow", "mountain", "mouse_left", "mouse_off", "mouse_pointer_2_off", "mouse_pointer_2", "mouse_pointer_ban", "mouse_pointer_click", "mouse_pointer", "mouse_right", "mouse", "move_3d", "move_diagonal_2", "move_diagonal", "move_down_left", "move_down_right", "move_down", "move_horizontal", "move_left", "move_right", "move_up_left", "move_up_right", "move_up", "move_vertical", "move", "music_2", "music_3", "music_4", "music", "navigation_2_off", "navigation_2", "navigation_off", "navigation", "network", "newspaper", "nfc", "non_binary", "notebook_pen", "notebook_tabs", "notebook_text", "notebook", "notepad_text_dashed", "notepad_text", "nut_off", "nut", "octagon_alert", "octagon_minus", "octagon_pause", "octagon_x", "octagon", "omega", "option", "orbit", "origami", "package_2", "package_check", "package_minus", "package_open", "package_plus", "package_search", "package_x", "package", "paint_bucket", "paint_roller", "paintbrush_2", "paintbrush_vertical", "paintbrush", "palette", "panda", "panel_bottom_close", "panel_bottom_dashed", "panel_bottom_open", "panel_bottom", "panel_left_close", "panel_left_dashed", "panel_left_open", "panel_left_right_dashed", "panel_left", "panel_right_close", "panel_right_dashed", "panel_right_open", "panel_right", "panel_top_bottom_dashed", "panel_top_close", "panel_top_dashed", "panel_top_open", "panel_top", "panels_left_bottom", "panels_right_bottom", "panels_top_left", "paperclip", "parentheses", "parking_meter", "party_popper", "pause", "paw_print", "pc_case", "pen_line", "pen_off", "pen_tool", "pen", "pencil_line", "pencil_off", "pencil_ruler", "pencil", "pentagon", "percent", "person_standing", "philippine_peso", "phone_call", "phone_forwarded", "phone_incoming", "phone_missed", "phone_off", "phone_outgoing", "phone", "pi", "piano", "pickaxe", "picture_in_picture_2", "picture_in_picture", "pie_chart", "piggy_bank", "pilcrow_left", "pilcrow_right", "pilcrow", "pill_bottle", "pill", "pin_off", "pin", "pipette", "pizza", "plane_landing", "plane_takeoff", "plane", "play", "plug_2", "plug_zap_2", "plug_zap", "plug", "plus", "pocket_knife", "pocket", "podcast", "pointer_off", "pointer", "popcorn", "popsicle", "pound_sterling", "power_off", "power", "presentation", "printer_check", "printer_x", "printer", "projector", "proportions", "puzzle", "pyramid", "qr_code", "quote", "rabbit", "radar", "radiation", "radical", "radio_receiver", "radio_tower", "radio", "radius", "rail_symbol", "rainbow", "rat", "ratio", "receipt_cent", "receipt_euro", "receipt_indian_rupee", "receipt_japanese_yen", "receipt_pound_sterling", "receipt_russian_ruble", "receipt_swiss_franc", "receipt_text", "receipt_turkish_lira", "receipt", "rectangle_circle", "rectangle_ellipsis", "rectangle_goggles", "rectangle_horizontal", "rectangle_vertical", "recycle", "redo_2", "redo_dot", "redo", "refresh_ccw_dot", "refresh_ccw", "refresh_cw_off", "refresh_cw", "refrigerator", "regex", "remove_formatting", "repeat_1", "repeat_2", "repeat", "replace_all", "replace", "reply_all", "reply", "rewind", "ribbon", "rocket", "rocking_chair", "roller_coaster", "rose", "rotate_3d", "rotate_ccw_key", "rotate_ccw_square", "rotate_ccw", "rotate_cw_square", "rotate_cw", "route_off", "route", "router", "rows_2", "rows_3", "rows_4", "rss", "ruler_dimension_line", "ruler", "russian_ruble", "sailboat", "salad", "sandwich", "satellite_dish", "satellite", "saudi_riyal", "save_all", "save_off", "save", "scale_3d", "scale", "scaling", "scan_barcode", "scan_eye", "scan_face", "scan_heart", "scan_line", "scan_qr_code", "scan_search", "scan_text", "scan", "scatter_chart", "school", "scissors_line_dashed", "scissors", "scooter", "screen_share_off", "screen_share", "scroll_text", "scroll", "search_alert", "search_check", "search_code", "search_slash", "search_x", "search", "section", "send_horizontal", "send_to_back", "send", "separator_horizontal", "separator_vertical", "server_cog", "server_crash", "server_off", "server", "settings_2", "settings", "shapes", "share_2", "share", "sheet", "shell", "shelving_unit", "shield_alert", "shield_ban", "shield_check", "shield_ellipsis", "shield_half", "shield_minus", "shield_off", "shield_plus", "shield_question", "shield_user", "shield_x", "shield", "ship_wheel", "ship", "shirt", "shopping_bag", "shopping_basket", "shopping_cart", "shovel", "shower_head", "shredder", "shrimp", "shrink", "shrub", "shuffle", "sigma", "signal_high", "signal_low", "signal_medium", "signal_zero", "signal", "signature", "signpost_big", "signpost", "siren", "skip_back", "skip_forward", "skull", "slack", "slash", "slice", "sliders_horizontal", "sliders_vertical", "smartphone_charging", "smartphone_nfc", "smartphone", "smile_plus", "smile", "snail", "snowflake", "soap_dispenser_droplet", "sofa", "solar_panel", "soup", "space", "spade", "sparkle", "sparkles", "speaker", "speech", "spell_check_2", "spell_check", "spline_pointer", "spline", "split", "spool", "spotlight", "spray_can", "sprout", "square_activity", "square_arrow_down_left", "square_arrow_down_right", "square_arrow_down", "square_arrow_left", "square_arrow_out_down_left", "square_arrow_out_down_right", "square_arrow_out_up_left", "square_arrow_out_up_right", "square_arrow_right_enter", "square_arrow_right_exit", "square_arrow_right", "square_arrow_up_left", "square_arrow_up_right", "square_arrow_up", "square_asterisk", "square_bottom_dashed_scissors", "square_chart_gantt", "square_check_big", "square_check", "square_chevron_down", "square_chevron_left", "square_chevron_right", "square_chevron_up", "square_code", "square_dashed_bottom_code", "square_dashed_bottom", "square_dashed_kanban", "square_dashed_mouse_pointer", "square_dashed_top_solid", "square_dashed", "square_divide", "square_dot", "square_equal", "square_function", "square_gantt_chart", "square_kanban", "square_library", "square_m", "square_menu", "square_minus", "square_mouse_pointer", "square_parking_off", "square_parking", "square_pen", "square_percent", "square_pi", "square_pilcrow", "square_play", "square_plus", "square_power", "square_radical", "square_round_corner", "square_scissors", "square_sigma", "square_slash", "square_split_horizontal", "square_split_vertical", "square_square", "square_stack", "square_terminal", "square_user_round", "square_user", "square_x", "square_centerline_dashed_horizontal", "square_centerline_dashed_vertical", "square", "squares_exclude", "squares_intersect", "squares_subtract", "squares_unite", "squircle_dashed", "squircle", "squirrel", "stamp", "star_half", "star_off", "star", "step_back", "step_forward", "stethoscope", "sticker", "sticky_note", "stone", "store", "stretch_horizontal", "stretch_vertical", "strikethrough", "subscript", "sun_dim", "sun_medium", "sun_moon", "sun_snow", "sun", "sunrise", "sunset", "superscript", "swatch_book", "swiss_franc", "switch_camera", "sword", "swords", "syringe", "table_2", "table_cells_merge", "table_cells_split", "table_columns_split", "table_of_contents", "table_properties", "table_rows_split", "table", "tablet_smartphone", "tablet", "tablets", "tag", "tags", "tally_1", "tally_2", "tally_3", "tally_4", "tally_5", "tangent", "target", "telescope", "tent_tree", "tent", "terminal", "test_tube_diagonal", "test_tube", "test_tubes", "text_cursor_input", "text_cursor", "text_quote", "text_search", "text_select", "text", "theater", "thermometer_snowflake", "thermometer_sun", "thermometer", "thumbs_down", "thumbs_up", "ticket_check", "ticket_minus", "ticket_percent", "ticket_plus", "ticket_slash", "ticket_x", "ticket", "tickets_plane", "tickets", "timer_off", "timer_reset", "timer", "toggle_left", "toggle_right", "toilet", "tool_case", "toolbox", "tornado", "torus", "touchpad_off", "touchpad", "towel_rack", "tower_control", "toy_brick", "tractor", "traffic_cone", "train_front_tunnel", "train_front", "train_track", "tram_front", "transgender", "trash_2", "trash", "tree_deciduous", "tree_palm", "tree_pine", "trees", "trello", "trending_down", "trending_up_down", "trending_up", "triangle_alert", "triangle_dashed", "triangle_right", "triangle", "trophy", "truck_electric", "truck", "turkish_lira", "turntable", "turtle", "tv_2", "tv_minimal_play", "tv_minimal", "tv", "twitch", "twitter", "type_outline", "type", "umbrella_off", "umbrella", "underline", "undo_2", "undo_dot", "undo", "unfold_horizontal", "unfold_vertical", "ungroup", "university", "unlink_2", "unlink", "unplug", "upload", "usb", "user_check", "user_cog", "user_key", "user_lock", "user_minus", "user_pen", "user_plus", "user_round_check", "user_round_cog", "user_round_key", "user_round_minus", "user_round_pen", "user_round_plus", "user_round_search", "user_round_x", "user_round", "user_search", "user_star", "user_x", "user", "users_round", "users", "utensils_crossed", "utensils", "utility_pole", "van", "variable", "vault", "vector_square", "vegan", "venetian_mask", "venus_and_mars", "venus", "vibrate_off", "vibrate", "video_off", "video", "videotape", "view", "voicemail", "volleyball", "volume_1", "volume_2", "volume_off", "volume_x", "volume", "vote", "wallet_cards", "wallet_minimal", "wallet", "wallpaper", "wand_sparkles", "wand", "warehouse", "washing_machine", "watch", "waves_arrow_down", "waves_arrow_up", "waves_ladder", "waves", "waypoints", "webcam", "webhook_off", "webhook", "weight_tilde", "weight", "wheat_off", "wheat", "whole_word", "wifi_cog", "wifi_high", "wifi_low", "wifi_off", "wifi_pen", "wifi_sync", "wifi_zero", "wifi", "wind_arrow_down", "wind", "wine_off", "wine", "workflow", "worm", "wrap_text", "wrench", "x_line_top", "x", "youtube", "zap_off", "zap", "zoom_in", "zoom_out", ] # The default transformation of some icon names doesn't match how the # icons are exported from Lucide. Manual overrides can go here. LUCIDE_ICON_MAPPING_OVERRIDE = { "grid_3x2": "Grid3x2Icon", "fingerprint": "FingerprintPattern", } ================================================ FILE: reflex/components/markdown/__init__.py ================================================ """Markdown components.""" from .markdown import markdown as markdown ================================================ FILE: reflex/components/markdown/markdown.py ================================================ """Markdown component.""" from __future__ import annotations import dataclasses import textwrap from collections.abc import Callable, Sequence from functools import lru_cache from hashlib import md5 from types import SimpleNamespace from typing import Any from reflex.components.component import ( BaseComponent, Component, ComponentNamespace, CustomComponent, field, ) from reflex.components.el.elements.typography import Div from reflex.components.tags.tag import Tag from reflex.utils import console from reflex.utils.imports import ImportDict, ImportTypes, ImportVar from reflex.vars.base import LiteralVar, Var, VarData from reflex.vars.function import ArgsFunctionOperation, DestructuredArg from reflex.vars.number import ternary_operation from reflex.vars.sequence import LiteralArrayVar # Special vars used in the component map. _CHILDREN = Var(_js_expr="children", _var_type=str) _PROPS = Var(_js_expr="props") _PROPS_SPREAD = Var(_js_expr="...props") _REST = Var(_js_expr="rest") _REST_SPREAD = Var(_js_expr="...rest") _MOCK_ARG = Var(_js_expr="", _var_type=str) _LANGUAGE = Var(_js_expr="_language", _var_type=str) class Plugin(SimpleNamespace): """Create new remark/rehype plugin or access pre-wrapped plugins.""" @staticmethod def create( package: str, tag: str, additional_imports: dict[str, ImportTypes] | None = None, **import_var_kwargs, ) -> Var: """Create a plugin Var. Args: package: The package to import the plugin from. tag: The imported identifier. additional_imports: Additional imports to include in the VarData, such as CSS. **import_var_kwargs: Additional kwargs to pass to the ImportVar. Returns: The plugin Var. """ import_var_kwargs.setdefault("is_default", True) return Var( _js_expr=tag, _var_data=VarData( imports={ package: ImportVar( tag=tag, **import_var_kwargs, ), **(additional_imports or {}), } ), ) __call__ = create math = create("remark-math@6.0.0", "remarkMath") gfm = create("remark-gfm@4.0.1", "remarkGfm") unwrap_images = create("rehype-unwrap-images@1.0.0", "rehypeUnwrapImages") katex = create( "rehype-katex@7.0.1", "rehypeKatex", additional_imports={ "": "katex/dist/katex.min.css", }, ) raw = create("rehype-raw@7.0.0", "rehypeRaw") _undefined = Var(_js_expr="() => undefined") def _h1(value: object): from reflex.components.radix.themes.typography.heading import Heading return Heading.create(value, as_="h1", size="6", margin_y="0.5em") def _h2(value: object): from reflex.components.radix.themes.typography.heading import Heading return Heading.create(value, as_="h2", size="5", margin_y="0.5em") def _h3(value: object): from reflex.components.radix.themes.typography.heading import Heading return Heading.create(value, as_="h3", size="4", margin_y="0.5em") def _h4(value: object): from reflex.components.radix.themes.typography.heading import Heading return Heading.create(value, as_="h4", size="3", margin_y="0.5em") def _h5(value: object): from reflex.components.radix.themes.typography.heading import Heading return Heading.create(value, as_="h5", size="2", margin_y="0.5em") def _h6(value: object): from reflex.components.radix.themes.typography.heading import Heading return Heading.create(value, as_="h6", size="1", margin_y="0.5em") def _p(value: object): from reflex.components.radix.themes.typography.text import Text return Text.create(value, margin_y="1em") def _ul(value: object): from reflex.components.radix.themes.layout.list import UnorderedList return UnorderedList.create(value, margin_y="1em") def _ol(value: object): from reflex.components.radix.themes.layout.list import OrderedList return OrderedList.create(value, margin_y="1em") def _li(value: object): from reflex.components.radix.themes.layout.list import ListItem return ListItem.create(value, margin_y="0.5em") def _a(value: object): from reflex.components.radix.themes.typography.link import Link return Link.create(value) def _code(value: object): from reflex.components.radix.themes.typography.code import Code return Code.create(value) def _codeblock(value: object, **props): from reflex.components.datadisplay.code import CodeBlock return CodeBlock.create(value, margin_y="1em", wrap_long_lines=True, **props) # Component Mapping @lru_cache def get_base_component_map() -> dict[str, Callable]: """Get the base component map. Returns: The base component map. """ return { "h1": _h1, "h2": _h2, "h3": _h3, "h4": _h4, "h5": _h5, "h6": _h6, "p": _p, "ul": _ul, "ol": _ol, "li": _li, "a": _a, "code": _code, "pre": _codeblock, } @dataclasses.dataclass() class MarkdownComponentMap: """Mixin class for handling custom component maps in Markdown components.""" _explicit_return: bool = dataclasses.field(default=False) @classmethod def get_component_map_custom_code(cls) -> Var: """Get the custom code for the component map. Returns: The custom code for the component map. """ return Var("") @classmethod def create_map_fn_var( cls, fn_body: Var | None = None, fn_args: Sequence[str] | None = None, explicit_return: bool | None = None, var_data: VarData | None = None, ) -> Var: """Create a function Var for the component map. Args: fn_body: The formatted component as a string. fn_args: The function arguments. explicit_return: Whether to use explicit return syntax. var_data: The var data for the function. Returns: The function Var for the component map. """ fn_args = fn_args or cls.get_fn_args() fn_body = fn_body if fn_body is not None else cls.get_fn_body() explicit_return = explicit_return or cls._explicit_return return ArgsFunctionOperation.create( args_names=(DestructuredArg(fields=tuple(fn_args)),), return_expr=fn_body, explicit_return=explicit_return, _var_data=var_data, ) @classmethod def get_fn_args(cls) -> Sequence[str]: """Get the function arguments for the component map. Returns: The function arguments as a list of strings. """ return ["node", _CHILDREN._js_expr, _PROPS_SPREAD._js_expr] @classmethod def get_fn_body(cls) -> Var: """Get the function body for the component map. Returns: The function body as a string. """ return Var(_js_expr="undefined", _var_type=None) class Markdown(Component): """A markdown component.""" library = "react-markdown@10.1.0" tag = "ReactMarkdown" is_default = True # The component map from a tag to a lambda that creates a component. component_map: dict[str, Any] = field( default_factory=dict, is_javascript_property=False ) # The hash of the component map, generated at create() time. component_map_hash: str = field(default="", is_javascript_property=False) # Remark plugins to use when rendering the content. Provide (plugin, options) if the plugin requires options. remark_plugins: Var[Sequence[Var | tuple[Var, Var]]] # Rehype (HTML processor) plugins to use when rendering the content. Provide (plugin, options) if the plugin requires options. rehype_plugins: Var[Sequence[Var | tuple[Var, Var]]] @classmethod def create( cls, *children, **props, ) -> Component: """Create a markdown component. Args: *children: The children of the component. **props: The properties of the component. Returns: The markdown component. Raises: ValueError: If the children are not valid. """ if len(children) != 1 or not isinstance(children[0], (str, Var)): msg = "Markdown component must have exactly one child containing the markdown source." raise ValueError(msg) # Update the base component map with the custom component map. component_map = {**get_base_component_map(), **props.pop("component_map", {})} if "codeblock" in component_map: console.deprecate( feature_name="'codeblock' in component_map", reason="Use 'pre' instead of 'codeblock' to customize code block rendering in markdown", deprecation_version="0.8.25", removal_version="0.9.0", ) component_map["pre"] = component_map.pop("codeblock") # Get the markdown source. src = children[0] # Dedent the source. if isinstance(src, str): src = textwrap.dedent(src) # Create the component. return super().create( src, component_map=component_map, component_map_hash=cls._component_map_hash(component_map), **props, ) def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the markdown component. Returns: The imports for the markdown component. """ return [ *[ component(_MOCK_ARG)._get_all_imports() for component in self.component_map.values() ], *( [codeblock_var_data.old_school_imports()] if ( codeblock_var_data := self._get_codeblock_fn_var()._get_all_var_data() ) is not None else [] ), ] def _get_tag_map_fn_var(self, tag: str) -> Var: return self._get_map_fn_var_from_children(self.get_component(tag), tag) def format_component_map(self) -> dict[str, Var]: """Format the component map for rendering. Returns: The formatted component map. """ components = { tag: self._get_tag_map_fn_var(tag) for tag in self.component_map if tag != "pre" } # Special handling for code blocks to extract the language. components["pre"] = self._get_codeblock_fn_var() return components def _get_codeblock_fn_var(self) -> Var: """Get the function variable for codeblock. This function creates a Var that represents a function to handle both code blocks in markdown. Returns: The Var for pre code. """ # Get any custom code from the code block "pre" component. custom_code_list = self._get_map_fn_custom_code_from_children( self.get_component("pre") ) var_data = VarData.merge(*[ code._get_all_var_data() for code in custom_code_list if isinstance(code, Var) ]) codeblock_custom_code = "\n".join(map(str, custom_code_list)) # Format the code to handle code block with language extraction. formatted_code = f""" const {{node: childNode, className, children: components, {_PROPS_SPREAD._js_expr}}} = {_REST._js_expr}.children.props; const {_CHILDREN._js_expr} = String(Array.isArray(components) ? components.join('\\n') : components).replace(/\\n$/, ''); const match = (className || '').match(/language-(?.*)/); let {_LANGUAGE!s} = match ? match[1] : ''; {codeblock_custom_code}; return {self.format_component("pre", language=_LANGUAGE)}; """.replace("\n", " ") return MarkdownComponentMap.create_map_fn_var( fn_body=Var(_js_expr=formatted_code), fn_args=["node", _REST_SPREAD._js_expr], explicit_return=True, var_data=var_data, ) def get_component(self, tag: str, **props) -> Component: """Get the component for a tag and props. Args: tag: The tag of the component. **props: The props of the component. Returns: The component. Raises: ValueError: If the tag is invalid. """ # Check the tag is valid. if tag not in self.component_map: msg = f"No markdown component found for tag: {tag}." raise ValueError(msg) # If the children are set as a prop, don't pass them as children. children = [_CHILDREN] if props.get("children") is None else [] # Get the component. return self.component_map[tag](*children, **props).set(special_props=[_PROPS]) def format_component(self, tag: str, **props) -> str: """Format a component for rendering in the component map. Args: tag: The tag of the component. **props: Extra props to pass to the component function. Returns: The formatted component. """ return str(self.get_component(tag, **props)).replace("\n", "") def _get_map_fn_var_from_children(self, component: Component, tag: str) -> Var: """Create a function Var for the component map for the specified tag. Args: component: The component to check for custom code. tag: The tag of the component. Returns: The function Var for the component map. """ formatted_component = Var( _js_expr=f"({self.format_component(tag)})", _var_type=str ) if isinstance(component, MarkdownComponentMap): return component.create_map_fn_var(fn_body=formatted_component) # fallback to the default fn Var creation if the component is not a MarkdownComponentMap. return MarkdownComponentMap.create_map_fn_var(fn_body=formatted_component) def _get_map_fn_custom_code_from_children( self, component: BaseComponent ) -> list[str | Var]: """Recursively get markdown custom code from children components. Args: component: The component to check for custom code. Returns: A list of markdown custom code strings. """ custom_code_list: list[str | Var] = [] if isinstance(component, MarkdownComponentMap): custom_code_list.append(component.get_component_map_custom_code()) # If the component is a custom component(rx.memo), obtain the underlining # component and get the custom code from the children. if isinstance(component, CustomComponent): custom_code_list.extend( self._get_map_fn_custom_code_from_children( component.component_fn(*component.get_prop_vars()) ) ) elif isinstance(component, Component): for child in component.children: custom_code_list.extend( self._get_map_fn_custom_code_from_children(child) ) return custom_code_list @staticmethod def _component_map_hash(component_map: dict) -> str: inp = str({ tag: ( f"{component.__module__}.{component.__qualname__}" if ( "<" not in component.__name__ ) # simple way to check against lambdas else component(_MOCK_ARG) ) for tag, component in component_map.items() }).encode() return md5(inp).hexdigest() def _get_component_map_name(self) -> str: return f"ComponentMap_{self.component_map_hash}" def _get_custom_code(self) -> str | None: hooks = {} from reflex.compiler.templates import _render_hooks for component_factory in self.component_map.values(): comp = component_factory(_MOCK_ARG) hooks.update(comp._get_all_hooks()) formatted_hooks = _render_hooks(hooks) return f""" function {self._get_component_map_name()} () {{ {formatted_hooks} return ( {LiteralVar.create(self.format_component_map())!s} ) }} """ def _render(self) -> Tag: return ( super() ._render() .add_props( components=Var(_js_expr=f"{self._get_component_map_name()}()"), ) .remove_props("componentMap", "componentMapHash") ) class MarkdownWrapper(Div): """A markdown component, with optional div-wrapping when style props are given.""" @classmethod def create( cls, *children, use_math: bool | Var[bool] = True, use_gfm: bool | Var[bool] = True, use_unwrap_images: bool | Var[bool] = True, use_katex: bool | Var[bool] = True, use_raw: bool | Var[bool] = True, **props, ) -> Component: """Create a markdown component. Args: *children: The children of the component. use_math: Whether to use the remark-math plugin. use_gfm: Whether to use the GitHub Flavored Markdown plugin. use_unwrap_images: Whether to use the unwrap images plugin. use_katex: Whether to use the KaTeX plugin. use_raw: Whether to use the raw HTML plugin. **props: The properties of the component. Returns: The markdown component or div wrapping markdown component. Raises: ValueError: If the children are not valid. """ # Assemble the plugin lists. builtin_remark_plugins = [] if isinstance(use_math, Var): builtin_remark_plugins.append( ternary_operation( use_math, markdown.plugin.math, markdown.plugin._undefined ) ) elif use_math: builtin_remark_plugins.append(markdown.plugin.math) if isinstance(use_gfm, Var): builtin_remark_plugins.append( ternary_operation( use_gfm, markdown.plugin.gfm, markdown.plugin._undefined ) ) elif use_gfm: builtin_remark_plugins.append(markdown.plugin.gfm) remark_plugins = LiteralArrayVar.create(builtin_remark_plugins) if (user_remark_plugins := props.pop("remark_plugins", None)) is not None: if not isinstance(user_remark_plugins, Var): user_remark_plugins = Var.create(user_remark_plugins) remark_plugins = remark_plugins + user_remark_plugins.to(list) builtin_rehype_plugins = [] if isinstance(use_katex, Var): builtin_rehype_plugins.append( ternary_operation( use_katex, markdown.plugin.katex, markdown.plugin._undefined ) ) elif use_katex: builtin_rehype_plugins.append(markdown.plugin.katex) if isinstance(use_raw, Var): builtin_rehype_plugins.append( ternary_operation( use_raw, markdown.plugin.raw, markdown.plugin._undefined ) ) elif use_raw: builtin_rehype_plugins.append(markdown.plugin.raw) if isinstance(use_unwrap_images, Var): builtin_rehype_plugins.append( ternary_operation( use_unwrap_images, markdown.plugin.unwrap_images, markdown.plugin._undefined, ) ) elif use_unwrap_images: builtin_rehype_plugins.append(markdown.plugin.unwrap_images) rehype_plugins = LiteralArrayVar.create(builtin_rehype_plugins) if (user_rehype_plugins := props.pop("rehype_plugins", None)) is not None: if not isinstance(user_rehype_plugins, Var): user_rehype_plugins = Var.create(user_rehype_plugins) rehype_plugins = rehype_plugins + user_rehype_plugins.to(list) return super().create( Markdown.create( *children, component_map=props.pop("component_map", {}), remark_plugins=remark_plugins.to(list[Var | tuple[Var, Var]]), rehype_plugins=rehype_plugins.to(list[Var | tuple[Var, Var]]), ), **props, ) class MarkdownNamespace(ComponentNamespace): """A namespace for markdown components.""" __call__ = staticmethod(MarkdownWrapper.create) root = staticmethod(Markdown.create) plugin = Plugin() markdown = MarkdownNamespace() ================================================ FILE: reflex/components/moment/__init__.py ================================================ """Moment.js component.""" from .moment import Moment, MomentDelta moment = Moment.create ================================================ FILE: reflex/components/moment/moment.py ================================================ """Moment component for humanized date rendering.""" import dataclasses from datetime import date, datetime, time, timedelta from reflex.components.component import NoSSRComponent from reflex.event import EventHandler, passthrough_event_spec from reflex.utils.imports import ImportDict from reflex.vars.base import LiteralVar, Var @dataclasses.dataclass(frozen=True) class MomentDelta: """A delta used for add/subtract prop in Moment.""" years: int | None = dataclasses.field(default=None) quarters: int | None = dataclasses.field(default=None) months: int | None = dataclasses.field(default=None) weeks: int | None = dataclasses.field(default=None) days: int | None = dataclasses.field(default=None) hours: int | None = dataclasses.field(default=None) minutes: int | None = dataclasses.field(default=None) seconds: int | None = dataclasses.field(default=None) milliseconds: int | None = dataclasses.field(default=None) class Moment(NoSSRComponent): """The Moment component.""" tag: str | None = "Moment" is_default = True library: str | None = "react-moment@1.2.2" lib_dependencies: list[str] = ["moment@2.30.1"] # How often the date update (how often time update / 0 to disable). interval: Var[int] # Formats the date according to the given format string. format: Var[str] # When formatting duration time, the largest-magnitude tokens are automatically trimmed when they have no value. trim: Var[bool] # Use the parse attribute to tell moment how to parse the given date when non-standard. parse: Var[str] # Add a delta to the base date (keys are "years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds") add: Var[MomentDelta] # Subtract a delta to the base date (keys are "years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds") subtract: Var[MomentDelta] # Displays the date as the time from now, e.g. "5 minutes ago". from_now: Var[bool] # Displays the relative time in a short format using abbreviated units (e.g., "1h", "2d", "3mo", "1y" instead of "1 hour ago", "2 days ago", etc.). from_now_short: Var[bool] # Setting fromNowDuring will display the relative time as with fromNow but just during its value in milliseconds, after that format will be used instead. from_now_during: Var[int] # Similar to fromNow, but gives the opposite interval. to_now: Var[bool] # Adds a title attribute to the element with the complete date. with_title: Var[bool] # How the title date is formatted when using the withTitle attribute. title_format: Var[str] # Show the different between this date and the rendered child. diff: Var[str] # Display the diff as decimal. decimal: Var[bool] # Display the diff in given unit. unit: Var[str] # Shows the duration (elapsed time) between two dates. duration property should be behind date property time-wise. duration: Var[str] # The date to display (also work if passed as children). date: Var[str | datetime | date | time | timedelta] # Shows the duration (elapsed time) between now and the provided datetime. duration_from_now: Var[bool] # Tells Moment to parse the given date value as a unix timestamp. unix: Var[bool] # Outputs the result in local time. local: Var[bool] # Display the date in the given timezone. tz: Var[str] # The locale to use when rendering. locale: Var[str] # Fires when the date changes. on_change: EventHandler[passthrough_event_spec(str)] def add_imports(self) -> ImportDict: """Add the imports for the Moment component. Returns: The import dict for the component. """ imports = {} if isinstance(self.locale, LiteralVar): imports[""] = f"moment/locale/{self.locale._var_value}" elif self.locale is not None: # If the user is using a variable for the locale, we can't know the # value at compile time so import all locales available. imports[""] = "moment/min/locales" if self.tz is not None: imports["moment-timezone"] = "" return imports ================================================ FILE: reflex/components/plotly/__init__.py ================================================ """Plotly components.""" from reflex.components.component import ComponentNamespace from .plotly import ( Plotly, PlotlyBasic, PlotlyCartesian, PlotlyFinance, PlotlyGeo, PlotlyGl2d, PlotlyGl3d, PlotlyMapbox, PlotlyStrict, ) class PlotlyNamespace(ComponentNamespace): """Plotly namespace.""" __call__ = Plotly.create basic = PlotlyBasic.create cartesian = PlotlyCartesian.create geo = PlotlyGeo.create gl2d = PlotlyGl2d.create gl3d = PlotlyGl3d.create finance = PlotlyFinance.create mapbox = PlotlyMapbox.create strict = PlotlyStrict.create plotly = PlotlyNamespace() ================================================ FILE: reflex/components/plotly/plotly.py ================================================ """Component for displaying a plotly graph.""" from __future__ import annotations from typing import TYPE_CHECKING, Any, TypedDict, TypeVar from reflex.components.component import Component, NoSSRComponent from reflex.components.core.cond import color_mode_cond from reflex.event import EventHandler, no_args_event_spec from reflex.utils import console from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import LiteralVar, Var try: from plotly.graph_objs import Figure from plotly.graph_objs.layout import Template except ImportError: console.warn("Plotly is not installed. Please run `pip install plotly`.") if not TYPE_CHECKING: Figure = Any Template = Any def _event_points_data_signature(e0: Var) -> tuple[Var[list[Point]]]: """For plotly events with event data containing a point array. Args: e0: The event data. Returns: The event data and the extracted points. """ return (Var(_js_expr=f"extractPoints({e0}?.points)"),) T = TypeVar("T") ItemOrList = T | list[T] class BBox(TypedDict): """Bounding box for a point in a plotly graph.""" x0: float | int | None x1: float | int | None y0: float | int | None y1: float | int | None z0: float | int | None z1: float | int | None class Point(TypedDict): """A point in a plotly graph.""" x: float | int | None y: float | int | None z: float | int | None lat: float | int | None lon: float | int | None curveNumber: int | None pointNumber: int | None pointNumbers: list[int] | None pointIndex: int | None markerColor: ItemOrList[ItemOrList[float | int | str | None]] | None markerSize: ItemOrList[ItemOrList[float | int | None,]] | None bbox: BBox | None class Plotly(NoSSRComponent): """Display a plotly graph.""" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js@3.4.0"] tag = "Plot" is_default = True # The figure to display. This can be a plotly figure or a plotly data json. data: Var[Figure] # The layout of the graph. layout: Var[dict] # The template for visual appearance of the graph. template: Var[Template] # The config of the graph. config: Var[dict] # If true, the graph will resize when the window is resized. use_resize_handler: Var[bool] = LiteralVar.create(True) # Fired after the plot is redrawn. on_after_plot: EventHandler[no_args_event_spec] # Fired after the plot was animated. on_animated: EventHandler[no_args_event_spec] # Fired while animating a single frame (does not currently pass data through). on_animating_frame: EventHandler[no_args_event_spec] # Fired when an animation is interrupted (to start a new animation for example). on_animation_interrupted: EventHandler[no_args_event_spec] # Fired when the plot is responsively sized. on_autosize: EventHandler[no_args_event_spec] # Fired whenever mouse moves over a plot. on_before_hover: EventHandler[no_args_event_spec] # Fired when a plotly UI button is clicked. on_button_clicked: EventHandler[no_args_event_spec] # Fired when the plot is clicked. on_click: EventHandler[_event_points_data_signature] # Fired when a selection is cleared (via double click). on_deselect: EventHandler[no_args_event_spec] # Fired when the plot is double clicked. on_double_click: EventHandler[no_args_event_spec] # Fired when a plot element is hovered over. on_hover: EventHandler[_event_points_data_signature] # Fired after the plot is laid out (zoom, pan, etc). on_relayout: EventHandler[no_args_event_spec] # Fired while the plot is being laid out. on_relayouting: EventHandler[no_args_event_spec] # Fired after the plot style is changed. on_restyle: EventHandler[no_args_event_spec] # Fired after the plot is redrawn. on_redraw: EventHandler[no_args_event_spec] # Fired after selecting plot elements. on_selected: EventHandler[_event_points_data_signature] # Fired while dragging a selection. on_selecting: EventHandler[_event_points_data_signature] # Fired while an animation is occurring. on_transitioning: EventHandler[no_args_event_spec] # Fired when a transition is stopped early. on_transition_interrupted: EventHandler[no_args_event_spec] # Fired when a hovered element is no longer hovered. on_unhover: EventHandler[_event_points_data_signature] def add_imports(self) -> dict[str, str]: """Add imports for the plotly component. Returns: The imports for the plotly component. """ return { # For merging plotly data/layout/templates. "mergician@v2.0.2": "mergician" } def add_custom_code(self) -> list[str]: """Add custom codes for processing the plotly points data. Returns: Custom code snippets for the module level. """ return [ "const removeUndefined = (obj) => {Object.keys(obj).forEach(key => obj[key] === undefined && delete obj[key]); return obj}", """ const extractPoints = (points) => { if (!points) return []; return points.map(point => { const bbox = point.bbox ? removeUndefined({ x0: point.bbox.x0, x1: point.bbox.x1, y0: point.bbox.y0, y1: point.bbox.y1, z0: point.bbox.y0, z1: point.bbox.y1, }) : undefined; return removeUndefined({ x: point.x, y: point.y, z: point.z, lat: point.lat, lon: point.lon, curveNumber: point.curveNumber, pointNumber: point.pointNumber, pointNumbers: point.pointNumbers, pointIndex: point.pointIndex, markerColor: point['marker.color'], markerSize: point['marker.size'], bbox: bbox, }) }) } """, ] @classmethod def create(cls, *children, **props) -> Component: """Create the Plotly component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Plotly component. """ from plotly.graph_objs.layout import Template from plotly.io import templates responsive_template = color_mode_cond( light=LiteralVar.create(templates["plotly"]), dark=LiteralVar.create(templates["plotly_dark"]), ) if isinstance(responsive_template, Var): # Mark the conditional Var as a Template to avoid type mismatch responsive_template = responsive_template.to(Template) props.setdefault("template", responsive_template) return super().create(*children, **props) def _exclude_props(self) -> set[str]: # These props are handled specially in the _render function return {"data", "layout", "template"} def _render(self): tag = super()._render() figure = self.data.to(dict) if self.data is not None else Var.create({}) merge_dicts = [] # Data will be merged and spread from these dict Vars if self.layout is not None: # Why is this not a literal dict? Great question... it didn't work # reliably because of how _var_name_unwrapped strips the outer curly # brackets if any of the contained Vars depend on state. layout_dict = LiteralVar.create({"layout": self.layout}) merge_dicts.append(layout_dict) if self.template is not None: template_dict = LiteralVar.create({"layout": {"template": self.template}}) merge_dicts.append(template_dict._without_data()) if merge_dicts: tag = tag.set( special_props=[ *tag.special_props, # Merge all dictionaries and spread the result over props. Var( _js_expr=f"{{...mergician({figure!s}," f"{','.join(str(md) for md in merge_dicts)})}}", ), ] ) else: tag = tag.set( special_props=[ *tag.special_props, # Spread the figure dict over props, nothing to merge. Var(_js_expr=str(figure)), ] ) return tag CREATE_PLOTLY_COMPONENT: ImportDict = { "react-plotly.js": [ ImportVar( tag="createPlotlyComponent", is_default=True, package_path="/factory", ), ] } def dynamic_plotly_import(name: str, package: str) -> str: """Create a dynamic import for a plotly component. Args: name: The name of the component. package: The package path of the component. Returns: The dynamic import for the plotly component. """ library_import = f"import('{package}')" mod_import = ".then((mod) => createPlotlyComponent(mod))" return f""" const {name} = ClientSide(() => {library_import}{mod_import} ) """ class PlotlyBasic(Plotly): """Display a basic plotly graph.""" tag: str = "BasicPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-basic-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly basic component. Returns: The imports for the plotly basic component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly basic component. Returns: The dynamic imports for the plotly basic component. """ return dynamic_plotly_import(self.tag, "plotly.js-basic-dist-min") class PlotlyCartesian(Plotly): """Display a plotly cartesian graph.""" tag: str = "CartesianPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-cartesian-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly cartesian component. Returns: The imports for the plotly cartesian component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly cartesian component. Returns: The dynamic imports for the plotly cartesian component. """ return dynamic_plotly_import(self.tag, "plotly.js-cartesian-dist-min") class PlotlyGeo(Plotly): """Display a plotly geo graph.""" tag: str = "GeoPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-geo-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly geo component. Returns: The imports for the plotly geo component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly geo component. Returns: The dynamic imports for the plotly geo component. """ return dynamic_plotly_import(self.tag, "plotly.js-geo-dist-min") class PlotlyGl3d(Plotly): """Display a plotly 3d graph.""" tag: str = "Gl3dPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-gl3d-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly 3d component. Returns: The imports for the plotly 3d component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly 3d component. Returns: The dynamic imports for the plotly 3d component. """ return dynamic_plotly_import(self.tag, "plotly.js-gl3d-dist-min") class PlotlyGl2d(Plotly): """Display a plotly 2d graph.""" tag: str = "Gl2dPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-gl2d-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly 2d component. Returns: The imports for the plotly 2d component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly 2d component. Returns: The dynamic imports for the plotly 2d component. """ return dynamic_plotly_import(self.tag, "plotly.js-gl2d-dist-min") class PlotlyMapbox(Plotly): """Display a plotly mapbox graph.""" tag: str = "MapboxPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-mapbox-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly mapbox component. Returns: The imports for the plotly mapbox component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly mapbox component. Returns: The dynamic imports for the plotly mapbox component. """ return dynamic_plotly_import(self.tag, "plotly.js-mapbox-dist-min") class PlotlyFinance(Plotly): """Display a plotly finance graph.""" tag: str = "FinancePlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-finance-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly finance component. Returns: The imports for the plotly finance component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly finance component. Returns: The dynamic imports for the plotly finance component. """ return dynamic_plotly_import(self.tag, "plotly.js-finance-dist-min") class PlotlyStrict(Plotly): """Display a plotly strict graph.""" tag: str = "StrictPlotlyPlot" library = "react-plotly.js@2.6.0" lib_dependencies: list[str] = ["plotly.js-strict-dist-min@3.4.0"] def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the plotly strict component. Returns: The imports for the plotly strict component. """ return CREATE_PLOTLY_COMPONENT def _get_dynamic_imports(self) -> str: """Get the dynamic imports for the plotly strict component. Returns: The dynamic imports for the plotly strict component. """ return dynamic_plotly_import(self.tag, "plotly.js-strict-dist-min") ================================================ FILE: reflex/components/props.py ================================================ """A class that holds props to be passed or applied to a component.""" from __future__ import annotations from collections.abc import Callable from dataclasses import _MISSING_TYPE, MISSING from typing import Any, TypeVar, get_args, get_origin from typing_extensions import dataclass_transform from reflex.components.field import BaseField, FieldBasedMeta from reflex.event import EventChain, args_specs_from_fields from reflex.utils import format from reflex.utils.exceptions import InvalidPropValueError from reflex.utils.serializers import serializer from reflex.utils.types import is_union from reflex.vars.object import LiteralObjectVar PROPS_FIELD_TYPE = TypeVar("PROPS_FIELD_TYPE") def _get_props_subclass(field_type: Any) -> type | None: """Extract the Props subclass from a field type annotation. Args: field_type: The type annotation to check. Returns: The Props subclass if found, None otherwise. """ from reflex.utils.types import typehint_issubclass # For direct class types, we can return them directly if they're Props subclasses if isinstance(field_type, type): return field_type if typehint_issubclass(field_type, PropsBase) else None # For Union types, check each union member if is_union(field_type): for arg in get_args(field_type): result = _get_props_subclass(arg) if result is not None: return result return None def _find_props_in_list_annotation(field_type: Any) -> type | None: """Find Props subclass within a list type annotation. Args: field_type: The type annotation to check (e.g., list[SomeProps] or list[SomeProps] | None). Returns: The Props subclass if found in a list annotation, None otherwise. """ origin = get_origin(field_type) if origin is list: args = get_args(field_type) if args: return _get_props_subclass(args[0]) # Handle Union types - check if any union member is a list if is_union(field_type): for arg in get_args(field_type): if arg is not type(None): # Skip None from Optional list_element = _find_props_in_list_annotation(arg) if list_element is not None: return list_element return None class PropsField(BaseField[PROPS_FIELD_TYPE]): """A field for a props class.""" def __init__( self, default: PROPS_FIELD_TYPE | _MISSING_TYPE = MISSING, default_factory: Callable[[], PROPS_FIELD_TYPE] | None = None, annotated_type: type[Any] | _MISSING_TYPE = MISSING, ) -> None: """Initialize the field. Args: default: The default value for the field. default_factory: The default factory for the field. annotated_type: The annotated type for the field. """ super().__init__(default, default_factory, annotated_type) self._name: str = "" # Will be set by metaclass @property def required(self) -> bool: """Check if the field is required (for Pydantic compatibility). Returns: True if the field has no default value or factory. """ return self.default is MISSING and self.default_factory is None @property def name(self) -> str | None: """Field name (for Pydantic compatibility). Note: This is set by the metaclass when processing fields. Returns: The field name if set, None otherwise. """ return getattr(self, "_name", None) def get_default(self) -> Any: """Get the default value (for Pydantic compatibility). Returns: The default value for the field, or None if required. """ try: return self.default_value() except ValueError: # Field is required (no default) return None def __repr__(self) -> str: """Represent the field in a readable format. Returns: The string representation of the field. """ annotated_type_str = ( f", annotated_type={self.annotated_type!r}" if self.annotated_type is not MISSING else "" ) if self.default is not MISSING: return f"PropsField(default={self.default!r}{annotated_type_str})" return ( f"PropsField(default_factory={self.default_factory!r}{annotated_type_str})" ) def props_field( default: PROPS_FIELD_TYPE | _MISSING_TYPE = MISSING, default_factory: Callable[[], PROPS_FIELD_TYPE] | None = None, ) -> PROPS_FIELD_TYPE: """Create a field for a props class. Args: default: The default value for the field. default_factory: The default factory for the field. Returns: The field for the props class. Raises: ValueError: If both default and default_factory are specified. """ if default is not MISSING and default_factory is not None: msg = "cannot specify both default and default_factory" raise ValueError(msg) return PropsField( # pyright: ignore [reportReturnType] default=default, default_factory=default_factory, annotated_type=MISSING, ) @dataclass_transform(field_specifiers=(props_field,)) class PropsBaseMeta(FieldBasedMeta): """Meta class for PropsBase.""" @classmethod def _process_annotated_fields( cls, namespace: dict[str, Any], annotations: dict[str, Any], inherited_fields: dict[str, PropsField], ) -> dict[str, PropsField]: own_fields: dict[str, PropsField] = {} for key, annotation in annotations.items(): value = namespace.get(key, MISSING) if value is MISSING: # Field with only annotation, no default value field = PropsField(annotated_type=annotation, default=None) elif not isinstance(value, PropsField): # Field with default value field = PropsField(annotated_type=annotation, default=value) else: # Field is already a PropsField, update annotation field = PropsField( annotated_type=annotation, default=value.default, default_factory=value.default_factory, ) own_fields[key] = field return own_fields @classmethod def _create_field( cls, annotated_type: Any, default: Any = MISSING, default_factory: Callable[[], Any] | None = None, ) -> PropsField: return PropsField( annotated_type=annotated_type, default=default, default_factory=default_factory, ) @classmethod def _finalize_fields( cls, namespace: dict[str, Any], inherited_fields: dict[str, PropsField], own_fields: dict[str, PropsField], ) -> None: # Call parent implementation super()._finalize_fields(namespace, inherited_fields, own_fields) # Add Pydantic compatibility namespace["__fields__"] = namespace["_fields"] class PropsBase(metaclass=PropsBaseMeta): """Base for a class containing props that can be serialized as a JS object.""" def __init__(self, **kwargs): """Initialize the props with field values. Args: **kwargs: The field values to set. """ # Set field values from kwargs with nested object instantiation for key, value in kwargs.items(): field_info = self.get_fields().get(key) if field_info: field_type = field_info.annotated_type # Check if this field expects a specific Props type and we got a dict if isinstance(value, dict): props_class = _get_props_subclass(field_type) if props_class is not None: value = props_class(**value) # Check if this field expects a list of Props and we got a list of dicts elif isinstance(value, list): element_type = _find_props_in_list_annotation(field_type) if element_type is not None: # Convert each dict in the list to the appropriate Props class value = [ element_type(**item) if isinstance(item, dict) else item for item in value ] setattr(self, key, value) # Set default values for fields not provided for field_name, field in self.get_fields().items(): if field_name not in kwargs: if field.default is not MISSING: setattr(self, field_name, field.default) elif field.default_factory is not None: setattr(self, field_name, field.default_factory()) # Note: Fields with no default and no factory remain unset (required fields) # Convert EventHandler to EventChain args_specs = args_specs_from_fields(self.get_fields()) for handler_name, args_spec in args_specs.items(): if (handler := getattr(self, handler_name, None)) is not None: setattr( self, handler_name, EventChain.create( value=handler, args_spec=args_spec, key=handler_name, ), ) @classmethod def get_fields(cls) -> dict[str, Any]: """Get the fields of the object. Returns: The fields of the object. """ return getattr(cls, "_fields", {}) def json(self) -> str: """Convert the object to a json-like string. Vars will be unwrapped so they can represent actual JS var names and functions. Keys will be converted to camelCase. Returns: The object as a Javascript Object literal. """ return LiteralObjectVar.create({ format.to_camel_case(key): value for key, value in self.dict().items() }).json() def dict( self, exclude_none: bool = True, include: set[str] | None = None, exclude: set[str] | None = None, **kwargs, ): """Convert the object to a dictionary. Keys will be converted to camelCase. By default, None values are excluded (exclude_none=True). Args: exclude_none: Whether to exclude None values. include: Fields to include in the output. exclude: Fields to exclude from the output. **kwargs: Additional keyword arguments (for compatibility). Returns: The object as a dictionary. """ result = {} for field_name in self.get_fields(): if hasattr(self, field_name): value = getattr(self, field_name) # Apply include/exclude filters if include is not None and field_name not in include: continue if exclude is not None and field_name in exclude: continue # Apply exclude_none logic if exclude_none and value is None: continue # Recursively convert nested structures value = self._convert_to_camel_case( value, exclude_none, include, exclude ) # Convert key to camelCase camel_key = format.to_camel_case(field_name) result[camel_key] = value return result def _convert_to_camel_case( self, value: Any, exclude_none: bool = True, include: set[str] | None = None, exclude: set[str] | None = None, ) -> Any: """Recursively convert nested dictionaries and lists to camelCase. Args: value: The value to convert. exclude_none: Whether to exclude None values. include: Fields to include in the output. exclude: Fields to exclude from the output. Returns: The converted value with camelCase keys. """ if isinstance(value, PropsBase): # Convert nested PropsBase objects return value.dict( exclude_none=exclude_none, include=include, exclude=exclude ) if isinstance(value, dict): # Convert dictionary keys to camelCase return { format.to_camel_case(k): self._convert_to_camel_case( v, exclude_none, include, exclude ) for k, v in value.items() if not (exclude_none and v is None) } if isinstance(value, (list, tuple)): # Convert list/tuple items recursively return [ self._convert_to_camel_case(item, exclude_none, include, exclude) for item in value ] # Return primitive values as-is return value @serializer(to=dict) def serialize_props_base(value: PropsBase) -> dict: """Serialize a PropsBase instance. Unlike serialize_base, this preserves callables (lambdas) since they're needed for AG Grid and other components that process them on the frontend. Args: value: The PropsBase instance to serialize. Returns: Dictionary representation of the PropsBase instance. """ return value.dict() class NoExtrasAllowedProps(PropsBase): """A class that holds props to be passed or applied to a component with no extra props allowed.""" def __init__(self, component_name: str | None = None, **kwargs): """Initialize the props with validation. Args: component_name: The custom name of the component. kwargs: Kwargs to initialize the props. Raises: InvalidPropValueError: If invalid props are passed on instantiation. """ component_name = component_name or type(self).__name__ # Validate fields BEFORE setting them known_fields = set(self.__class__.get_fields().keys()) provided_fields = set(kwargs.keys()) invalid_fields = provided_fields - known_fields if invalid_fields: invalid_fields_str = ", ".join(invalid_fields) supported_props_str = ", ".join(f'"{field}"' for field in known_fields) msg = f"Invalid prop(s) {invalid_fields_str} for {component_name!r}. Supported props are {supported_props_str}" raise InvalidPropValueError(msg) # Use parent class initialization after validation super().__init__(**kwargs) ================================================ FILE: reflex/components/radix/__init__.py ================================================ """Namespace for components provided by @radix-ui packages.""" from __future__ import annotations from reflex import RADIX_MAPPING from reflex.utils import lazy_loader _SUBMODULES: set[str] = {"themes", "primitives"} _SUBMOD_ATTRS: dict[str, list[str]] = { "".join(k.split("components.radix.")[-1]): v for k, v in RADIX_MAPPING.items() } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/radix/primitives/__init__.py ================================================ """Radix primitive components (https://www.radix-ui.com/primitives).""" from __future__ import annotations from reflex import RADIX_PRIMITIVES_MAPPING from reflex.utils import lazy_loader _SUBMOD_ATTRS: dict[str, list[str]] = { "".join(k.split("components.radix.primitives.")[-1]): v for k, v in RADIX_PRIMITIVES_MAPPING.items() } | {"dialog": ["dialog"]} __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/radix/primitives/accordion.py ================================================ """Radix accordion components.""" from __future__ import annotations from collections.abc import Sequence from typing import Any, ClassVar, Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.core.colors import color from reflex.components.core.cond import cond from reflex.components.lucide.icon import Icon from reflex.components.radix.primitives.base import RadixPrimitiveComponent from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler from reflex.style import Style from reflex.vars import get_uuid_string_var from reflex.vars.base import LiteralVar, Var LiteralAccordionType = Literal["single", "multiple"] LiteralAccordionDir = Literal["ltr", "rtl"] LiteralAccordionOrientation = Literal["vertical", "horizontal"] LiteralAccordionVariant = Literal["classic", "soft", "surface", "outline", "ghost"] DEFAULT_ANIMATION_DURATION = 250 DEFAULT_ANIMATION_EASING = "cubic-bezier(0.87, 0, 0.13, 1)" def _inherited_variant_selector( variant: Var[LiteralAccordionVariant] | LiteralAccordionVariant, *selectors: str, ) -> str: """Create a multi CSS selector for targeting variant against the given selectors. Args: variant: The variant to target. selectors: The selectors to apply the variant to (default &) Returns: A CSS selector that is more specific on elements that directly set the variant. """ if not selectors: selectors = ("&",) # Prefer the `data-variant` that is set directly on the selector, # but also inherit the `data-variant` from any parent element. return ", ".join([ f"{selector}[data-variant='{variant}'], *:where([data-variant='{variant}']) {selector}" for selector in selectors ]) class AccordionComponent(RadixPrimitiveComponent): """Base class for all @radix-ui/accordion components.""" library = "@radix-ui/react-accordion@1.2.12" # The color scheme of the component. color_scheme: Var[LiteralAccentColor] # The variant of the component. variant: Var[LiteralAccordionVariant] def add_style(self): """Add style to the component.""" if self.color_scheme is not None: self.custom_attrs["data-accent-color"] = self.color_scheme if self.variant is not None: self.custom_attrs["data-variant"] = self.variant def _exclude_props(self) -> list[str]: return ["color_scheme", "variant"] def on_value_change(value: Var[str | list[str]]) -> tuple[Var[str | list[str]]]: """Handle the on_value_change event. Args: value: The value of the event. Returns: The value of the event. """ return (value,) class AccordionRoot(AccordionComponent): """An accordion component.""" tag = "Root" alias = "RadixAccordionRoot" # The type of accordion (single or multiple). type: Var[LiteralAccordionType] # The value of the item to expand. value: Var[str | Sequence[str]] # The default value of the item to expand. default_value: Var[str | Sequence[str]] # Whether or not the accordion is collapsible. collapsible: Var[bool] # Whether or not the accordion is disabled. disabled: Var[bool] # The reading direction of the accordion when applicable. dir: Var[LiteralAccordionDir] # The orientation of the accordion. orientation: Var[LiteralAccordionOrientation] # The radius of the accordion corners. radius: Var[LiteralRadius] # The time in milliseconds to animate open and close duration: Var[int] = LiteralVar.create(DEFAULT_ANIMATION_DURATION) # The easing function to use for the animation. easing: Var[str] = LiteralVar.create(DEFAULT_ANIMATION_EASING) # Whether to show divider lines between items. show_dividers: Var[bool] _valid_children: ClassVar[list[str]] = ["AccordionItem"] # Fired when the opened the accordions changes. on_value_change: EventHandler[on_value_change] def _exclude_props(self) -> list[str]: return [ *super()._exclude_props(), "radius", "duration", "easing", "show_dividers", ] def add_style(self): """Add style to the component. Returns: The style of the component. """ if self.radius is not None: self.custom_attrs["data-radius"] = self.radius if self.variant is None: # The default variant is classic self.custom_attrs["data-variant"] = "classic" style = { "border_radius": "var(--radius-4)", "box_shadow": f"0 2px 10px {color('black', 1, alpha=True)}", "&[data-variant='classic']": { "background_color": color("accent", 9), "box_shadow": f"0 2px 10px {color('black', 4, alpha=True)}", }, "&[data-variant='soft']": { "background_color": color("accent", 3), }, "&[data-variant='outline']": { "border": f"1px solid {color('accent', 6)}", }, "&[data-variant='surface']": { "border": f"1px solid {color('accent', 6)}", "background_color": "var(--accent-surface)", }, "&[data-variant='ghost']": { "background_color": "none", "box_shadow": "None", }, "--animation-duration": f"{self.duration}ms", "--animation-easing": self.easing, } if self.show_dividers is not None: style["--divider-px"] = cond(self.show_dividers, "1px", "0") else: style["&[data-variant='outline']"]["--divider-px"] = "1px" style["&[data-variant='surface']"]["--divider-px"] = "1px" return Style(style) class AccordionItem(AccordionComponent): """An accordion component.""" tag = "Item" alias = "RadixAccordionItem" # A unique identifier for the item. value: Var[str] # When true, prevents the user from interacting with the item. disabled: Var[bool] # The header of the accordion item. header: Var[Component | str] # The content of the accordion item. content: Var[Component | str | None] = Var.create(None) _valid_children: ClassVar[list[str]] = [ "AccordionHeader", "AccordionTrigger", "AccordionContent", ] _valid_parents: ClassVar[list[str]] = ["AccordionRoot"] @classmethod def create( cls, *children, **props, ) -> Component: """Create an accordion item. Args: *children: The list of children to use if header and content are not provided. **props: Additional properties to apply to the accordion item. Returns: The accordion item. """ header = props.pop("header", None) content = props.pop("content", None) # The item requires a value to toggle (use a random unique name if not provided). value = props.pop("value", get_uuid_string_var()) if "AccordionItem" not in ( cls_name := props.pop("class_name", "AccordionItem") ): cls_name = f"{cls_name} AccordionItem" color_scheme = props.get("color_scheme") variant = props.get("variant") if (header is not None) and (content is not None): children = [ AccordionHeader.create( AccordionTrigger.create( header, AccordionIcon.create( color_scheme=color_scheme, variant=variant, ), color_scheme=color_scheme, variant=variant, ), color_scheme=color_scheme, variant=variant, ), AccordionContent.create( content, color_scheme=color_scheme, variant=variant, ), ] return super().create(*children, value=value, **props, class_name=cls_name) def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ divider_style = f"var(--divider-px) solid {color('gray', 6, alpha=True)}" return { "overflow": "hidden", "width": "100%", "margin_top": "1px", "border_top": divider_style, "&:first-child": { "margin_top": 0, "border_top": 0, "border_top_left_radius": "var(--radius-4)", "border_top_right_radius": "var(--radius-4)", }, "&:last-child": { "border_bottom_left_radius": "var(--radius-4)", "border_bottom_right_radius": "var(--radius-4)", }, "&:focus-within": { "position": "relative", "z_index": 1, }, _inherited_variant_selector("ghost", "&:first-child"): { "border_radius": 0, "border_top": divider_style, }, _inherited_variant_selector("ghost", "&:last-child"): { "border_radius": 0, "border_bottom": divider_style, }, } def _exclude_props(self) -> list[str]: return ["header", "content"] class AccordionHeader(AccordionComponent): """An accordion component.""" tag = "Header" alias = "RadixAccordionHeader" @classmethod def create(cls, *children, **props) -> Component: """Create the Accordion header component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Accordion header Component. """ if "AccordionHeader" not in ( cls_name := props.pop("class_name", "AccordionHeader") ): cls_name = f"{cls_name} AccordionHeader" return super().create(*children, class_name=cls_name, **props) def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "display": "flex", # Reset some values to ensure consistent styling without tailwind reset. "margin": "0", } class AccordionTrigger(AccordionComponent): """An accordion component.""" tag = "Trigger" alias = "RadixAccordionTrigger" _memoization_mode = MemoizationMode(recursive=False) @classmethod def create(cls, *children, **props) -> Component: """Create the Accordion trigger component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Accordion trigger Component. """ if "AccordionTrigger" not in ( cls_name := props.pop("class_name", "AccordionTrigger") ): cls_name = f"{cls_name} AccordionTrigger" return super().create(*children, class_name=cls_name, **props) def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "color": color("accent", 11), "font_size": "1.1em", "line_height": 1, "justify_content": "space-between", "align_items": "center", "flex": 1, "display": "flex", "padding": "var(--space-3) var(--space-4)", "width": "100%", "box_shadow": f"0 var(--divider-px) 0 {color('gray', 6, alpha=True)}", "&[data-state='open'] > .AccordionChevron": { "transform": "rotate(180deg)", }, "&:hover": { "background_color": color("accent", 4), }, "& > .AccordionChevron": { "transition": "transform var(--animation-duration) var(--animation-easing)", }, _inherited_variant_selector("classic"): { "color": "var(--accent-contrast)", "&:hover": { "background_color": color("accent", 10), }, "& > .AccordionChevron": { "color": "var(--accent-contrast)", }, }, # Reset some values to ensure consistent styling without tailwind reset. "background": "none", "border": "none", } class AccordionIcon(Icon): """An accordion icon component.""" @classmethod def create(cls, *children, **props) -> Component: """Create the Accordion icon component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Accordion icon Component. """ if "AccordionChevron" not in ( cls_name := props.pop("class_name", "AccordionChevron") ): cls_name = f"{cls_name} AccordionChevron" return super().create(tag="chevron_down", class_name=cls_name, **props) SLIDE_DOWN = Var( r'keyframes({ from: { height: 0 }, to: { height: "var(--radix-accordion-content-height)" } })' ) SLIDE_UP = Var( r'keyframes({ from: { height: "var(--radix-accordion-content-height)" }, to: { height: 0 } })' ) class AccordionContent(AccordionComponent): """An accordion component.""" tag = "Content" alias = "RadixAccordionContent" def add_imports(self) -> dict: """Add imports to the component. Returns: The imports of the component. """ return {"@emotion/react": "keyframes"} @classmethod def create(cls, *children, **props) -> Component: """Create the Accordion content component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Accordion content Component. """ if "AccordionContent" not in ( cls_name := props.pop("class_name", "AccordionContent") ): cls_name = f"{cls_name} AccordionContent" return super().create(*children, class_name=cls_name, **props) def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ slide_down = SLIDE_DOWN.to(str) + Var.create( " var(--animation-duration) var(--animation-easing)", ) slide_up = SLIDE_UP.to(str) + Var.create( " var(--animation-duration) var(--animation-easing)", ) return { "overflow": "hidden", "color": color("accent", 11), "padding_x": "var(--space-4)", # Apply before and after content to avoid height animation jank. "&:before, &:after": { "content": "' '", "display": "block", "height": "var(--space-3)", }, "&[data-state='open']": {"animation": slide_down}, "&[data-state='closed']": {"animation": slide_up}, _inherited_variant_selector("classic"): { "color": "var(--accent-contrast)", }, } class Accordion(ComponentNamespace): """Accordion component.""" content = staticmethod(AccordionContent.create) header = staticmethod(AccordionHeader.create) item = staticmethod(AccordionItem.create) icon = staticmethod(AccordionIcon.create) root = staticmethod(AccordionRoot.create) trigger = staticmethod(AccordionTrigger.create) accordion = Accordion() ================================================ FILE: reflex/components/radix/primitives/base.py ================================================ """The base component for Radix primitives.""" from typing import Any from reflex.components.component import Component from reflex.components.tags.tag import Tag from reflex.utils import format from reflex.vars.base import Var class RadixPrimitiveComponent(Component): """Basic component for radix Primitives.""" # Change the default rendered element for the one passed as a child. as_child: Var[bool] class RadixPrimitiveComponentWithClassName(RadixPrimitiveComponent): """Basic component for radix Primitives with a class name prop.""" def _render(self) -> Tag: return ( super() ._render() .add_props( class_name=f"{format.to_title_case(self.tag or '')} {self.class_name or ''}" ) ) class RadixPrimitiveTriggerComponent(RadixPrimitiveComponent): """Base class for Trigger, Close, Cancel, and Accept components. These components trigger some action in an overlay component that depends on the on_click event, and thus if a child is provided and has on_click specified, it will overtake the internal action, unless it is wrapped in some inert component, in this case, a Flex. """ @classmethod def create(cls, *children: Any, **props: Any) -> Component: """Create a new RadixPrimitiveTriggerComponent instance. Args: children: The children of the component. props: The properties of the component. Returns: The new RadixPrimitiveTriggerComponent instance. """ from reflex.components.el.elements.typography import Div for child in children: if "on_click" in getattr(child, "event_triggers", {}): children = (Div.create(*children),) break return super().create(*children, **props) ================================================ FILE: reflex/components/radix/primitives/dialog.py ================================================ """Interactive components provided by @radix-ui/react-dialog.""" from typing import Any, ClassVar from reflex.components.component import ComponentNamespace from reflex.components.el import elements from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var from .base import RadixPrimitiveComponent, RadixPrimitiveTriggerComponent class DialogElement(RadixPrimitiveComponent): """Base class for all @radix-ui/react-dialog components.""" library = "@radix-ui/react-dialog@1.1.15" class DialogRoot(DialogElement): """Root component for Dialog.""" tag = "Root" alias = "RadixPrimitiveDialogRoot" # The controlled open state of the dialog. open: Var[bool] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] # The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # The modality of the dialog. When set to true, interaction with outside elements will be disabled and only dialog content will be visible to screen readers. modal: Var[bool] _valid_children: ClassVar[list[str]] = [ "DialogTrigger", "DialogPortal", ] class DialogPortal(DialogElement): """Portal component for Dialog.""" tag = "Portal" alias = "RadixPrimitiveDialogPortal" # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. If used on this part, it will be inherited by Dialog.Overlay and Dialog.Content. force_mount: Var[bool] # Specify a container element to portal the content into. container: Var[Any] _valid_parents: ClassVar[list[str]] = ["DialogRoot"] class DialogOverlay(DialogElement): """A layer that covers the inert portion of the view when the dialog is open.""" tag = "Overlay" alias = "RadixPrimitiveDialogOverlay" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. It inherits from Dialog.Portal. force_mount: Var[bool] _valid_parents: ClassVar[list[str]] = ["DialogPortal"] class DialogTrigger(DialogElement, RadixPrimitiveTriggerComponent): """Trigger an action or event, to open a Dialog modal.""" tag = "Trigger" alias = "RadixPrimitiveDialogTrigger" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] _memoization_mode = MemoizationMode(recursive=False) _valid_parents: ClassVar[list[str]] = ["DialogRoot"] class DialogContent(elements.Div, DialogElement): """Content component to display inside a Dialog modal.""" tag = "Content" alias = "RadixPrimitiveDialogContent" # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. It inherits from Dialog.Portal. force_mount: Var[bool] # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Fired when the dialog is opened. on_open_auto_focus: EventHandler[no_args_event_spec] # Fired when the dialog is closed. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the dialog. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when the pointer interacts outside the dialog. on_interact_outside: EventHandler[no_args_event_spec] _valid_parents: ClassVar[list[str]] = ["DialogPortal"] class DialogTitle(DialogElement): """Title component to display inside a Dialog modal.""" tag = "Title" alias = "RadixPrimitiveDialogTitle" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] class DialogDescription(DialogElement): """Description component to display inside a Dialog modal.""" tag = "Description" alias = "RadixPrimitiveDialogDescription" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] class DialogClose(DialogElement, RadixPrimitiveTriggerComponent): """Close button component to close an open Dialog modal.""" tag = "Close" alias = "RadixPrimitiveDialogClose" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] class Dialog(ComponentNamespace): """Dialog components namespace.""" root = __call__ = staticmethod(DialogRoot.create) portal = staticmethod(DialogPortal.create) trigger = staticmethod(DialogTrigger.create) title = staticmethod(DialogTitle.create) overlay = staticmethod(DialogOverlay.create) content = staticmethod(DialogContent.create) description = staticmethod(DialogDescription.create) close = staticmethod(DialogClose.create) dialog = Dialog() ================================================ FILE: reflex/components/radix/primitives/drawer.py ================================================ """Drawer components based on Radix primitives.""" # Based on Vaul: https://github.com/emilkowalski/vaul # Style based on https://ui.shadcn.com/docs/components/drawer from __future__ import annotations from collections.abc import Sequence from typing import Any, Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.radix.primitives.base import RadixPrimitiveComponent from reflex.components.radix.themes.base import Theme from reflex.components.radix.themes.layout.flex import Flex from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var class DrawerComponent(RadixPrimitiveComponent): """A Drawer component.""" library = "vaul@1.1.2" lib_dependencies: list[str] = ["@radix-ui/react-dialog@1.1.15"] LiteralDirectionType = Literal["top", "bottom", "left", "right"] class DrawerRoot(DrawerComponent): """The Root component of a Drawer, contains all parts of a drawer.""" tag = "Drawer.Root" alias = "Vaul" + tag # The open state of the drawer when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # Whether the drawer is open or not. open: Var[bool] # Fires when the drawer is opened or closed. on_open_change: EventHandler[passthrough_event_spec(bool)] # When `False`, it allows interaction with elements outside of the drawer without closing it. Defaults to `True`. modal: Var[bool] # Direction of the drawer. This adjusts the animations and the drag direction. Defaults to `"bottom"` direction: Var[LiteralDirectionType] # Gets triggered after the open or close animation ends, it receives an open argument with the open state of the drawer by the time the function was triggered. on_animation_end: EventHandler[passthrough_event_spec(bool)] # When `False`, dragging, clicking outside, pressing esc, etc. will not close the drawer. Use this in combination with the open prop, otherwise you won't be able to open/close the drawer. dismissible: Var[bool] # When `True`, dragging will only be possible by the handle. handle_only: Var[bool] # Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. Should go from least visible. Also Accept px values, which doesn't take screen height into account. snap_points: Sequence[str | float] | None # Index of a snapPoint from which the overlay fade should be applied. Defaults to the last snap point. fade_from_index: Var[int] # Duration for which the drawer is not draggable after scrolling content inside of the drawer. Defaults to 500ms scroll_lock_timeout: Var[int] # When `True`, it prevents scroll restoration. Defaults to `True`. prevent_scroll_restoration: Var[bool] # Enable background scaling, it requires container element with `vaul-drawer-wrapper` attribute to scale its background. should_scale_background: Var[bool] # Number between 0 and 1 that determines when the drawer should be closed. close_threshold: Var[float] class DrawerTrigger(DrawerComponent): """The button that opens the dialog.""" tag = "Drawer.Trigger" alias = "Vaul" + tag # Defaults to true, if the first child acts as the trigger. as_child: Var[bool] = Var.create(True) _memoization_mode = MemoizationMode(recursive=False) @classmethod def create(cls, *children: Any, **props: Any) -> Component: """Create a new DrawerTrigger instance. Args: *children: The children of the element. **props: The properties of the element. Returns: The new DrawerTrigger instance. """ for child in children: if "on_click" in getattr(child, "event_triggers", {}): children = (Flex.create(*children),) break return super().create(*children, **props) class DrawerPortal(DrawerComponent): """Portals your drawer into the body.""" tag = "Drawer.Portal" alias = "Vaul" + tag # Based on https://www.radix-ui.com/primitives/docs/components/dialog#content class DrawerContent(DrawerComponent): """Content that should be rendered in the drawer.""" tag = "Drawer.Content" alias = "Vaul" + tag # Style set partially based on the source code at https://ui.shadcn.com/docs/components/drawer def add_style(self) -> dict: """Get the style for the component. Returns: The dictionary of the component style as value and the style notation as key. """ return { "left": "0", "right": "0", "bottom": "0", "top": "0", "position": "fixed", "z_index": 50, "display": "flex", } # Fired when the drawer content is opened. on_open_auto_focus: EventHandler[no_args_event_spec] # Fired when the drawer content is closed. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the drawer content. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when interacting outside the drawer content. on_interact_outside: EventHandler[no_args_event_spec] @classmethod def create(cls, *children, **props): """Create a Drawer Content. We wrap the Drawer content in an `rx.theme` to make radix themes definitions available to rendered div in the DOM. This is because Vaul Drawer injects the Drawer overlay content in a sibling div to the root div rendered by radix which contains styling definitions. Wrapping in `rx.theme` makes the styling available to the overlay. Args: *children: The list of children to use. **props: Additional properties to apply to the drawer content. Returns: The drawer content. """ comp = super().create(*children, **props) return Theme.create(comp) class DrawerOverlay(DrawerComponent): """A layer that covers the inert portion of the view when the dialog is open.""" tag = "Drawer.Overlay" alias = "Vaul" + tag # Style set based on the source code at https://ui.shadcn.com/docs/components/drawer def add_style(self) -> dict: """Get the style for the component. Returns: The dictionary of the component style as value and the style notation as key. """ return { "position": "fixed", "left": "0", "right": "0", "bottom": "0", "top": "0", "z_index": 50, "background": "rgba(0, 0, 0, 0.5)", } class DrawerClose(DrawerTrigger): """A button that closes the drawer.""" tag = "Drawer.Close" alias = "Vaul" + tag class DrawerTitle(DrawerComponent): """A title for the drawer.""" tag = "Drawer.Title" alias = "Vaul" + tag # Style set based on the source code at https://ui.shadcn.com/docs/components/drawer def add_style(self) -> dict: """Get the style for the component. Returns: The dictionary of the component style as value and the style notation as key. """ return { "font-size": "1.125rem", "font-weight": "600", "line-height": "1", "letter-spacing": "-0.05em", } class DrawerDescription(DrawerComponent): """A description for the drawer.""" tag = "Drawer.Description" alias = "Vaul" + tag # Style set based on the source code at https://ui.shadcn.com/docs/components/drawer def add_style(self) -> dict: """Get the style for the component. Returns: The dictionary of the component style as value and the style notation as key. """ return { "font-size": "0.875rem", } class DrawerHandle(DrawerComponent): """A description for the drawer.""" tag = "Drawer.Handle" alias = "Vaul" + tag class Drawer(ComponentNamespace): """A namespace for Drawer components.""" root = __call__ = staticmethod(DrawerRoot.create) trigger = staticmethod(DrawerTrigger.create) portal = staticmethod(DrawerPortal.create) content = staticmethod(DrawerContent.create) overlay = staticmethod(DrawerOverlay.create) close = staticmethod(DrawerClose.create) title = staticmethod(DrawerTitle.create) description = staticmethod(DrawerDescription.create) handle = staticmethod(DrawerHandle.create) drawer = Drawer() ================================================ FILE: reflex/components/radix/primitives/form.py ================================================ """Radix form component.""" from __future__ import annotations from typing import Any, Literal from reflex.components.component import ComponentNamespace from reflex.components.core.debounce import DebounceInput from reflex.components.el.elements.forms import Form as HTMLForm from reflex.components.radix.themes.components.text_field import TextFieldRoot from reflex.event import EventHandler, no_args_event_spec from reflex.vars.base import Var from .base import RadixPrimitiveComponentWithClassName class FormComponent(RadixPrimitiveComponentWithClassName): """Base class for all @radix-ui/react-form components.""" library = "@radix-ui/react-form@0.1.8" class FormRoot(FormComponent, HTMLForm): """The root component of a radix form.""" tag = "Root" alias = "RadixFormRoot" # Fired when the errors are cleared. on_clear_server_errors: EventHandler[no_args_event_spec] def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return {"width": "100%"} class FormField(FormComponent): """A form field component.""" tag = "Field" alias = "RadixFormField" # The name of the form field, that is passed down to the control and used to match with validation messages. name: Var[str] # Flag to mark the form field as invalid, for server side validation. server_invalid: Var[bool] def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return {"display": "grid", "margin_bottom": "10px"} class FormLabel(FormComponent): """A form label component.""" tag = "Label" alias = "RadixFormLabel" def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return {"font_size": "15px", "font_weight": "500", "line_height": "35px"} class FormControl(FormComponent): """A form control component.""" tag = "Control" alias = "RadixFormControl" @classmethod def create(cls, *children, **props): """Create a Form Control component. Args: *children: The children of the form. **props: The properties of the form. Returns: The form control component. Raises: ValueError: If the number of children is greater than 1. TypeError: If a child exists but it is not a TextFieldInput. """ if len(children) > 1: msg = f"FormControl can only have at most one child, got {len(children)} children" raise ValueError(msg) for child in children: if not isinstance(child, (TextFieldRoot, DebounceInput)): msg = "Only Radix TextFieldRoot and DebounceInput are allowed as children of FormControl" raise TypeError(msg) return super().create(*children, **props) LiteralMatcher = Literal[ "badInput", "patternMismatch", "rangeOverflow", "rangeUnderflow", "stepMismatch", "tooLong", "tooShort", "typeMismatch", "valid", "valueMissing", ] class FormMessage(FormComponent): """A form message component.""" tag = "Message" alias = "RadixFormMessage" # Used to target a specific field by name when rendering outside of a Field part. name: Var[str] # Used to indicate on which condition the message should be visible. match: Var[LiteralMatcher] # Forces the message to be shown. This is useful when using server-side validation. force_match: Var[bool] def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return {"font_size": "13px", "opacity": "0.8", "color": "white"} class FormValidityState(FormComponent): """A form validity state component.""" tag = "ValidityState" alias = "RadixFormValidityState" class FormSubmit(FormComponent): """A form submit component.""" tag = "Submit" alias = "RadixFormSubmit" # This class is created mainly for reflex-web docs. class Form(FormRoot): """The Form component.""" class FormNamespace(ComponentNamespace): """Form components.""" root = staticmethod(FormRoot.create) control = staticmethod(FormControl.create) field = staticmethod(FormField.create) label = staticmethod(FormLabel.create) message = staticmethod(FormMessage.create) submit = staticmethod(FormSubmit.create) validity_state = staticmethod(FormValidityState.create) __call__ = staticmethod(Form.create) form = FormNamespace() ================================================ FILE: reflex/components/radix/primitives/progress.py ================================================ """Progress.""" from __future__ import annotations from typing import Any from reflex.components.component import Component, ComponentNamespace from reflex.components.core.colors import color from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius from reflex.vars.base import Var class ProgressComponent(RadixPrimitiveComponentWithClassName): """A Progress component.""" library = "@radix-ui/react-progress@1.1.8" class ProgressRoot(ProgressComponent): """The Progress Root component.""" tag = "Root" alias = "RadixProgressRoot" # Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ if self.radius is not None: self.custom_attrs["data-radius"] = self.radius return { "position": "relative", "overflow": "hidden", "background": color("gray", 3, alpha=True), "border_radius": "max(var(--radius-2), var(--radius-full))", "width": "100%", "height": "20px", "boxShadow": f"inset 0 0 0 1px {color('gray', 5, alpha=True)}", } def _exclude_props(self) -> list[str]: return ["radius"] class ProgressIndicator(ProgressComponent): """The Progress bar indicator.""" tag = "Indicator" alias = "RadixProgressIndicator" # The current progress value. value: Var[int | None] # The maximum progress value. max: Var[int | None] # The color scheme of the progress indicator. color_scheme: Var[LiteralAccentColor] def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ if self.color_scheme is not None: self.custom_attrs["data-accent-color"] = self.color_scheme return { "background_color": color("accent", 9), "width": "100%", "height": "100%", "transition": f"transform {DEFAULT_ANIMATION_DURATION}ms linear", "&[data_state='loading']": { "transition": f"transform {DEFAULT_ANIMATION_DURATION}ms linear", }, "transform": f"translateX(calc(-100% + ({self.value} / {self.max} * 100%)))", "boxShadow": "inset 0 0 0 1px var(--gray-a5)", } def _exclude_props(self) -> list[str]: return ["color_scheme"] class Progress(ProgressRoot): """The high-level Progress component.""" # Override theme color for progress bar indicator color_scheme: Var[LiteralAccentColor] # The current progress value. value: Var[int | None] # The maximum progress value. max: Var[int | None] @classmethod def create(cls, **props) -> Component: """High-level API for progress bar. Args: **props: The props of the progress bar. Returns: The progress bar. """ progress_indicator_props = {} if "color_scheme" in props: progress_indicator_props["color_scheme"] = props.pop("color_scheme") return ProgressRoot.create( ProgressIndicator.create( value=props.pop("value", 0), max=props.pop("max", 100), **progress_indicator_props, ), **props, ) class ProgressNamespace(ComponentNamespace): """High-level API for progress bar.""" root = staticmethod(ProgressRoot.create) indicator = staticmethod(ProgressIndicator.create) __call__ = staticmethod(Progress.create) progress = ProgressNamespace() ================================================ FILE: reflex/components/radix/primitives/slider.py ================================================ """Radix slider components.""" from __future__ import annotations from collections.abc import Sequence from typing import Any, Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName from reflex.event import EventHandler, passthrough_event_spec from reflex.vars.base import Var LiteralSliderOrientation = Literal["horizontal", "vertical"] LiteralSliderDir = Literal["ltr", "rtl"] class SliderComponent(RadixPrimitiveComponentWithClassName): """Base class for all @radix-ui/react-slider components.""" library = "@radix-ui/react-slider@1.3.6" class SliderRoot(SliderComponent): """The Slider component containing all slider parts.""" tag = "Root" alias = "RadixSliderRoot" default_value: Var[Sequence[int]] value: Var[Sequence[int]] name: Var[str] disabled: Var[bool] orientation: Var[LiteralSliderOrientation] dir: Var[LiteralSliderDir] inverted: Var[bool] min: Var[int] max: Var[int] step: Var[int] min_steps_between_thumbs: Var[int] # Fired when the value of a thumb changes. on_value_change: EventHandler[passthrough_event_spec(list[float])] # Fired when a thumb is released. on_value_commit: EventHandler[passthrough_event_spec(list[float])] def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "position": "relative", "display": "flex", "align_items": "center", "user_select": "none", "touch_action": "none", "width": "200px", "height": "20px", "&[data-orientation='vertical']": { "flex_direction": "column", "width": "20px", "height": "100px", }, } class SliderTrack(SliderComponent): """A Slider Track component.""" tag = "Track" alias = "RadixSliderTrack" def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "position": "relative", "flex_grow": "1", "background_color": "black", "border_radius": "9999px", "height": "3px", "&[data-orientation='vertical']": {"width": "3px"}, } class SliderRange(SliderComponent): """A SliderRange component.""" tag = "Range" alias = "RadixSliderRange" def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "position": "absolute", "background_color": "white", "height": "100%", "&[data-orientation='vertical']": {"width": "100%"}, } class SliderThumb(SliderComponent): """A SliderThumb component.""" tag = "Thumb" alias = "RadixSliderThumb" def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "display": "block", "width": "20px", "height": "20px", "background_color": "black", "box_shadow": "0 2px 10px black", "border_radius": "10px", "&:hover": { "background_color": "gray", }, "&:focus": { "outline": "none", "box_shadow": "0 0 0 4px gray", }, } class Slider(ComponentNamespace): """High level API for slider.""" root = staticmethod(SliderRoot.create) track = staticmethod(SliderTrack.create) range = staticmethod(SliderRange.create) thumb = staticmethod(SliderThumb.create) @staticmethod def __call__(**props) -> Component: """High level API for slider. Args: **props: The props of the slider. Returns: A slider component. """ track = SliderTrack.create(SliderRange.create()) # if default_value is not set, the thumbs will not render properly but the slider will still work if "default_value" in props: children = [ track, *[SliderThumb.create() for _ in props.get("default_value", [])], ] else: children = [ track, # Foreach.create(props.get("value"), lambda e: SliderThumb.create()), # foreach doesn't render Thumbs properly # noqa: ERA001 ] return SliderRoot.create(*children, **props) slider = Slider() ================================================ FILE: reflex/components/radix/themes/__init__.py ================================================ """Namespace for components provided by the @radix-ui/themes library.""" from __future__ import annotations from reflex.utils import lazy_loader _SUBMODULES: set[str] = {"components", "layout", "typography"} _SUBMOD_ATTRS: dict[str, list[str]] = { "base": [ "theme", "theme_panel", ], "color_mode": [ "color_mode", ], } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submodules=_SUBMODULES, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/radix/themes/base.py ================================================ """Base classes for radix-themes components.""" from __future__ import annotations from typing import Any, ClassVar, Literal from reflex.components import Component from reflex.components.core.breakpoints import Responsive from reflex.components.tags import Tag from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import Var LiteralAlign = Literal["start", "center", "end", "baseline", "stretch"] LiteralJustify = Literal["start", "center", "end", "between"] LiteralSpacing = Literal["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] LiteralVariant = Literal["classic", "solid", "soft", "surface", "outline", "ghost"] LiteralAppearance = Literal["inherit", "light", "dark"] LiteralGrayColor = Literal["gray", "mauve", "slate", "sage", "olive", "sand", "auto"] LiteralPanelBackground = Literal["solid", "translucent"] LiteralRadius = Literal["none", "small", "medium", "large", "full"] LiteralScaling = Literal["90%", "95%", "100%", "105%", "110%"] LiteralAccentColor = Literal[ "tomato", "red", "ruby", "crimson", "pink", "plum", "purple", "violet", "iris", "indigo", "blue", "cyan", "teal", "jade", "green", "grass", "brown", "orange", "sky", "mint", "lime", "yellow", "amber", "gold", "bronze", "gray", ] class CommonMarginProps(Component): """Many radix-themes elements accept shorthand margin props.""" # Margin: "0" - "9" # noqa: ERA001 m: Var[LiteralSpacing] # Margin horizontal: "0" - "9" mx: Var[LiteralSpacing] # Margin vertical: "0" - "9" my: Var[LiteralSpacing] # Margin top: "0" - "9" mt: Var[LiteralSpacing] # Margin right: "0" - "9" mr: Var[LiteralSpacing] # Margin bottom: "0" - "9" mb: Var[LiteralSpacing] # Margin left: "0" - "9" ml: Var[LiteralSpacing] class CommonPaddingProps(Component): """Many radix-themes elements accept shorthand padding props.""" # Padding: "0" - "9" # noqa: ERA001 p: Var[Responsive[LiteralSpacing]] # Padding horizontal: "0" - "9" px: Var[Responsive[LiteralSpacing]] # Padding vertical: "0" - "9" py: Var[Responsive[LiteralSpacing]] # Padding top: "0" - "9" pt: Var[Responsive[LiteralSpacing]] # Padding right: "0" - "9" pr: Var[Responsive[LiteralSpacing]] # Padding bottom: "0" - "9" pb: Var[Responsive[LiteralSpacing]] # Padding left: "0" - "9" pl: Var[Responsive[LiteralSpacing]] class RadixLoadingProp(Component): """Base class for components that can be in a loading state.""" # If set, show an rx.spinner instead of the component children. loading: Var[bool] class RadixThemesComponent(Component): """Base class for all @radix-ui/themes components.""" library = "@radix-ui/themes@3.3.0" # "Fake" prop color_scheme is used to avoid shadowing CSS prop "color". _rename_props: ClassVar[dict[str, str]] = {"colorScheme": "color"} @classmethod def create( cls, *children, **props, ) -> Component: """Create a new component instance. Will prepend "RadixThemes" to the component tag to avoid conflicts with other UI libraries for common names, like Text and Button. Args: *children: Child components. **props: Component properties. Returns: A new component instance. """ component = super().create(*children, **props) if component.library is None: component.library = RadixThemesComponent.get_fields()[ "library" ].default_value() component.alias = "RadixThemes" + (component.tag or type(component).__name__) return component @staticmethod def _get_app_wrap_components() -> dict[tuple[int, str], Component]: return { (45, "RadixThemesColorModeProvider"): RadixThemesColorModeProvider.create(), } class RadixThemesTriggerComponent(RadixThemesComponent): """Base class for Trigger, Close, Cancel, and Accept components. These components trigger some action in an overlay component that depends on the on_click event, and thus if a child is provided and has on_click specified, it will overtake the internal action, unless it is wrapped in some inert component, in this case, a Flex. """ @classmethod def create(cls, *children: Any, **props: Any) -> Component: """Create a new RadixThemesTriggerComponent instance. Args: children: The children of the component. props: The properties of the component. Returns: The new RadixThemesTriggerComponent instance. """ from .layout.flex import Flex for child in children: if "on_click" in getattr(child, "event_triggers", {}): children = (Flex.create(*children),) break return super().create(*children, **props) class Theme(RadixThemesComponent): """A theme provider for radix components. This should be applied as `App.theme` to apply the theme to all radix components in the app with the given settings. It can also be used in a normal page to apply specified properties to all child elements as an override of the main theme. """ tag = "Theme" # Whether to apply the themes background color to the theme node. Defaults to True. has_background: Var[bool] # Override light or dark mode theme: "inherit" | "light" | "dark". Defaults to "inherit". appearance: Var[LiteralAppearance] # The color used for default buttons, typography, backgrounds, etc accent_color: Var[LiteralAccentColor] # The shade of gray, defaults to "auto". gray_color: Var[LiteralGrayColor] # Whether panel backgrounds are translucent: "solid" | "translucent" (default) panel_background: Var[LiteralPanelBackground] # Element border radius: "none" | "small" | "medium" | "large" | "full". Defaults to "medium". radius: Var[LiteralRadius] # Scale of all theme items: "90%" | "95%" | "100%" | "105%" | "110%". Defaults to "100%" scaling: Var[LiteralScaling] @classmethod def create( cls, *children, color_mode: LiteralAppearance | None = None, theme_panel: bool = False, **props, ) -> Component: """Create a new Radix Theme specification. Args: *children: Child components. color_mode: Map to appearance prop. theme_panel: Whether to include a panel for editing the theme. **props: Component properties. Returns: A new component instance. """ if color_mode is not None: props["appearance"] = color_mode if theme_panel: children = [ThemePanel.create(), *children] return super().create(*children, **props) def add_imports(self) -> ImportDict | list[ImportDict]: """Add imports for the Theme component. Returns: The import dict. """ return { "$/utils/theme": [ImportVar(tag="theme", is_default=True)], } def _render(self, props: dict[str, Any] | None = None) -> Tag: tag = super()._render(props) return tag.add_props( css=Var( _js_expr="{...theme.styles.global[':root'], ...theme.styles.global.body}" ), ).remove_props("appearance") class ThemePanel(RadixThemesComponent): """Visual editor for creating and editing themes. Include as a child component of Theme to use in your app. """ tag = "ThemePanel" # Whether the panel is open. Defaults to False. default_open: Var[bool] def add_imports(self) -> dict[str, str]: """Add imports for the ThemePanel component. Returns: The import dict. """ return {"react": "useEffect"} class RadixThemesColorModeProvider(Component): """React-themes integration for radix themes components.""" library = "$/components/reflex/radix_themes_color_mode_provider" tag = "RadixThemesColorModeProvider" is_default = True theme = Theme.create theme_panel = ThemePanel.create ================================================ FILE: reflex/components/radix/themes/color_mode.py ================================================ """A switch component for toggling color_mode. To style components based on color mode, use style props with `color_mode_cond`: ``` rx.text( "Hover over me", _hover={ "background": rx.color_mode_cond( light="var(--accent-2)", dark="var(--accent-4)", ), }, ) ``` """ from __future__ import annotations from typing import Any, Literal, get_args from reflex.components.component import BaseComponent, field from reflex.components.core.cond import Cond, color_mode_cond, cond from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.components.dropdown_menu import dropdown_menu from reflex.components.radix.themes.components.switch import Switch from reflex.style import ( LIGHT_COLOR_MODE, color_mode, resolved_color_mode, set_color_mode, toggle_color_mode, ) from reflex.vars.base import Var from reflex.vars.sequence import LiteralArrayVar from .components.icon_button import IconButton DEFAULT_LIGHT_ICON: Icon = Icon.create(tag="sun") DEFAULT_DARK_ICON: Icon = Icon.create(tag="moon") class ColorModeIcon(Cond): """Displays the current color mode as an icon.""" @classmethod def create( cls, light_component: BaseComponent | None = None, dark_component: BaseComponent | None = None, ): """Create an icon component based on color_mode. Args: light_component: the component to display when color mode is default dark_component: the component to display when color mode is dark (non-default) Returns: The conditionally rendered component """ return color_mode_cond( light=light_component or DEFAULT_LIGHT_ICON, dark=dark_component or DEFAULT_DARK_ICON, ) LiteralPosition = Literal["top-left", "top-right", "bottom-left", "bottom-right"] position_values: list[str] = list(get_args(LiteralPosition)) position_map: dict[str, list[str]] = { "position": position_values, "left": ["top-left", "bottom-left"], "right": ["top-right", "bottom-right"], "top": ["top-left", "top-right"], "bottom": ["bottom-left", "bottom-right"], } # needed to inverse contains for find def _find(const: list[str], var: Any): return LiteralArrayVar.create(const).contains(var) def _set_var_default( props: dict, position: Any, prop: str, default1: str, default2: str | Var = "" ): props.setdefault( prop, cond(_find(position_map[prop], position), default1, default2) ) def _set_static_default(props: dict, position: Any, prop: str, default: str): if prop in position: props.setdefault(prop, default) class ColorModeIconButton(IconButton): """Icon Button for toggling light / dark mode via toggle_color_mode.""" # The position of the icon button. Follow document flow if None. position: LiteralPosition | Var[LiteralPosition] | None = field( default=None, is_javascript_property=False ) # Allow picking the "system" value for the color mode. allow_system: bool = field(default=False, is_javascript_property=False) @classmethod def create( cls, **props, ): """Create an icon button component that calls toggle_color_mode on click. Args: **props: The props to pass to the component. Returns: The button component. """ position: str | Var = props.pop("position", None) allow_system = props.pop("allow_system", False) # position is used to set nice defaults for positioning the icon button if isinstance(position, Var): _set_var_default(props, position, "position", "fixed", position) _set_var_default(props, position, "bottom", "2rem") _set_var_default(props, position, "top", "2rem") _set_var_default(props, position, "left", "2rem") _set_var_default(props, position, "right", "2rem") elif position is not None: if position in position_values: props.setdefault("position", "fixed") _set_static_default(props, position, "bottom", "2rem") _set_static_default(props, position, "top", "2rem") _set_static_default(props, position, "left", "2rem") _set_static_default(props, position, "right", "2rem") else: props["position"] = position props.setdefault("background", "transparent") props.setdefault("color", "inherit") props.setdefault("z_index", "20") props.setdefault(":hover", {"cursor": "pointer"}) if allow_system: def color_mode_item(_color_mode: Literal["light", "dark", "system"]): return dropdown_menu.item( _color_mode.title(), on_click=set_color_mode(_color_mode) ) return dropdown_menu.root( dropdown_menu.trigger( super().create( ColorModeIcon.create(), ), **props, ), dropdown_menu.content( color_mode_item("light"), color_mode_item("dark"), color_mode_item("system"), ), ) return IconButton.create( ColorModeIcon.create(), on_click=toggle_color_mode, **props, ) class ColorModeSwitch(Switch): """Switch for toggling light / dark mode via toggle_color_mode.""" @classmethod def create(cls, *children, **props): """Create a switch component bound to color_mode. Args: *children: The children of the component. **props: The props to pass to the component. Returns: The switch component. """ return Switch.create( *children, checked=resolved_color_mode != LIGHT_COLOR_MODE, on_change=toggle_color_mode, **props, ) class ColorModeNamespace(Var): """Namespace for color mode components.""" icon = staticmethod(ColorModeIcon.create) button = staticmethod(ColorModeIconButton.create) switch = staticmethod(ColorModeSwitch.create) color_mode = color_mode_var_and_namespace = ColorModeNamespace( _js_expr=color_mode._js_expr, _var_type=color_mode._var_type, _var_data=color_mode._get_all_var_data(), ) ================================================ FILE: reflex/components/radix/themes/components/__init__.py ================================================ """Radix themes components.""" from __future__ import annotations from reflex import RADIX_THEMES_COMPONENTS_MAPPING from reflex.utils import lazy_loader _SUBMOD_ATTRS: dict[str, list[str]] = { "".join(k.split("components.radix.themes.components.")[-1]): v for k, v in RADIX_THEMES_COMPONENTS_MAPPING.items() } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/radix/themes/components/alert_dialog.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( RadixThemesComponent, RadixThemesTriggerComponent, ) from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var LiteralContentSize = Literal["1", "2", "3", "4"] class AlertDialogRoot(RadixThemesComponent): """Contains all the parts of the dialog.""" tag = "AlertDialog.Root" # The controlled open state of the dialog. open: Var[bool] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] # The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] class AlertDialogTrigger(RadixThemesTriggerComponent): """Wraps the control that will open the dialog.""" tag = "AlertDialog.Trigger" _memoization_mode = MemoizationMode(recursive=False) class AlertDialogContent(elements.Div, RadixThemesComponent): """Contains the content of the dialog. This component is based on the div element.""" tag = "AlertDialog.Content" # The size of the content. size: Var[Responsive[LiteralContentSize]] # Whether to force mount the content on open. force_mount: Var[bool] # Fired when the dialog is opened. on_open_auto_focus: EventHandler[no_args_event_spec] # Fired when the dialog is closed. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] class AlertDialogTitle(RadixThemesComponent): """An accessible title that is announced when the dialog is opened. This part is based on the Heading component with a pre-defined font size and leading trim on top. """ tag = "AlertDialog.Title" class AlertDialogDescription(RadixThemesComponent): """An optional accessible description that is announced when the dialog is opened. This part is based on the Text component with a pre-defined font size. """ tag = "AlertDialog.Description" class AlertDialogAction(RadixThemesTriggerComponent): """Wraps the control that will close the dialog. This should be distinguished visually from the Cancel control. """ tag = "AlertDialog.Action" class AlertDialogCancel(RadixThemesTriggerComponent): """Wraps the control that will close the dialog. This should be distinguished visually from the Action control. """ tag = "AlertDialog.Cancel" class AlertDialog(ComponentNamespace): """AlertDialog components namespace.""" root = staticmethod(AlertDialogRoot.create) trigger = staticmethod(AlertDialogTrigger.create) content = staticmethod(AlertDialogContent.create) title = staticmethod(AlertDialogTitle.create) description = staticmethod(AlertDialogDescription.create) action = staticmethod(AlertDialogAction.create) cancel = staticmethod(AlertDialogCancel.create) alert_dialog = AlertDialog() ================================================ FILE: reflex/components/radix/themes/components/aspect_ratio.py ================================================ """Interactive components provided by @radix-ui/themes.""" from reflex.components.radix.themes.base import RadixThemesComponent from reflex.vars.base import Var class AspectRatio(RadixThemesComponent): """Displays content with a desired ratio.""" tag = "AspectRatio" # The ratio of the width to the height of the element ratio: Var[float | int] aspect_ratio = AspectRatio.create ================================================ FILE: reflex/components/radix/themes/components/avatar.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, RadixThemesComponent, ) from reflex.vars.base import Var LiteralSize = Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"] class Avatar(RadixThemesComponent): """An image element with a fallback for representing the user.""" tag = "Avatar" # The variant of the avatar variant: Var[Literal["solid", "soft"]] # The size of the avatar: "1" - "9" size: Var[Responsive[LiteralSize]] # Color theme of the avatar color_scheme: Var[LiteralAccentColor] # Whether to render the avatar with higher contrast color against background high_contrast: Var[bool] # Override theme radius for avatar: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] # The src of the avatar image src: Var[str] # The rendered fallback text fallback: Var[str] avatar = Avatar.create ================================================ FILE: reflex/components/radix/themes/components/badge.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, RadixThemesComponent, ) from reflex.vars.base import Var class Badge(elements.Span, RadixThemesComponent): """A stylized badge element.""" tag = "Badge" # The variant of the badge variant: Var[Literal["solid", "soft", "surface", "outline"]] # The size of the badge size: Var[Responsive[Literal["1", "2", "3"]]] # Color theme of the badge color_scheme: Var[LiteralAccentColor] # Whether to render the badge with higher contrast color against background high_contrast: Var[bool] # Override theme radius for badge: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] badge = Badge.create ================================================ FILE: reflex/components/radix/themes/components/button.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, LiteralVariant, RadixLoadingProp, RadixThemesComponent, ) from reflex.vars.base import Var LiteralButtonSize = Literal["1", "2", "3", "4"] class Button(elements.Button, RadixLoadingProp, RadixThemesComponent): """Trigger an action or event, such as submitting a form or displaying a dialog.""" tag = "Button" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Button size "1" - "4" size: Var[Responsive[LiteralButtonSize]] # Variant of button: "solid" | "soft" | "outline" | "ghost" variant: Var[LiteralVariant] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Whether to render the button with higher contrast color against background high_contrast: Var[bool] # Override theme radius for button: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] button = Button.create ================================================ FILE: reflex/components/radix/themes/components/callout.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal import reflex as rx from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var CalloutVariant = Literal["soft", "surface", "outline"] class CalloutRoot(elements.Div, RadixThemesComponent): """Groups Icon and Text parts of a Callout.""" tag = "Callout.Root" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Size "1" - "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button: "soft" | "surface" | "outline" variant: Var[CalloutVariant] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Whether to render the button with higher contrast color against background high_contrast: Var[bool] class CalloutIcon(elements.Div, RadixThemesComponent): """Provides width and height for the icon associated with the callout.""" tag = "Callout.Icon" class CalloutText(elements.P, RadixThemesComponent): """Renders the callout text. This component is based on the p element.""" tag = "Callout.Text" class Callout(CalloutRoot): """A short message to attract user's attention.""" # The text of the callout. text: Var[str] # The icon of the callout. icon: Var[str] @classmethod def create(cls, text: str | Var[str], **props) -> Component: """Create a callout component. Args: text: The text of the callout. **props: The properties of the component. Returns: The callout component. """ return CalloutRoot.create( ( CalloutIcon.create(Icon.create(tag=props["icon"])) if "icon" in props else rx.fragment() ), CalloutText.create(text), **props, ) class CalloutNamespace(ComponentNamespace): """Callout components namespace.""" root = staticmethod(CalloutRoot.create) icon = staticmethod(CalloutIcon.create) text = staticmethod(CalloutText.create) __call__ = staticmethod(Callout.create) callout = CalloutNamespace() ================================================ FILE: reflex/components/radix/themes/components/card.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import RadixThemesComponent from reflex.vars.base import Var class Card(elements.Div, RadixThemesComponent): """Container that groups related content and actions.""" tag = "Card" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Card size: "1" - "5" size: Var[Responsive[Literal["1", "2", "3", "4", "5"],]] # Variant of Card: "surface" | "classic" | "ghost" variant: Var[Literal["surface", "classic", "ghost"]] card = Card.create ================================================ FILE: reflex/components/radix/themes/components/checkbox.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralSpacing, RadixThemesComponent, ) from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.typography.text import Text from reflex.event import EventHandler, passthrough_event_spec from reflex.vars.base import Var LiteralCheckboxSize = Literal["1", "2", "3"] LiteralCheckboxVariant = Literal["classic", "surface", "soft"] class Checkbox(RadixThemesComponent): """Selects a single value, typically for submission in a form.""" tag = "Checkbox" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Checkbox size "1" - "3" size: Var[Responsive[LiteralCheckboxSize]] # Variant of checkbox: "classic" | "surface" | "soft" variant: Var[LiteralCheckboxVariant] # Override theme color for checkbox color_scheme: Var[LiteralAccentColor] # Whether to render the checkbox with higher contrast color against background high_contrast: Var[bool] # Whether the checkbox is checked by default default_checked: Var[bool] # Whether the checkbox is checked checked: Var[bool] # Whether the checkbox is disabled disabled: Var[bool] # Whether the checkbox is required required: Var[bool] # The name of the checkbox control when submitting the form. name: Var[str] # The value of the checkbox control when submitting the form. value: Var[str] # Props to rename _rename_props = {"onChange": "onCheckedChange"} # Fired when the checkbox is checked or unchecked. on_change: EventHandler[passthrough_event_spec(bool)] class HighLevelCheckbox(RadixThemesComponent): """A checkbox component with a label.""" tag = "Checkbox" # The text label for the checkbox. text: Var[str] # The gap between the checkbox and the label. spacing: Var[LiteralSpacing] # The size of the checkbox "1" - "3". size: Var[LiteralCheckboxSize] # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Variant of checkbox: "classic" | "surface" | "soft" variant: Var[LiteralCheckboxVariant] # Override theme color for checkbox color_scheme: Var[LiteralAccentColor] # Whether to render the checkbox with higher contrast color against background high_contrast: Var[bool] # Whether the checkbox is checked by default default_checked: Var[bool] # Whether the checkbox is checked checked: Var[bool] # Whether the checkbox is disabled disabled: Var[bool] # Whether the checkbox is required required: Var[bool] # The name of the checkbox control when submitting the form. name: Var[str] # The value of the checkbox control when submitting the form. value: Var[str] # Props to rename _rename_props = {"onChange": "onCheckedChange"} # Fired when the checkbox is checked or unchecked. on_change: EventHandler[passthrough_event_spec(bool)] @classmethod def create(cls, text: Var[str] = Var.create(""), **props) -> Component: """Create a checkbox with a label. Args: text: The text of the label. **props: Additional properties to apply to the checkbox item. Returns: The checkbox component with a label. """ spacing = props.pop("spacing", "2") size = props.pop("size", "2") flex_props = {} if "gap" in props: flex_props["gap"] = props.pop("gap", None) return Text.create( Flex.create( Checkbox.create( size=size, **props, ), text, spacing=spacing, **flex_props, ), as_="label", size=size, ) class CheckboxNamespace(ComponentNamespace): """Checkbox components namespace.""" __call__ = staticmethod(HighLevelCheckbox.create) checkbox = CheckboxNamespace() ================================================ FILE: reflex/components/radix/themes/components/checkbox_cards.py ================================================ """Components for the Radix CheckboxCards component.""" from types import SimpleNamespace from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var class CheckboxCardsRoot(RadixThemesComponent): """Root element for a CheckboxCards component.""" tag = "CheckboxCards.Root" # The size of the checkbox cards: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button: "classic" | "surface" | "soft" variant: Var[Literal["classic", "surface"]] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Uses a higher contrast color for the component. high_contrast: Var[bool] # The number of columns: columns: Var[Responsive[str | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]]] # The gap between the checkbox cards: gap: Var[Responsive[str | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]]] class CheckboxCardsItem(RadixThemesComponent): """An item in the CheckboxCards component.""" tag = "CheckboxCards.Item" class CheckboxCards(SimpleNamespace): """CheckboxCards components namespace.""" root = staticmethod(CheckboxCardsRoot.create) item = staticmethod(CheckboxCardsItem.create) checkbox_cards = CheckboxCards() ================================================ FILE: reflex/components/radix/themes/components/checkbox_group.py ================================================ """Components for the CheckboxGroup component of Radix Themes.""" from collections.abc import Sequence from types import SimpleNamespace from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var class CheckboxGroupRoot(RadixThemesComponent): """Root element for a CheckboxGroup component.""" tag = "CheckboxGroup.Root" # Use the size prop to control the checkbox size. size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button: "classic" | "surface" | "soft" variant: Var[Literal["classic", "surface", "soft"]] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Uses a higher contrast color for the component. high_contrast: Var[bool] # determines which checkboxes, if any, are checked by default. default_value: Var[Sequence[str]] # used to assign a name to the entire group of checkboxes name: Var[str] class CheckboxGroupItem(RadixThemesComponent): """An item in the CheckboxGroup component.""" tag = "CheckboxGroup.Item" # specifies the value associated with a particular checkbox option. value: Var[str] # Use the native disabled attribute to create a disabled checkbox. disabled: Var[bool] class CheckboxGroup(SimpleNamespace): """CheckboxGroup components namespace.""" root = staticmethod(CheckboxGroupRoot.create) item = staticmethod(CheckboxGroupItem.create) checkbox_group = CheckboxGroup() ================================================ FILE: reflex/components/radix/themes/components/context_menu.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import ClassVar, Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var from .checkbox import Checkbox from .radio_group import HighLevelRadioGroup LiteralDirType = Literal["ltr", "rtl"] LiteralSizeType = Literal["1", "2"] LiteralVariantType = Literal["solid", "soft"] LiteralSideType = Literal["top", "right", "bottom", "left"] LiteralAlignType = Literal["start", "center", "end"] LiteralStickyType = Literal[ "partial", "always", ] class ContextMenuRoot(RadixThemesComponent): """Menu representing a set of actions, displayed at the origin of a pointer right-click or long-press.""" tag = "ContextMenu.Root" # The modality of the context menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers. modal: Var[bool] _invalid_children: ClassVar[list[str]] = ["ContextMenuItem"] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] # The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. dir: Var[LiteralDirType] class ContextMenuTrigger(RadixThemesComponent): """Wraps the element that will open the context menu.""" tag = "ContextMenu.Trigger" # Whether the trigger is disabled disabled: Var[bool] _valid_parents: ClassVar[list[str]] = ["ContextMenuRoot"] _invalid_children: ClassVar[list[str]] = ["ContextMenuContent"] _memoization_mode = MemoizationMode(recursive=False) class ContextMenuContent(RadixThemesComponent): """The component that pops out when the context menu is open.""" tag = "ContextMenu.Content" # Dropdown Menu Content size "1" - "2" size: Var[Responsive[LiteralSizeType]] # Variant of Dropdown Menu Content: "solid" | "soft" variant: Var[LiteralVariantType] # Override theme color for Dropdown Menu Content color_scheme: Var[LiteralAccentColor] # Renders the Dropdown Menu Content in higher contrast high_contrast: Var[bool] # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When True, keyboard navigation will loop from last item to first, and vice versa. Defaults to False. loop: Var[bool] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Var[bool] # The preferred side of the trigger to render against when open. Will be reversed when collisions occur and `avoid_collisions` is enabled.The position of the tooltip. Defaults to "top". side: Var[LiteralSideType] # The distance in pixels from the trigger. Defaults to 0. side_offset: Var[float | int] # The preferred alignment against the trigger. May change when collisions occur. Defaults to "center". align: Var[LiteralAlignType] # An offset in pixels from the "start" or "end" alignment options. align_offset: Var[float | int] # When true, overrides the side and align preferences to prevent collisions with boundary edges. Defaults to True. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: Var[float | int | dict[str, float | int]] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: Var[LiteralStickyType] # Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Var[bool] # Fired when focus moves back after closing. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when a pointer down event happens outside the context menu. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when focus moves outside the context menu. on_focus_outside: EventHandler[no_args_event_spec] # Fired when the pointer interacts outside the context menu. on_interact_outside: EventHandler[no_args_event_spec] class ContextMenuSub(RadixThemesComponent): """Contains all the parts of a submenu.""" tag = "ContextMenu.Sub" # The controlled open state of the submenu. Must be used in conjunction with `on_open_change`. open: Var[bool] # The open state of the submenu when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] class ContextMenuSubTrigger(RadixThemesComponent): """An item that opens a submenu.""" tag = "ContextMenu.SubTrigger" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # Whether the trigger is disabled disabled: Var[bool] # Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside. text_value: Var[str] _valid_parents: ClassVar[list[str]] = ["ContextMenuContent", "ContextMenuSub"] _memoization_mode = MemoizationMode(recursive=False) class ContextMenuSubContent(RadixThemesComponent): """The component that pops out when a submenu is open.""" tag = "ContextMenu.SubContent" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When True, keyboard navigation will loop from last item to first, and vice versa. Defaults to False. loop: Var[bool] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Var[bool] # The distance in pixels from the trigger. Defaults to 0. side_offset: Var[float | int] # An offset in pixels from the "start" or "end" alignment options. align_offset: Var[float | int] # When true, overrides the side and align preferences to prevent collisions with boundary edges. Defaults to True. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: Var[float | int | dict[str, float | int]] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: Var[LiteralStickyType] # Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Var[bool] _valid_parents: ClassVar[list[str]] = ["ContextMenuSub"] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when a pointer down event happens outside the context menu. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when focus moves outside the context menu. on_focus_outside: EventHandler[no_args_event_spec] # Fired when interacting outside the context menu. on_interact_outside: EventHandler[no_args_event_spec] class ContextMenuItem(RadixThemesComponent): """The component that contains the context menu items.""" tag = "ContextMenu.Item" # Override theme color for button color_scheme: Var[LiteralAccentColor] # Shortcut to render a menu item as a link shortcut: Var[str] # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When true, prevents the user from interacting with the item. disabled: Var[bool] # Optional text used for typeahead purposes. By default the typeahead behavior will use the content of the item. Use this when the content is complex, or you have non-textual content inside. text_value: Var[str] _valid_parents: ClassVar[list[str]] = [ "ContextMenuContent", "ContextMenuSubContent", "ContextMenuGroup", ] # Fired when the item is selected. on_select: EventHandler[no_args_event_spec] class ContextMenuSeparator(RadixThemesComponent): """Separates items in a context menu.""" tag = "ContextMenu.Separator" class ContextMenuCheckbox(Checkbox): """The component that contains the checkbox.""" tag = "ContextMenu.CheckboxItem" # Text to render as shortcut. shortcut: Var[str] class ContextMenuLabel(RadixThemesComponent): """The component that contains the label.""" tag = "ContextMenu.Label" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] class ContextMenuGroup(RadixThemesComponent): """The component that contains the group.""" tag = "ContextMenu.Group" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] _valid_parents: ClassVar[list[str]] = [ "ContextMenuContent", "ContextMenuSubContent", ] class ContextMenuRadioGroup(RadixThemesComponent): """The component that contains context menu radio items.""" tag = "ContextMenu.RadioGroup" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # The value of the selected item in the group. value: Var[str] # Props to rename _rename_props = {"onChange": "onValueChange"} # Fired when the value of the radio group changes. on_change: EventHandler[passthrough_event_spec(str)] _valid_parents: ClassVar[list[str]] = [ "ContextMenuRadioItem", "ContextMenuSubContent", "ContextMenuContent", "ContextMenuSub", ] class ContextMenuRadioItem(HighLevelRadioGroup): """The component that contains context menu radio items.""" tag = "ContextMenu.RadioItem" # Override theme color for Dropdown Menu Content color_scheme: Var[LiteralAccentColor] # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # The unique value of the item. value: Var[str] # When true, prevents the user from interacting with the item. disabled: Var[bool] # Event handler called when the user selects an item (via mouse or keyboard). Calling event.preventDefault in this handler will prevent the context menu from closing when selecting that item. on_select: EventHandler[no_args_event_spec] # Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside. text_value: Var[str] class ContextMenu(ComponentNamespace): """Menu representing a set of actions, displayed at the origin of a pointer right-click or long-press.""" root = staticmethod(ContextMenuRoot.create) trigger = staticmethod(ContextMenuTrigger.create) content = staticmethod(ContextMenuContent.create) sub = staticmethod(ContextMenuSub.create) sub_trigger = staticmethod(ContextMenuSubTrigger.create) sub_content = staticmethod(ContextMenuSubContent.create) item = staticmethod(ContextMenuItem.create) separator = staticmethod(ContextMenuSeparator.create) checkbox = staticmethod(ContextMenuCheckbox.create) label = staticmethod(ContextMenuLabel.create) group = staticmethod(ContextMenuGroup.create) radio_group = staticmethod(ContextMenuRadioGroup.create) radio = staticmethod(ContextMenuRadioItem.create) context_menu = ContextMenu() ================================================ FILE: reflex/components/radix/themes/components/data_list.py ================================================ """Components for the DataList component of Radix Themes.""" from types import SimpleNamespace from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var class DataListRoot(RadixThemesComponent): """Root element for a DataList component.""" tag = "DataList.Root" # The orientation of the data list item: "horizontal" | "vertical" orientation: Var[Responsive[Literal["horizontal", "vertical"]]] # The size of the data list item: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Trims the leading whitespace from the start or end of the text. trim: Var[Responsive[Literal["normal", "start", "end", "both"]]] class DataListItem(RadixThemesComponent): """An item in the DataList component.""" tag = "DataList.Item" # The alignment of the data list item within its container. align: Var[Responsive[Literal["start", "center", "end", "baseline", "stretch"]]] class DataListLabel(RadixThemesComponent): """A label in the DataList component.""" tag = "DataList.Label" # The width of the component width: Var[Responsive[str]] # The minimum width of the component min_width: Var[Responsive[str]] # The maximum width of the component max_width: Var[Responsive[str]] # The color scheme for the DataList component. color_scheme: Var[LiteralAccentColor] class DataListValue(RadixThemesComponent): """A value in the DataList component.""" tag = "DataList.Value" class DataList(SimpleNamespace): """DataList components namespace.""" root = staticmethod(DataListRoot.create) item = staticmethod(DataListItem.create) label = staticmethod(DataListLabel.create) value = staticmethod(DataListValue.create) data_list = DataList() ================================================ FILE: reflex/components/radix/themes/components/dialog.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( RadixThemesComponent, RadixThemesTriggerComponent, ) from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var class DialogRoot(RadixThemesComponent): """Root component for Dialog.""" tag = "Dialog.Root" # The controlled open state of the dialog. open: Var[bool] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] # The open state of the dialog when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] class DialogTrigger(RadixThemesTriggerComponent): """Trigger an action or event, to open a Dialog modal.""" tag = "Dialog.Trigger" _memoization_mode = MemoizationMode(recursive=False) class DialogTitle(RadixThemesComponent): """Title component to display inside a Dialog modal.""" tag = "Dialog.Title" class DialogContent(elements.Div, RadixThemesComponent): """Content component to display inside a Dialog modal.""" tag = "Dialog.Content" # DialogContent size "1" - "4" size: Var[Responsive[Literal["1", "2", "3", "4"]]] # Fired when the dialog is opened. on_open_auto_focus: EventHandler[no_args_event_spec] # Fired when the dialog is closed. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the dialog. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when the pointer interacts outside the dialog. on_interact_outside: EventHandler[no_args_event_spec] class DialogDescription(RadixThemesComponent): """Description component to display inside a Dialog modal.""" tag = "Dialog.Description" class DialogClose(RadixThemesTriggerComponent): """Close button component to close an open Dialog modal.""" tag = "Dialog.Close" class Dialog(ComponentNamespace): """Dialog components namespace.""" root = __call__ = staticmethod(DialogRoot.create) trigger = staticmethod(DialogTrigger.create) title = staticmethod(DialogTitle.create) content = staticmethod(DialogContent.create) description = staticmethod(DialogDescription.create) close = staticmethod(DialogClose.create) dialog = Dialog() ================================================ FILE: reflex/components/radix/themes/components/dropdown_menu.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import ClassVar, Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import ( LiteralAccentColor, RadixThemesComponent, RadixThemesTriggerComponent, ) from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var LiteralDirType = Literal["ltr", "rtl"] LiteralSizeType = Literal["1", "2"] LiteralVariantType = Literal["solid", "soft"] LiteralSideType = Literal["top", "right", "bottom", "left"] LiteralAlignType = Literal["start", "center", "end"] LiteralStickyType = Literal[ "partial", "always", ] class DropdownMenuRoot(RadixThemesComponent): """The Dropdown Menu Root Component.""" tag = "DropdownMenu.Root" # The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # The controlled open state of the dropdown menu. Must be used in conjunction with onOpenChange. open: Var[bool] # The modality of the dropdown menu. When set to true, interaction with outside elements will be disabled and only menu content will be visible to screen readers. Defaults to True. modal: Var[bool] # The reading direction of submenus when applicable. If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. dir: Var[LiteralDirType] _invalid_children: ClassVar[list[str]] = ["DropdownMenuItem"] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] class DropdownMenuTrigger(RadixThemesTriggerComponent): """The button that toggles the dropdown menu.""" tag = "DropdownMenu.Trigger" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] _valid_parents: ClassVar[list[str]] = ["DropdownMenuRoot"] _invalid_children: ClassVar[list[str]] = ["DropdownMenuContent"] _memoization_mode = MemoizationMode(recursive=False) class DropdownMenuContent(RadixThemesComponent): """The Dropdown Menu Content component that pops out when the dropdown menu is open.""" tag = "DropdownMenu.Content" # Dropdown Menu Content size "1" - "2" size: Var[Responsive[LiteralSizeType]] # Variant of Dropdown Menu Content: "solid" | "soft" variant: Var[LiteralVariantType] # Override theme color for Dropdown Menu Content color_scheme: Var[LiteralAccentColor] # Renders the Dropdown Menu Content in higher contrast high_contrast: Var[bool] # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When True, keyboard navigation will loop from last item to first, and vice versa. Defaults to False. loop: Var[bool] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Var[bool] # The preferred side of the trigger to render against when open. Will be reversed when collisions occur and `avoid_collisions` is enabled.The position of the tooltip. Defaults to "top". side: Var[LiteralSideType] # The distance in pixels from the trigger. Defaults to 0. side_offset: Var[float | int] # The preferred alignment against the trigger. May change when collisions occur. Defaults to "center". align: Var[LiteralAlignType] # An offset in pixels from the "start" or "end" alignment options. align_offset: Var[float | int] # When true, overrides the side and align preferences to prevent collisions with boundary edges. Defaults to True. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: Var[float | int | dict[str, float | int]] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: Var[LiteralStickyType] # Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Var[bool] # Fired when the dialog is closed. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the dialog. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when focus moves outside the dialog. on_focus_outside: EventHandler[no_args_event_spec] # Fired when the pointer interacts outside the dialog. on_interact_outside: EventHandler[no_args_event_spec] class DropdownMenuSubTrigger(RadixThemesTriggerComponent): """An item that opens a submenu.""" tag = "DropdownMenu.SubTrigger" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When true, prevents the user from interacting with the item. disabled: Var[bool] # Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside. text_value: Var[str] _valid_parents: ClassVar[list[str]] = ["DropdownMenuContent", "DropdownMenuSub"] _memoization_mode = MemoizationMode(recursive=False) class DropdownMenuSub(RadixThemesComponent): """Contains all the parts of a submenu.""" tag = "DropdownMenu.Sub" # The controlled open state of the submenu. Must be used in conjunction with `on_open_change`. open: Var[bool] # The open state of the submenu when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] class DropdownMenuSubContent(RadixThemesComponent): """The component that pops out when a submenu is open. Must be rendered inside DropdownMenuSub.""" tag = "DropdownMenu.SubContent" # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When True, keyboard navigation will loop from last item to first, and vice versa. Defaults to False. loop: Var[bool] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Var[bool] # The distance in pixels from the trigger. Defaults to 0. side_offset: Var[float | int] # An offset in pixels from the "start" or "end" alignment options. align_offset: Var[float | int] # When true, overrides the side and align preferences to prevent collisions with boundary edges. Defaults to True. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: Var[float | int | dict[str, float | int]] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: Var[LiteralStickyType] # Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Var[bool] _valid_parents: ClassVar[list[str]] = ["DropdownMenuSub"] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the dialog. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when focus moves outside the dialog. on_focus_outside: EventHandler[no_args_event_spec] # Fired when the pointer interacts outside the dialog. on_interact_outside: EventHandler[no_args_event_spec] class DropdownMenuItem(RadixThemesComponent): """The Dropdown Menu Item Component.""" tag = "DropdownMenu.Item" # Override theme color for Dropdown Menu Item color_scheme: Var[LiteralAccentColor] # Shortcut to render a menu item as a link shortcut: Var[str] # Change the default rendered element for the one passed as a child, merging their props and behavior. Defaults to False. as_child: Var[bool] # When true, prevents the user from interacting with the item. disabled: Var[bool] # Optional text used for typeahead purposes. By default the typeahead behavior will use the .textContent of the item. Use this when the content is complex, or you have non-textual content inside. text_value: Var[str] _valid_parents: ClassVar[list[str]] = [ "DropdownMenuContent", "DropdownMenuSubContent", ] # Fired when the item is selected. on_select: EventHandler[no_args_event_spec] class DropdownMenuSeparator(RadixThemesComponent): """Dropdown Menu Separator Component. Used to visually separate items in the dropdown menu.""" tag = "DropdownMenu.Separator" class DropdownMenu(ComponentNamespace): """DropdownMenu components namespace.""" root = staticmethod(DropdownMenuRoot.create) trigger = staticmethod(DropdownMenuTrigger.create) content = staticmethod(DropdownMenuContent.create) sub_trigger = staticmethod(DropdownMenuSubTrigger.create) sub = staticmethod(DropdownMenuSub.create) sub_content = staticmethod(DropdownMenuSubContent.create) item = staticmethod(DropdownMenuItem.create) separator = staticmethod(DropdownMenuSeparator.create) menu = dropdown_menu = DropdownMenu() ================================================ FILE: reflex/components/radix/themes/components/hover_card.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( RadixThemesComponent, RadixThemesTriggerComponent, ) from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, passthrough_event_spec from reflex.vars.base import Var class HoverCardRoot(RadixThemesComponent): """For sighted users to preview content available behind a link.""" tag = "HoverCard.Root" # The open state of the hover card when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # The controlled open state of the hover card. Must be used in conjunction with onOpenChange. open: Var[bool] # The duration from when the mouse enters the trigger until the hover card opens. open_delay: Var[int] # The duration from when the mouse leaves the trigger until the hover card closes. close_delay: Var[int] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] class HoverCardTrigger(RadixThemesTriggerComponent): """Wraps the link that will open the hover card.""" tag = "HoverCard.Trigger" _memoization_mode = MemoizationMode(recursive=False) class HoverCardContent(elements.Div, RadixThemesComponent): """Contains the content of the open hover card.""" tag = "HoverCard.Content" # The preferred side of the trigger to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. side: Var[Responsive[Literal["top", "right", "bottom", "left"]]] # The distance in pixels from the trigger. side_offset: Var[int] # The preferred alignment against the trigger. May change when collisions occur. align: Var[Literal["start", "center", "end"]] # An offset in pixels from the "start" or "end" alignment options. align_offset: Var[int] # Whether or not the hover card should avoid collisions with its trigger. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. collision_padding: Var[float | int | dict[str, float | int]] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless sticky: Var[Literal["partial", "always"]] # Whether to hide the content when the trigger becomes fully occluded. hide_when_detached: Var[bool] # Hovercard size "1" - "3" size: Var[Responsive[Literal["1", "2", "3"]]] class HoverCard(ComponentNamespace): """For sighted users to preview content available behind a link.""" root = __call__ = staticmethod(HoverCardRoot.create) trigger = staticmethod(HoverCardTrigger.create) content = staticmethod(HoverCardContent.create) hover_card = HoverCard() ================================================ FILE: reflex/components/radix/themes/components/icon_button.py ================================================ """Interactive components provided by @radix-ui/themes.""" from __future__ import annotations from typing import Literal from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive from reflex.components.core.match import Match from reflex.components.el import elements from reflex.components.lucide import Icon from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, LiteralVariant, RadixLoadingProp, RadixThemesComponent, ) from reflex.style import Style from reflex.vars.base import Var LiteralButtonSize = Literal["1", "2", "3", "4"] RADIX_TO_LUCIDE_SIZE = {"1": 12, "2": 24, "3": 36, "4": 48} class IconButton(elements.Button, RadixLoadingProp, RadixThemesComponent): """A button designed specifically for usage with a single icon.""" tag = "IconButton" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Button size "1" - "4" size: Var[Responsive[LiteralButtonSize]] # Variant of button: "classic" | "solid" | "soft" | "surface" | "outline" | "ghost" variant: Var[LiteralVariant] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Whether to render the button with higher contrast color against background high_contrast: Var[bool] # Override theme radius for button: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] @classmethod def create(cls, *children, **props) -> Component: """Create a IconButton component. Args: *children: The children of the component. **props: The properties of the component. Returns: The IconButton component. Raises: ValueError: If no children are passed. """ if children: if isinstance(children[0], str): children = [ Icon.create( children[0], ) ] else: msg = "IconButton requires a child icon. Pass a string as the first child or a rx.icon." raise ValueError(msg) if "size" in props: if isinstance(props["size"], str): children[0].size = RADIX_TO_LUCIDE_SIZE[props["size"]] # pyright: ignore[reportAttributeAccessIssue] else: size_map_var = Match.create( props["size"], *list(RADIX_TO_LUCIDE_SIZE.items()), 12, ) if not isinstance(size_map_var, Var): msg = f"Match did not return a Var: {size_map_var}" raise ValueError(msg) children[0].size = size_map_var # pyright: ignore[reportAttributeAccessIssue] return super().create(*children, **props) def add_style(self): """Add style to the component. Returns: The style of the component. """ return Style({"padding": "6px"}) icon_button = IconButton.create ================================================ FILE: reflex/components/radix/themes/components/inset.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import RadixThemesComponent from reflex.vars.base import Var LiteralButtonSize = Literal["1", "2", "3", "4"] class Inset(elements.Div, RadixThemesComponent): """Applies a negative margin to allow content to bleed into the surrounding container.""" tag = "Inset" # The side side: Var[Responsive[Literal["x", "y", "top", "bottom", "right", "left"]]] # How to clip the element's content: "border-box" | "padding-box" clip: Var[Responsive[Literal["border-box", "padding-box"]]] # Padding p: Var[Responsive[int | str]] # Padding on the x axis px: Var[Responsive[int | str]] # Padding on the y axis py: Var[Responsive[int | str]] # Padding on the top pt: Var[Responsive[int | str]] # Padding on the right pr: Var[Responsive[int | str]] # Padding on the bottom pb: Var[Responsive[int | str]] # Padding on the left pl: Var[Responsive[int | str]] inset = Inset.create ================================================ FILE: reflex/components/radix/themes/components/popover.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( RadixThemesComponent, RadixThemesTriggerComponent, ) from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var class PopoverRoot(RadixThemesComponent): """Floating element for displaying rich content, triggered by a button.""" tag = "Popover.Root" # The controlled open state of the popover. open: Var[bool] # The modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers. modal: Var[bool] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] # The open state of the popover when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] class PopoverTrigger(RadixThemesTriggerComponent): """Wraps the control that will open the popover.""" tag = "Popover.Trigger" _memoization_mode = MemoizationMode(recursive=False) class PopoverContent(elements.Div, RadixThemesComponent): """Contains content to be rendered in the open popover.""" tag = "Popover.Content" # Size of the button: "1" | "2" | "3" | "4" size: Var[Responsive[Literal["1", "2", "3", "4"]]] # The preferred side of the anchor to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. side: Var[Literal["top", "right", "bottom", "left"]] # The distance in pixels from the anchor. side_offset: Var[int] # The preferred alignment against the anchor. May change when collisions occur. align: Var[Literal["start", "center", "end"]] # The vertical distance in pixels from the anchor. align_offset: Var[int] # When true, overrides the side andalign preferences to prevent collisions with boundary edges. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: Var[float | int | dict[str, float | int]] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: Var[Literal["partial", "always"]] # Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Var[bool] # Fired when the dialog is opened. on_open_auto_focus: EventHandler[no_args_event_spec] # Fired when the dialog is closed. on_close_auto_focus: EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the dialog. on_pointer_down_outside: EventHandler[no_args_event_spec] # Fired when focus moves outside the dialog. on_focus_outside: EventHandler[no_args_event_spec] # Fired when the pointer interacts outside the dialog. on_interact_outside: EventHandler[no_args_event_spec] class PopoverClose(RadixThemesTriggerComponent): """Wraps the control that will close the popover.""" tag = "Popover.Close" class Popover(ComponentNamespace): """Floating element for displaying rich content, triggered by a button.""" root = staticmethod(PopoverRoot.create) trigger = staticmethod(PopoverTrigger.create) content = staticmethod(PopoverContent.create) close = staticmethod(PopoverClose.create) popover = Popover() ================================================ FILE: reflex/components/radix/themes/components/progress.py ================================================ """Progress from Radix Themes.""" from typing import Literal from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.style import Style from reflex.vars.base import Var class Progress(RadixThemesComponent): """A progress bar component.""" tag = "Progress" # The value of the progress bar: 0 to max (default 100) value: Var[int] # The maximum progress value. max: Var[int] # The size of the progress bar: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # The variant of the progress bar: "classic" | "surface" | "soft" variant: Var[Literal["classic", "surface", "soft"]] # The color theme of the progress bar color_scheme: Var[LiteralAccentColor] # Whether to render the progress bar with higher contrast color against background high_contrast: Var[bool] # Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full" radius: Var[Literal["none", "small", "medium", "large", "full"]] # The duration of the progress bar animation. Once the duration times out, the progress bar will start an indeterminate animation. duration: Var[str] # The color of the progress bar fill animation. fill_color: Var[str] @staticmethod def _color_selector(color: str) -> Style: """Return a style object with the correct color and css selector. Args: color: Color of the fill part. Returns: Style: Style object with the correct css selector and color. """ return Style({".rt-ProgressIndicator": {"background_color": color}}) @classmethod def create(cls, *children, **props) -> Component: """Create a Progress component. Args: *children: The children of the component. **props: The properties of the component. Returns: The Progress Component. """ props.setdefault("width", "100%") if "fill_color" in props: color = props.get("fill_color", "") style = props.get("style", {}) style = style | cls._color_selector(color) props["style"] = style return super().create(*children, **props) progress = Progress.create ================================================ FILE: reflex/components/radix/themes/components/radio.py ================================================ """Radio component from Radix Themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var class Radio(RadixThemesComponent): """A radio component.""" tag = "Radio" # The size of the radio: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button: "classic" | "surface" | "soft" variant: Var[Literal["classic", "surface", "soft"]] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Uses a higher contrast color for the component. high_contrast: Var[bool] # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child = Var[bool] radio = Radio.create ================================================ FILE: reflex/components/radix/themes/components/radio_cards.py ================================================ """Radio component from Radix Themes.""" from types import SimpleNamespace from typing import ClassVar, Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.event import EventHandler, passthrough_event_spec from reflex.vars.base import Var class RadioCardsRoot(RadixThemesComponent): """Root element for RadioCards component.""" tag = "RadioCards.Root" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # The size of the checkbox cards: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button: "classic" | "surface" | "soft" variant: Var[Literal["classic", "surface"]] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Uses a higher contrast color for the component. high_contrast: Var[bool] # The number of columns: columns: Var[Responsive[str | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]]] # The gap between the checkbox cards: gap: Var[Responsive[str | Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"]]] default_value: Var[str] # The controlled value of the radio item to check. Should be used in conjunction with onValueChange. value: Var[str] # The name of the group. Submitted with its owning form as part of a name/value pair. name: Var[str] # When true, prevents the user from interacting with radio items. disabled: Var[bool] # When true, indicates that the user must check a radio item before the owning form can be submitted. required: Var[bool] # The orientation of the component. orientation: Var[Literal["horizontal", "vertical", "undefined"]] # The reading direction of the radio group. If omitted, # inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode. dir: Var[Literal["ltr", "rtl"]] # When true, keyboard navigation will loop from last item to first, and vice versa. loop: Var[bool] # Event handler called when the value changes. on_value_change: EventHandler[passthrough_event_spec(str)] class RadioCardsItem(RadixThemesComponent): """Item element for RadioCards component.""" tag = "RadioCards.Item" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # The value given as data when submitted with a name. value: Var[str] # When true, prevents the user from interacting with the radio item. disabled: Var[bool] # When true, indicates that the user must check the radio item before the owning form can be submitted. required: Var[bool] _valid_parents: ClassVar[list[str]] = ["RadioCardsRoot"] class RadioCards(SimpleNamespace): """RadioCards components namespace.""" root = staticmethod(RadioCardsRoot.create) item = staticmethod(RadioCardsItem.create) radio_cards = RadioCards() ================================================ FILE: reflex/components/radix/themes/components/radio_group.py ================================================ """Interactive components provided by @radix-ui/themes.""" from __future__ import annotations from collections.abc import Sequence from typing import Literal import reflex as rx from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralSpacing, RadixThemesComponent, ) from reflex.components.radix.themes.layout.flex import Flex from reflex.components.radix.themes.typography.text import Text from reflex.event import EventHandler, passthrough_event_spec from reflex.utils import types from reflex.vars.base import LiteralVar, Var from reflex.vars.sequence import StringVar LiteralFlexDirection = Literal["row", "column", "row-reverse", "column-reverse"] class RadioGroupRoot(RadixThemesComponent): """A set of interactive radio buttons where only one can be selected at a time.""" tag = "RadioGroup.Root" # The size of the radio group: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] = LiteralVar.create("2") # The variant of the radio group variant: Var[Literal["classic", "surface", "soft"]] = LiteralVar.create("classic") # The color of the radio group color_scheme: Var[LiteralAccentColor] # Whether to render the radio group with higher contrast color against background high_contrast: Var[bool] # The controlled value of the radio item to check. Should be used in conjunction with on_change. value: Var[str] # The initial value of checked radio item. Should be used in conjunction with on_change. default_value: Var[str] # Whether the radio group is disabled disabled: Var[bool] # The name of the group. Submitted with its owning form as part of a name/value pair. name: Var[str] # Whether the radio group is required required: Var[bool] # Props to rename _rename_props = {"onChange": "onValueChange"} # Fired when the value of the radio group changes. on_change: EventHandler[passthrough_event_spec(str)] class RadioGroupItem(RadixThemesComponent): """An item in the group that can be checked.""" tag = "RadioGroup.Item" # The value of the radio item to check. Should be used in conjunction with on_change. value: Var[str] # When true, prevents the user from interacting with the radio item. disabled: Var[bool] # When true, indicates that the user must check the radio item before the owning form can be submitted. required: Var[bool] class HighLevelRadioGroup(RadixThemesComponent): """High level wrapper for the RadioGroup component.""" # The items of the radio group. items: Var[Sequence[str]] # The direction of the radio group. direction: Var[LiteralFlexDirection] = LiteralVar.create("row") # The gap between the items of the radio group. spacing: Var[LiteralSpacing] = LiteralVar.create("2") # The size of the radio group. size: Var[Literal["1", "2", "3"]] = LiteralVar.create("2") # The variant of the radio group variant: Var[Literal["classic", "surface", "soft"]] = LiteralVar.create("classic") # The color of the radio group color_scheme: Var[LiteralAccentColor] # Whether to render the radio group with higher contrast color against background high_contrast: Var[bool] # The controlled value of the radio item to check. Should be used in conjunction with on_change. value: Var[str] # The initial value of checked radio item. Should be used in conjunction with on_change. default_value: Var[str] # Whether the radio group is disabled disabled: Var[bool] # The name of the group. Submitted with its owning form as part of a name/value pair. name: Var[str] # Whether the radio group is required required: Var[bool] # Props to rename _rename_props = {"onChange": "onValueChange"} @classmethod def create( cls, items: Var[Sequence[str | int | float | list | dict | bool | None]], **props, ) -> Component: """Create a radio group component. Args: items: The items of the radio group. **props: Additional properties to apply to the accordion item. Returns: The created radio group component. Raises: TypeError: If the type of items is invalid. """ direction = props.pop("direction", "row") spacing = props.pop("spacing", "2") size = props.pop("size", "2") variant = props.pop("variant", "classic") color_scheme = props.pop("color_scheme", None) default_value = props.pop("default_value", "") if not isinstance(items, (list, Var)) or ( isinstance(items, Var) and not types._issubclass(items._var_type, list) ): items_type = type(items) if not isinstance(items, Var) else items._var_type msg = f"The radio group component takes in a list, got {items_type} instead" raise TypeError(msg) default_value = LiteralVar.create(default_value) # convert only non-strings to json(JSON.stringify) so quotes are not rendered # for string literal types. if isinstance(default_value, str) or ( isinstance(default_value, Var) and default_value._var_type is str ): default_value = LiteralVar.create(default_value) else: default_value = LiteralVar.create(default_value).to_string() def radio_group_item(value: Var) -> Component: item_value = rx.cond( value.js_type() == "string", value, value.to_string(), ).to(StringVar) return Text.create( Flex.create( RadioGroupItem.create( value=item_value, disabled=props.get("disabled", LiteralVar.create(False)), ), item_value, spacing="2", ), size=size, as_="label", ) children = [ rx.foreach( items, radio_group_item, ) ] return RadioGroupRoot.create( Flex.create( *children, direction=direction, spacing=spacing, ), size=size, variant=variant, color_scheme=color_scheme, default_value=default_value, **props, ) class RadioGroup(ComponentNamespace): """RadioGroup components namespace.""" root = staticmethod(RadioGroupRoot.create) item = staticmethod(RadioGroupItem.create) __call__ = staticmethod(HighLevelRadioGroup.create) radio = radio_group = RadioGroup() ================================================ FILE: reflex/components/radix/themes/components/scroll_area.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.radix.themes.base import RadixThemesComponent from reflex.vars.base import Var class ScrollArea(RadixThemesComponent): """Custom styled, cross-browser scrollable area using native functionality.""" tag = "ScrollArea" # The alignment of the scroll area scrollbars: Var[Literal["vertical", "horizontal", "both"]] # Describes the nature of scrollbar visibility, similar to how the scrollbar preferences in MacOS control visibility of native scrollbars. "auto" | "always" | "scroll" | "hover" type: Var[Literal["auto", "always", "scroll", "hover"]] # If type is set to either "scroll" or "hover", this prop determines the length of time, in milliseconds, before the scrollbars are hidden after the user stops interacting with scrollbars. scroll_hide_delay: Var[int] scroll_area = ScrollArea.create ================================================ FILE: reflex/components/radix/themes/components/segmented_control.py ================================================ """SegmentedControl from Radix Themes.""" from __future__ import annotations from collections.abc import Sequence from types import SimpleNamespace from typing import ClassVar, Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.event import EventHandler from reflex.vars.base import Var def on_value_change( value: Var[str | list[str]], ) -> tuple[Var[str | list[str]]]: """Handle the on_value_change event. Args: value: The value of the event. Returns: The value of the event. """ return (value,) class SegmentedControlRoot(RadixThemesComponent): """Root element for a SegmentedControl component.""" tag = "SegmentedControl.Root" # The size of the segmented control: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button: "classic" | "surface" variant: Var[Literal["classic", "surface"]] # The type of the segmented control, either "single" for selecting one option or "multiple" for selecting multiple options. type: Var[Literal["single", "multiple"]] # Override theme color for button color_scheme: Var[LiteralAccentColor] # The radius of the segmented control: "none" | "small" | "medium" | "large" | "full" radius: Var[Literal["none", "small", "medium", "large", "full"]] # The default value of the segmented control. default_value: Var[str | Sequence[str]] # The current value of the segmented control. value: Var[str | Sequence[str]] # Handles the `onChange` event for the SegmentedControl component. on_change: EventHandler[on_value_change] _rename_props = {"onChange": "onValueChange"} class SegmentedControlItem(RadixThemesComponent): """An item in the SegmentedControl component.""" tag = "SegmentedControl.Item" # The value of the item. value: Var[str] _valid_parents: ClassVar[list[str]] = ["SegmentedControlRoot"] class SegmentedControl(SimpleNamespace): """SegmentedControl components namespace.""" root = staticmethod(SegmentedControlRoot.create) item = staticmethod(SegmentedControlItem.create) segmented_control = SegmentedControl() ================================================ FILE: reflex/components/radix/themes/components/select.py ================================================ """Interactive components provided by @radix-ui/themes.""" from collections.abc import Sequence from typing import ClassVar, Literal import reflex as rx from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, RadixThemesComponent, ) from reflex.constants.compiler import MemoizationMode from reflex.event import no_args_event_spec, passthrough_event_spec from reflex.vars.base import Var class SelectRoot(RadixThemesComponent): """Displays a list of options for the user to pick from, triggered by a button.""" tag = "Select.Root" # The size of the select: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # The value of the select when initially rendered. Use when you do not need to control the state of the select. default_value: Var[str] # The controlled value of the select. Should be used in conjunction with on_change. value: Var[str] # The open state of the select when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # The controlled open state of the select. Must be used in conjunction with on_open_change. open: Var[bool] # The name of the select control when submitting the form. name: Var[str] # When True, prevents the user from interacting with select. disabled: Var[bool] # When True, indicates that the user must select a value before the owning form can be submitted. required: Var[bool] # Props to rename _rename_props = {"onChange": "onValueChange"} # Fired when the value of the select changes. on_change: rx.EventHandler[passthrough_event_spec(str)] # Fired when the select is opened or closed. on_open_change: rx.EventHandler[passthrough_event_spec(bool)] class SelectTrigger(RadixThemesComponent): """The button that toggles the select.""" tag = "Select.Trigger" # Variant of the select trigger variant: Var[Literal["classic", "surface", "soft", "ghost"]] # The color of the select trigger color_scheme: Var[LiteralAccentColor] # The radius of the select trigger radius: Var[LiteralRadius] # The placeholder of the select trigger placeholder: Var[str] _valid_parents: ClassVar[list[str]] = ["SelectRoot"] _memoization_mode = MemoizationMode(recursive=False) class SelectContent(RadixThemesComponent): """The component that pops out when the select is open.""" tag = "Select.Content" # The variant of the select content variant: Var[Literal["solid", "soft"]] # The color of the select content color_scheme: Var[LiteralAccentColor] # Whether to render the select content with higher contrast color against background high_contrast: Var[bool] # The positioning mode to use, item-aligned is the default and behaves similarly to a native MacOS menu by positioning content relative to the active item. popper positions content in the same way as our other primitives, for example Popover or DropdownMenu. position: Var[Literal["item-aligned", "popper"]] # The preferred side of the anchor to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. Only available when position is set to popper. side: Var[Literal["top", "right", "bottom", "left"]] # The distance in pixels from the anchor. Only available when position is set to popper. side_offset: Var[int] # The preferred alignment against the anchor. May change when collisions occur. Only available when position is set to popper. align: Var[Literal["start", "center", "end"]] # The vertical distance in pixels from the anchor. Only available when position is set to popper. align_offset: Var[int] # Fired when the select content is closed. on_close_auto_focus: rx.EventHandler[no_args_event_spec] # Fired when the escape key is pressed. on_escape_key_down: rx.EventHandler[no_args_event_spec] # Fired when a pointer down event happens outside the select content. on_pointer_down_outside: rx.EventHandler[no_args_event_spec] class SelectGroup(RadixThemesComponent): """Used to group multiple items.""" tag = "Select.Group" _valid_parents: ClassVar[list[str]] = ["SelectContent"] class SelectItem(RadixThemesComponent): """The component that contains the select items.""" tag = "Select.Item" # The value given as data when submitting a form with a name. value: Var[str] # Whether the select item is disabled disabled: Var[bool] _valid_parents: ClassVar[list[str]] = ["SelectGroup", "SelectContent"] class SelectLabel(RadixThemesComponent): """Used to render the label of a group, it isn't focusable using arrow keys.""" tag = "Select.Label" _valid_parents: ClassVar[list[str]] = ["SelectGroup"] class SelectSeparator(RadixThemesComponent): """Used to visually separate items in the Select.""" tag = "Select.Separator" class HighLevelSelect(SelectRoot): """High level wrapper for the Select component.""" # The items of the select. items: Var[Sequence[str]] # The placeholder of the select. placeholder: Var[str] # The label of the select. label: Var[str] # The color of the select. color_scheme: Var[LiteralAccentColor] # Whether to render the select with higher contrast color against background. high_contrast: Var[bool] # The variant of the select. variant: Var[Literal["classic", "surface", "soft", "ghost"]] # The radius of the select. radius: Var[LiteralRadius] # The width of the select. width: Var[str] # The positioning mode to use. Default is "item-aligned". position: Var[Literal["item-aligned", "popper"]] @classmethod def create(cls, items: list[str] | Var[list[str]], **props) -> Component: """Create a select component. Args: items: The items of the select. **props: Additional properties to apply to the select component. Returns: The select component. """ trigger_prop_list = [ "id", "placeholder", "variant", "radius", "width", "flex_shrink", "custom_attrs", ] content_props = { prop: props.pop(prop) for prop in ["high_contrast", "position"] if prop in props } trigger_props = { prop: props.pop(prop) for prop in trigger_prop_list if prop in props } color_scheme = props.pop("color_scheme", None) if color_scheme is not None: content_props["color_scheme"] = color_scheme trigger_props["color_scheme"] = color_scheme label = props.pop("label", None) if isinstance(items, Var): child = [ rx.foreach(items, lambda item: SelectItem.create(item, value=item)) ] else: child = [SelectItem.create(item, value=item) for item in items] return SelectRoot.create( SelectTrigger.create( **trigger_props, ), SelectContent.create( SelectGroup.create( SelectLabel.create(label) if label is not None else "", *child, ), **content_props, ), **props, ) class Select(ComponentNamespace): """Select components namespace.""" root = staticmethod(SelectRoot.create) trigger = staticmethod(SelectTrigger.create) content = staticmethod(SelectContent.create) group = staticmethod(SelectGroup.create) item = staticmethod(SelectItem.create) separator = staticmethod(SelectSeparator.create) label = staticmethod(SelectLabel.create) __call__ = staticmethod(HighLevelSelect.create) select = Select() ================================================ FILE: reflex/components/radix/themes/components/separator.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import LiteralVar, Var LiteralSeparatorSize = Literal["1", "2", "3", "4"] class Separator(RadixThemesComponent): """Visually or semantically separates content.""" tag = "Separator" # The size of the separator: "1" | "2" | "3" | "4" size: Var[Responsive[LiteralSeparatorSize]] = LiteralVar.create("4") # The color of the separator color_scheme: Var[LiteralAccentColor] # The orientation of the separator. orientation: Var[Responsive[Literal["horizontal", "vertical"]]] # When true, signifies that it is purely visual, carries no semantic meaning, and ensures it is not present in the accessibility tree. decorative: Var[bool] # Alias to divider. divider = separator = Separator.create ================================================ FILE: reflex/components/radix/themes/components/skeleton.py ================================================ """Skeleton theme from Radix components.""" from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import RadixLoadingProp, RadixThemesComponent from reflex.constants.compiler import MemoizationMode from reflex.vars.base import Var class Skeleton(RadixLoadingProp, RadixThemesComponent): """Skeleton component.""" tag = "Skeleton" # The width of the skeleton width: Var[Responsive[str]] # The minimum width of the skeleton min_width: Var[Responsive[str]] # The maximum width of the skeleton max_width: Var[Responsive[str]] # The height of the skeleton height: Var[Responsive[str]] # The minimum height of the skeleton min_height: Var[Responsive[str]] # The maximum height of the skeleton max_height: Var[Responsive[str]] _memoization_mode = MemoizationMode(recursive=False) skeleton = Skeleton.create ================================================ FILE: reflex/components/radix/themes/components/slider.py ================================================ """Interactive components provided by @radix-ui/themes.""" from __future__ import annotations from collections.abc import Sequence from typing import Literal from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.event import EventHandler, passthrough_event_spec from reflex.utils.types import typehint_issubclass from reflex.vars.base import Var on_value_event_spec = ( passthrough_event_spec(list[float]), passthrough_event_spec(list[int]), ) class Slider(RadixThemesComponent): """Provides user selection from a range of values.""" tag = "Slider" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Button size "1" - "3" size: Var[Responsive[Literal["1", "2", "3"]]] # Variant of button variant: Var[Literal["classic", "surface", "soft"]] # Override theme color for button color_scheme: Var[LiteralAccentColor] # Whether to render the button with higher contrast color against background high_contrast: Var[bool] # Override theme radius for button: "none" | "small" | "full" radius: Var[Literal["none", "small", "full"]] # The value of the slider when initially rendered. Use when you do not need to control the state of the slider. default_value: Var[Sequence[float | int] | float | int] # The controlled value of the slider. Must be used in conjunction with onValueChange. value: Var[Sequence[float | int]] # The name of the slider. Submitted with its owning form as part of a name/value pair. name: Var[str] # The width of the slider. width: Var[str | None] = Var.create("100%") # The minimum value of the slider. min: Var[float | int] # The maximum value of the slider. max: Var[float | int] # The step value of the slider. step: Var[float | int] # Whether the slider is disabled disabled: Var[bool] # The orientation of the slider. orientation: Var[Literal["horizontal", "vertical"]] # Props to rename _rename_props = {"onChange": "onValueChange"} # Fired when the value of the slider changes. on_change: EventHandler[on_value_event_spec] # Fired when a thumb is released after being dragged. on_value_commit: EventHandler[on_value_event_spec] @classmethod def create( cls, *children, **props, ) -> Component: """Create a Slider component. Args: *children: The children of the component. **props: The properties of the component. Returns: The component. """ default_value = props.pop("default_value", [50]) width = props.pop("width", "100%") if isinstance(default_value, Var): if typehint_issubclass(default_value._var_type, int | float): default_value = [default_value] elif isinstance(default_value, (int, float)): default_value = [default_value] style = props.setdefault("style", {}) style.update({ "width": width, }) return super().create(*children, default_value=default_value, **props) slider = Slider.create ================================================ FILE: reflex/components/radix/themes/components/spinner.py ================================================ """Radix Spinner Component.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import RadixLoadingProp, RadixThemesComponent from reflex.vars.base import Var LiteralSpinnerSize = Literal["1", "2", "3"] class Spinner(RadixLoadingProp, RadixThemesComponent): """A spinner component.""" tag = "Spinner" is_default = False # The size of the spinner. size: Var[Responsive[LiteralSpinnerSize]] spinner = Spinner.create ================================================ FILE: reflex/components/radix/themes/components/switch.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.event import EventHandler, passthrough_event_spec from reflex.vars.base import Var LiteralSwitchSize = Literal["1", "2", "3"] class Switch(RadixThemesComponent): """A toggle switch alternative to the checkbox.""" tag = "Switch" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Whether the switch is checked by default default_checked: Var[bool] # Whether the switch is checked checked: Var[bool] # If true, prevent the user from interacting with the switch disabled: Var[bool] # If true, the user must interact with the switch to submit the form required: Var[bool] # The name of the switch (when submitting a form) name: Var[str] # The value associated with the "on" position value: Var[str] # Switch size "1" - "4" size: Var[Responsive[LiteralSwitchSize]] # Variant of switch: "classic" | "surface" | "soft" variant: Var[Literal["classic", "surface", "soft"]] # Override theme color for switch color_scheme: Var[LiteralAccentColor] # Whether to render the switch with higher contrast color against background high_contrast: Var[bool] # Override theme radius for switch: "none" | "small" | "full" radius: Var[Literal["none", "small", "full"]] # Props to rename _rename_props = {"onChange": "onCheckedChange"} # Fired when the value of the switch changes on_change: EventHandler[passthrough_event_spec(bool)] switch = Switch.create ================================================ FILE: reflex/components/radix/themes/components/table.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import ClassVar, Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import CommonPaddingProps, RadixThemesComponent from reflex.vars.base import Var class TableRoot(elements.Table, RadixThemesComponent): """A semantic table for presenting tabular data.""" tag = "Table.Root" # The size of the table: "1" | "2" | "3" size: Var[Responsive[Literal["1", "2", "3"]]] # The variant of the table variant: Var[Literal["surface", "ghost"]] class TableHeader(elements.Thead, RadixThemesComponent): """The header of the table defines column names and other non-data elements.""" tag = "Table.Header" _invalid_children: ClassVar[list[str]] = ["TableBody"] _valid_parents: ClassVar[list[str]] = ["TableRoot"] class TableRow(elements.Tr, RadixThemesComponent): """A row containing table cells.""" tag = "Table.Row" # The alignment of the row align: Var[Literal["start", "center", "end", "baseline"]] _invalid_children: ClassVar[list[str]] = ["TableBody", "TableHeader", "TableRow"] class TableColumnHeaderCell(elements.Th, RadixThemesComponent): """A table cell that is semantically treated as a column header.""" tag = "Table.ColumnHeaderCell" # The justification of the column justify: Var[Literal["start", "center", "end"]] # The minimum width of the cell min_width: Var[Responsive[str]] # The maximum width of the cell max_width: Var[Responsive[str]] _invalid_children: ClassVar[list[str]] = [ "TableBody", "TableHeader", "TableRow", "TableCell", "TableColumnHeaderCell", "TableRowHeaderCell", ] class TableBody(elements.Tbody, RadixThemesComponent): """The body of the table contains the data rows.""" tag = "Table.Body" _invalid_children: ClassVar[list[str]] = [ "TableHeader", "TableRowHeaderCell", "TableColumnHeaderCell", "TableCell", ] _valid_parents: ClassVar[list[str]] = ["TableRoot"] class TableCell(elements.Td, CommonPaddingProps, RadixThemesComponent): """A cell containing data.""" tag = "Table.Cell" # The justification of the column justify: Var[Literal["start", "center", "end"]] # The minimum width of the cell min_width: Var[Responsive[str]] # The maximum width of the cell max_width: Var[Responsive[str]] _invalid_children: ClassVar[list[str]] = [ "TableBody", "TableHeader", "TableRowHeaderCell", "TableColumnHeaderCell", "TableCell", ] class TableRowHeaderCell(elements.Th, CommonPaddingProps, RadixThemesComponent): """A table cell that is semantically treated as a row header.""" tag = "Table.RowHeaderCell" # The justification of the column justify: Var[Literal["start", "center", "end"]] # The minimum width of the cell min_width: Var[Responsive[str]] # The maximum width of the cell max_width: Var[Responsive[str]] _invalid_children: ClassVar[list[str]] = [ "TableBody", "TableHeader", "TableRow", "TableCell", "TableColumnHeaderCell", "TableRowHeaderCell", ] class Table(ComponentNamespace): """Table components namespace.""" root = staticmethod(TableRoot.create) header = staticmethod(TableHeader.create) body = staticmethod(TableBody.create) row = staticmethod(TableRow.create) cell = staticmethod(TableCell.create) column_header_cell = staticmethod(TableColumnHeaderCell.create) row_header_cell = staticmethod(TableRowHeaderCell.create) table = Table() ================================================ FILE: reflex/components/radix/themes/components/tabs.py ================================================ """Interactive components provided by @radix-ui/themes.""" from __future__ import annotations from typing import Any, ClassVar, Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.core.colors import color from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, passthrough_event_spec from reflex.vars.base import Var vertical_orientation_css = "&[data-orientation='vertical']" class TabsRoot(RadixThemesComponent): """Set of content sections to be displayed one at a time.""" tag = "Tabs.Root" # The value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs. default_value: Var[str] # The controlled value of the tab that should be active. Use when you need to control the state of the tabs. value: Var[str] # The orientation of the tabs. orientation: Var[Literal["horizontal", "vertical"]] # Reading direction of the tabs. dir: Var[Literal["ltr", "rtl"]] # The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked. activation_mode: Var[Literal["automatic", "manual"]] # Props to rename _rename_props = {"onChange": "onValueChange"} # Fired when the value of the tabs changes. on_change: EventHandler[passthrough_event_spec(str)] def add_style(self) -> dict[str, Any] | None: """Add style for the component. Returns: The style to add. """ return { vertical_orientation_css: { "display": "flex", } } class TabsList(RadixThemesComponent): """Contains the triggers that sit alongside the active content.""" tag = "Tabs.List" # Tabs size "1" - "2" size: Var[Responsive[Literal["1", "2"]]] # When true, the tabs will loop when reaching the end. loop: Var[bool] def add_style(self): """Add style for the component. Returns: The style to add. """ return { vertical_orientation_css: { "display": "block", "box_shadow": f"inset -1px 0 0 0 {color('gray', 5, alpha=True)}", }, } class TabsTrigger(RadixThemesComponent): """The button that activates its associated content.""" tag = "Tabs.Trigger" # The value of the tab. Must be unique for each tab. value: Var[str] # Whether the tab is disabled disabled: Var[bool] # The color of the line under the tab when active. color_scheme: Var[LiteralAccentColor] _valid_parents: ClassVar[list[str]] = ["TabsList"] _memoization_mode = MemoizationMode(recursive=False) @classmethod def create(cls, *children, **props) -> Component: """Create a TabsTrigger component. Args: *children: The children of the component. **props: The properties of the component. Returns: The TabsTrigger Component. """ if "color_scheme" in props: custom_attrs = props.setdefault("custom_attrs", {}) custom_attrs["data-accent-color"] = props["color_scheme"] return super().create(*children, **props) def _exclude_props(self) -> list[str]: return ["color_scheme"] def add_style(self) -> dict[str, Any] | None: """Add style for the component. Returns: The style to add. """ return {vertical_orientation_css: {"width": "100%"}} class TabsContent(RadixThemesComponent): """Contains the content associated with each trigger.""" tag = "Tabs.Content" # The value of the tab. Must be unique for each tab. value: Var[str] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Var[bool] def add_style(self) -> dict[str, Any] | None: """Add style for the component. Returns: The style to add. """ return { vertical_orientation_css: {"width": "100%", "margin": None}, } class Tabs(ComponentNamespace): """Set of content sections to be displayed one at a time.""" root = __call__ = staticmethod(TabsRoot.create) list = staticmethod(TabsList.create) trigger = staticmethod(TabsTrigger.create) content = staticmethod(TabsContent.create) tabs = Tabs() ================================================ FILE: reflex/components/radix/themes/components/text_area.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive from reflex.components.core.debounce import DebounceInput from reflex.components.el import elements from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, RadixThemesComponent, ) from reflex.vars.base import Var LiteralTextAreaSize = Literal["1", "2", "3"] LiteralTextAreaResize = Literal["none", "vertical", "horizontal", "both"] class TextArea(RadixThemesComponent, elements.Textarea): """The input part of a TextArea, may be used by itself.""" tag = "TextArea" # The size of the text area: "1" | "2" | "3" size: Var[Responsive[LiteralTextAreaSize]] # The variant of the text area variant: Var[Literal["classic", "surface", "soft"]] # The resize behavior of the text area: "none" | "vertical" | "horizontal" | "both" resize: Var[Responsive[LiteralTextAreaResize]] # The color of the text area color_scheme: Var[LiteralAccentColor] # The radius of the text area: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] # Whether the form control should have autocomplete enabled auto_complete: Var[bool] # Automatically focuses the textarea when the page loads auto_focus: Var[bool] # The default value of the textarea when initially rendered default_value: Var[str] # Name part of the textarea to submit in 'dir' and 'name' pair when form is submitted dirname: Var[str] # Disables the textarea disabled: Var[bool] # Associates the textarea with a form (by id) form: Var[str] # Maximum number of characters allowed in the textarea max_length: Var[int] # Minimum number of characters required in the textarea min_length: Var[int] # Name of the textarea, used when submitting the form name: Var[str] # Placeholder text in the textarea placeholder: Var[str] # Indicates whether the textarea is read-only read_only: Var[bool] # Indicates that the textarea is required required: Var[bool] # Visible number of lines in the text control rows: Var[str] # The controlled value of the textarea, read only unless used with on_change value: Var[str] # How the text in the textarea is to be wrapped when submitting the form wrap: Var[str] @classmethod def create(cls, *children, **props) -> Component: """Create an Input component. Args: *children: The children of the component. **props: The properties of the component. Returns: The component. """ if props.get("value") is not None and props.get("on_change") is not None: # create a debounced input if the user requests full control to avoid typing jank return DebounceInput.create(super().create(*children, **props)) return super().create(*children, **props) def add_style(self): """Add the style to the component. Returns: The style of the component. """ added_style: dict[str, dict] = {} added_style.setdefault("& textarea", {}) if "padding" in self.style: added_style["& textarea"]["padding"] = self.style.pop("padding") return added_style text_area = TextArea.create ================================================ FILE: reflex/components/radix/themes/components/text_field.py ================================================ """Interactive components provided by @radix-ui/themes.""" from __future__ import annotations from typing import Literal from reflex.components.component import Component, ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.core.debounce import DebounceInput from reflex.components.el import elements from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralRadius, RadixThemesComponent, ) from reflex.event import EventHandler, input_event, key_event from reflex.utils.types import is_optional from reflex.vars.base import Var from reflex.vars.number import ternary_operation LiteralTextFieldSize = Literal["1", "2", "3"] LiteralTextFieldVariant = Literal["classic", "surface", "soft"] class TextFieldRoot(elements.Input, RadixThemesComponent): """Captures user input with an optional slot for buttons and icons.""" tag = "TextField.Root" # Text field size "1" - "3" size: Var[Responsive[LiteralTextFieldSize]] # Variant of text field: "classic" | "surface" | "soft" variant: Var[LiteralTextFieldVariant] # Override theme color for text field color_scheme: Var[LiteralAccentColor] # Override theme radius for text field: "none" | "small" | "medium" | "large" | "full" radius: Var[LiteralRadius] # Whether the input should have autocomplete enabled auto_complete: Var[bool] # The value of the input when initially rendered. default_value: Var[str] # Disables the input disabled: Var[bool] # Specifies the maximum number of characters allowed in the input max_length: Var[int] # Specifies the minimum number of characters required in the input min_length: Var[int] # Name of the input, used when sending form data name: Var[str] # Placeholder text in the input placeholder: Var[str] # Indicates whether the input is read-only read_only: Var[bool] # Indicates that the input is required required: Var[bool] # Specifies the type of input type: Var[str] # Value of the input value: Var[str | int | float] # References a datalist for suggested options list: Var[str] # Fired when the value of the textarea changes. on_change: EventHandler[input_event] # Fired when the textarea is focused. on_focus: EventHandler[input_event] # Fired when the textarea is blurred. on_blur: EventHandler[input_event] # Fired when a key is pressed down. on_key_down: EventHandler[key_event] # Fired when a key is released. on_key_up: EventHandler[key_event] @classmethod def create(cls, *children, **props) -> Component: """Create an Input component. Args: *children: The children of the component. **props: The properties of the component. Returns: The component. """ value = props.get("value") # React expects an empty string(instead of null) for controlled inputs. if value is not None and is_optional( (value_var := Var.create(value))._var_type ): value_var_is_not_none = value_var != Var.create(None) value_var_is_not_undefined = value_var != Var(_js_expr="undefined") props["value"] = ternary_operation( value_var_is_not_none & value_var_is_not_undefined, value, Var.create(""), ) component = super().create(*children, **props) if props.get("value") is not None and props.get("on_change") is not None: # create a debounced input if the user requests full control to avoid typing jank return DebounceInput.create(component) return component class TextFieldSlot(RadixThemesComponent): """Contains icons or buttons associated with an Input.""" tag = "TextField.Slot" # Override theme color for text field slot color_scheme: Var[LiteralAccentColor] # Which side of the input the slot should be placed on side: Var[Literal["left", "right"]] class TextField(ComponentNamespace): """TextField components namespace.""" slot = staticmethod(TextFieldSlot.create) __call__ = staticmethod(TextFieldRoot.create) input = text_field = TextField() ================================================ FILE: reflex/components/radix/themes/components/tooltip.py ================================================ """Interactive components provided by @radix-ui/themes.""" from typing import Literal from reflex.components.component import Component from reflex.components.radix.themes.base import RadixThemesComponent from reflex.constants.compiler import MemoizationMode from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec from reflex.utils import format from reflex.vars.base import Var LiteralSideType = Literal[ "top", "right", "bottom", "left", ] LiteralAlignType = Literal[ "start", "center", "end", ] LiteralStickyType = Literal[ "partial", "always", ] ARIA_LABEL_KEY = "aria_label" # The Tooltip inherits props from the Tooltip.Root, Tooltip.Portal, Tooltip.Content class Tooltip(RadixThemesComponent): """Floating element that provides a control with contextual information via pointer or focus.""" tag = "Tooltip" # The content of the tooltip. content: Var[str] # The open state of the tooltip when it is initially rendered. Use when you do not need to control its open state. default_open: Var[bool] # The controlled open state of the tooltip. Must be used in conjunction with `on_open_change`. open: Var[bool] # The preferred side of the trigger to render against when open. Will be reversed when collisions occur and `avoid_collisions` is enabled.The position of the tooltip. Defaults to "top". side: Var[LiteralSideType] # The distance in pixels from the trigger. Defaults to 0. side_offset: Var[float | int] # The preferred alignment against the trigger. May change when collisions occur. Defaults to "center". align: Var[LiteralAlignType] # An offset in pixels from the "start" or "end" alignment options. align_offset: Var[float | int] # When true, overrides the side and align preferences to prevent collisions with boundary edges. Defaults to True. avoid_collisions: Var[bool] # The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { "top": 20, "left": 20 }. Defaults to 0. collision_padding: Var[float | int | dict[str, float | int]] # The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. Defaults to 0. arrow_padding: Var[float | int] # The sticky behavior on the align axis. "partial" will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. Defaults to "partial". sticky: Var[LiteralStickyType] # Whether to hide the content when the trigger becomes fully occluded. Defaults to False. hide_when_detached: Var[bool] # Override the duration in milliseconds to customize the open delay for a specific tooltip. Default is 700. delay_duration: Var[float | int] # Prevents Tooltip content from remaining open when hovering. disable_hoverable_content: Var[bool] # Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries. force_mount: Var[bool] # By default, screenreaders will announce the content inside the component. If this is not descriptive enough, or you have content that cannot be announced, use aria-label as a more descriptive label. aria_label: Var[str] # Fired when the open state changes. on_open_change: EventHandler[passthrough_event_spec(bool)] # Fired when the escape key is pressed. on_escape_key_down: EventHandler[no_args_event_spec] # Fired when the pointer is down outside the tooltip. on_pointer_down_outside: EventHandler[no_args_event_spec] _memoization_mode = MemoizationMode(recursive=False) @classmethod def create(cls, *children, **props) -> Component: """Initialize the Tooltip component. Run some additional handling on the props. Args: *children: The positional arguments **props: The keyword arguments Returns: The created component. """ if props.get(ARIA_LABEL_KEY) is not None: props[format.to_kebab_case(ARIA_LABEL_KEY)] = props.pop(ARIA_LABEL_KEY) return super().create(*children, **props) tooltip = Tooltip.create ================================================ FILE: reflex/components/radix/themes/layout/__init__.py ================================================ """Layout components.""" from __future__ import annotations from reflex import RADIX_THEMES_LAYOUT_MAPPING from reflex.utils import lazy_loader _SUBMOD_ATTRS: dict[str, list[str]] = { "".join(k.split("components.radix.themes.layout.")[-1]): v for k, v in RADIX_THEMES_LAYOUT_MAPPING.items() } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/radix/themes/layout/base.py ================================================ """Declarative layout and common spacing props.""" from __future__ import annotations from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import ( CommonMarginProps, CommonPaddingProps, RadixThemesComponent, ) from reflex.vars.base import Var LiteralBoolNumber = Literal["0", "1"] class LayoutComponent(CommonMarginProps, CommonPaddingProps, RadixThemesComponent): """Box, Flex and Grid are foundational elements you'll use to construct layouts. Box provides block-level spacing and sizing, while Flex and Grid let you create flexible columns, rows and grids. """ # Whether the element will take up the smallest possible space: "0" | "1" flex_shrink: Var[Responsive[LiteralBoolNumber]] # Whether the element will take up the largest possible space: "0" | "1" flex_grow: Var[Responsive[LiteralBoolNumber]] ================================================ FILE: reflex/components/radix/themes/layout/box.py ================================================ """Declarative layout and common spacing props.""" from __future__ import annotations from reflex.components.el import elements from reflex.components.radix.themes.base import RadixThemesComponent class Box(elements.Div, RadixThemesComponent): """A fundamental layout building block, based on `div` element.""" tag = "Box" box = Box.create ================================================ FILE: reflex/components/radix/themes/layout/center.py ================================================ """A center component.""" from __future__ import annotations from typing import Any from .flex import Flex class Center(Flex): """A center component.""" def add_style(self) -> dict[str, Any] | None: """Add style that center the content. Returns: The style of the component. """ return { "display": "flex", "align_items": "center", "justify_content": "center", } center = Center.create ================================================ FILE: reflex/components/radix/themes/layout/container.py ================================================ """Declarative layout and common spacing props.""" from __future__ import annotations from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import RadixThemesComponent from reflex.style import STACK_CHILDREN_FULL_WIDTH from reflex.vars.base import LiteralVar, Var LiteralContainerSize = Literal["1", "2", "3", "4"] class Container(elements.Div, RadixThemesComponent): """Constrains the maximum width of page content. See https://www.radix-ui.com/themes/docs/components/container """ tag = "Container" # The size of the container: "1" - "4" (default "3") size: Var[Responsive[LiteralContainerSize]] = LiteralVar.create("3") @classmethod def create( cls, *children, padding: str = "16px", stack_children_full_width: bool = False, **props, ): """Create the container component. Args: children: The children components. padding: The padding of the container. stack_children_full_width: If True, any vstack/hstack children will have 100% width. props: The properties of the container. Returns: The container component. """ if stack_children_full_width: props["style"] = {**STACK_CHILDREN_FULL_WIDTH, **props.pop("style", {})} return super().create( *children, padding=padding, **props, ) container = Container.create ================================================ FILE: reflex/components/radix/themes/layout/flex.py ================================================ """Declarative layout and common spacing props.""" from __future__ import annotations from typing import ClassVar, Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( LiteralAlign, LiteralJustify, LiteralSpacing, RadixThemesComponent, ) from reflex.vars.base import Var LiteralFlexDirection = Literal["row", "column", "row-reverse", "column-reverse"] LiteralFlexWrap = Literal["nowrap", "wrap", "wrap-reverse"] class Flex(elements.Div, RadixThemesComponent): """Component for creating flex layouts.""" tag = "Flex" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # How child items are laid out: "row" | "column" | "row-reverse" | "column-reverse" direction: Var[Responsive[LiteralFlexDirection]] # Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" align: Var[Responsive[LiteralAlign]] # Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Var[Responsive[LiteralJustify]] # Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse" wrap: Var[Responsive[LiteralFlexWrap]] # Gap between children: "0" - "9" spacing: Var[Responsive[LiteralSpacing]] # Reflex maps the "spacing" prop to "gap" prop. _rename_props: ClassVar[dict[str, str]] = {"spacing": "gap"} flex = Flex.create ================================================ FILE: reflex/components/radix/themes/layout/grid.py ================================================ """Declarative layout and common spacing props.""" from __future__ import annotations from typing import ClassVar, Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import ( LiteralAlign, LiteralJustify, LiteralSpacing, RadixThemesComponent, ) from reflex.vars.base import Var LiteralGridFlow = Literal["row", "column", "dense", "row-dense", "column-dense"] class Grid(elements.Div, RadixThemesComponent): """Component for creating grid layouts.""" tag = "Grid" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Number of columns columns: Var[Responsive[str]] # Number of rows rows: Var[Responsive[str]] # How the grid items are laid out: "row" | "column" | "dense" | "row-dense" | "column-dense" flow: Var[Responsive[LiteralGridFlow]] # Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" align: Var[Responsive[LiteralAlign]] # Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Var[Responsive[LiteralJustify]] # Gap between children: "0" - "9" spacing: Var[Responsive[LiteralSpacing]] # Gap between children horizontal: "0" - "9" spacing_x: Var[Responsive[LiteralSpacing]] # Gap between children vertical: "0" - "9" spacing_y: Var[Responsive[LiteralSpacing]] # Reflex maps the "spacing" prop to "gap" prop. _rename_props: ClassVar[dict[str, str]] = { "spacing": "gap", "spacing_x": "gap_x", "spacing_y": "gap_y", } grid = Grid.create ================================================ FILE: reflex/components/radix/themes/layout/list.py ================================================ """List components.""" from __future__ import annotations from collections.abc import Iterable from typing import Any, Literal from reflex.components.component import ComponentNamespace from reflex.components.core.foreach import Foreach from reflex.components.el.elements.base import BaseHTML from reflex.components.el.elements.typography import Li, Ol, Ul from reflex.components.lucide.icon import Icon from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.radix.themes.typography.text import Text from reflex.vars.base import Var LiteralListStyleTypeUnordered = Literal[ "none", "disc", "circle", "square", ] LiteralListStyleTypeOrdered = Literal[ "none", "decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-latin", "upper-latin", "armenian", "georgian", "lower-alpha", "upper-alpha", "hiragana", "katakana", ] class BaseList(BaseHTML, MarkdownComponentMap): """Base class for ordered and unordered lists.""" tag = "ul" # The style of the list. Default to "none". list_style_type: Var[ LiteralListStyleTypeUnordered | LiteralListStyleTypeOrdered ] = Var.create("none") # A list of items to add to the list. items: Var[Iterable] = Var.create([]) @classmethod def create( cls, *children, **props, ): """Create a list component. Args: *children: The children of the component. **props: The properties of the component. Returns: The list component. """ items = props.pop("items", None) list_style_type = props.pop("list_style_type", "none") if not children and items is not None: if isinstance(items, Var): children = [Foreach.create(items, ListItem.create)] else: children = [ListItem.create(item) for item in items] props["direction"] = "column" style = props.setdefault("style", {}) style["list_style_type"] = list_style_type if "gap" in props: style["gap"] = props["gap"] return super().create(*children, **props) def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "direction": "column", } def _exclude_props(self) -> list[str]: return ["items", "list_style_type"] class UnorderedList(BaseList, Ul): """Display an unordered list.""" tag = "ul" @classmethod def create( cls, *children, **props, ): """Create an unordered list component. Args: *children: The children of the component. **props: The properties of the component. Returns: The list component. """ items = props.pop("items", None) list_style_type = props.pop("list_style_type", "disc") props["margin_left"] = props.get("margin_left", "1.5rem") return super().create( *children, items=items, list_style_type=list_style_type, **props ) class OrderedList(BaseList, Ol): """Display an ordered list.""" tag = "ol" @classmethod def create( cls, *children, **props, ): """Create an ordered list component. Args: *children: The children of the component. **props: The properties of the component. Returns: The list component. """ items = props.pop("items", None) list_style_type = props.pop("list_style_type", "decimal") props["margin_left"] = props.get("margin_left", "1.5rem") return super().create( *children, items=items, list_style_type=list_style_type, **props ) class ListItem(Li, MarkdownComponentMap): """Display an item of an ordered or unordered list.""" @classmethod def create(cls, *children, **props): """Create a list item component. Args: *children: The children of the component. **props: The properties of the component. Returns: The list item component. """ for child in children: if isinstance(child, Text): child.as_ = "span" # pyright: ignore[reportAttributeAccessIssue] elif isinstance(child, Icon) and "display" not in child.style: child.style["display"] = "inline" return super().create(*children, **props) class List(ComponentNamespace): """List components.""" item = staticmethod(ListItem.create) ordered = staticmethod(OrderedList.create) unordered = staticmethod(UnorderedList.create) __call__ = staticmethod(BaseList.create) list_ns = List() list_item = list_ns.item ordered_list = list_ns.ordered unordered_list = list_ns.unordered def __getattr__(name: Any): # special case for when accessing list to avoid shadowing # python's built in list object. if name == "list": return list_ns try: return globals()[name] except KeyError: msg = f"module '{__name__} has no attribute '{name}'" raise AttributeError(msg) from None ================================================ FILE: reflex/components/radix/themes/layout/section.py ================================================ """Declarative layout and common spacing props.""" from __future__ import annotations from typing import Literal from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import RadixThemesComponent from reflex.vars.base import LiteralVar, Var LiteralSectionSize = Literal["1", "2", "3"] class Section(elements.Section, RadixThemesComponent): """Denotes a section of page content.""" tag = "Section" # The size of the section: "1" - "3" (default "2") size: Var[Responsive[LiteralSectionSize]] = LiteralVar.create("2") section = Section.create ================================================ FILE: reflex/components/radix/themes/layout/spacer.py ================================================ """A spacer component.""" from __future__ import annotations from typing import Any from .flex import Flex class Spacer(Flex): """A spacer component.""" def add_style(self) -> dict[str, Any] | None: """Add style to the component. Returns: The style of the component. """ return { "flex": 1, "justify_self": "stretch", "align_self": "stretch", } spacer = Spacer.create ================================================ FILE: reflex/components/radix/themes/layout/stack.py ================================================ """Stack components.""" from __future__ import annotations from reflex.components.component import Component from reflex.components.core.breakpoints import Responsive from reflex.components.radix.themes.base import LiteralAlign, LiteralSpacing from reflex.vars.base import Var from .flex import Flex, LiteralFlexDirection class Stack(Flex): """A stack component.""" # The spacing between each stack item. spacing: Var[Responsive[LiteralSpacing]] = Var.create("3") # The alignment of the stack items. align: Var[Responsive[LiteralAlign]] = Var.create("start") @classmethod def create( cls, *children, **props, ) -> Component: """Create a new instance of the component. Args: *children: The children of the stack. **props: The properties of the stack. Returns: The stack component. """ # Apply the default classname given_class_name = props.pop("class_name", []) if not isinstance(given_class_name, list): given_class_name = [given_class_name] props["class_name"] = ["rx-Stack", *given_class_name] return super().create( *children, **props, ) class VStack(Stack): """A vertical stack component.""" # The direction of the stack. direction: Var[Responsive[LiteralFlexDirection]] = Var.create("column") class HStack(Stack): """A horizontal stack component.""" # The direction of the stack. direction: Var[Responsive[LiteralFlexDirection]] = Var.create("row") stack = Stack.create hstack = HStack.create vstack = VStack.create ================================================ FILE: reflex/components/radix/themes/typography/__init__.py ================================================ """Typographic components.""" from __future__ import annotations from reflex import RADIX_THEMES_TYPOGRAPHY_MAPPING from reflex.utils import lazy_loader _SUBMOD_ATTRS: dict[str, list[str]] = { "".join(k.split("components.radix.themes.typography.")[-1]): v for k, v in RADIX_THEMES_TYPOGRAPHY_MAPPING.items() } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/radix/themes/typography/base.py ================================================ """Components for rendering text. https://www.radix-ui.com/themes/docs/theme/typography """ from __future__ import annotations from typing import Literal LiteralTextWeight = Literal["light", "regular", "medium", "bold"] LiteralTextAlign = Literal["left", "center", "right"] LiteralTextSize = Literal["1", "2", "3", "4", "5", "6", "7", "8", "9"] LiteralTextTrim = Literal["normal", "start", "end", "both"] ================================================ FILE: reflex/components/radix/themes/typography/blockquote.py ================================================ """Components for rendering heading. https://www.radix-ui.com/themes/docs/theme/typography """ from __future__ import annotations from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var from .base import LiteralTextSize, LiteralTextWeight class Blockquote(elements.Blockquote, RadixThemesComponent): """A block level extended quotation.""" tag = "Blockquote" # Text size: "1" - "9" size: Var[Responsive[LiteralTextSize]] # Thickness of text: "light" | "regular" | "medium" | "bold" weight: Var[Responsive[LiteralTextWeight]] # Overrides the accent color inherited from the Theme. color_scheme: Var[LiteralAccentColor] # Whether to render the text with higher contrast color high_contrast: Var[bool] blockquote = Blockquote.create ================================================ FILE: reflex/components/radix/themes/typography/code.py ================================================ """Components for rendering heading. https://www.radix-ui.com/themes/docs/theme/typography """ from __future__ import annotations from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.radix.themes.base import ( LiteralAccentColor, LiteralVariant, RadixThemesComponent, ) from reflex.vars.base import Var from .base import LiteralTextSize, LiteralTextWeight class Code(elements.Code, RadixThemesComponent, MarkdownComponentMap): """A block level extended quotation.""" tag = "Code" # The visual variant to apply: "solid" | "soft" | "outline" | "ghost" variant: Var[LiteralVariant] # Text size: "1" - "9" size: Var[Responsive[LiteralTextSize]] # Thickness of text: "light" | "regular" | "medium" | "bold" weight: Var[Responsive[LiteralTextWeight]] # Overrides the accent color inherited from the Theme. color_scheme: Var[LiteralAccentColor] # Whether to render the text with higher contrast color high_contrast: Var[bool] code = Code.create ================================================ FILE: reflex/components/radix/themes/typography/heading.py ================================================ """Components for rendering heading. https://www.radix-ui.com/themes/docs/theme/typography """ from __future__ import annotations from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var from .base import LiteralTextAlign, LiteralTextSize, LiteralTextTrim, LiteralTextWeight class Heading(elements.H1, RadixThemesComponent, MarkdownComponentMap): """A foundational text primitive based on the element.""" tag = "Heading" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Change the default rendered element into a semantically appropriate alternative (cannot be used with asChild) as_: Var[str] # Text size: "1" - "9" size: Var[Responsive[LiteralTextSize]] # Thickness of text: "light" | "regular" | "medium" | "bold" weight: Var[Responsive[LiteralTextWeight]] # Alignment of text in element: "left" | "center" | "right" align: Var[Responsive[LiteralTextAlign]] # Removes the leading trim space: "normal" | "start" | "end" | "both" trim: Var[Responsive[LiteralTextTrim]] # Overrides the accent color inherited from the Theme. color_scheme: Var[LiteralAccentColor] # Whether to render the text with higher contrast color high_contrast: Var[bool] heading = Heading.create ================================================ FILE: reflex/components/radix/themes/typography/link.py ================================================ """Components for rendering heading. https://www.radix-ui.com/themes/docs/theme/typography """ from __future__ import annotations from typing import Literal from reflex.components.component import Component, MemoizationLeaf from reflex.components.core.breakpoints import Responsive from reflex.components.core.colors import color from reflex.components.core.cond import cond from reflex.components.el.elements.inline import A from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.components.react_router.dom import ReactRouterLink from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import Var from .base import LiteralTextSize, LiteralTextTrim, LiteralTextWeight LiteralLinkUnderline = Literal["auto", "hover", "always", "none"] _KNOWN_REACT_ROUTER_LINK_PROPS = frozenset(ReactRouterLink.get_props()) class Link(RadixThemesComponent, A, MemoizationLeaf, MarkdownComponentMap): """A semantic element for navigation between pages.""" tag = "Link" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Text size: "1" - "9" size: Var[Responsive[LiteralTextSize]] # Thickness of text: "light" | "regular" | "medium" | "bold" weight: Var[Responsive[LiteralTextWeight]] # Removes the leading trim space: "normal" | "start" | "end" | "both" trim: Var[Responsive[LiteralTextTrim]] # Sets the visibility of the underline affordance: "auto" | "hover" | "always" | "none" underline: Var[LiteralLinkUnderline] # Overrides the accent color inherited from the Theme. color_scheme: Var[LiteralAccentColor] # Whether to render the text with higher contrast color high_contrast: Var[bool] # If True, the link will open in a new tab is_external: Var[bool] def add_imports(self) -> ImportDict: """Add imports for the Link component. Returns: The import dict. """ return { "react-router": [ImportVar(tag="Link", alias="ReactRouterLink")], } @classmethod def create(cls, *children, **props) -> Component: """Create a Link component. Args: *children: The children of the component. **props: The props of the component. Returns: Component: The link component Raises: ValueError: in case of missing children """ props.setdefault("_hover", {"color": color("accent", 8)}) href = props.get("href") is_external = props.pop("is_external", None) if is_external is not None: props["target"] = cond(is_external, "_blank", "") if href is not None: if not len(children): msg = "Link without a child will not display" raise ValueError(msg) if "as_child" not in props: # Extract props for the ReactRouterLink, the rest go to the Link/A element. react_router_link_props = {} for prop in props.copy(): if prop in _KNOWN_REACT_ROUTER_LINK_PROPS: react_router_link_props[prop] = props.pop(prop) react_router_link_props["to"] = react_router_link_props.pop( "href", href ) # If user does not use `as_child`, by default we render using react_router_link to avoid page refresh during internal navigation return super().create( ReactRouterLink.create(*children, **react_router_link_props), as_child=True, **props, ) else: props["href"] = "#" return super().create(*children, **props) link = Link.create ================================================ FILE: reflex/components/radix/themes/typography/text.py ================================================ """Components for rendering text. https://www.radix-ui.com/themes/docs/theme/typography """ from __future__ import annotations from typing import Literal from reflex.components.component import ComponentNamespace from reflex.components.core.breakpoints import Responsive from reflex.components.el import elements from reflex.components.markdown.markdown import MarkdownComponentMap from reflex.components.radix.themes.base import LiteralAccentColor, RadixThemesComponent from reflex.vars.base import Var from .base import LiteralTextAlign, LiteralTextSize, LiteralTextTrim, LiteralTextWeight LiteralType = Literal[ "p", "label", "div", "span", "b", "i", "u", "abbr", "cite", "del", "em", "ins", "kbd", "mark", "s", "samp", "sub", "sup", ] class Text(elements.Span, RadixThemesComponent, MarkdownComponentMap): """A foundational text primitive based on the element.""" tag = "Text" # Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Var[bool] # Change the default rendered element into a semantically appropriate alternative (cannot be used with asChild) as_: Var[LiteralType] = Var.create("p") # Text size: "1" - "9" size: Var[Responsive[LiteralTextSize]] # Thickness of text: "light" | "regular" | "medium" | "bold" weight: Var[Responsive[LiteralTextWeight]] # Alignment of text in element: "left" | "center" | "right" align: Var[Responsive[LiteralTextAlign]] # Removes the leading trim space: "normal" | "start" | "end" | "both" trim: Var[Responsive[LiteralTextTrim]] # Overrides the accent color inherited from the Theme. color_scheme: Var[LiteralAccentColor] # Whether to render the text with higher contrast color high_contrast: Var[bool] class Span(Text): """A variant of text rendering as element.""" as_: Var[LiteralType] = Var.create("span") class Em(elements.Em, RadixThemesComponent): """Marks text to stress emphasis.""" tag = "Em" class Kbd(elements.Kbd, RadixThemesComponent): """Represents keyboard input or a hotkey.""" tag = "Kbd" # Text size: "1" - "9" size: Var[LiteralTextSize] class Quote(elements.Q, RadixThemesComponent): """A short inline quotation.""" tag = "Quote" class Strong(elements.Strong, RadixThemesComponent): """Marks text to signify strong importance.""" tag = "Strong" class TextNamespace(ComponentNamespace): """Checkbox components namespace.""" __call__ = staticmethod(Text.create) em = staticmethod(Em.create) kbd = staticmethod(Kbd.create) quote = staticmethod(Quote.create) strong = staticmethod(Strong.create) span = staticmethod(Span.create) text = TextNamespace() ================================================ FILE: reflex/components/react_player/__init__.py ================================================ """React Player component for audio and video.""" from . import react_player from .audio import Audio from .video import Video audio = Audio.create video = Video.create ================================================ FILE: reflex/components/react_player/audio.py ================================================ """A audio component.""" from reflex.components.react_player.react_player import ReactPlayer class Audio(ReactPlayer): """Audio component share with Video component.""" ================================================ FILE: reflex/components/react_player/react_player.py ================================================ """React-Player component.""" from __future__ import annotations from typing import Any, TypedDict from reflex.components.component import Component from reflex.components.core.cond import cond from reflex.event import EventHandler, no_args_event_spec from reflex.utils import console from reflex.vars.base import Var from reflex.vars.object import ObjectVar ReactPlayerEvent = ObjectVar[dict[str, dict[str, dict[str, Any]]]] class Progress(TypedDict): """Callback containing played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds.""" played: float playedSeconds: float loaded: float loadedSeconds: float duration: float def _on_progress_signature(event: ReactPlayerEvent) -> list[Var[Progress]]: """Type signature for on_progress event. Args: event: The event variable. Returns: The progress information extracted from the event. """ player_info = event["target"]["api"]["playerInfo"].to(dict) progress_state = player_info["progressState"].to(dict) current = progress_state["current"].to(float) loaded = progress_state["loaded"].to(float) duration = progress_state["duration"].to(float) return [ cond( progress_state, { "played": cond(duration, current / duration, 0.0), "playedSeconds": current, "loaded": cond(duration, loaded / duration, 0.0), "loadedSeconds": loaded, "duration": duration, }, { "played": 0.0, "playedSeconds": 0.0, "loaded": 0.0, "loadedSeconds": 0.0, "duration": 0.0, }, ).to(Progress) ] def _player_info_key_or_zero(event: ReactPlayerEvent, key: str) -> Var[float]: """Helper to extract a value from playerInfo or return 0.0 if not available. Args: event: The event variable. key: The key to extract from playerInfo. Returns: The extracted value or 0.0 if not available. """ player_info = event["target"]["api"]["playerInfo"].to(dict) return cond( player_info[key], player_info[key], 0.0, ).to(float) def _on_time_update_signature(event: ReactPlayerEvent) -> list[Var[float]]: """Type signature for on_time_update event. Args: event: The event variable. Returns: The current timestamp in seconds. """ return [_player_info_key_or_zero(event, "currentTime")] def _on_duration_change_signature(event: ReactPlayerEvent) -> list[Var[float]]: """Type signature for on_duration_change event. Args: event: The event variable. Returns: The active media's duration in seconds. """ return [_player_info_key_or_zero(event, "duration")] def _on_rate_change_signature(event: ReactPlayerEvent) -> list[Var[float]]: """Type signature for on_rate_change event. Args: event: The event variable. Returns: The current playback rate. """ return [_player_info_key_or_zero(event, "playbackRate")] _DEPRECATED_PROP_MAP = { "url": "src", "on_duration": "on_duration_change", "on_playback_rate_change": "on_rate_change", "on_seek": "on_seeked", "on_buffer": "on_waiting", "on_buffer_end": "on_playing", "on_enable_pip": "on_enter_picture_in_picture", "on_disable_pip": "on_leave_picture_in_picture", } class ReactPlayer(Component): """Using react-player and not implement all props and callback yet. reference: https://github.com/cookpete/react-player. """ library = "react-player@3.4.0" tag = "ReactPlayer" is_default = True # The url of a video or song to play src: Var[str | list[str] | list[dict[str, str]]] # Set to true or false to pause or play the media playing: Var[bool] # Set to true or false to loop the media loop: Var[bool] # Set to true or false to display native player controls. controls: Var[bool] = Var.create(True) # Set to true to show just the video thumbnail, which loads the full player on click light: Var[bool] # Set the volume of the player, between 0 and 1 volume: Var[float] # Mutes the player muted: Var[bool] # Player-specific configuration parameters. config: Var[dict[str, Any]] # Set to true to disable the default remote playback option on supported devices. disable_remote_playback: Var[bool] # Called when media is loaded and ready to play. If playing is set to true, media will play immediately. on_ready: EventHandler[no_args_event_spec] # Called when media starts playing. on_start: EventHandler[no_args_event_spec] # Called when playing is set to true. on_play: EventHandler[no_args_event_spec] # Called when media starts or resumes playing after pausing or buffering. on_playing: EventHandler[no_args_event_spec] # Called while the video is loading only. Contains played and loaded progress as a fraction, and playedSeconds and loadedSeconds in seconds. eg { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 } on_progress: EventHandler[_on_progress_signature] # Called when the media's current time changes (~4Hz, use .throttle to limit calls to backend). on_time_update: EventHandler[_on_time_update_signature] # Callback containing duration of the media, in seconds. on_duration_change: EventHandler[_on_duration_change_signature] # Called when media is paused. on_pause: EventHandler[no_args_event_spec] # Called when media starts buffering. on_waiting: EventHandler[no_args_event_spec] # Called when the media is seeking. on_seeking: EventHandler[no_args_event_spec] # Called when media seeks with seconds parameter. on_seeked: EventHandler[_on_time_update_signature] # Called when playback rate of the player changed. Only supported by YouTube, Vimeo (if enabled), Wistia, and file paths. on_rate_change: EventHandler[_on_rate_change_signature] # Called when media finishes playing. Does not fire when loop is set to true. on_ended: EventHandler[no_args_event_spec] # Called when an error occurs whilst attempting to play media. on_error: EventHandler[no_args_event_spec] # Called when user clicks the light mode preview. on_click_preview: EventHandler[no_args_event_spec] # Called when picture-in-picture mode is enabled. on_enter_picture_in_picture: EventHandler[no_args_event_spec] # Called when picture-in-picture mode is disabled. on_leave_picture_in_picture: EventHandler[no_args_event_spec] @classmethod def create(cls, *children, **props) -> ReactPlayer: """Create a component. Args: children: The children of the component. props: The props of the component. Returns: The created component. Raises: ValueError: If both a deprecated prop and its replacement are both passed. """ for prop, new_prop in _DEPRECATED_PROP_MAP.items(): if prop in props: if new_prop in props: msg = ( f"The prop {prop!r} is deprecated, but the replacement {new_prop!r} is also passed. Please remove {prop!r}.", ) raise ValueError(msg) console.warn( f"The prop {prop!r} has been replaced by {new_prop!r}, please update your code.", ) props[new_prop] = props.pop(prop) return super().create(*children, **props) # type: ignore[return-value] def _render(self, props: dict[str, Any] | None = None): """Render the component. Adds width and height set to None because react-player will set them to some random value that overrides the css width and height. Args: props: The props to pass to the component. Returns: The rendered component. """ return ( super() ._render(props) .add_props( width=Var.create(None), height=Var.create(None), ) ) ================================================ FILE: reflex/components/react_player/video.py ================================================ """A video component.""" from reflex.components.react_player.react_player import ReactPlayer class Video(ReactPlayer): """Video component share with audio component.""" ================================================ FILE: reflex/components/react_router/__init__.py ================================================ """React-router internal components.""" from .dom import ReactRouterLink link = ReactRouterLink.create ================================================ FILE: reflex/components/react_router/dom.py ================================================ """Components for client side navigation within React Router applications.""" from __future__ import annotations from typing import ClassVar, Literal, TypedDict from reflex.components.el.elements.inline import A from reflex.vars.base import Var LiteralLinkDiscover = Literal["none", "render"] class To(TypedDict): """Structured object for navigating via the `to` prop.""" # A URL pathname, beginning with a / pathname: str # A URL search string, beginning with a ?. search: str # A URL fragment identifier, beginning with a #. hash: str class ReactRouterLink(A): """Links are accessible elements used primarily for navigation. This component is styled to resemble a hyperlink and semantically renders an .""" library = "react-router" tag = "Link" alias = "ReactRouterLink" # The page to link to. to: Var[str | To] # Replaces the current entry in the history stack instead of pushing a new one onto it. replace: Var[bool] # Will use document navigation instead of client side routing when the link is clicked: the browser will handle the transition normally (as if it were an ). reload_document: Var[bool] # Prevents the scroll position from being reset to the top of the window when the link is clicked and the app is using ScrollRestoration. This only prevents new locations resetting scroll to the top, scroll position will be restored for back/forward button navigation. prevent_scroll_reset: Var[bool] # Defines the link discovery behavior discover: Var[LiteralLinkDiscover] # Enables a View Transition for this navigation. view_transition: Var[bool] @classmethod def create(cls, *children, **props): """Create a ReactRouterLink component for client-side navigation. Args: *children: The children of the component. **props: The props of the component. Returns: The ReactRouterLink component. """ # React Router special behavior is triggered on the `to` prop, not href. if "to" not in props and "href" in props: props["to"] = props.pop("href") return super().create(*children, **props) _invalid_children: ClassVar[list[str]] = ["A", "ReactRouterLink"] ================================================ FILE: reflex/components/recharts/__init__.py ================================================ """Recharts components.""" from __future__ import annotations from reflex.utils import lazy_loader _SUBMOD_ATTRS: dict = { "cartesian": [ "area", "Area", "bar", "Bar", "line", "Line", "scatter", "Scatter", "x_axis", "XAxis", "y_axis", "YAxis", "z_axis", "ZAxis", "brush", "Brush", "cartesian_axis", "CartesianAxis", "cartesian_grid", "CartesianGrid", "reference_line", "ReferenceLine", "reference_dot", "ReferenceDot", "reference_area", "ReferenceArea", "error_bar", "ErrorBar", "funnel", "Funnel", ], "charts": [ "area_chart", "AreaChart", "bar_chart", "BarChart", "line_chart", "LineChart", "composed_chart", "ComposedChart", "pie_chart", "PieChart", "radar_chart", "RadarChart", "radial_bar_chart", "RadialBarChart", "scatter_chart", "ScatterChart", "funnel_chart", "FunnelChart", "treemap", "Treemap", ], "general": [ "responsive_container", "ResponsiveContainer", "legend", "Legend", "tooltip", "graphing_tooltip", "GraphingTooltip", "label", "Label", "label_list", "LabelList", "cell", "Cell", ], "polar": [ "pie", "Pie", "radar", "Radar", "radial_bar", "RadialBar", "polar_angle_axis", "PolarAngleAxis", "polar_grid", "PolarGrid", "polar_radius_axis", "PolarRadiusAxis", ], "recharts": [ "LiteralAnimationEasing", "LiteralAxisType", "LiteralBarChartStackOffset", "LiteralComposedChartBaseValue", "LiteralCurveType", "LiteralDirection", "LiteralGridType", "LiteralIconType", "LiteralIfOverflow", "LiteralInterval", "LiteralLayout", "LiteralLegendAlign", "LiteralLegendType", "LiteralLineType", "LiteralOrientation", "LiteralOrientationLeftRightMiddle", "LiteralOrientationTopBottom", "LiteralOrientationTopBottomLeftRight", "LiteralPolarRadiusType", "LiteralScale", "LiteralShape", "LiteralStackOffset", "LiteralSyncMethod", "LiteralVerticalAlign", ], } __getattr__, __dir__, __all__ = lazy_loader.attach( __name__, submod_attrs=_SUBMOD_ATTRS, ) ================================================ FILE: reflex/components/recharts/cartesian.py ================================================ """Cartesian charts in Recharts.""" from __future__ import annotations from collections.abc import Sequence from typing import Any, ClassVar, TypedDict from reflex.constants import EventTriggers from reflex.constants.colors import Color from reflex.event import EventHandler, no_args_event_spec from reflex.vars.base import LiteralVar, Var from .recharts import ( ACTIVE_DOT_TYPE, LiteralAnimationEasing, LiteralCurveType, LiteralDirection, LiteralIfOverflow, LiteralInterval, LiteralIntervalAxis, LiteralLayout, LiteralLegendType, LiteralLineType, LiteralOrientationLeftRight, LiteralOrientationTopBottom, LiteralOrientationTopBottomLeftRight, LiteralPolarRadiusType, LiteralScale, LiteralShape, LiteralTextAnchor, Recharts, ) class Axis(Recharts): """A base class for axes in Recharts.""" # The key of data displayed in the axis. data_key: Var[str | int] # If set true, the axis do not display in the chart. Default: False hide: Var[bool] # The width of axis which is usually calculated internally. width: Var[str | int] # The height of axis, which can be set by user. height: Var[str | int] # The type of axis 'number' | 'category' type_: Var[LiteralPolarRadiusType] # If set 0, all the ticks will be shown. If set preserveStart", "preserveEnd" or "preserveStartEnd", the ticks which is to be shown or hidden will be calculated automatically. Default: "preserveEnd" interval: Var[LiteralIntervalAxis | int] # Allow the ticks of Axis to be decimals or not. Default: True allow_decimals: Var[bool] # When domain of the axis is specified and the type of the axis is 'number', if allowDataOverflow is set to be false, the domain will be adjusted when the minimum value of data is smaller than domain[0] or the maximum value of data is greater than domain[1] so that the axis displays all data values. If set to true, graphic elements (line, area, bars) will be clipped to conform to the specified domain. Default: False allow_data_overflow: Var[bool] # Allow the axis has duplicated categorys or not when the type of axis is "category". Default: True allow_duplicated_category: Var[bool] # The range of the axis. Work best in conjunction with allow_data_overflow. Default: [0, "auto"] domain: Var[Sequence] # If set false, no axis line will be drawn. Default: True axis_line: Var[bool] # If set true, flips ticks around the axis line, displaying the labels inside the chart instead of outside. Default: False mirror: Var[bool] # Reverse the ticks or not. Default: False reversed: Var[bool] # The label of axis, which appears next to the axis. label: Var[str | int | dict[str, Any]] # If 'auto' set, the scale function is decided by the type of chart, and the props type. 'auto' | 'linear' | 'pow' | 'sqrt' | 'log' | 'identity' | 'time' | 'band' | 'point' | 'ordinal' | 'quantile' | 'quantize' | 'utc' | 'sequential' | 'threshold'. Default: "auto" scale: Var[LiteralScale] # The unit of data displayed in the axis. This option will be used to represent an index unit in a scatter chart. unit: Var[str | int] # The name of data displayed in the axis. This option will be used to represent an index in a scatter chart. name: Var[str | int] # Set the values of axis ticks manually. ticks: Var[Sequence[str | int]] # If set false, no ticks will be drawn. tick: Var[bool | dict] # The count of axis ticks. Not used if 'type' is 'category'. Default: 5 tick_count: Var[int] # If set false, no axis tick lines will be drawn. Default: True tick_line: Var[bool] # The length of tick line. Default: 6 tick_size: Var[int] # The minimum gap between two adjacent labels. Default: 5 min_tick_gap: Var[int] # The stroke color of axis. Default: rx.color("gray", 9) stroke: Var[str | Color] = LiteralVar.create(Color("gray", 9)) # The text anchor of axis. Default: "middle" text_anchor: Var[LiteralTextAnchor] # The customized event handler of click on the ticks of this axis on_click: EventHandler[no_args_event_spec] # The customized event handler of mousedown on the ticks of this axis on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the ticks of this axis on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the ticks of this axis on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the ticks of this axis on_mouse_out: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the ticks of this axis on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the ticks of this axis on_mouse_leave: EventHandler[no_args_event_spec] class XAxis(Axis): """An XAxis component in Recharts.""" tag = "XAxis" alias = "RechartsXAxis" # The orientation of axis 'top' | 'bottom'. Default: "bottom" orientation: Var[LiteralOrientationTopBottom] # The id of x-axis which is corresponding to the data. Default: 0 x_axis_id: Var[str | int] # Ensures that all datapoints within a chart contribute to its domain calculation, even when they are hidden. Default: False include_hidden: Var[bool] # The angle of axis ticks. Default: 0 angle: Var[int] # Specify the padding of x-axis. Default: {"left": 0, "right": 0} padding: Var[dict[str, int]] class YAxis(Axis): """A YAxis component in Recharts.""" tag = "YAxis" alias = "RechartsYAxis" # The orientation of axis 'left' | 'right'. Default: "left" orientation: Var[LiteralOrientationLeftRight] # The id of y-axis which is corresponding to the data. Default: 0 y_axis_id: Var[str | int] # Specify the padding of y-axis. Default: {"top": 0, "bottom": 0} padding: Var[dict[str, int]] class ZAxis(Recharts): """A ZAxis component in Recharts.""" tag = "ZAxis" alias = "RechartsZAxis" # The key of data displayed in the axis. data_key: Var[str | int] # The unique id of z-axis. Default: 0 z_axis_id: Var[str | int] # The range of axis. Default: [60, 400] range: Var[Sequence[int]] = LiteralVar.create([60, 400]) # The unit of data displayed in the axis. This option will be used to represent an index unit in a scatter chart. unit: Var[str | int] # The name of data displayed in the axis. This option will be used to represent an index in a scatter chart. name: Var[str | int] # If 'auto' set, the scale function is decided by the type of chart, and the props type. Default: "auto" scale: Var[LiteralScale] class Brush(Recharts): """A Brush component in Recharts.""" tag = "Brush" alias = "RechartsBrush" # Stroke color. Default: rx.color("gray", 9) stroke: Var[str | Color] = LiteralVar.create(Color("gray", 9)) # The fill color of brush. Default: rx.color("gray", 2) fill: Var[str | Color] = LiteralVar.create(Color("gray", 2)) # The key of data displayed in the axis. data_key: Var[str | int] # The x-coordinate of brush. Default: 0 x: Var[int] # The y-coordinate of brush. Default: 0 y: Var[int] # The width of brush. Default: 0 width: Var[int] # The height of brush. Default: 40 height: Var[int] # The original data of a LineChart, a BarChart or an AreaChart. data: Var[Sequence[Any]] # The width of each traveller. Default: 5 traveller_width: Var[int] # The data with gap of refreshing chart. If the option is not set, the chart will be refreshed every time. Default: 1 gap: Var[int] # The default start index of brush. If the option is not set, the start index will be 0. Default: 0 start_index: Var[int] # The default end index of brush. If the option is not set, the end index will be calculated by the length of data. end_index: Var[int] @classmethod def get_event_triggers(cls) -> dict[str, Var | Any]: """Get the event triggers that pass the component's value to the handler. Returns: A dict mapping the event trigger to the var that is passed to the handler. """ return { EventTriggers.ON_CHANGE: no_args_event_spec, } class Cartesian(Recharts): """A base class for cartesian charts in Recharts.""" # The layout of bar in the chart, usually inherited from parent. 'horizontal' | 'vertical' layout: Var[LiteralLayout] # The key of a group of data which should be unique in an area chart. data_key: Var[str | int] # The id of x-axis which is corresponding to the data. Default: 0 x_axis_id: Var[str | int] # The id of y-axis which is corresponding to the data. Default: 0 y_axis_id: Var[str | int] # The type of icon in legend. If set to 'none', no legend item will be rendered. 'line' | 'plainline' | 'square' | 'rect'| 'circle' | 'cross' | 'diamond' | 'star' | 'triangle' | 'wye' | 'none' optional legend_type: Var[LiteralLegendType] # If false set, labels will not be drawn. If true set, labels will be drawn which have the props calculated internally. Default: False label: Var[bool | dict[str, Any]] # If set false, animation of bar will be disabled. Default: True is_animation_active: Var[bool] # Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Var[int] # Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Var[int] # The type of easing function. Default: "ease" animation_easing: Var[LiteralAnimationEasing] # The unit of data. This option will be used in tooltip. unit: Var[str | int] # The name of data. This option will be used in tooltip and legend to represent the component. If no value was set to this option, the value of dataKey will be used alternatively. name: Var[str | int] # The customized event handler of animation start on_animation_start: EventHandler[no_args_event_spec] # The customized event handler of animation end on_animation_end: EventHandler[no_args_event_spec] # The customized event handler of click on the component in this group on_click: EventHandler[no_args_event_spec] # The customized event handler of mousedown on the component in this group on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the component in this group on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the component in this group on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseover on the component in this group on_mouse_over: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the component in this group on_mouse_out: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the component in this group on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the component in this group on_mouse_leave: EventHandler[no_args_event_spec] class Area(Cartesian): """An Area component in Recharts.""" tag = "Area" alias = "RechartsArea" # The color of the line stroke. Default: rx.color("accent", 9) stroke: Var[str | Color] = LiteralVar.create(Color("accent", 9)) # The width of the line stroke. Default: 1 stroke_width: Var[str | int | float] # The color of the area fill. Default: rx.color("accent", 5) fill: Var[str | Color] = LiteralVar.create(Color("accent", 5)) # The interpolation type of area. And customized interpolation function can be set to type. 'basis' | 'basisClosed' | 'basisOpen' | 'bumpX' | 'bumpY' | 'bump' | 'linear' | 'linearClosed' | 'natural' | 'monotoneX' | 'monotoneY' | 'monotone' | 'step' | 'stepBefore' | 'stepAfter'. Default: "monotone" type_: Var[LiteralCurveType] = LiteralVar.create("monotone") # If false set, dots will not be drawn. If true set, dots will be drawn which have the props calculated internally. Default: False dot: Var[ACTIVE_DOT_TYPE] # The dot is shown when user enter an area chart and this chart has tooltip. If false set, no active dot will not be drawn. If true set, active dot will be drawn which have the props calculated internally. Default: {stroke: rx.color("accent", 2), fill: rx.color("accent", 10)} active_dot: Var[ACTIVE_DOT_TYPE] = LiteralVar.create({ "stroke": Color("accent", 2), "fill": Color("accent", 10), }) # The value which can describle the line, usually calculated internally. base_line: Var[int | Sequence[dict[str, Any]]] # The coordinates of all the points in the area, usually calculated internally. points: Var[Sequence[dict[str, Any]]] # The stack id of area, when two areas have the same value axis and same stack_id, then the two areas are stacked in order. stack_id: Var[str | int] # Whether to connect a graph area across null points. Default: False connect_nulls: Var[bool] # Valid children components _valid_children: ClassVar[list[str]] = ["LabelList"] class Bar(Cartesian): """A Bar component in Recharts.""" tag = "Bar" alias = "RechartsBar" # The color of the line stroke. stroke: Var[str | Color] # The width of the line stroke. stroke_width: Var[str | int | float] # The width of the line stroke. Default: Color("accent", 9) fill: Var[str | Color] = LiteralVar.create(Color("accent", 9)) # If false set, background of bars will not be drawn. If true set, background of bars will be drawn which have the props calculated internally. Default: False background: Var[bool] # The stack id of bar, when two bars have the same value axis and same stack_id, then the two bars are stacked in order. stack_id: Var[str] # The unit of data. This option will be used in tooltip. unit: Var[str | int] # The minimal height of a bar in a horizontal BarChart, or the minimal width of a bar in a vertical BarChart. By default, 0 values are not shown. To visualize a 0 (or close to zero) point, set the minimal point size to a pixel value like 3. In stacked bar charts, minPointSize might not be respected for tightly packed values. So we strongly recommend not using this prop in stacked BarCharts. min_point_size: Var[int] # The name of data. This option will be used in tooltip and legend to represent a bar. If no value was set to this option, the value of dataKey will be used alternatively. name: Var[str | int] # Size of the bar (if one bar_size is set then a bar_size must be set for all bars) bar_size: Var[int] # Max size of the bar max_bar_size: Var[int] # If set a value, the option is the radius of all the rounded corners. If set a array, the option are in turn the radiuses of top-left corner, top-right corner, bottom-right corner, bottom-left corner. Default: 0 radius: Var[int | Sequence[int]] # The active bar is shown when a user enters a bar chart and this chart has tooltip. If set to false, no active bar will be drawn. If set to true, active bar will be drawn with the props calculated internally. If passed an object, active bar will be drawn, and the internally calculated props will be merged with the key value pairs of the passed object. # active_bar: Var[Union[bool, dict[str, Any]]] #noqa: ERA001 # Valid children components _valid_children: ClassVar[list[str]] = ["Cell", "LabelList", "ErrorBar"] class Line(Cartesian): """A Line component in Recharts.""" tag = "Line" alias = "RechartsLine" # The interpolation type of line. And customized interpolation function can be set to type. It's the same as type in Area. type_: Var[LiteralCurveType] # The color of the line stroke. Default: rx.color("accent", 9) stroke: Var[str | Color] = LiteralVar.create(Color("accent", 9)) # The width of the line stroke. Default: 1 stroke_width: Var[str | int | float] # The dot is shown when mouse enter a line chart and this chart has tooltip. If false set, no active dot will not be drawn. If true set, active dot will be drawn which have the props calculated internally. Default: {"stroke": rx.color("accent", 10), "fill": rx.color("accent", 4)} dot: Var[ACTIVE_DOT_TYPE] = LiteralVar.create({ "stroke": Color("accent", 10), "fill": Color("accent", 4), }) # The dot is shown when user enter an area chart and this chart has tooltip. If false set, no active dot will not be drawn. If true set, active dot will be drawn which have the props calculated internally. Default: {"stroke": rx.color("accent", 2), "fill": rx.color("accent", 10)} active_dot: Var[ACTIVE_DOT_TYPE] = LiteralVar.create({ "stroke": Color("accent", 2), "fill": Color("accent", 10), }) # Hides the line when true, useful when toggling visibility state via legend. Default: False hide: Var[bool] # Whether to connect a graph line across null points. connect_nulls: Var[bool] # The unit of data. This option will be used in tooltip. unit: Var[str | int] # The coordinates of all the points in the line, usually calculated internally. points: Var[Sequence[dict[str, Any]]] # The pattern of dashes and gaps used to paint the line. stroke_dasharray: Var[str] # Valid children components _valid_children: ClassVar[list[str]] = ["LabelList", "ErrorBar"] class Scatter(Recharts): """A Scatter component in Recharts.""" tag = "Scatter" alias = "RechartsScatter" # The source data, in which each element is an object. data: Var[Sequence[dict[str, Any]]] # The name of the data. It is used to represent the scatter in legend. name: Var[str] # The type of icon in legend. If set to 'none', no legend item will be rendered. 'line' | 'plainline' | 'square' | 'rect'| 'circle' | 'cross' | 'diamond' | 'square' | 'star' | 'triangle' | 'wye' | 'none'. Default: "circle" legend_type: Var[LiteralLegendType] # The id of x-axis which is corresponding to the data. Default: 0 x_axis_id: Var[str | int] # The id of y-axis which is corresponding to the data. Default: 0 y_axis_id: Var[str | int] # The id of z-axis which is corresponding to the data. Default: 0 z_axis_id: Var[str | int] # If false set, line will not be drawn. If true set, line will be drawn which have the props calculated internally. Default: False line: Var[bool] # If a string set, specified symbol will be used to show scatter item. 'circle' | 'cross' | 'diamond' | 'square' | 'star' | 'triangle' | 'wye'. Default: "circle" shape: Var[LiteralShape] # If 'joint' set, line will generated by just jointing all the points. If 'fitting' set, line will be generated by fitting algorithm. 'joint' | 'fitting'. Default: "joint" line_type: Var[LiteralLineType] # The fill color of the scatter. Default: rx.color("accent", 9) fill: Var[str | Color] = LiteralVar.create(Color("accent", 9)) # Valid children components. _valid_children: ClassVar[list[str]] = ["LabelList", "ErrorBar"] # If set false, animation of bar will be disabled. Default: True in CSR, False in SSR is_animation_active: Var[bool] # Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Var[int] # Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Var[int] # The type of easing function. Default: "ease" animation_easing: Var[LiteralAnimationEasing] # The customized event handler of click on the component in this group on_click: EventHandler[no_args_event_spec] # The customized event handler of mousedown on the component in this group on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the component in this group on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the component in this group on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseover on the component in this group on_mouse_over: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the component in this group on_mouse_out: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the component in this group on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the component in this group on_mouse_leave: EventHandler[no_args_event_spec] class Funnel(Recharts): """A Funnel component in Recharts.""" tag = "Funnel" alias = "RechartsFunnel" # The source data, in which each element is an object. data: Var[Sequence[dict[str, Any]]] # The key or getter of a group of data which should be unique in a FunnelChart. data_key: Var[str | int] # The key of each sector's name. Default: "name" name_key: Var[str] # The type of icon in legend. If set to 'none', no legend item will be rendered. Default: "line" legend_type: Var[LiteralLegendType] # If set false, animation of line will be disabled. Default: True is_animation_active: Var[bool] # Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Var[int] # Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Var[int] # The type of easing function. 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'. Default "ease" animation_easing: Var[LiteralAnimationEasing] # Stroke color. Default: rx.color("gray", 3) stroke: Var[str | Color] = LiteralVar.create(Color("gray", 3)) # The coordinates of all the trapezoids in the funnel, usually calculated internally. trapezoids: Var[Sequence[dict[str, Any]]] # Valid children components _valid_children: ClassVar[list[str]] = ["LabelList", "Cell"] # The customized event handler of animation start on_animation_start: EventHandler[no_args_event_spec] # The customized event handler of animation end on_animation_end: EventHandler[no_args_event_spec] # The customized event handler of click on the component in this group on_click: EventHandler[no_args_event_spec] # The customized event handler of mousedown on the component in this group on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the component in this group on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the component in this group on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseover on the component in this group on_mouse_over: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the component in this group on_mouse_out: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the component in this group on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the component in this group on_mouse_leave: EventHandler[no_args_event_spec] class ErrorBar(Recharts): """An ErrorBar component in Recharts.""" tag = "ErrorBar" alias = "RechartsErrorBar" # Only used for ScatterChart with error bars in two directions. Only accepts a value of "x" or "y" and makes the error bars lie in that direction. direction: Var[LiteralDirection] # The key of a group of data which should be unique in an area chart. data_key: Var[str | int] # The width of the error bar ends. Default: 5 width: Var[int] # The stroke color of error bar. Default: rx.color("gray", 8) stroke: Var[str | Color] = LiteralVar.create(Color("gray", 8)) # The stroke width of error bar. Default: 1.5 stroke_width: Var[str | int | float] class Reference(Recharts): """A base class for reference components in Reference.""" # The id of x-axis which is corresponding to the data. Default: 0 x_axis_id: Var[str | int] # The id of y-axis which is corresponding to the data. Default: 0 y_axis_id: Var[str | int] # Defines how to draw the reference line if it falls partly outside the canvas. If set to 'discard', the reference line will not be drawn at all. If set to 'hidden', the reference line will be clipped to the canvas. If set to 'visible', the reference line will be drawn completely. If set to 'extendDomain', the domain of the overflown axis will be extended such that the reference line fits into the canvas. Default: "discard" if_overflow: Var[LiteralIfOverflow] # If set a string or a number, default label will be drawn, and the option is content. label: Var[str | int] class Segment(TypedDict): """A segment in a ReferenceLine or ReferenceArea.""" x: str | int y: str | int class ReferenceLine(Reference): """A ReferenceLine component in Recharts.""" tag = "ReferenceLine" alias = "RechartsReferenceLine" # If set a string or a number, a vertical line perpendicular to the x-axis specified by xAxisId will be drawn. If the specified x-axis is a number axis, the type of x must be Number. If the specified x-axis is a category axis, the value of x must be one of the categorys, otherwise no line will be drawn. x: Var[str | int] # If set a string or a number, a horizontal line perpendicular to the y-axis specified by yAxisId will be drawn. If the specified y-axis is a number axis, the type of y must be Number. If the specified y-axis is a category axis, the value of y must be one of the categorys, otherwise no line will be drawn. y: Var[str | int] # The color of the reference line. stroke: Var[str | Color] # The width of the stroke. Default: 1 stroke_width: Var[str | int | float] # Valid children components _valid_children: ClassVar[list[str]] = ["Label"] # Array of endpoints in { x, y } format. These endpoints would be used to draw the ReferenceLine. segment: Var[Sequence[Segment]] class ReferenceDot(Reference): """A ReferenceDot component in Recharts.""" tag = "ReferenceDot" alias = "RechartsReferenceDot" # If set a string or a number, a vertical line perpendicular to the x-axis specified by xAxisId will be drawn. If the specified x-axis is a number axis, the type of x must be Number. If the specified x-axis is a category axis, the value of x must be one of the categorys, otherwise no line will be drawn. x: Var[str | int] # If set a string or a number, a horizontal line perpendicular to the y-axis specified by yAxisId will be drawn. If the specified y-axis is a number axis, the type of y must be Number. If the specified y-axis is a category axis, the value of y must be one of the categorys, otherwise no line will be drawn. y: Var[str | int] # The radius of dot. r: Var[int] # The color of the area fill. fill: Var[str | Color] # The color of the line stroke. stroke: Var[str | Color] # Valid children components _valid_children: ClassVar[list[str]] = ["Label"] # The customized event handler of click on the component in this chart on_click: EventHandler[no_args_event_spec] # The customized event handler of mousedown on the component in this chart on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the component in this chart on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mouseover on the component in this chart on_mouse_over: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the component in this chart on_mouse_out: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the component in this chart on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the component in this chart on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the component in this chart on_mouse_leave: EventHandler[no_args_event_spec] class ReferenceArea(Recharts): """A ReferenceArea component in Recharts.""" tag = "ReferenceArea" alias = "RechartsReferenceArea" # Stroke color stroke: Var[str | Color] # Fill color fill: Var[str | Color] # The opacity of area. fill_opacity: Var[float] # The id of x-axis which is corresponding to the data. x_axis_id: Var[str | int] # The id of y-axis which is corresponding to the data. y_axis_id: Var[str | int] # A boundary value of the area. If the specified x-axis is a number axis, the type of x must be Number. If the specified x-axis is a category axis, the value of x must be one of the categorys. If one of x1 or x2 is invalidate, the area will cover along x-axis. x1: Var[str | int] # A boundary value of the area. If the specified x-axis is a number axis, the type of x must be Number. If the specified x-axis is a category axis, the value of x must be one of the categorys. If one of x1 or x2 is invalidate, the area will cover along x-axis. x2: Var[str | int] # A boundary value of the area. If the specified y-axis is a number axis, the type of y must be Number. If the specified y-axis is a category axis, the value of y must be one of the categorys. If one of y1 or y2 is invalidate, the area will cover along y-axis. y1: Var[str | int] # A boundary value of the area. If the specified y-axis is a number axis, the type of y must be Number. If the specified y-axis is a category axis, the value of y must be one of the categorys. If one of y1 or y2 is invalidate, the area will cover along y-axis. y2: Var[str | int] # Defines how to draw the reference line if it falls partly outside the canvas. If set to 'discard', the reference line will not be drawn at all. If set to 'hidden', the reference line will be clipped to the canvas. If set to 'visible', the reference line will be drawn completely. If set to 'extendDomain', the domain of the overflown axis will be extended such that the reference line fits into the canvas. Default: "discard" if_overflow: Var[LiteralIfOverflow] # Valid children components _valid_children: ClassVar[list[str]] = ["Label"] class Grid(Recharts): """A base class for grid components in Recharts.""" # The x-coordinate of grid. Default: 0 x: Var[int] # The y-coordinate of grid. Default: 0 y: Var[int] # The width of grid. Default: 0 width: Var[int] # The height of grid. Default: 0 height: Var[int] class CartesianGrid(Grid): """A CartesianGrid component in Recharts.""" tag = "CartesianGrid" alias = "RechartsCartesianGrid" # The horizontal line configuration. Default: True horizontal: Var[bool] # The vertical line configuration. Default: True vertical: Var[bool] # The x-coordinates in pixel values of all vertical lines. Default: [] vertical_points: Var[Sequence[str | int]] # The x-coordinates in pixel values of all vertical lines. Default: [] horizontal_points: Var[Sequence[str | int]] # The background of grid. fill: Var[str | Color] # The opacity of the background used to fill the space between grid lines. fill_opacity: Var[float] # The pattern of dashes and gaps used to paint the lines of the grid. stroke_dasharray: Var[str] # the stroke color of grid. Default: rx.color("gray", 7) stroke: Var[str | Color] = LiteralVar.create(Color("gray", 7)) class CartesianAxis(Grid): """A CartesianAxis component in Recharts.""" tag = "CartesianAxis" alias = "RechartsCartesianAxis" # The orientation of axis 'top' | 'bottom' | 'left' | 'right'. Default: "bottom" orientation: Var[LiteralOrientationTopBottomLeftRight] # The box of viewing area. Default: {"x": 0, "y": 0, "width": 0, "height": 0} view_box: Var[dict[str, Any]] # If set false, no axis line will be drawn. If set a object, the option is the configuration of axis line. Default: True axis_line: Var[bool | dict] # If set false, no ticks will be drawn. tick: Var[bool | dict] # If set false, no axis tick lines will be drawn. If set a object, the option is the configuration of tick lines. Default: True tick_line: Var[bool] # The length of tick line. Default: 6 tick_size: Var[int] # If set 0, all the ticks will be shown. If set preserveStart", "preserveEnd" or "preserveStartEnd", the ticks which is to be shown or hidden will be calculated automatically. Default: "preserveEnd" interval: Var[LiteralInterval] # If set a string or a number, default label will be drawn, and the option is content. label: Var[str | int] # If set true, flips ticks around the axis line, displaying the labels inside the chart instead of outside. Default: False mirror: Var[bool] # The margin between tick line and tick. tick_margin: Var[int] area = Area.create bar = Bar.create line = Line.create scatter = Scatter.create x_axis = XAxis.create y_axis = YAxis.create z_axis = ZAxis.create brush = Brush.create cartesian_axis = CartesianAxis.create cartesian_grid = CartesianGrid.create reference_line = ReferenceLine.create reference_dot = ReferenceDot.create reference_area = ReferenceArea.create error_bar = ErrorBar.create funnel = Funnel.create ================================================ FILE: reflex/components/recharts/charts.py ================================================ """A module that defines the chart components in Recharts.""" from __future__ import annotations from collections.abc import Sequence from typing import Any, ClassVar from reflex.components.component import Component from reflex.components.recharts.general import ResponsiveContainer from reflex.constants import EventTriggers from reflex.constants.colors import Color from reflex.event import EventHandler, no_args_event_spec from reflex.vars.base import Var from .recharts import ( LiteralAnimationEasing, LiteralComposedChartBaseValue, LiteralLayout, LiteralStackOffset, LiteralSyncMethod, RechartsCharts, ) class ChartBase(RechartsCharts): """A component that wraps a Recharts charts.""" # The width of chart container. String or Integer width: Var[str | int] = Var.create("100%") # The height of chart container. height: Var[str | int] = Var.create("100%") # The customized event handler of click on the component in this chart on_click: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the component in this chart on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the component in this chart on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the component in this chart on_mouse_leave: EventHandler[no_args_event_spec] @staticmethod def _ensure_valid_dimension(name: str, value: Any) -> None: """Ensure that the value is an int type or str percentage. Unfortunately str Vars cannot be checked and are implicitly not allowed. Args: name: The name of the prop. value: The value to check. Raises: ValueError: If the value is not an int type or str percentage. """ if value is None: return if isinstance(value, int): return if isinstance(value, str) and value.endswith("%"): return if isinstance(value, Var) and issubclass(value._var_type, int): return msg = ( f"Chart {name} must be specified as int pixels or percentage, not {value!r}. " "CSS unit dimensions are allowed on parent container." ) raise ValueError(msg) @classmethod def create(cls, *children: Any, **props: Any) -> Component: """Create a chart component. Args: *children: The children of the chart component. **props: The properties of the chart component. Returns: The chart component wrapped in a responsive container. """ width = props.pop("width", None) height = props.pop("height", None) cls._ensure_valid_dimension("width", width) cls._ensure_valid_dimension("height", height) # Ensure that the min_height and min_width are set to prevent the chart from collapsing. # We are using small values so that height and width can still be used over min_height and min_width. # Without this, sometimes the chart will not be visible. Causing confusion to the user. # With this, the user will see a small chart and can adjust the height and width and can figure out that the issue is with the size. min_height = props.pop("min_height", 10) min_width = props.pop("min_width", 10) return ResponsiveContainer.create( super().create(*children, **props), width=width if width is not None else "100%", height=height if height is not None else "100%", min_width=min_width, min_height=min_height, ) class CategoricalChartBase(ChartBase): """A component that wraps a Categorical Recharts charts.""" # The source data, in which each element is an object. data: Var[Sequence[dict[str, Any]]] # The sizes of whitespace around the chart, i.e. {"top": 50, "right": 30, "left": 20, "bottom": 5}. margin: Var[dict[str, Any]] # If any two categorical charts(rx.line_chart, rx.area_chart, rx.bar_chart, rx.composed_chart) have the same sync_id, these two charts can sync the position GraphingTooltip, and the start_index, end_index of Brush. sync_id: Var[str] # When sync_id is provided, allows customisation of how the charts will synchronize GraphingTooltips and brushes. Using 'index' (default setting), other charts will reuse current datum's index within the data array. In cases where data does not have the same length, this might yield unexpected results. In that case use 'value' which will try to match other charts values, or a fully custom function which will receive tick, data as argument and should return an index. 'index' | 'value' | function. Default: "index" sync_method: Var[LiteralSyncMethod] # The layout of area in the chart. 'horizontal' | 'vertical'. Default: "horizontal" layout: Var[LiteralLayout] # The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. 'expand' | 'none' | 'wiggle' | 'silhouette' stack_offset: Var[LiteralStackOffset] class AreaChart(CategoricalChartBase): """An Area chart component in Recharts.""" tag = "AreaChart" alias = "RechartsAreaChart" # The base value of area. Number | 'dataMin' | 'dataMax' | 'auto'. Default: "auto" base_value: Var[int | LiteralComposedChartBaseValue] # Valid children components _valid_children: ClassVar[list[str]] = [ "XAxis", "YAxis", "ReferenceArea", "ReferenceDot", "ReferenceLine", "Brush", "CartesianGrid", "Legend", "GraphingTooltip", "Area", "Defs", ] class BarChart(CategoricalChartBase): """A Bar chart component in Recharts.""" tag = "BarChart" alias = "RechartsBarChart" # The gap between two bar categories, which can be a percent value or a fixed value. Percentage | Number. Default: "10%" bar_category_gap: Var[str | int] # The gap between two bars in the same category, which can be a percent value or a fixed value. Percentage | Number. Default: 4 bar_gap: Var[str | int] # The width of all the bars in the chart. Number bar_size: Var[int] # The maximum width of all the bars in a horizontal BarChart, or maximum height in a vertical BarChart. max_bar_size: Var[int] # The type of offset function used to generate the lower and upper values in the series array. The four types are built-in offsets in d3-shape. Default: "none" stack_offset: Var[LiteralStackOffset] # If false set, stacked items will be rendered left to right. If true set, stacked items will be rendered right to left. (Render direction affects SVG layering, not x position.) Default: False reverse_stack_order: Var[bool] # Valid children components _valid_children: ClassVar[list[str]] = [ "XAxis", "YAxis", "ReferenceArea", "ReferenceDot", "ReferenceLine", "Brush", "CartesianGrid", "Legend", "GraphingTooltip", "Bar", ] class LineChart(CategoricalChartBase): """A Line chart component in Recharts.""" tag = "LineChart" alias = "RechartsLineChart" # Valid children components _valid_children: ClassVar[list[str]] = [ "XAxis", "YAxis", "ReferenceArea", "ReferenceDot", "ReferenceLine", "Brush", "CartesianGrid", "Legend", "GraphingTooltip", "Line", ] class ComposedChart(CategoricalChartBase): """A Composed chart component in Recharts.""" tag = "ComposedChart" alias = "RechartsComposedChart" # The base value of area. Number | 'dataMin' | 'dataMax' | 'auto'. Default: "auto" base_value: Var[int | LiteralComposedChartBaseValue] # The gap between two bar categories, which can be a percent value or a fixed value. Percentage | Number. Default: "10%" bar_category_gap: Var[str | int] # The gap between two bars in the same category. Default: 4 bar_gap: Var[int] # The width or height of each bar. If the barSize is not specified, the size of the bar will be calculated by the barCategoryGap, barGap and the quantity of bar groups. bar_size: Var[int] # If false set, stacked items will be rendered left to right. If true set, stacked items will be rendered right to left. (Render direction affects SVG layering, not x position). Default: False reverse_stack_order: Var[bool] # Valid children components _valid_children: ClassVar[list[str]] = [ "XAxis", "YAxis", "ReferenceArea", "ReferenceDot", "ReferenceLine", "Brush", "CartesianGrid", "Legend", "GraphingTooltip", "Area", "Line", "Bar", ] class PieChart(ChartBase): """A Pie chart component in Recharts.""" tag = "PieChart" alias = "RechartsPieChart" # The sizes of whitespace around the chart, i.e. {"top": 50, "right": 30, "left": 20, "bottom": 5}. margin: Var[dict[str, Any]] # Valid children components _valid_children: ClassVar[list[str]] = [ "PolarAngleAxis", "PolarRadiusAxis", "PolarGrid", "Legend", "GraphingTooltip", "Pie", ] # The customized event handler of mousedown on the sectors in this group on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the sectors in this group on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mouseover on the sectors in this group on_mouse_over: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the sectors in this group on_mouse_out: EventHandler[no_args_event_spec] class RadarChart(ChartBase): """A Radar chart component in Recharts.""" tag = "RadarChart" alias = "RechartsRadarChart" # The source data, in which each element is an object. data: Var[Sequence[dict[str, Any]]] # The sizes of whitespace around the chart, i.e. {"top": 50, "right": 30, "left": 20, "bottom": 5}. Default: {"top": 0, "right": 0, "left": 0, "bottom": 0} margin: Var[dict[str, Any]] # The The x-coordinate of center. If set a percentage, the final value is obtained by multiplying the percentage of width. Number | Percentage. Default: "50%" cx: Var[int | str] # The The y-coordinate of center. If set a percentage, the final value is obtained by multiplying the percentage of height. Number | Percentage. Default: "50%" cy: Var[int | str] # The angle of first radial direction line. Default: 90 start_angle: Var[int] # The angle of last point in the circle which should be startAngle - 360 or startAngle + 360. We'll calculate the direction of chart by 'startAngle' and 'endAngle'. Default: -270 end_angle: Var[int] # The inner radius of first circle grid. If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy. Number | Percentage. Default: 0 inner_radius: Var[int | str] # The outer radius of last circle grid. If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy. Number | Percentage. Default: "80%" outer_radius: Var[int | str] # Valid children components _valid_children: ClassVar[list[str]] = [ "PolarAngleAxis", "PolarRadiusAxis", "PolarGrid", "Legend", "GraphingTooltip", "Radar", ] @classmethod def get_event_triggers(cls) -> dict[str, Var | Any]: """Get the event triggers that pass the component's value to the handler. Returns: A dict mapping the event trigger to the var that is passed to the handler. """ return { EventTriggers.ON_CLICK: no_args_event_spec, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec, } class RadialBarChart(ChartBase): """A RadialBar chart component in Recharts.""" tag = "RadialBarChart" alias = "RechartsRadialBarChart" # The source data which each element is an object. data: Var[Sequence[dict[str, Any]]] # The sizes of whitespace around the chart. Default: {"top": 5, "right": 5, "left": 5 "bottom": 5} margin: Var[dict[str, Any]] # The The x-coordinate of center. If set a percentage, the final value is obtained by multiplying the percentage of width. Number | Percentage. Default: "50%" cx: Var[int | str] # The The y-coordinate of center. If set a percentage, the final value is obtained by multiplying the percentage of height. Number | Percentage. Default: "50%" cy: Var[int | str] # The angle of first radial direction line. Default: 0 start_angle: Var[int] # The angle of last point in the circle which should be startAngle - 360 or startAngle + 360. We'll calculate the direction of chart by 'startAngle' and 'endAngle'. Default: 360 end_angle: Var[int] # The inner radius of first circle grid. If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy. Number | Percentage. Default: "30%" inner_radius: Var[int | str] # The outer radius of last circle grid. If set a percentage, the final value is obtained by multiplying the percentage of maxRadius which is calculated by the width, height, cx, cy. Number | Percentage. Default: "100%" outer_radius: Var[int | str] # The gap between two bar categories, which can be a percent value or a fixed value. Percentage | Number. Default: "10%" bar_category_gap: Var[int | str] # The gap between two bars in the same category, which can be a percent value or a fixed value. Percentage | Number. Default: 4 bar_gap: Var[str] # The size of each bar. If the barSize is not specified, the size of bar will be calculated by the barCategoryGap, barGap and the quantity of bar groups. bar_size: Var[int] # Valid children components _valid_children: ClassVar[list[str]] = [ "PolarAngleAxis", "PolarRadiusAxis", "PolarGrid", "Legend", "GraphingTooltip", "RadialBar", ] class ScatterChart(ChartBase): """A Scatter chart component in Recharts.""" tag = "ScatterChart" alias = "RechartsScatterChart" # The sizes of whitespace around the chart. Default: {"top": 5, "right": 5, "bottom": 5, "left": 5} margin: Var[dict[str, Any]] # Valid children components _valid_children: ClassVar[list[str]] = [ "XAxis", "YAxis", "ZAxis", "ReferenceArea", "ReferenceDot", "ReferenceLine", "Brush", "CartesianGrid", "Legend", "GraphingTooltip", "Scatter", ] @classmethod def get_event_triggers(cls) -> dict[str, Var | Any]: """Get the event triggers that pass the component's value to the handler. Returns: A dict mapping the event trigger to the var that is passed to the handler. """ return { EventTriggers.ON_CLICK: no_args_event_spec, EventTriggers.ON_MOUSE_DOWN: no_args_event_spec, EventTriggers.ON_MOUSE_UP: no_args_event_spec, EventTriggers.ON_MOUSE_MOVE: no_args_event_spec, EventTriggers.ON_MOUSE_OVER: no_args_event_spec, EventTriggers.ON_MOUSE_OUT: no_args_event_spec, EventTriggers.ON_MOUSE_ENTER: no_args_event_spec, EventTriggers.ON_MOUSE_LEAVE: no_args_event_spec, } class FunnelChart(ChartBase): """A Funnel chart component in Recharts.""" tag = "FunnelChart" alias = "RechartsFunnelChart" # The layout of bars in the chart. Default: "centric" layout: Var[str] # The sizes of whitespace around the chart. Default: {"top": 5, "right": 5, "bottom": 5, "left": 5} margin: Var[dict[str, Any]] # The stroke color of each bar. String | Object stroke: Var[str | Color] # Valid children components _valid_children: ClassVar[list[str]] = ["Legend", "GraphingTooltip", "Funnel"] class Treemap(RechartsCharts): """A Treemap chart component in Recharts.""" tag = "Treemap" alias = "RechartsTreemap" # The width of chart container. String or Integer. Default: "100%" width: Var[str | int] = Var.create("100%") # The height of chart container. String or Integer. Default: "100%" height: Var[str | int] = Var.create("100%") # data of treemap. Array data: Var[Sequence[dict[str, Any]]] # The key of a group of data which should be unique in a treemap. String | Number. Default: "value" data_key: Var[str | int] # The key of each sector's name. String. Default: "name" name_key: Var[str] # The treemap will try to keep every single rectangle's aspect ratio near the aspectRatio given. Number aspect_ratio: Var[int] # If set false, animation of area will be disabled. Default: True is_animation_active: Var[bool] # Specifies when the animation should begin, the unit of this option is ms. Default: 0 animation_begin: Var[int] # Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Var[int] # The type of easing function. 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'. Default: "ease" animation_easing: Var[LiteralAnimationEasing] # The customized event handler of animation start on_animation_start: EventHandler[no_args_event_spec] # The customized event handler of animation end on_animation_end: EventHandler[no_args_event_spec] @classmethod def create(cls, *children, **props) -> Component: """Create a chart component. Args: *children: The children of the chart component. **props: The properties of the chart component. Returns: The Treemap component wrapped in a responsive container. """ return ResponsiveContainer.create( super().create(*children, **props), width=props.pop("width", "100%"), height=props.pop("height", "100%"), ) area_chart = AreaChart.create bar_chart = BarChart.create line_chart = LineChart.create composed_chart = ComposedChart.create pie_chart = PieChart.create radar_chart = RadarChart.create radial_bar_chart = RadialBarChart.create scatter_chart = ScatterChart.create funnel_chart = FunnelChart.create treemap = Treemap.create ================================================ FILE: reflex/components/recharts/general.py ================================================ """General components for Recharts.""" from __future__ import annotations from collections.abc import Sequence from typing import Any, ClassVar from reflex.components.component import MemoizationLeaf from reflex.constants.colors import Color from reflex.event import EventHandler, no_args_event_spec from reflex.vars.base import LiteralVar, Var from .recharts import ( LiteralAnimationEasing, LiteralIconType, LiteralLayout, LiteralLegendAlign, LiteralPosition, LiteralVerticalAlign, Recharts, ) class ResponsiveContainer(Recharts, MemoizationLeaf): """A base class for responsive containers in Recharts.""" tag = "ResponsiveContainer" alias = "RechartsResponsiveContainer" # The aspect ratio of the container. The final aspect ratio of the SVG element will be (width / height) * aspect. Number aspect: Var[int] # The width of chart container. Can be a number or string. Default: "100%" width: Var[int | str] # The height of chart container. Can be a number or string. Default: "100%" height: Var[int | str] # The minimum width of chart container. Number or string. min_width: Var[int | str] # The minimum height of chart container. Number or string. min_height: Var[int | str] # If specified a positive number, debounced function will be used to handle the resize event. Default: 0 debounce: Var[int] # If specified provides a callback providing the updated chart width and height values. on_resize: EventHandler[no_args_event_spec] # Valid children components _valid_children: ClassVar[list[str]] = [ "AreaChart", "BarChart", "LineChart", "PieChart", "RadarChart", "RadialBarChart", "ResponsiveContainer", "ScatterChart", "Treemap", "ComposedChart", "FunnelChart", ] class Legend(Recharts): """A Legend component in Recharts.""" tag = "Legend" alias = "RechartsLegend" # The width of legend container. Number width: Var[int] # The height of legend container. Number height: Var[int] # The layout of legend items. 'horizontal' | 'vertical'. Default: "horizontal" layout: Var[LiteralLayout] # The alignment of legend items in 'horizontal' direction, which can be 'left', 'center', 'right'. Default: "center" align: Var[LiteralLegendAlign] # The alignment of legend items in 'vertical' direction, which can be 'top', 'middle', 'bottom'. Default: "bottom" vertical_align: Var[LiteralVerticalAlign] # The size of icon in each legend item. Default: 14 icon_size: Var[int] # The type of icon in each legend item. 'line' | 'plainline' | 'square' | 'rect' | 'circle' | 'cross' | 'diamond' | 'star' | 'triangle' | 'wye' icon_type: Var[LiteralIconType] # The source data of the content to be displayed in the legend, usually calculated internally. Default: [] payload: Var[Sequence[dict[str, Any]]] # The width of chart container, usually calculated internally. chart_width: Var[int] # The height of chart container, usually calculated internally. chart_height: Var[int] # The margin of chart container, usually calculated internally. margin: Var[dict[str, Any]] # The customized event handler of click on the items in this group on_click: EventHandler[no_args_event_spec] # The customized event handler of mousedown on the items in this group on_mouse_down: EventHandler[no_args_event_spec] # The customized event handler of mouseup on the items in this group on_mouse_up: EventHandler[no_args_event_spec] # The customized event handler of mousemove on the items in this group on_mouse_move: EventHandler[no_args_event_spec] # The customized event handler of mouseover on the items in this group on_mouse_over: EventHandler[no_args_event_spec] # The customized event handler of mouseout on the items in this group on_mouse_out: EventHandler[no_args_event_spec] # The customized event handler of mouseenter on the items in this group on_mouse_enter: EventHandler[no_args_event_spec] # The customized event handler of mouseleave on the items in this group on_mouse_leave: EventHandler[no_args_event_spec] class GraphingTooltip(Recharts): """A Tooltip component in Recharts.""" tag = "Tooltip" alias = "RechartsTooltip" # The separator between name and value. Default: ":" separator: Var[str] # The offset size of tooltip. Number. Default: 10 offset: Var[int] # When an item of the payload has value null or undefined, this item won't be displayed. Default: True filter_null: Var[bool] # If set false, no cursor will be drawn when tooltip is active. Default: {"strokeWidth": 1, "fill": rx.color("gray", 3)} cursor: Var[dict[str, Any] | bool] = LiteralVar.create({ "strokeWidth": 1, "fill": Color("gray", 3), }) # The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally. view_box: Var[dict[str, Any]] # The style of default tooltip content item which is a li element. Default: {"color": rx.color("gray", 12)} item_style: Var[dict[str, Any]] = LiteralVar.create({ "color": Color("gray", 12), }) # The style of tooltip wrapper which is a dom element. Default: {} wrapper_style: Var[dict[str, Any]] # The style of tooltip content which is a dom element. Default: {"background": rx.color("gray", 1), "borderColor": rx.color("gray", 4), "borderRadius": "8px"} content_style: Var[dict[str, Any]] = LiteralVar.create({ "background": Color("gray", 1), "borderColor": Color("gray", 4), "borderRadius": "8px", }) # The style of default tooltip label which is a p element. Default: {"color": rx.color("gray", 11)} label_style: Var[dict[str, Any]] = LiteralVar.create({"color": Color("gray", 11)}) # This option allows the tooltip to extend beyond the viewBox of the chart itself. Default: {"x": False, "y": False} allow_escape_view_box: Var[dict[str, bool]] # If set true, the tooltip is displayed. If set false, the tooltip is hidden, usually calculated internally. Default: False active: Var[bool] # If this field is set, the tooltip position will be fixed and will not move anymore. position: Var[dict[str, Any]] # The coordinate of tooltip which is usually calculated internally. Default: {"x": 0, "y": 0} coordinate: Var[dict[str, Any]] # If set false, animation of tooltip will be disabled. Default: True is_animation_active: Var[bool] # Specifies the duration of animation, the unit of this option is ms. Default: 1500 animation_duration: Var[int] # The type of easing function. Default: "ease" animation_easing: Var[LiteralAnimationEasing] class Label(Recharts): """A Label component in Recharts.""" tag = "Label" alias = "RechartsLabel" # The box of viewing area, which has the shape of {x: someVal, y: someVal, width: someVal, height: someVal}, usually calculated internally. view_box: Var[dict[str, Any]] # The value of label, which can be specified by this props or the children of