Merge branch 'main' into lendemor/test_stateless_app
This commit is contained in:
commit
001b01f541
@ -11,7 +11,7 @@ omit =
|
||||
[report]
|
||||
show_missing = true
|
||||
# TODO bump back to 79
|
||||
fail_under = 60
|
||||
fail_under = 70
|
||||
precision = 2
|
||||
|
||||
# Regexes for lines to exclude from consideration
|
||||
|
2
.github/actions/setup_build_env/action.yml
vendored
2
.github/actions/setup_build_env/action.yml
vendored
@ -18,7 +18,7 @@ inputs:
|
||||
poetry-version:
|
||||
description: 'Poetry version to install'
|
||||
required: false
|
||||
default: '1.3.1'
|
||||
default: '1.8.3'
|
||||
run-poetry-install:
|
||||
description: 'Whether to run poetry install on current dir'
|
||||
required: false
|
||||
|
8
.github/workflows/benchmarks.yml
vendored
8
.github/workflows/benchmarks.yml
vendored
@ -81,17 +81,13 @@ jobs:
|
||||
matrix:
|
||||
# Show OS combos first in GUI
|
||||
os: [ubuntu-latest, windows-latest, macos-12]
|
||||
python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
exclude:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.13'
|
||||
- os: windows-latest
|
||||
python-version: '3.9.18'
|
||||
- os: windows-latest
|
||||
python-version: '3.8.18'
|
||||
# keep only one python version for MacOS
|
||||
- os: macos-latest
|
||||
python-version: '3.8.18'
|
||||
- os: macos-latest
|
||||
python-version: '3.9.18'
|
||||
- os: macos-latest
|
||||
@ -103,8 +99,6 @@ jobs:
|
||||
python-version: '3.10.11'
|
||||
- os: windows-latest
|
||||
python-version: '3.9.13'
|
||||
- os: windows-latest
|
||||
python-version: '3.8.10'
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
40
.github/workflows/check_node_latest.yml
vendored
Normal file
40
.github/workflows/check_node_latest.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: integration-node-latest
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TELEMETRY_ENABLED: false
|
||||
REFLEX_USE_SYSTEM_NODE: true
|
||||
|
||||
jobs:
|
||||
check_latest_node:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ['3.12']
|
||||
node-version: ['node']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup_build_env
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: |
|
||||
poetry run uv pip install pyvirtualdisplay pillow
|
||||
poetry run playwright install --with-deps
|
||||
- run: |
|
||||
poetry run pytest tests/test_node_version.py
|
||||
poetry run pytest tests/integration
|
||||
|
||||
|
||||
|
88
.github/workflows/check_outdated_dependencies.yml
vendored
Normal file
88
.github/workflows/check_outdated_dependencies.yml
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
name: check-outdated-dependencies
|
||||
|
||||
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.9'
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
|
||||
- name: Check outdated backend dependencies
|
||||
run: |
|
||||
outdated=$(poetry show -oT)
|
||||
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.10.11'
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
- name: Clone Reflex Website Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: reflex-dev/reflex-web
|
||||
ref: main
|
||||
path: reflex-web
|
||||
- name: Install Requirements for reflex-web
|
||||
working-directory: ./reflex-web
|
||||
run: poetry run uv pip install -r requirements.txt
|
||||
- name: Install additional dependencies for DB access
|
||||
run: poetry run uv pip install psycopg2-binary
|
||||
- name: Init Website for reflex-web
|
||||
working-directory: ./reflex-web
|
||||
run: poetry run reflex init
|
||||
- name: Run Website and Check for errors
|
||||
run: |
|
||||
poetry 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|react-markdown|remark-math|remark-gfm|rehype-katex|rehype-raw|remark-unwrap-images' || 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
|
||||
|
13
.github/workflows/integration_app_harness.yml
vendored
13
.github/workflows/integration_app_harness.yml
vendored
@ -23,8 +23,8 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
state_manager: ['redis', 'memory']
|
||||
python-version: ['3.8.18', '3.11.5', '3.12.0']
|
||||
runs-on: ubuntu-latest
|
||||
python-version: ['3.11.5', '3.12.0']
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
# Label used to access the service container
|
||||
redis:
|
||||
@ -47,16 +47,11 @@ jobs:
|
||||
create-venv-at-path: .venv
|
||||
- run: poetry run uv pip install pyvirtualdisplay pillow playwright pytest-playwright
|
||||
- name: Run app harness tests
|
||||
uses: nick-fields/retry@v3
|
||||
env:
|
||||
SCREENSHOT_DIR: /tmp/screenshots
|
||||
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 2
|
||||
command: |
|
||||
poetry run playwright install --with-deps
|
||||
poetry run pytest integration
|
||||
run: |
|
||||
poetry run pytest tests/integration
|
||||
- uses: actions/upload-artifact@v4
|
||||
name: Upload failed test screenshots
|
||||
if: always()
|
||||
|
59
.github/workflows/integration_tests.yml
vendored
59
.github/workflows/integration_tests.yml
vendored
@ -42,22 +42,18 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Show OS combos first in GUI
|
||||
os: [ubuntu-latest, windows-latest, macos-12]
|
||||
python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
exclude:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.13'
|
||||
- os: windows-latest
|
||||
python-version: '3.9.18'
|
||||
- os: windows-latest
|
||||
python-version: '3.8.18'
|
||||
include:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.11'
|
||||
- os: windows-latest
|
||||
python-version: '3.9.13'
|
||||
- os: windows-latest
|
||||
python-version: '3.8.10'
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@ -126,7 +122,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Show OS combos first in GUI
|
||||
os: [ubuntu-latest, windows-latest, macos-12]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
python-version: ['3.10.11', '3.11.4']
|
||||
|
||||
env:
|
||||
@ -156,11 +152,7 @@ jobs:
|
||||
working-directory: ./reflex-web
|
||||
run: poetry run reflex init
|
||||
- name: Run Website and Check for errors
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 2
|
||||
command: |
|
||||
run: |
|
||||
# Check that npm is home
|
||||
npm -v
|
||||
poetry run bash scripts/integration.sh ./reflex-web prod
|
||||
@ -169,4 +161,45 @@ jobs:
|
||||
poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
|
||||
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--app-name "reflex-web" --path ./reflex-web/.web
|
||||
--app-name "reflex-web" --path ./reflex-web/.web
|
||||
|
||||
reflex-web-macos:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ['3.11.5', '3.12.0']
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup_build_env
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
- name: Clone Reflex Website Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: reflex-dev/reflex-web
|
||||
ref: main
|
||||
path: reflex-web
|
||||
- name: Install Requirements for reflex-web
|
||||
working-directory: ./reflex-web
|
||||
run: poetry run uv pip install -r requirements.txt
|
||||
- name: Install additional dependencies for DB access
|
||||
run: poetry run uv pip install psycopg2-binary
|
||||
- name: Init Website for reflex-web
|
||||
working-directory: ./reflex-web
|
||||
run: poetry run reflex init
|
||||
- name: Run Website and Check for errors
|
||||
run: |
|
||||
# Check that npm is home
|
||||
npm -v
|
||||
poetry run bash scripts/integration.sh ./reflex-web prod
|
||||
- name: Measure and upload .web size
|
||||
run:
|
||||
poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
|
||||
--python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
|
||||
--pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
|
||||
--app-name "reflex-web" --path ./reflex-web/.web
|
||||
|
2
.github/workflows/integration_tests_wsl.yml
vendored
2
.github/workflows/integration_tests_wsl.yml
vendored
@ -37,6 +37,8 @@ jobs:
|
||||
path: reflex-examples
|
||||
|
||||
- uses: Vampire/setup-wsl@v3
|
||||
with:
|
||||
distribution: Ubuntu-24.04
|
||||
|
||||
- name: Install Python
|
||||
shell: wsl-bash {0}
|
||||
|
@ -28,5 +28,5 @@ jobs:
|
||||
# Run reflex init in a docker container
|
||||
|
||||
# cwd is repo root
|
||||
docker build -f integration/init-test/Dockerfile -t reflex-init-test integration/init-test
|
||||
docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/integration/init-test/in_docker_test_script.sh
|
||||
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
|
||||
|
43
.github/workflows/unit_tests.yml
vendored
43
.github/workflows/unit_tests.yml
vendored
@ -27,24 +27,21 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-12]
|
||||
python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
# Windows is a bit behind on Python version availability in Github
|
||||
exclude:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.13'
|
||||
- os: windows-latest
|
||||
python-version: '3.9.18'
|
||||
- os: windows-latest
|
||||
python-version: '3.8.18'
|
||||
include:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.11'
|
||||
- os: windows-latest
|
||||
python-version: '3.9.13'
|
||||
- os: windows-latest
|
||||
python-version: '3.8.10'
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
# Service containers to run with `runner-job`
|
||||
services:
|
||||
# Label used to access the service container
|
||||
@ -69,17 +66,43 @@ jobs:
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
export PYTHONUNBUFFERED=1
|
||||
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
|
||||
poetry 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 REDIS_URL=redis://localhost:6379
|
||||
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
|
||||
poetry 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
|
||||
poetry run uv pip install "pydantic~=1.10"
|
||||
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
|
||||
- run: poetry run coverage html
|
||||
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
|
||||
- name: Generate coverage report
|
||||
run: poetry 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.9.18', '3.10.13', '3.11.5', '3.12.0']
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/actions/setup_build_env
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
run-poetry-install: true
|
||||
create-venv-at-path: .venv
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
export PYTHONUNBUFFERED=1
|
||||
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
|
||||
- name: Run unit tests w/ pydantic v1
|
||||
run: |
|
||||
export PYTHONUNBUFFERED=1
|
||||
poetry run uv pip install "pydantic~=1.10"
|
||||
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
|
@ -3,10 +3,10 @@ fail_fast: true
|
||||
repos:
|
||||
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.4.10
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
args: [integration, reflex, tests]
|
||||
args: [reflex, tests]
|
||||
- id: ruff
|
||||
args: ["--fix", "--exit-non-zero-on-fix"]
|
||||
exclude: '^integration/benchmarks/'
|
||||
@ -25,7 +25,7 @@ repos:
|
||||
rev: v1.1.313
|
||||
hooks:
|
||||
- id: pyright
|
||||
args: [integration, reflex, tests]
|
||||
args: [reflex, tests]
|
||||
language: system
|
||||
|
||||
- repo: https://github.com/terrencepreilly/darglint
|
||||
|
@ -8,7 +8,7 @@ Here is a quick guide on how to run Reflex repo locally so you can start contrib
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
- Python >= 3.8
|
||||
- Python >= 3.9
|
||||
- Poetry version >= 1.4.0 and add it to your path (see [Poetry Docs](https://python-poetry.org/docs/#installation) for more info).
|
||||
|
||||
**1. Fork this repository:**
|
||||
@ -69,7 +69,7 @@ In your `reflex` directory run make sure all the unit tests are still passing us
|
||||
This will fail if code coverage is below 70%.
|
||||
|
||||
``` bash
|
||||
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
|
||||
poetry 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.
|
||||
@ -87,7 +87,7 @@ poetry run ruff format .
|
||||
```
|
||||
|
||||
Consider installing git pre-commit hooks so Ruff, Pyright, Darglint and `make_pyi` will run automatically before each commit.
|
||||
Note that pre-commit will only be installed when you use a Python version >= 3.8.
|
||||
Note that pre-commit will only be installed when you use a Python version >= 3.9.
|
||||
|
||||
``` bash
|
||||
pre-commit install
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
### **✨ Performant, customizable web apps in pure Python. Deploy in seconds. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -18,7 +17,7 @@
|
||||
|
||||
---
|
||||
|
||||
[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)
|
||||
[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)
|
||||
|
||||
---
|
||||
|
||||
@ -35,7 +34,7 @@ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architectu
|
||||
|
||||
## ⚙️ Installation
|
||||
|
||||
Open a terminal and run (Requires Python 3.8+):
|
||||
Open a terminal and run (Requires Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -3,8 +3,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from utils import send_data_to_posthog
|
||||
|
||||
@ -28,7 +28,7 @@ def insert_benchmarking_data(
|
||||
send_data_to_posthog("lighthouse_benchmark", properties)
|
||||
|
||||
|
||||
def get_lighthouse_scores(directory_path: str) -> dict:
|
||||
def get_lighthouse_scores(directory_path: str | Path) -> dict:
|
||||
"""Extracts the Lighthouse scores from the JSON files in the specified directory.
|
||||
|
||||
Args:
|
||||
@ -38,24 +38,21 @@ def get_lighthouse_scores(directory_path: str) -> dict:
|
||||
dict: The Lighthouse scores.
|
||||
"""
|
||||
scores = {}
|
||||
|
||||
directory_path = Path(directory_path)
|
||||
try:
|
||||
for filename in os.listdir(directory_path):
|
||||
if filename.endswith(".json") and filename != "manifest.json":
|
||||
file_path = os.path.join(directory_path, filename)
|
||||
with open(file_path, "r") as file:
|
||||
data = json.load(file)
|
||||
# Extract scores and add them to the dictionary with the filename as key
|
||||
scores[data["finalUrl"].replace("http://localhost:3000/", "/")] = {
|
||||
"performance_score": data["categories"]["performance"]["score"],
|
||||
"accessibility_score": data["categories"]["accessibility"][
|
||||
"score"
|
||||
],
|
||||
"best_practices_score": data["categories"]["best-practices"][
|
||||
"score"
|
||||
],
|
||||
"seo_score": data["categories"]["seo"]["score"],
|
||||
}
|
||||
for filename in directory_path.iterdir():
|
||||
if filename.suffix == ".json" and filename.stem != "manifest":
|
||||
file_path = directory_path / filename
|
||||
data = json.loads(file_path.read_text())
|
||||
# Extract scores and add them to the dictionary with the filename as key
|
||||
scores[data["finalUrl"].replace("http://localhost:3000/", "/")] = {
|
||||
"performance_score": data["categories"]["performance"]["score"],
|
||||
"accessibility_score": data["categories"]["accessibility"]["score"],
|
||||
"best_practices_score": data["categories"]["best-practices"][
|
||||
"score"
|
||||
],
|
||||
"seo_score": data["categories"]["seo"]["score"],
|
||||
}
|
||||
except Exception as e:
|
||||
return {"error": e}
|
||||
|
||||
|
@ -2,11 +2,12 @@
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from utils import get_directory_size, get_python_version, send_data_to_posthog
|
||||
|
||||
|
||||
def get_package_size(venv_path, os_name):
|
||||
def get_package_size(venv_path: Path, os_name):
|
||||
"""Get the size of a specified package.
|
||||
|
||||
Args:
|
||||
@ -26,14 +27,12 @@ def get_package_size(venv_path, os_name):
|
||||
|
||||
is_windows = "windows" in os_name
|
||||
|
||||
full_path = (
|
||||
["lib", f"python{python_version}", "site-packages"]
|
||||
package_dir: Path = (
|
||||
venv_path / "lib" / f"python{python_version}" / "site-packages"
|
||||
if not is_windows
|
||||
else ["Lib", "site-packages"]
|
||||
else venv_path / "Lib" / "site-packages"
|
||||
)
|
||||
|
||||
package_dir = os.path.join(venv_path, *full_path)
|
||||
if not os.path.exists(package_dir):
|
||||
if not package_dir.exists():
|
||||
raise ValueError(
|
||||
"Error: Virtual environment does not exist or is not activated."
|
||||
)
|
||||
@ -63,9 +62,9 @@ def insert_benchmarking_data(
|
||||
path: The path to the dir or file to check size.
|
||||
"""
|
||||
if "./dist" in path:
|
||||
size = get_directory_size(path)
|
||||
size = get_directory_size(Path(path))
|
||||
else:
|
||||
size = get_package_size(path, os_type_version)
|
||||
size = get_package_size(Path(path), os_type_version)
|
||||
|
||||
# Prepare the event data
|
||||
properties = {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from utils import get_directory_size, send_data_to_posthog
|
||||
|
||||
@ -28,7 +29,7 @@ def insert_benchmarking_data(
|
||||
pr_id: The id of the PR.
|
||||
path: The path to the dir or file to check size.
|
||||
"""
|
||||
size = get_directory_size(path)
|
||||
size = get_directory_size(Path(path))
|
||||
|
||||
# Prepare the event data
|
||||
properties = {
|
||||
|
@ -130,7 +130,6 @@ def render_multiple_pages(app, num: int):
|
||||
|
||||
def AppWithOnePage():
|
||||
"""A reflex app with one page."""
|
||||
import reflex_chakra as rc
|
||||
from rxconfig import config # type: ignore
|
||||
|
||||
import reflex as rx
|
||||
@ -145,7 +144,7 @@ def AppWithOnePage():
|
||||
|
||||
def index() -> rx.Component:
|
||||
return rx.center(
|
||||
rc.input(
|
||||
rx.input(
|
||||
id="token", value=State.router.session.client_token, is_read_only=True
|
||||
),
|
||||
rx.vstack(
|
||||
|
@ -2,12 +2,13 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
from httpx import HTTPError
|
||||
|
||||
|
||||
def get_python_version(venv_path, os_name):
|
||||
def get_python_version(venv_path: Path, os_name):
|
||||
"""Get the python version of python in a virtual env.
|
||||
|
||||
Args:
|
||||
@ -18,13 +19,13 @@ def get_python_version(venv_path, os_name):
|
||||
The python version.
|
||||
"""
|
||||
python_executable = (
|
||||
os.path.join(venv_path, "bin", "python")
|
||||
venv_path / "bin" / "python"
|
||||
if "windows" not in os_name
|
||||
else os.path.join(venv_path, "Scripts", "python.exe")
|
||||
else venv_path / "Scripts" / "python.exe"
|
||||
)
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
[python_executable, "--version"], stderr=subprocess.STDOUT
|
||||
[str(python_executable), "--version"], stderr=subprocess.STDOUT
|
||||
)
|
||||
python_version = output.decode("utf-8").strip().split()[1]
|
||||
return ".".join(python_version.split(".")[:-1])
|
||||
@ -32,7 +33,7 @@ def get_python_version(venv_path, os_name):
|
||||
return None
|
||||
|
||||
|
||||
def get_directory_size(directory):
|
||||
def get_directory_size(directory: Path):
|
||||
"""Get the size of a directory in bytes.
|
||||
|
||||
Args:
|
||||
@ -44,8 +45,8 @@ def get_directory_size(directory):
|
||||
total_size = 0
|
||||
for dirpath, _, filenames in os.walk(directory):
|
||||
for f in filenames:
|
||||
fp = os.path.join(dirpath, f)
|
||||
total_size += os.path.getsize(fp)
|
||||
fp = Path(dirpath) / f
|
||||
total_size += fp.stat().st_size
|
||||
return total_size
|
||||
|
||||
|
||||
|
@ -1,133 +1,30 @@
|
||||
# Reflex Docker Container
|
||||
# Reflex Docker Examples
|
||||
|
||||
This example describes how to create and use a container image for Reflex with your own code.
|
||||
This directory contains several examples of how to deploy Reflex apps using docker.
|
||||
|
||||
## Update Requirements
|
||||
In all cases, ensure that your `requirements.txt` file is up to date and
|
||||
includes the `reflex` package.
|
||||
|
||||
The `requirements.txt` includes the reflex package which is needed to install
|
||||
Reflex framework. If you use additional packages in your project you have to add
|
||||
this in the `requirements.txt` first. Copy the `Dockerfile`, `.dockerignore` and
|
||||
the `requirements.txt` file in your project folder.
|
||||
## `simple-two-port`
|
||||
|
||||
## Build Simple Reflex Container Image
|
||||
The most basic production deployment exposes two HTTP ports and relies on an
|
||||
existing load balancer to forward the traffic appropriately.
|
||||
|
||||
The main `Dockerfile` is intended to build a very simple, single container deployment that runs
|
||||
the Reflex frontend and backend together, exposing ports 3000 and 8000.
|
||||
## `simple-one-port`
|
||||
|
||||
To build your container image run the following command:
|
||||
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.
|
||||
|
||||
```bash
|
||||
docker build -t reflex-app:latest .
|
||||
```
|
||||
## `production-compose`
|
||||
|
||||
## Start Container Service
|
||||
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.
|
||||
|
||||
Finally, you can start your Reflex container service as follows:
|
||||
## `production-app-platform`
|
||||
|
||||
```bash
|
||||
docker run -it --rm -p 3000:3000 -p 8000:8000 --name app reflex-app:latest
|
||||
```
|
||||
|
||||
It may take a few seconds for the service to become available.
|
||||
|
||||
Access your app at http://localhost:3000.
|
||||
|
||||
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.
|
||||
|
||||
# Production Service with Docker Compose and Caddy
|
||||
|
||||
An example production deployment uses automatic TLS with Caddy serving static files
|
||||
for the frontend and proxying requests to both the frontend and backend.
|
||||
|
||||
Copy the following files to your project directory:
|
||||
* `compose.yaml`
|
||||
* `compose.prod.yaml`
|
||||
* `compose.tools.yaml`
|
||||
* `prod.Dockerfile`
|
||||
* `Caddy.Dockerfile`
|
||||
* `Caddyfile`
|
||||
|
||||
The production app container, based on `prod.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
|
||||
```
|
||||
|
||||
# Container Hosting
|
||||
|
||||
Most container hosting services automatically terminate TLS and expect the app
|
||||
to be listening on a single port (typically `$PORT`).
|
||||
|
||||
To host a Reflex app on one of these platforms, like Google Cloud Run, Render,
|
||||
Railway, etc, use `app.Dockerfile` to build a single image containing a reverse
|
||||
proxy that will serve that frontend as static files and proxy requests to the
|
||||
backend for specific endpoints.
|
||||
|
||||
If the chosen platform does not support buildx and thus heredoc, you can copy
|
||||
the Caddyfile configuration into a separate Caddyfile in the root of the
|
||||
project.
|
||||
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.
|
5
docker-example/production-app-platform/.dockerignore
Normal file
5
docker-example/production-app-platform/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
.web
|
||||
.git
|
||||
__pycache__/*
|
||||
Dockerfile
|
||||
uploaded_files
|
65
docker-example/production-app-platform/Dockerfile
Normal file
65
docker-example/production-app-platform/Dockerfile
Normal file
@ -0,0 +1,65 @@
|
||||
# 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/_static
|
||||
#
|
||||
# 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/_static` 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.11 as init
|
||||
|
||||
ARG uv=/root/.cargo/bin/uv
|
||||
|
||||
# Install `uv` for faster package boostrapping
|
||||
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.11-slim
|
||||
WORKDIR /app
|
||||
RUN adduser --disabled-password --home /app reflex
|
||||
COPY --chown=reflex --from=init /app /app
|
||||
# Install libpq-dev for psycopg2 (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}
|
113
docker-example/production-app-platform/README.md
Normal file
113
docker-example/production-app-platform/README.md
Normal file
@ -0,0 +1,113 @@
|
||||
# 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 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
|
||||
`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 REDIS_URL and 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
|
@ -42,10 +42,11 @@ COPY --chown=reflex --from=init /app /app
|
||||
# Install libpq-dev for psycopg2 (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"
|
||||
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 reflex db migrate && reflex run --env prod --backend-only
|
||||
CMD [ -d alembic ] && reflex db migrate; \
|
||||
exec reflex run --env prod --backend-only
|
75
docker-example/production-compose/README.md
Normal file
75
docker-example/production-compose/README.md
Normal file
@ -0,0 +1,75 @@
|
||||
# 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
|
||||
```
|
@ -12,7 +12,6 @@ services:
|
||||
DB_URL: sqlite:///data/reflex.db
|
||||
build:
|
||||
context: .
|
||||
dockerfile: prod.Dockerfile
|
||||
volumes:
|
||||
- db-data:/app/data
|
||||
- upload-data:/app/uploaded_files
|
@ -1 +0,0 @@
|
||||
reflex
|
5
docker-example/simple-one-port/.dockerignore
Normal file
5
docker-example/simple-one-port/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
.web
|
||||
.git
|
||||
__pycache__/*
|
||||
Dockerfile
|
||||
uploaded_files
|
14
docker-example/simple-one-port/Caddyfile
Normal file
14
docker-example/simple-one-port/Caddyfile
Normal file
@ -0,0 +1,14 @@
|
||||
:{$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
|
||||
}
|
@ -10,31 +10,13 @@ FROM python:3.11
|
||||
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 API_URL=${API_URL:-http://localhost:$PORT}
|
||||
ENV PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} REDIS_URL=redis://localhost PYTHONUNBUFFERED=1
|
||||
|
||||
# Install Caddy server inside image
|
||||
RUN apt-get update -y && apt-get install -y caddy && rm -rf /var/lib/apt/lists/*
|
||||
# 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
|
||||
|
||||
# Create a simple Caddyfile to serve as reverse proxy
|
||||
RUN cat > Caddyfile <<EOF
|
||||
:{\$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
|
||||
}
|
||||
EOF
|
||||
|
||||
# Copy local context to `/app` inside container (see .dockerignore)
|
||||
COPY . .
|
||||
|
||||
@ -54,4 +36,6 @@ EXPOSE $PORT
|
||||
|
||||
# Apply migrations before starting the backend.
|
||||
CMD [ -d alembic ] && reflex db migrate; \
|
||||
caddy start && reflex run --env prod --backend-only --loglevel debug
|
||||
caddy start && \
|
||||
redis-server --daemonize yes && \
|
||||
exec reflex run --env prod --backend-only
|
36
docker-example/simple-one-port/README.md
Normal file
36
docker-example/simple-one-port/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
# 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 NextJS 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.
|
5
docker-example/simple-two-port/.dockerignore
Normal file
5
docker-example/simple-two-port/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
.web
|
||||
.git
|
||||
__pycache__/*
|
||||
Dockerfile
|
||||
uploaded_files
|
@ -1,5 +1,8 @@
|
||||
# This Dockerfile is used to deploy a simple single-container Reflex app instance.
|
||||
FROM python:3.11
|
||||
FROM python:3.12
|
||||
|
||||
RUN apt-get update && apt-get install -y redis-server && rm -rf /var/lib/apt/lists/*
|
||||
ENV REDIS_URL=redis://localhost PYTHONUNBUFFERED=1
|
||||
|
||||
# Copy local context to `/app` inside container (see .dockerignore)
|
||||
WORKDIR /app
|
||||
@ -18,4 +21,6 @@ RUN reflex export --frontend-only --no-zip
|
||||
STOPSIGNAL SIGKILL
|
||||
|
||||
# Always apply migrations before starting the backend.
|
||||
CMD [ -d alembic ] && reflex db migrate; reflex run --env prod
|
||||
CMD [ -d alembic ] && reflex db migrate; \
|
||||
redis-server --daemonize yes && \
|
||||
exec reflex run --env prod
|
44
docker-example/simple-two-port/README.md
Normal file
44
docker-example/simple-two-port/README.md
Normal file
@ -0,0 +1,44 @@
|
||||
# simple-two-port
|
||||
|
||||
This docker deployment runs Reflex in prod mode, exposing two HTTP ports:
|
||||
* `3000` - node NextJS 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
|
||||
```
|
@ -10,7 +10,6 @@
|
||||
|
||||
### **✨ Performante, anpassbare Web-Apps in purem Python. Bereitstellung in Sekunden. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -35,7 +34,7 @@ Auf unserer [Architektur-Seite](https://reflex.dev/blog/2024-03-21-reflex-archit
|
||||
|
||||
## ⚙️ Installation
|
||||
|
||||
Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.8+):
|
||||
Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -35,7 +35,7 @@ Consulta nuestra [página de arquitectura](https://reflex.dev/blog/2024-03-21-re
|
||||
|
||||
## ⚙️ Instalación
|
||||
|
||||
Abra un terminal y ejecute (Requiere Python 3.8+):
|
||||
Abra un terminal y ejecute (Requiere Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -11,7 +11,6 @@ Pynecone की तलाश हैं? आप सही रेपो में
|
||||
### **✨ प्रदर्शनकारी, अनुकूलित वेब ऐप्स, शुद्ध Python में। सेकंडों में तैनात करें। ✨**
|
||||
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -36,7 +35,7 @@ Reflex के अंदर के कामकाज को जानने क
|
||||
|
||||
## ⚙️ इंस्टॉलेशन (Installation)
|
||||
|
||||
एक टर्मिनल खोलें और चलाएं (Python 3.8+ की आवश्यकता है):
|
||||
एक टर्मिनल खोलें और चलाएं (Python 3.9+ की आवश्यकता है):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
### **✨ App web performanti e personalizzabili in puro Python. Distribuisci in pochi secondi. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -23,7 +22,7 @@
|
||||
|
||||
## ⚙️ Installazione
|
||||
|
||||
Apri un terminale ed esegui (Richiede Python 3.8+):
|
||||
Apri un terminale ed esegui (Richiede Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -11,7 +11,6 @@
|
||||
### **✨ 即時デプロイが可能な、Pure Python で作ったパフォーマンスと汎用性が高い Web アプリケーション ✨**
|
||||
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -38,7 +37,7 @@ Reflex がどのように動作しているかを知るには、[アーキテク
|
||||
|
||||
## ⚙️ インストール
|
||||
|
||||
ターミナルを開いて以下のコマンドを実行してください。(Python 3.8 以上が必要です。):
|
||||
ターミナルを開いて以下のコマンドを実行してください。(Python 3.9 以上が必要です。):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
### **✨ 순수 Python으로 고성능 사용자 정의 웹앱을 만들어 보세요. 몇 초만에 배포 가능합니다. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -21,7 +20,7 @@
|
||||
---
|
||||
## ⚙️ 설치
|
||||
|
||||
터미널을 열고 실행하세요. (Python 3.8+ 필요):
|
||||
터미널을 열고 실행하세요. (Python 3.9+ 필요):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
### **✨ برنامه های تحت وب قابل تنظیم، کارآمد تماما پایتونی که در چند ثانیه مستقر(دپلوی) میشود. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -35,7 +34,7 @@
|
||||
|
||||
## ⚙️ Installation - نصب و راه اندازی
|
||||
|
||||
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.8+):
|
||||
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -21,7 +21,7 @@
|
||||
---
|
||||
## ⚙️ Instalação
|
||||
|
||||
Abra um terminal e execute (Requer Python 3.8+):
|
||||
Abra um terminal e execute (Requer Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -11,7 +11,6 @@
|
||||
### **✨ Saf Python'da performanslı, özelleştirilebilir web uygulamaları. Saniyeler içinde dağıtın. ✨**
|
||||
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -25,7 +24,7 @@
|
||||
|
||||
## ⚙️ Kurulum
|
||||
|
||||
Bir terminal açın ve çalıştırın (Python 3.8+ gerekir):
|
||||
Bir terminal açın ve çalıştırın (Python 3.9+ gerekir):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
267
docs/vi/README.md
Normal file
267
docs/vi/README.md
Normal file
@ -0,0 +1,267 @@
|
||||
```diff
|
||||
+ Bạn đang tìm kiếm Pynecone? Bạn đã tìm đúng. Pynecone đã được đổi tên thành Reflex. +
|
||||
```
|
||||
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/images/reflex_dark.svg#gh-light-mode-only" alt="Reflex Logo" width="300px">
|
||||
<img src="https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/images/reflex_light.svg#gh-dark-mode-only" alt="Reflex Logo" width="300px">
|
||||
|
||||
<hr>
|
||||
|
||||
### **✨ Ứng dụng web hiệu suất cao, tùy chỉnh bằng Python thuần. Deploy trong vài giây. ✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
[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.9+):
|
||||
|
||||
```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.
|
||||
|
||||
|
||||
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/reflex-dev/reflex/main/docs/images/dalle.gif" alt="A frontend wrapper for DALL·E, shown in the process of generating an image." width="550" />
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
Đâ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.
|
||||
|
||||
<div align="center">
|
||||
<img src="../images/dalle_colored_code_example.png" alt="Explaining the differences between backend and frontend parts of the DALL-E app." width="900" />
|
||||
</div>
|
||||
|
||||
|
||||
### **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
|
||||
|
||||
<div align="center">
|
||||
|
||||
📑 [Docs](https://reflex.dev/docs/getting-started/introduction) | 🗞️ [Blog](https://reflex.dev/blog) | 📱 [Component Library](https://reflex.dev/docs/library) | 🖼️ [Gallery](https://reflex.dev/docs/gallery) | 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## ✅ Status
|
||||
|
||||
Reflex phát hành vào tháng 12/2022 với tên là Pynecone.
|
||||
|
||||
Đến tháng 02/2024, chúng tôi tạo ra dịch vụ dưới phiên bản alpha! Trong thời gian này mọi người có thể triển khai ứng dụng hoàn toàn miễn phí. Xem [roadmap](https://github.com/reflex-dev/reflex/issues/2727) để biết thêm chi tiết.
|
||||
|
||||
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
|
||||
[CONTIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md)
|
||||
|
||||
|
||||
## Xin cảm ơn các Contributors:
|
||||
<a href="https://github.com/reflex-dev/reflex/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=reflex-dev/reflex" />
|
||||
</a>
|
||||
|
||||
## License
|
||||
|
||||
Reflex là mã nguồn mở và sử dụng giấy phép [Apache License 2.0](LICENSE).
|
@ -10,7 +10,6 @@
|
||||
|
||||
### **✨ 使用 Python 创建高效且可自定义的网页应用程序,几秒钟内即可部署.✨**
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -35,7 +34,7 @@ Reflex 是一个使用纯Python构建全栈web应用的库。
|
||||
|
||||
## ⚙️ 安装
|
||||
|
||||
打开一个终端并且运行(要求Python3.8+):
|
||||
打开一个终端并且运行(要求Python3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -11,7 +11,6 @@
|
||||
**✨ 使用 Python 建立高效且可自訂的網頁應用程式,幾秒鐘內即可部署。✨**
|
||||
|
||||
[](https://badge.fury.io/py/reflex)
|
||||

|
||||

|
||||
[](https://reflex.dev/docs/getting-started/introduction)
|
||||
[](https://discord.gg/T5WSbC2YtQ)
|
||||
@ -37,7 +36,7 @@ Reflex 是一個可以用純 Python 構建全端網頁應用程式的函式庫
|
||||
|
||||
## ⚙️ 安裝
|
||||
|
||||
開啟一個終端機並且執行 (需要 Python 3.8+):
|
||||
開啟一個終端機並且執行 (需要 Python 3.9+):
|
||||
|
||||
```bash
|
||||
pip install reflex
|
||||
|
@ -1,107 +0,0 @@
|
||||
"""Test that per-component state scaffold works and operates independently."""
|
||||
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
from reflex.testing import AppHarness
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
def ComponentStateApp():
|
||||
"""App using per component state."""
|
||||
import reflex as rx
|
||||
|
||||
class MultiCounter(rx.ComponentState):
|
||||
count: int = 0
|
||||
|
||||
def increment(self):
|
||||
self.count += 1
|
||||
|
||||
@classmethod
|
||||
def get_component(cls, *children, **props):
|
||||
return rx.vstack(
|
||||
*children,
|
||||
rx.heading(cls.count, id=f"count-{props.get('id', 'default')}"),
|
||||
rx.button(
|
||||
"Increment",
|
||||
on_click=cls.increment,
|
||||
id=f"button-{props.get('id', 'default')}",
|
||||
),
|
||||
**props,
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State) # noqa
|
||||
|
||||
@rx.page()
|
||||
def index():
|
||||
mc_a = MultiCounter.create(id="a")
|
||||
mc_b = MultiCounter.create(id="b")
|
||||
assert mc_a.State != mc_b.State
|
||||
return rx.vstack(
|
||||
mc_a,
|
||||
mc_b,
|
||||
rx.button(
|
||||
"Inc A",
|
||||
on_click=mc_a.State.increment, # type: ignore
|
||||
id="inc-a",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def component_state_app(tmp_path) -> Generator[AppHarness, None, None]:
|
||||
"""Start ComponentStateApp app at tmp_path via AppHarness.
|
||||
|
||||
Args:
|
||||
tmp_path: pytest tmp_path fixture
|
||||
|
||||
Yields:
|
||||
running AppHarness instance
|
||||
"""
|
||||
with AppHarness.create(
|
||||
root=tmp_path,
|
||||
app_source=ComponentStateApp, # type: ignore
|
||||
) as harness:
|
||||
yield harness
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_component_state_app(component_state_app: AppHarness):
|
||||
"""Increment counters independently.
|
||||
|
||||
Args:
|
||||
component_state_app: harness for ComponentStateApp app
|
||||
"""
|
||||
assert component_state_app.app_instance is not None, "app is not running"
|
||||
driver = component_state_app.frontend()
|
||||
|
||||
ss = utils.SessionStorage(driver)
|
||||
assert AppHarness._poll_for(lambda: ss.get("token") is not None), "token not found"
|
||||
|
||||
count_a = driver.find_element(By.ID, "count-a")
|
||||
count_b = driver.find_element(By.ID, "count-b")
|
||||
button_a = driver.find_element(By.ID, "button-a")
|
||||
button_b = driver.find_element(By.ID, "button-b")
|
||||
button_inc_a = driver.find_element(By.ID, "inc-a")
|
||||
|
||||
assert count_a.text == "0"
|
||||
|
||||
button_a.click()
|
||||
assert component_state_app.poll_for_content(count_a, exp_not_equal="0") == "1"
|
||||
|
||||
button_a.click()
|
||||
assert component_state_app.poll_for_content(count_a, exp_not_equal="1") == "2"
|
||||
|
||||
button_inc_a.click()
|
||||
assert component_state_app.poll_for_content(count_a, exp_not_equal="2") == "3"
|
||||
|
||||
assert count_b.text == "0"
|
||||
|
||||
button_b.click()
|
||||
assert component_state_app.poll_for_content(count_b, exp_not_equal="0") == "1"
|
||||
|
||||
button_b.click()
|
||||
assert component_state_app.poll_for_content(count_b, exp_not_equal="1") == "2"
|
@ -1,173 +0,0 @@
|
||||
"""Integration tests for table and related components."""
|
||||
|
||||
from typing import Generator
|
||||
|
||||
import pytest
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
from reflex.testing import AppHarness
|
||||
|
||||
|
||||
def Table():
|
||||
"""App using table component."""
|
||||
from typing import List
|
||||
|
||||
import reflex_chakra as rc
|
||||
|
||||
import reflex as rx
|
||||
|
||||
class TableState(rx.State):
|
||||
rows: List[List[str]] = [
|
||||
["John", "30", "New York"],
|
||||
["Jane", "31", "San Fransisco"],
|
||||
["Joe", "32", "Los Angeles"],
|
||||
]
|
||||
|
||||
headers: List[str] = ["Name", "Age", "Location"]
|
||||
|
||||
footers: List[str] = ["footer1", "footer2", "footer3"]
|
||||
|
||||
caption: str = "random caption"
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
return rx.center(
|
||||
rc.input(
|
||||
id="token",
|
||||
value=TableState.router.session.client_token,
|
||||
is_read_only=True,
|
||||
),
|
||||
rc.table_container(
|
||||
rc.table(
|
||||
headers=TableState.headers,
|
||||
rows=TableState.rows,
|
||||
footers=TableState.footers,
|
||||
caption=TableState.caption,
|
||||
variant="striped",
|
||||
color_scheme="blue",
|
||||
width="100%",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@app.add_page
|
||||
def another():
|
||||
return rx.center(
|
||||
rc.table_container(
|
||||
rc.table( # type: ignore
|
||||
rc.thead( # type: ignore
|
||||
rc.tr( # type: ignore
|
||||
rc.th("Name"),
|
||||
rc.th("Age"),
|
||||
rc.th("Location"),
|
||||
)
|
||||
),
|
||||
rc.tbody( # type: ignore
|
||||
rc.tr( # type: ignore
|
||||
rc.td("John"),
|
||||
rc.td(30),
|
||||
rc.td("New York"),
|
||||
),
|
||||
rc.tr( # type: ignore
|
||||
rc.td("Jane"),
|
||||
rc.td(31),
|
||||
rc.td("San Francisco"),
|
||||
),
|
||||
rc.tr( # type: ignore
|
||||
rc.td("Joe"),
|
||||
rc.td(32),
|
||||
rc.td("Los Angeles"),
|
||||
),
|
||||
),
|
||||
rc.tfoot( # type: ignore
|
||||
rc.tr(
|
||||
rc.td("footer1"),
|
||||
rc.td("footer2"),
|
||||
rc.td("footer3"),
|
||||
) # type: ignore
|
||||
),
|
||||
rc.table_caption("random caption"),
|
||||
variant="striped",
|
||||
color_scheme="teal",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def table(tmp_path_factory) -> Generator[AppHarness, None, None]:
|
||||
"""Start Table app at tmp_path via AppHarness.
|
||||
|
||||
Args:
|
||||
tmp_path_factory: pytest tmp_path_factory fixture
|
||||
|
||||
Yields:
|
||||
running AppHarness instance
|
||||
|
||||
"""
|
||||
with AppHarness.create(
|
||||
root=tmp_path_factory.mktemp("table"),
|
||||
app_source=Table, # type: ignore
|
||||
) as harness:
|
||||
assert harness.app_instance is not None, "app is not running"
|
||||
yield harness
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def driver(table: AppHarness):
|
||||
"""GEt an instance of the browser open to the table app.
|
||||
|
||||
Args:
|
||||
table: harness for Table app
|
||||
|
||||
Yields:
|
||||
WebDriver instance.
|
||||
"""
|
||||
driver = table.frontend()
|
||||
try:
|
||||
token_input = driver.find_element(By.ID, "token")
|
||||
assert token_input
|
||||
# wait for the backend connection to send the token
|
||||
token = table.poll_for_value(token_input)
|
||||
assert token is not None
|
||||
|
||||
yield driver
|
||||
finally:
|
||||
driver.quit()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("route", ["", "/another"])
|
||||
def test_table(driver, table: AppHarness, route):
|
||||
"""Test that a table component is rendered properly.
|
||||
|
||||
Args:
|
||||
driver: Selenium WebDriver open to the app
|
||||
table: Harness for Table app
|
||||
route: Page route or path.
|
||||
"""
|
||||
driver.get(f"{table.frontend_url}/{route}")
|
||||
assert table.app_instance is not None, "app is not running"
|
||||
|
||||
thead = driver.find_element(By.TAG_NAME, "thead")
|
||||
# poll till page is fully loaded.
|
||||
table.poll_for_content(element=thead)
|
||||
# check headers
|
||||
assert thead.find_element(By.TAG_NAME, "tr").text == "NAME AGE LOCATION"
|
||||
# check first row value
|
||||
assert (
|
||||
driver.find_element(By.TAG_NAME, "tbody")
|
||||
.find_elements(By.TAG_NAME, "tr")[0]
|
||||
.text
|
||||
== "John 30 New York"
|
||||
)
|
||||
# check footer
|
||||
assert (
|
||||
driver.find_element(By.TAG_NAME, "tfoot")
|
||||
.find_element(By.TAG_NAME, "tr")
|
||||
.text.lower()
|
||||
== "footer1 footer2 footer3"
|
||||
)
|
||||
# check caption
|
||||
assert driver.find_element(By.TAG_NAME, "caption").text == "random caption"
|
@ -1,68 +0,0 @@
|
||||
"""Integration tests for all urls in Reflex."""
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
|
||||
def check_urls(repo_dir):
|
||||
"""Check that all URLs in the repo are valid and secure.
|
||||
|
||||
Args:
|
||||
repo_dir: The directory of the repo.
|
||||
|
||||
Returns:
|
||||
A list of errors.
|
||||
"""
|
||||
url_pattern = re.compile(r'http[s]?://reflex\.dev[^\s")]*')
|
||||
errors = []
|
||||
|
||||
for root, _dirs, files in os.walk(repo_dir):
|
||||
if "__pycache__" in root:
|
||||
continue
|
||||
|
||||
for file_name in files:
|
||||
if not file_name.endswith(".py") and not file_name.endswith(".md"):
|
||||
continue
|
||||
|
||||
file_path = os.path.join(root, file_name)
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as file:
|
||||
for line in file:
|
||||
urls = url_pattern.findall(line)
|
||||
for url in set(urls):
|
||||
if url.startswith("http://"):
|
||||
errors.append(
|
||||
f"Found insecure HTTP URL: {url} in {file_path}"
|
||||
)
|
||||
url = url.strip('"\n')
|
||||
try:
|
||||
response = requests.head(
|
||||
url, allow_redirects=True, timeout=5
|
||||
)
|
||||
response.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
errors.append(
|
||||
f"Error accessing URL: {url} in {file_path} | Error: {e}, , Check your path ends with a /"
|
||||
)
|
||||
except Exception as e:
|
||||
errors.append(f"Error reading file: {file_path} | Error: {e}")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"repo_dir",
|
||||
[Path(__file__).resolve().parent.parent / "reflex"],
|
||||
)
|
||||
def test_find_and_check_urls(repo_dir):
|
||||
"""Test that all URLs in the repo are valid and secure.
|
||||
|
||||
Args:
|
||||
repo_dir: The directory of the repo.
|
||||
"""
|
||||
errors = check_urls(repo_dir)
|
||||
assert not errors, "\n".join(errors)
|
2048
poetry.lock
generated
2048
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "reflex"
|
||||
version = "0.6.0a1"
|
||||
version = "0.6.4dev1"
|
||||
description = "Web apps in pure Python."
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
@ -26,14 +26,14 @@ packages = [
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.8"
|
||||
dill = ">=0.3.8,<0.4"
|
||||
python = "^3.9"
|
||||
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
|
||||
gunicorn = ">=20.1.0,<24.0"
|
||||
jinja2 = ">=3.1.2,<4.0"
|
||||
psutil = ">=5.9.4,<7.0"
|
||||
pydantic = ">=1.10.2,<3.0"
|
||||
python-multipart = ">=0.0.5,<0.1"
|
||||
python-dotenv = ">=1.0.1"
|
||||
python-socketio = ">=5.7.0,<6.0"
|
||||
redis = ">=4.3.5,<6.0"
|
||||
rich = ">=13.0.0,<14.0"
|
||||
@ -54,37 +54,31 @@ reflex-hosting-cli = ">=0.1.2,<2.0"
|
||||
charset-normalizer = ">=3.3.2,<4.0"
|
||||
wheel = ">=0.42.0,<1.0"
|
||||
build = ">=1.0.3,<2.0"
|
||||
setuptools = ">=69.1.1,<70.2"
|
||||
setuptools = ">=75.0"
|
||||
httpx = ">=0.25.1,<1.0"
|
||||
twine = ">=4.0.0,<6.0"
|
||||
tomlkit = ">=0.12.4,<1.0"
|
||||
lazy_loader = ">=0.4"
|
||||
reflex-chakra = ">=0.6.0a"
|
||||
reflex-chakra = ">=0.6.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = ">=7.1.2,<8.0"
|
||||
pytest = ">=7.1.2,<9.0"
|
||||
pytest-mock = ">=3.10.0,<4.0"
|
||||
pyright = ">=1.1.229,<1.1.335"
|
||||
darglint = ">=1.8.1,<2.0"
|
||||
toml = ">=0.10.2,<1.0"
|
||||
pytest-asyncio = ">=0.20.1,<0.22.0" # https://github.com/pytest-dev/pytest-asyncio/issues/706
|
||||
pytest-cov = ">=4.0.0,<5.0"
|
||||
ruff = "^0.4.9"
|
||||
pandas = [
|
||||
{version = ">=2.1.1,<3.0", python = ">=3.9,<3.13"},
|
||||
{version = ">=1.5.3,<2.0", python = ">=3.8,<3.9"},
|
||||
]
|
||||
pillow = [
|
||||
{version = ">=10.0.0,<11.0", python = ">=3.8,<4.0"}
|
||||
]
|
||||
pytest-asyncio = ">=0.24.0"
|
||||
pytest-cov = ">=4.0.0,<6.0"
|
||||
ruff = "^0.7.0"
|
||||
pandas = ">=2.1.1,<3.0"
|
||||
pillow = ">=10.0.0,<12.0"
|
||||
plotly = ">=5.13.0,<6.0"
|
||||
asynctest = ">=0.13.0,<1.0"
|
||||
pre-commit = {version = ">=3.2.1", python = ">=3.8,<4.0"}
|
||||
pre-commit = ">=3.2.1"
|
||||
selenium = ">=4.11.0,<5.0"
|
||||
pytest-benchmark = ">=4.0.0,<5.0"
|
||||
playwright = "^1.46.0"
|
||||
pytest-playwright = "^0.5.1"
|
||||
|
||||
playwright = ">=1.46.0"
|
||||
pytest-playwright = ">=0.5.1"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
reflex = "reflex.reflex:cli"
|
||||
@ -96,9 +90,9 @@ build-backend = "poetry.core.masonry.api"
|
||||
[tool.pyright]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py38"
|
||||
target-version = "py39"
|
||||
lint.select = ["B", "D", "E", "F", "I", "SIM", "W"]
|
||||
lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541"]
|
||||
lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541", "SIM115"]
|
||||
lint.pydocstyle.convention = "google"
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
@ -107,3 +101,7 @@ lint.pydocstyle.convention = "google"
|
||||
"reflex/.templates/*.py" = ["D100", "D103", "D104"]
|
||||
"*.pyi" = ["D301", "D415", "D417", "D418", "E742"]
|
||||
"*/blank.py" = ["I001"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_default_fixture_loop_scope = "function"
|
||||
asyncio_mode = "auto"
|
@ -8,11 +8,11 @@ version = "0.0.1"
|
||||
description = "Reflex custom component {{ module_name }}"
|
||||
readme = "README.md"
|
||||
license = { text = "Apache-2.0" }
|
||||
requires-python = ">=3.8"
|
||||
requires-python = ">=3.9"
|
||||
authors = [{ name = "", email = "YOUREMAIL@domain.com" }]
|
||||
keywords = ["reflex","reflex-custom-components"]
|
||||
|
||||
dependencies = ["reflex>=0.4.2"]
|
||||
dependencies = ["reflex>={{ reflex_version }}"]
|
||||
|
||||
classifiers = ["Development Status :: 4 - Beta"]
|
||||
|
||||
|
@ -7,6 +7,9 @@ import '/styles/styles.css'
|
||||
{% block declaration %}
|
||||
import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js";
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
{% for library_alias, library_path in window_libraries %}
|
||||
import * as {{library_alias}} from "{{library_path}}";
|
||||
{% endfor %}
|
||||
|
||||
{% for custom_code in custom_codes %}
|
||||
{{custom_code}}
|
||||
@ -26,8 +29,17 @@ function AppWrap({children}) {
|
||||
}
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
React.useEffect(() => {
|
||||
// Make contexts and state objects available globally for dynamic eval'd components
|
||||
let windowImports = {
|
||||
{% for library_alias, library_path in window_libraries %}
|
||||
"{{library_path}}": {{library_alias}},
|
||||
{% endfor %}
|
||||
};
|
||||
window["__reflex"] = windowImports;
|
||||
}, []);
|
||||
return (
|
||||
<ThemeProvider defaultTheme={ defaultColorMode } storageKey="chakra-ui-color-mode" attribute="class">
|
||||
<ThemeProvider defaultTheme={ defaultColorMode } attribute="class">
|
||||
<AppWrap>
|
||||
<StateProvider>
|
||||
<EventLoopProvider>
|
||||
|
@ -64,11 +64,11 @@
|
||||
{# Args: #}
|
||||
{# component: component dictionary #}
|
||||
{% macro render_iterable_tag(component) %}
|
||||
{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
|
||||
<>{ {%- if component.iterable_type == 'dict' -%}Object.entries({{- component.iterable_state }}){%- else -%}{{- component.iterable_state }}{%- endif -%}.map(({{ component.arg_name }}, {{ component.arg_index }}) => (
|
||||
{% for child in component.children %}
|
||||
{{ render(child) }}
|
||||
{% endfor %}
|
||||
))}
|
||||
))}</>
|
||||
{%- endmacro %}
|
||||
|
||||
|
||||
@ -85,10 +85,10 @@
|
||||
{% macro render_match_tag(component) %}
|
||||
{
|
||||
(() => {
|
||||
switch (JSON.stringify({{ component.cond._var_name_unwrapped }})) {
|
||||
switch (JSON.stringify({{ component.cond._js_expr }})) {
|
||||
{% for case in component.match_cases %}
|
||||
{% for condition in case[:-1] %}
|
||||
case JSON.stringify({{ condition._var_name_unwrapped }}):
|
||||
case JSON.stringify({{ condition._js_expr }}):
|
||||
{% endfor %}
|
||||
return {{ case[-1] }};
|
||||
break;
|
||||
|
@ -1 +1 @@
|
||||
export default {{ theme|json_dumps }}
|
||||
export default {{ theme }}
|
@ -1,36 +0,0 @@
|
||||
import { useColorMode as chakraUseColorMode } from "@chakra-ui/react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ColorModeContext, defaultColorMode } from "/utils/context.js";
|
||||
|
||||
export default function ChakraColorModeProvider({ children }) {
|
||||
const { theme, resolvedTheme, setTheme } = useTheme();
|
||||
const { colorMode, toggleColorMode } = chakraUseColorMode();
|
||||
const [resolvedColorMode, setResolvedColorMode] = useState(colorMode);
|
||||
|
||||
useEffect(() => {
|
||||
if (colorMode != resolvedTheme) {
|
||||
toggleColorMode();
|
||||
}
|
||||
setResolvedColorMode(resolvedTheme);
|
||||
}, [theme, resolvedTheme]);
|
||||
|
||||
const rawColorMode = colorMode;
|
||||
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);
|
||||
};
|
||||
return (
|
||||
<ColorModeContext.Provider
|
||||
value={{ rawColorMode, resolvedColorMode, toggleColorMode, setColorMode }}
|
||||
>
|
||||
{children}
|
||||
</ColorModeContext.Provider>
|
||||
);
|
||||
}
|
29
reflex/.templates/web/components/shiki/code.js
Normal file
29
reflex/.templates/web/components/shiki/code.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { codeToHtml} from "shiki"
|
||||
|
||||
export function Code ({code, theme, language, transformers, ...divProps}) {
|
||||
const [codeResult, setCodeResult] = useState("")
|
||||
useEffect(() => {
|
||||
async function fetchCode() {
|
||||
let final_code;
|
||||
|
||||
if (Array.isArray(code)) {
|
||||
final_code = code[0];
|
||||
} else {
|
||||
final_code = code;
|
||||
}
|
||||
const result = await codeToHtml(final_code, {
|
||||
lang: language,
|
||||
theme,
|
||||
transformers
|
||||
});
|
||||
setCodeResult(result);
|
||||
}
|
||||
fetchCode();
|
||||
}, [code, language, theme, transformers]
|
||||
|
||||
)
|
||||
return (
|
||||
<div dangerouslySetInnerHTML={{__html: codeResult}} {...divProps} ></div>
|
||||
)
|
||||
}
|
@ -15,6 +15,7 @@ import {
|
||||
} from "utils/context.js";
|
||||
import debounce from "/utils/helpers/debounce";
|
||||
import throttle from "/utils/helpers/throttle";
|
||||
import * as Babel from "@babel/standalone";
|
||||
|
||||
// Endpoint URLs.
|
||||
const EVENTURL = env.EVENT;
|
||||
@ -117,8 +118,8 @@ export const isStateful = () => {
|
||||
if (event_queue.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return event_queue.some(event => event.name.startsWith("reflex___state"));
|
||||
}
|
||||
return event_queue.some((event) => event.name.startsWith("reflex___state"));
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply a delta to the state.
|
||||
@ -129,6 +130,22 @@ 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 output = Babel.transform(component, { presets: ["react"] }).code;
|
||||
const encodedJs = encodeURIComponent(output);
|
||||
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.
|
||||
@ -141,7 +158,7 @@ export const queueEventIfSocketExists = async (events, socket) => {
|
||||
return;
|
||||
}
|
||||
await queueEvents(events, socket);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle frontend event or send the event to the backend via Websocket.
|
||||
@ -208,7 +225,10 @@ export const applyEvent = async (event, socket) => {
|
||||
const a = document.createElement("a");
|
||||
a.hidden = true;
|
||||
// Special case when linking to uploaded files
|
||||
a.href = event.payload.url.replace("${getBackendURL(env.UPLOAD)}", getBackendURL(env.UPLOAD))
|
||||
a.href = event.payload.url.replace(
|
||||
"${getBackendURL(env.UPLOAD)}",
|
||||
getBackendURL(env.UPLOAD)
|
||||
);
|
||||
a.download = event.payload.filename;
|
||||
a.click();
|
||||
a.remove();
|
||||
@ -249,7 +269,7 @@ export const applyEvent = async (event, socket) => {
|
||||
} catch (e) {
|
||||
console.log("_call_script", e);
|
||||
if (window && window?.onerror) {
|
||||
window.onerror(e.message, null, null, null, e)
|
||||
window.onerror(e.message, null, null, null, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -290,10 +310,9 @@ export const applyEvent = async (event, socket) => {
|
||||
export const applyRestEvent = async (event, socket) => {
|
||||
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(Event(event.name), socket)
|
||||
return await applyEvent(Event(event.name), socket);
|
||||
}
|
||||
|
||||
// Start upload, but do not wait for it, which would block other events.
|
||||
@ -397,7 +416,7 @@ export const connect = async (
|
||||
console.log("Disconnect backend before bfcache on navigation");
|
||||
socket.current.disconnect();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Once the socket is open, hydrate the page.
|
||||
socket.current.on("connect", () => {
|
||||
@ -416,7 +435,7 @@ export const connect = async (
|
||||
});
|
||||
|
||||
// On each received message, queue the updates and events.
|
||||
socket.current.on("event", (message) => {
|
||||
socket.current.on("event", async (message) => {
|
||||
const update = JSON5.parse(message);
|
||||
for (const substate in update.delta) {
|
||||
dispatch[substate](update.delta[substate]);
|
||||
@ -525,13 +544,19 @@ export const uploadFiles = async (
|
||||
|
||||
/**
|
||||
* Create an event object.
|
||||
* @param name The name of the event.
|
||||
* @param payload The payload of the event.
|
||||
* @param handler The client handler to process event.
|
||||
* @param {string} name The name of the event.
|
||||
* @param {Object.<string, Any>} payload The payload of the event.
|
||||
* @param {Object.<string, (number|boolean)>} event_actions The actions to take on the event.
|
||||
* @param {string} handler The client handler to process event.
|
||||
* @returns The event object.
|
||||
*/
|
||||
export const Event = (name, payload = {}, handler = null) => {
|
||||
return { name, payload, handler };
|
||||
export const Event = (
|
||||
name,
|
||||
payload = {},
|
||||
event_actions = {},
|
||||
handler = null
|
||||
) => {
|
||||
return { name, payload, handler, event_actions };
|
||||
};
|
||||
|
||||
/**
|
||||
@ -574,7 +599,11 @@ export const hydrateClientStorage = (client_storage) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client_storage.cookies || client_storage.local_storage || client_storage.session_storage) {
|
||||
if (
|
||||
client_storage.cookies ||
|
||||
client_storage.local_storage ||
|
||||
client_storage.session_storage
|
||||
) {
|
||||
return client_storage_values;
|
||||
}
|
||||
return {};
|
||||
@ -614,15 +643,17 @@ const applyClientStorageDelta = (client_storage, delta) => {
|
||||
) {
|
||||
const options = client_storage.local_storage[state_key];
|
||||
localStorage.setItem(options.name || state_key, delta[substate][key]);
|
||||
} else if(
|
||||
} 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]);
|
||||
sessionStorage.setItem(
|
||||
session_options.name || state_key,
|
||||
delta[substate][key]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -651,7 +682,13 @@ export const useEventLoop = (
|
||||
if (!(args instanceof Array)) {
|
||||
args = [args];
|
||||
}
|
||||
const _e = args.filter((o) => o?.preventDefault !== undefined)[0]
|
||||
|
||||
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();
|
||||
@ -671,7 +708,7 @@ export const useEventLoop = (
|
||||
debounce(
|
||||
combined_name,
|
||||
() => queueEvents(events, socket),
|
||||
event_actions.debounce,
|
||||
event_actions.debounce
|
||||
);
|
||||
} else {
|
||||
queueEvents(events, socket);
|
||||
@ -696,30 +733,34 @@ export const useEventLoop = (
|
||||
}
|
||||
}, [router.isReady]);
|
||||
|
||||
// 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([Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: error.stack,
|
||||
})])
|
||||
return false;
|
||||
}
|
||||
// Handle frontend errors and send them to the backend via websocket.
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
//NOTE: Only works in Chrome v49+
|
||||
//https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events
|
||||
window.onunhandledrejection = function (event) {
|
||||
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: event.reason.stack,
|
||||
})])
|
||||
return false;
|
||||
}
|
||||
|
||||
},[])
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
addEvents([
|
||||
Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: 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([
|
||||
Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: event.reason.stack,
|
||||
component_stack: "",
|
||||
}),
|
||||
]);
|
||||
return false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Main event loop.
|
||||
useEffect(() => {
|
||||
@ -782,11 +823,11 @@ export const useEventLoop = (
|
||||
// Route after the initial page hydration.
|
||||
useEffect(() => {
|
||||
const change_start = () => {
|
||||
const main_state_dispatch = dispatch["reflex___state____state"]
|
||||
const main_state_dispatch = dispatch["reflex___state____state"];
|
||||
if (main_state_dispatch !== undefined) {
|
||||
main_state_dispatch({ is_hydrated: false })
|
||||
main_state_dispatch({ is_hydrated: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
const change_complete = () => addEvents(onLoadInternalEvent());
|
||||
router.events.on("routeChangeStart", change_start);
|
||||
router.events.on("routeChangeComplete", change_complete);
|
||||
@ -805,7 +846,9 @@ export const useEventLoop = (
|
||||
* @returns True if the value is truthy, false otherwise.
|
||||
*/
|
||||
export const isTrue = (val) => {
|
||||
return Array.isArray(val) ? val.length > 0 : !!val;
|
||||
if (Array.isArray(val)) return val.length > 0;
|
||||
if (val === Object(val)) return Object.keys(val).length > 0;
|
||||
return Boolean(val);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -89,6 +89,8 @@ from reflex.utils import (
|
||||
lazy_loader,
|
||||
)
|
||||
|
||||
from .event import event as event
|
||||
|
||||
# import this here explicitly to avoid returning the page module since page attr has the
|
||||
# same name as page module(page.py)
|
||||
from .page import page as page
|
||||
@ -206,6 +208,13 @@ RADIX_PRIMITIVES_MAPPING: dict = {
|
||||
"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 = {
|
||||
@ -248,7 +257,7 @@ RADIX_MAPPING: dict = {
|
||||
**RADIX_THEMES_COMPONENTS_MAPPING,
|
||||
**RADIX_THEMES_TYPOGRAPHY_MAPPING,
|
||||
**RADIX_THEMES_LAYOUT_MAPPING,
|
||||
**RADIX_PRIMITIVES_MAPPING,
|
||||
**RADIX_PRIMITIVES_SHORTCUT_MAPPING,
|
||||
}
|
||||
|
||||
_MAPPING: dict = {
|
||||
@ -311,25 +320,27 @@ _MAPPING: dict = {
|
||||
"upload_files",
|
||||
"window_alert",
|
||||
],
|
||||
"istate.storage": [
|
||||
"Cookie",
|
||||
"LocalStorage",
|
||||
"SessionStorage",
|
||||
],
|
||||
"middleware": ["middleware", "Middleware"],
|
||||
"model": ["session", "Model"],
|
||||
"state": [
|
||||
"var",
|
||||
"Cookie",
|
||||
"LocalStorage",
|
||||
"SessionStorage",
|
||||
"ComponentState",
|
||||
"State",
|
||||
"dynamic",
|
||||
],
|
||||
"style": ["Style", "toggle_color_mode"],
|
||||
"utils.imports": ["ImportVar"],
|
||||
"utils.serializers": ["serializer"],
|
||||
"vars": ["Var"],
|
||||
"vars": ["Var", "field", "Field"],
|
||||
}
|
||||
|
||||
_SUBMODULES: set[str] = {
|
||||
"components",
|
||||
"event",
|
||||
"app",
|
||||
"style",
|
||||
"admin",
|
||||
@ -338,7 +349,6 @@ _SUBMODULES: set[str] = {
|
||||
"testing",
|
||||
"utils",
|
||||
"vars",
|
||||
"ivars",
|
||||
"config",
|
||||
"compiler",
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ from . import base as base
|
||||
from . import compiler as compiler
|
||||
from . import components as components
|
||||
from . import config as config
|
||||
from . import event as event
|
||||
from . import ivars as ivars
|
||||
from . import model as model
|
||||
from . import style as style
|
||||
from . import testing as testing
|
||||
@ -132,6 +130,7 @@ from .components.radix.themes.layout.container import container as container
|
||||
from .components.radix.themes.layout.flex import flex as flex
|
||||
from .components.radix.themes.layout.grid import grid as grid
|
||||
from .components.radix.themes.layout.list import list_item as list_item
|
||||
from .components.radix.themes.layout.list import list_ns as list # noqa
|
||||
from .components.radix.themes.layout.list import ordered_list as ordered_list
|
||||
from .components.radix.themes.layout.list import unordered_list as unordered_list
|
||||
from .components.radix.themes.layout.section import section as section
|
||||
@ -161,6 +160,7 @@ from .event import clear_local_storage as clear_local_storage
|
||||
from .event import clear_session_storage as clear_session_storage
|
||||
from .event import console_log as console_log
|
||||
from .event import download as download
|
||||
from .event import event as event
|
||||
from .event import prevent_default as prevent_default
|
||||
from .event import redirect as redirect
|
||||
from .event import remove_cookie as remove_cookie
|
||||
@ -174,22 +174,25 @@ from .event import stop_propagation as stop_propagation
|
||||
from .event import upload_files as upload_files
|
||||
from .event import window_alert as window_alert
|
||||
from .experimental import _x as _x
|
||||
from .istate.storage import Cookie as Cookie
|
||||
from .istate.storage import LocalStorage as LocalStorage
|
||||
from .istate.storage import SessionStorage as SessionStorage
|
||||
from .middleware import Middleware as Middleware
|
||||
from .middleware import middleware as middleware
|
||||
from .model import Model as Model
|
||||
from .model import session as session
|
||||
from .page import page as page
|
||||
from .state import ComponentState as ComponentState
|
||||
from .state import Cookie as Cookie
|
||||
from .state import LocalStorage as LocalStorage
|
||||
from .state import SessionStorage as SessionStorage
|
||||
from .state import State as State
|
||||
from .state import dynamic as dynamic
|
||||
from .state import var as var
|
||||
from .style import Style as Style
|
||||
from .style import toggle_color_mode as toggle_color_mode
|
||||
from .utils.imports import ImportVar as ImportVar
|
||||
from .utils.serializers import serializer as serializer
|
||||
from .vars import Field as Field
|
||||
from .vars import Var as Var
|
||||
from .vars import field as field
|
||||
|
||||
del compat
|
||||
RADIX_THEMES_MAPPING: dict
|
||||
@ -197,6 +200,7 @@ RADIX_THEMES_COMPONENTS_MAPPING: dict
|
||||
RADIX_THEMES_LAYOUT_MAPPING: dict
|
||||
RADIX_THEMES_TYPOGRAPHY_MAPPING: dict
|
||||
RADIX_PRIMITIVES_MAPPING: dict
|
||||
RADIX_PRIMITIVES_SHORTCUT_MAPPING: dict
|
||||
COMPONENTS_CORE_MAPPING: dict
|
||||
COMPONENTS_BASE_MAPPING: dict
|
||||
RADIX_MAPPING: dict
|
||||
|
@ -9,6 +9,7 @@ import copy
|
||||
import functools
|
||||
import inspect
|
||||
import io
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import platform
|
||||
@ -63,7 +64,7 @@ from reflex.components.core.client_side_routing import (
|
||||
)
|
||||
from reflex.components.core.upload import Upload, get_upload_dir
|
||||
from reflex.components.radix import themes
|
||||
from reflex.config import get_config
|
||||
from reflex.config import environment, get_config
|
||||
from reflex.event import Event, EventHandler, EventSpec, window_alert
|
||||
from reflex.model import Model, get_db_status
|
||||
from reflex.page import (
|
||||
@ -269,13 +270,12 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
"`connect_error_component` is deprecated, use `overlay_component` instead"
|
||||
)
|
||||
super().__init__(**kwargs)
|
||||
base_state_subclasses = BaseState.__subclasses__()
|
||||
|
||||
# Special case to allow test cases have multiple subclasses of rx.BaseState.
|
||||
if not is_testing_env() and len(base_state_subclasses) > 1:
|
||||
# Only one Base State class is allowed.
|
||||
if not is_testing_env() and BaseState.__subclasses__() != [State]:
|
||||
# Only rx.State is allowed as Base State subclass.
|
||||
raise ValueError(
|
||||
"rx.BaseState cannot be subclassed multiple times. use rx.State instead"
|
||||
"rx.BaseState cannot be subclassed directly. Use rx.State instead"
|
||||
)
|
||||
|
||||
if "breakpoints" in self.style:
|
||||
@ -431,25 +431,12 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
The generated component.
|
||||
|
||||
Raises:
|
||||
VarOperationTypeError: When an invalid component var related function is passed.
|
||||
TypeError: When an invalid component function is passed.
|
||||
exceptions.MatchTypeError: If the return types of match cases in rx.match are different.
|
||||
"""
|
||||
from reflex.utils.exceptions import VarOperationTypeError
|
||||
|
||||
try:
|
||||
return component if isinstance(component, Component) else component()
|
||||
except exceptions.MatchTypeError:
|
||||
raise
|
||||
except TypeError as e:
|
||||
message = str(e)
|
||||
if "Var" in message:
|
||||
raise VarOperationTypeError(
|
||||
"You may be trying to use an invalid Python function on a state var. "
|
||||
"When referencing a var inside your render code, only limited var operations are supported. "
|
||||
"See the var operation docs here: https://reflex.dev/docs/vars/var-operations/"
|
||||
) from e
|
||||
raise e
|
||||
|
||||
def add_page(
|
||||
self,
|
||||
@ -482,9 +469,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
"""
|
||||
# If the route is not set, get it from the callable.
|
||||
if route is None:
|
||||
assert isinstance(
|
||||
component, Callable
|
||||
), "Route must be set if component is not a callable."
|
||||
if not isinstance(component, Callable):
|
||||
raise ValueError("Route must be set if component is not a callable.")
|
||||
# Format the route.
|
||||
route = format.format_route(component.__name__)
|
||||
else:
|
||||
@ -824,7 +810,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
for dep in deps:
|
||||
if dep not in state.vars and dep not in state.backend_vars:
|
||||
raise exceptions.VarDependencyError(
|
||||
f"ComputedVar {var._var_name} on state {state.__name__} has an invalid dependency {dep}"
|
||||
f"ComputedVar {var._js_expr} on state {state.__name__} has an invalid dependency {dep}"
|
||||
)
|
||||
|
||||
for substate in state.class_subclasses:
|
||||
@ -971,15 +957,16 @@ class App(MiddlewareMixin, LifespanMixin, Base):
|
||||
executor = None
|
||||
if (
|
||||
platform.system() in ("Linux", "Darwin")
|
||||
and os.environ.get("REFLEX_COMPILE_PROCESSES") is not None
|
||||
and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES)
|
||||
is not None
|
||||
):
|
||||
executor = concurrent.futures.ProcessPoolExecutor(
|
||||
max_workers=int(os.environ.get("REFLEX_COMPILE_PROCESSES", 0)) or None,
|
||||
max_workers=number_of_processes,
|
||||
mp_context=multiprocessing.get_context("fork"),
|
||||
)
|
||||
else:
|
||||
executor = concurrent.futures.ThreadPoolExecutor(
|
||||
max_workers=int(os.environ.get("REFLEX_COMPILE_THREADS", 0)) or None,
|
||||
max_workers=environment.REFLEX_COMPILE_THREADS
|
||||
)
|
||||
|
||||
with executor:
|
||||
@ -1528,20 +1515,28 @@ class EventNamespace(AsyncNamespace):
|
||||
async def on_event(self, sid, data):
|
||||
"""Event for receiving front-end websocket events.
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the Socket.IO is badly initialized.
|
||||
|
||||
Args:
|
||||
sid: The Socket.IO session id.
|
||||
data: The event data.
|
||||
"""
|
||||
fields = json.loads(data)
|
||||
# Get the event.
|
||||
event = Event.parse_raw(data)
|
||||
event = Event(
|
||||
**{k: v for k, v in fields.items() if k not in ("handler", "event_actions")}
|
||||
)
|
||||
|
||||
self.token_to_sid[event.token] = sid
|
||||
self.sid_to_token[sid] = event.token
|
||||
|
||||
# Get the event environment.
|
||||
assert self.app.sio is not None
|
||||
if self.app.sio is None:
|
||||
raise RuntimeError("Socket.IO is not initialized.")
|
||||
environ = self.app.sio.get_environ(sid, self.namespace)
|
||||
assert environ is not None
|
||||
if environ is None:
|
||||
raise RuntimeError("Socket.IO environ is not initialized.")
|
||||
|
||||
# Get the client headers.
|
||||
headers = {
|
||||
|
@ -6,11 +6,13 @@ import asyncio
|
||||
import contextlib
|
||||
import functools
|
||||
import inspect
|
||||
import sys
|
||||
from typing import Callable, Coroutine, Set, Union
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from reflex.utils import console
|
||||
from reflex.utils.exceptions import InvalidLifespanTaskType
|
||||
|
||||
from .mixin import AppMixin
|
||||
|
||||
|
||||
@ -26,6 +28,7 @@ class LifespanMixin(AppMixin):
|
||||
try:
|
||||
async with contextlib.AsyncExitStack() as stack:
|
||||
for task in self.lifespan_tasks:
|
||||
run_msg = f"Started lifespan task: {task.__name__} as {{type}}" # type: ignore
|
||||
if isinstance(task, asyncio.Task):
|
||||
running_tasks.append(task)
|
||||
else:
|
||||
@ -35,15 +38,19 @@ class LifespanMixin(AppMixin):
|
||||
_t = task()
|
||||
if isinstance(_t, contextlib._AsyncGeneratorContextManager):
|
||||
await stack.enter_async_context(_t)
|
||||
console.debug(run_msg.format(type="asynccontextmanager"))
|
||||
elif isinstance(_t, Coroutine):
|
||||
running_tasks.append(asyncio.create_task(_t))
|
||||
task_ = asyncio.create_task(_t)
|
||||
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:
|
||||
cancel_kwargs = (
|
||||
{"msg": "lifespan_cleanup"} if sys.version_info >= (3, 9) else {}
|
||||
)
|
||||
for task in running_tasks:
|
||||
task.cancel(**cancel_kwargs)
|
||||
console.debug(f"Canceling lifespan task: {task}")
|
||||
task.cancel(msg="lifespan_cleanup")
|
||||
|
||||
def register_lifespan_task(self, task: Callable | asyncio.Task, **task_kwargs):
|
||||
"""Register a task to run during the lifespan of the app.
|
||||
@ -51,7 +58,18 @@ class LifespanMixin(AppMixin):
|
||||
Args:
|
||||
task: The task to register.
|
||||
task_kwargs: The kwargs of the task.
|
||||
|
||||
Raises:
|
||||
InvalidLifespanTaskType: If the task is a generator function.
|
||||
"""
|
||||
if inspect.isgeneratorfunction(task) or inspect.isasyncgenfunction(task):
|
||||
raise InvalidLifespanTaskType(
|
||||
f"Task {task.__name__} of type generator must be decorated with contextlib.asynccontextmanager."
|
||||
)
|
||||
|
||||
if task_kwargs:
|
||||
original_task = task
|
||||
task = functools.partial(task, **task_kwargs) # type: ignore
|
||||
functools.update_wrapper(task, original_task) # type: ignore
|
||||
self.lifespan_tasks.add(task) # type: ignore
|
||||
console.debug(f"Registered lifespan task: {task.__name__}") # type: ignore
|
||||
|
@ -15,7 +15,7 @@ if constants.CompileVars.APP != "app":
|
||||
telemetry.send("compile")
|
||||
app_module = get_app(reload=False)
|
||||
app = getattr(app_module, constants.CompileVars.APP)
|
||||
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
|
||||
# For py3.9 compatibility when redis is used, we MUST add any decorator pages
|
||||
# before compiling the app in a thread to avoid event loop error (REF-2172).
|
||||
app._apply_decorated_pages()
|
||||
compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)
|
||||
|
@ -47,6 +47,9 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
|
||||
# shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
|
||||
pydantic_main.validate_field_name = validate_field_name # type: ignore
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
"""The base class subclassed by all Reflex classes.
|
||||
@ -92,7 +95,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def get_fields(cls) -> dict[str, Any]:
|
||||
def get_fields(cls) -> dict[str, ModelField]:
|
||||
"""Get the fields of the object.
|
||||
|
||||
Returns:
|
||||
@ -101,7 +104,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
return cls.__fields__
|
||||
|
||||
@classmethod
|
||||
def add_field(cls, var: Any, default_value: Any):
|
||||
def add_field(cls, var: Var, default_value: Any):
|
||||
"""Add a pydantic field after class definition.
|
||||
|
||||
Used by State.add_var() to correctly handle the new variable.
|
||||
@ -110,7 +113,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
var: The variable to add a pydantic field for.
|
||||
default_value: The default value of the field
|
||||
"""
|
||||
var_name = var._var_name.split(".")[-1]
|
||||
var_name = var._var_field_name
|
||||
new_field = ModelField.infer(
|
||||
name=var_name,
|
||||
value=default_value,
|
||||
@ -133,13 +136,4 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
# Seems like this function signature was wrong all along?
|
||||
# If the user wants a field that we know of, get it and pass it off to _get_value
|
||||
key = getattr(self, key)
|
||||
return self._get_value(
|
||||
key,
|
||||
to_dict=True,
|
||||
by_alias=False,
|
||||
include=None,
|
||||
exclude=None,
|
||||
exclude_unset=False,
|
||||
exclude_defaults=False,
|
||||
exclude_none=False,
|
||||
)
|
||||
return key
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Dict, Iterable, Optional, Type, Union
|
||||
@ -16,14 +15,13 @@ from reflex.components.component import (
|
||||
CustomComponent,
|
||||
StatefulComponent,
|
||||
)
|
||||
from reflex.config import get_config
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.config import environment, get_config
|
||||
from reflex.state import BaseState
|
||||
from reflex.style import SYSTEM_COLOR_MODE
|
||||
from reflex.utils.exec import is_prod_mode
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.utils.prerequisites import get_web_dir
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
|
||||
|
||||
def _compile_document_root(root: Component) -> str:
|
||||
@ -41,6 +39,20 @@ def _compile_document_root(root: Component) -> str:
|
||||
)
|
||||
|
||||
|
||||
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("-", "_")
|
||||
|
||||
|
||||
def _compile_app(app_root: Component) -> str:
|
||||
"""Compile the app template component.
|
||||
|
||||
@ -50,15 +62,25 @@ def _compile_app(app_root: Component) -> str:
|
||||
Returns:
|
||||
The compiled app.
|
||||
"""
|
||||
from reflex.components.dynamic import bundled_libraries
|
||||
|
||||
window_libraries = [
|
||||
(_normalize_library_name(name), name) for name in bundled_libraries
|
||||
] + [
|
||||
("utils_context", f"/{constants.Dirs.UTILS}/context"),
|
||||
("utils_state", f"/{constants.Dirs.UTILS}/state"),
|
||||
]
|
||||
|
||||
return templates.APP_ROOT.render(
|
||||
imports=utils.compile_imports(app_root._get_all_imports()),
|
||||
custom_codes=app_root._get_all_custom_code(),
|
||||
hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()},
|
||||
window_libraries=window_libraries,
|
||||
render=app_root.render(),
|
||||
)
|
||||
|
||||
|
||||
def _compile_theme(theme: dict) -> str:
|
||||
def _compile_theme(theme: str) -> str:
|
||||
"""Compile the theme.
|
||||
|
||||
Args:
|
||||
@ -172,7 +194,7 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
|
||||
stylesheet_full_path = (
|
||||
Path.cwd() / constants.Dirs.APP_ASSETS / stylesheet.strip("/")
|
||||
)
|
||||
if not os.path.exists(stylesheet_full_path):
|
||||
if not stylesheet_full_path.exists():
|
||||
raise FileNotFoundError(
|
||||
f"The stylesheet file {stylesheet_full_path} does not exist."
|
||||
)
|
||||
@ -378,7 +400,7 @@ def compile_theme(style: ComponentStyle) -> tuple[str, str]:
|
||||
theme = utils.create_theme(style)
|
||||
|
||||
# Compile the theme.
|
||||
code = _compile_theme(theme)
|
||||
code = _compile_theme(str(LiteralVar.create(theme)))
|
||||
return output_path, code
|
||||
|
||||
|
||||
@ -504,7 +526,7 @@ def remove_tailwind_from_postcss() -> tuple[str, str]:
|
||||
|
||||
def purge_web_pages_dir():
|
||||
"""Empty out .web/pages directory."""
|
||||
if not is_prod_mode() and os.environ.get("REFLEX_PERSIST_WEB_DIR"):
|
||||
if not is_prod_mode() and environment.REFLEX_PERSIST_WEB_DIR:
|
||||
# Skip purging the web directory in dev mode if REFLEX_PERSIST_WEB_DIR is set.
|
||||
return
|
||||
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, Optional, Type, Union
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from reflex.utils.prerequisites import get_web_dir
|
||||
from reflex.vars.base import Var
|
||||
|
||||
try:
|
||||
from pydantic.v1.fields import ModelField
|
||||
@ -28,11 +28,11 @@ from reflex.components.base import (
|
||||
Title,
|
||||
)
|
||||
from reflex.components.component import Component, ComponentStyle, CustomComponent
|
||||
from reflex.state import BaseState, Cookie, LocalStorage, SessionStorage
|
||||
from reflex.istate.storage import Cookie, LocalStorage, SessionStorage
|
||||
from reflex.state import BaseState
|
||||
from reflex.style import Style
|
||||
from reflex.utils import console, format, imports, path_ops
|
||||
from reflex.utils.imports import ImportVar, ParsedImportDict
|
||||
from reflex.vars import Var
|
||||
|
||||
# To re-export this function.
|
||||
merge_imports = imports.merge_imports
|
||||
@ -44,6 +44,9 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
|
||||
Args:
|
||||
fields: The set of fields to import from the library.
|
||||
|
||||
Raises:
|
||||
ValueError: If there is more than one default import.
|
||||
|
||||
Returns:
|
||||
The libraries for default and rest.
|
||||
default: default library. When install "import def from library".
|
||||
@ -54,7 +57,8 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
|
||||
|
||||
# Check for default imports.
|
||||
defaults = {field for field in fields_set if field.is_default}
|
||||
assert len(defaults) < 2
|
||||
if len(defaults) >= 2:
|
||||
raise ValueError("Only one default import is allowed.")
|
||||
|
||||
# Get the default import, and the specific imports.
|
||||
default = next(iter({field.name for field in defaults}), "")
|
||||
@ -92,6 +96,9 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
|
||||
Args:
|
||||
import_dict: The import dict to compile.
|
||||
|
||||
Raises:
|
||||
ValueError: If an import in the dict is invalid.
|
||||
|
||||
Returns:
|
||||
The list of import dict.
|
||||
"""
|
||||
@ -106,8 +113,10 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
|
||||
continue
|
||||
|
||||
if not lib:
|
||||
assert not default, "No default field allowed for empty library."
|
||||
assert rest is not None and len(rest) > 0, "No fields to import."
|
||||
if default:
|
||||
raise ValueError("No default field allowed for empty library.")
|
||||
if rest is None or len(rest) == 0:
|
||||
raise ValueError("No fields to import.")
|
||||
for module in sorted(rest):
|
||||
import_dicts.append(get_import_dict(module))
|
||||
continue
|
||||
@ -152,8 +161,10 @@ def compile_state(state: Type[BaseState]) -> dict:
|
||||
console.warn(
|
||||
f"Failed to compile initial state with computed vars, excluding them: {e}"
|
||||
)
|
||||
initial_state = state(_reflex_internal_init=True).dict(include_computed=False)
|
||||
return format.format_state(initial_state)
|
||||
initial_state = state(_reflex_internal_init=True).dict(
|
||||
initial=True, include_computed=False
|
||||
)
|
||||
return initial_state
|
||||
|
||||
|
||||
def _compile_client_storage_field(
|
||||
@ -266,7 +277,7 @@ def compile_custom_component(
|
||||
}
|
||||
|
||||
# Concatenate the props.
|
||||
props = [prop._var_name for prop in component.get_prop_vars()]
|
||||
props = [prop._js_expr for prop in component.get_prop_vars()]
|
||||
|
||||
# Compile the component.
|
||||
return (
|
||||
@ -427,11 +438,11 @@ def add_meta(
|
||||
Returns:
|
||||
The component with the metadata added.
|
||||
"""
|
||||
meta_tags = [Meta.create(**item) for item in meta]
|
||||
|
||||
children: list[Any] = [
|
||||
Title.create(title),
|
||||
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))
|
||||
@ -446,16 +457,16 @@ def add_meta(
|
||||
return page
|
||||
|
||||
|
||||
def write_page(path: str, code: str):
|
||||
def write_page(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_ops.mkdir(os.path.dirname(path))
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
path = Path(path)
|
||||
path_ops.mkdir(path.parent)
|
||||
path.write_text(code, encoding="utf-8")
|
||||
|
||||
|
||||
def empty_dir(path: str | Path, keep_files: list[str] | None = None):
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.vars.base import Var
|
||||
|
||||
|
||||
class AppWrap(Fragment):
|
||||
@ -15,4 +15,4 @@ class AppWrap(Fragment):
|
||||
Returns:
|
||||
A new AppWrap component containing {children}.
|
||||
"""
|
||||
return super().create(ImmutableVar.create("children"))
|
||||
return super().create(Var(_js_expr="children"))
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class AppWrap(Fragment):
|
||||
@overload
|
||||
@ -22,41 +22,21 @@ class AppWrap(Fragment):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "AppWrap":
|
||||
"""Create a new AppWrap component.
|
||||
|
@ -7,8 +7,7 @@ from typing import Any, Iterator
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.tags import Tag
|
||||
from reflex.components.tags.tagless import Tagless
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.vars import Var
|
||||
from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var
|
||||
|
||||
|
||||
class Bare(Component):
|
||||
@ -26,16 +25,16 @@ class Bare(Component):
|
||||
Returns:
|
||||
The component.
|
||||
"""
|
||||
if isinstance(contents, ImmutableVar):
|
||||
return cls(contents=contents)
|
||||
if isinstance(contents, Var):
|
||||
contents = contents.to(str)
|
||||
return cls(contents=contents)
|
||||
else:
|
||||
contents = str(contents) if contents is not None else ""
|
||||
return cls(contents=contents) # type: ignore
|
||||
|
||||
def _render(self) -> Tag:
|
||||
if isinstance(self.contents, ImmutableVar):
|
||||
if isinstance(self.contents, Var):
|
||||
if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)):
|
||||
return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
|
||||
return Tagless(contents=f"{{{str(self.contents)}}}")
|
||||
return Tagless(contents=str(self.contents))
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class Body(Component):
|
||||
@overload
|
||||
@ -22,41 +22,21 @@ class Body(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Body":
|
||||
"""Create the component.
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class NextDocumentLib(Component):
|
||||
@overload
|
||||
@ -22,41 +22,21 @@ class NextDocumentLib(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "NextDocumentLib":
|
||||
"""Create the component.
|
||||
@ -89,41 +69,21 @@ class Html(NextDocumentLib):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Html":
|
||||
"""Create the component.
|
||||
@ -155,41 +115,21 @@ class DocumentHead(NextDocumentLib):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "DocumentHead":
|
||||
"""Create the component.
|
||||
@ -221,41 +161,21 @@ class Main(NextDocumentLib):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Main":
|
||||
"""Create the component.
|
||||
@ -287,41 +207,21 @@ class NextScript(NextDocumentLib):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "NextScript":
|
||||
"""Create the component.
|
||||
|
@ -2,17 +2,32 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
from reflex.compiler.compiler import _compile_component
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.el import div, p
|
||||
from reflex.constants import Hooks, Imports
|
||||
from reflex.event import EventChain, EventHandler
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.ivars.function import FunctionVar
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import Var
|
||||
from reflex.event import EventHandler
|
||||
from reflex.state import FrontendEventExceptionState
|
||||
from reflex.vars.base import Var
|
||||
|
||||
|
||||
def on_error_spec(
|
||||
error: Var[Dict[str, str]], info: Var[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.stack,
|
||||
info.componentStack,
|
||||
)
|
||||
|
||||
|
||||
class ErrorBoundary(Component):
|
||||
@ -22,31 +37,13 @@ class ErrorBoundary(Component):
|
||||
tag = "ErrorBoundary"
|
||||
|
||||
# Fired when the boundary catches an error.
|
||||
on_error: EventHandler[lambda error, info: [error, info]] = ImmutableVar( # type: ignore
|
||||
"logFrontendError"
|
||||
).to(FunctionVar, EventChain)
|
||||
on_error: EventHandler[on_error_spec]
|
||||
|
||||
# Rendered instead of the children when an error is caught.
|
||||
Fallback_component: Var[Component] = ImmutableVar.create_safe("Fallback")._replace(
|
||||
Fallback_component: Var[Component] = Var(_js_expr="Fallback")._replace(
|
||||
_var_type=Component
|
||||
)
|
||||
|
||||
def add_imports(self) -> dict[str, list[ImportVar]]:
|
||||
"""Add imports for the component.
|
||||
|
||||
Returns:
|
||||
The imports to add.
|
||||
"""
|
||||
return Imports.EVENTS
|
||||
|
||||
def add_hooks(self) -> List[str | Var]:
|
||||
"""Add hooks for the component.
|
||||
|
||||
Returns:
|
||||
The hooks to add.
|
||||
"""
|
||||
return [Hooks.EVENTS, Hooks.FRONTEND_ERRORS]
|
||||
|
||||
def add_custom_code(self) -> List[str]:
|
||||
"""Add custom Javascript code into the page that contains this component.
|
||||
|
||||
@ -58,7 +55,7 @@ class ErrorBoundary(Component):
|
||||
fallback_container = div(
|
||||
p("Ooops...Unknown Reflex error has occured:"),
|
||||
p(
|
||||
ImmutableVar.create("error.message"),
|
||||
Var(_js_expr="error.message"),
|
||||
color="red",
|
||||
),
|
||||
p("Please contact the support."),
|
||||
@ -76,5 +73,20 @@ class ErrorBoundary(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
|
||||
return super().create(*children, **props)
|
||||
|
||||
|
||||
error_boundary = ErrorBoundary.create
|
||||
|
@ -3,69 +3,50 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, List, Optional, Union, overload
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
def on_error_spec(
|
||||
error: Var[Dict[str, str]], info: Var[Dict[str, str]]
|
||||
) -> Tuple[Var[str], Var[str]]: ...
|
||||
|
||||
class ErrorBoundary(Component):
|
||||
def add_imports(self) -> dict[str, list[ImportVar]]: ...
|
||||
def add_hooks(self) -> List[str | Var]: ...
|
||||
def add_custom_code(self) -> List[str]: ...
|
||||
@overload
|
||||
@classmethod
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
Fallback_component: Optional[Union[Var[Component], Component]] = None,
|
||||
Fallback_component: Optional[Union[Component, Var[Component]]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_error: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_error: Optional[EventType[str, str]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ErrorBoundary":
|
||||
"""Create the component.
|
||||
"""Create an ErrorBoundary component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
@ -79,7 +60,7 @@ class ErrorBoundary(Component):
|
||||
**props: The props of the component.
|
||||
|
||||
Returns:
|
||||
The component.
|
||||
The ErrorBoundary component.
|
||||
"""
|
||||
...
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class Fragment(Component):
|
||||
@overload
|
||||
@ -22,41 +22,21 @@ class Fragment(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Fragment":
|
||||
"""Create the component.
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component, MemoizationLeaf
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class NextHeadLib(Component):
|
||||
@overload
|
||||
@ -22,41 +22,21 @@ class NextHeadLib(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "NextHeadLib":
|
||||
"""Create the component.
|
||||
@ -88,41 +68,21 @@ class Head(NextHeadLib, MemoizationLeaf):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Head":
|
||||
"""Create a new memoization leaf component.
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Display the title of the current page."""
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
|
||||
class RawLink(Component):
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class RawLink(Component):
|
||||
@overload
|
||||
@ -24,41 +24,21 @@ class RawLink(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "RawLink":
|
||||
"""Create the component.
|
||||
@ -99,41 +79,21 @@ class ScriptTag(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ScriptTag":
|
||||
"""Create the component.
|
||||
|
@ -16,13 +16,15 @@ class Title(Component):
|
||||
def render(self) -> dict:
|
||||
"""Render the title component.
|
||||
|
||||
Raises:
|
||||
ValueError: If the title is not a single string.
|
||||
|
||||
Returns:
|
||||
The rendered title component.
|
||||
"""
|
||||
# Make sure the title is a single string.
|
||||
assert len(self.children) == 1 and isinstance(
|
||||
self.children[0], Bare
|
||||
), "Title must be a single string."
|
||||
if len(self.children) != 1 or not isinstance(self.children[0], Bare):
|
||||
raise ValueError("Title must be a single string.")
|
||||
return super().render()
|
||||
|
||||
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class Title(Component):
|
||||
def render(self) -> dict: ...
|
||||
@ -23,41 +23,21 @@ class Title(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Title":
|
||||
"""Create the component.
|
||||
@ -94,41 +74,21 @@ class Meta(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Meta":
|
||||
"""Create the component.
|
||||
@ -170,41 +130,21 @@ class Description(Meta):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Description":
|
||||
"""Create the component.
|
||||
@ -246,41 +186,21 @@ class Image(Meta):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Image":
|
||||
"""Create the component.
|
||||
|
@ -8,9 +8,8 @@ from __future__ import annotations
|
||||
from typing import Literal
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler
|
||||
from reflex.ivars.base import LiteralVar
|
||||
from reflex.vars import Var
|
||||
from reflex.event import EventHandler, empty_event
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
|
||||
|
||||
class Script(Component):
|
||||
@ -36,13 +35,13 @@ class Script(Component):
|
||||
)
|
||||
|
||||
# Triggered when the script is loading
|
||||
on_load: EventHandler[lambda: []]
|
||||
on_load: EventHandler[empty_event]
|
||||
|
||||
# Triggered when the script has loaded
|
||||
on_ready: EventHandler[lambda: []]
|
||||
on_ready: EventHandler[empty_event]
|
||||
|
||||
# Triggered when the script has errored
|
||||
on_error: EventHandler[lambda: []]
|
||||
on_error: EventHandler[empty_event]
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children, **props) -> Component:
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
|
||||
from typing import Any, Dict, Literal, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class Script(Component):
|
||||
@overload
|
||||
@ -19,8 +19,8 @@ class Script(Component):
|
||||
src: Optional[Union[Var[str], str]] = None,
|
||||
strategy: Optional[
|
||||
Union[
|
||||
Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]],
|
||||
Literal["afterInteractive", "beforeInteractive", "lazyOnload"],
|
||||
Var[Literal["afterInteractive", "beforeInteractive", "lazyOnload"]],
|
||||
]
|
||||
] = None,
|
||||
style: Optional[Style] = None,
|
||||
@ -29,44 +29,24 @@ class Script(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_error: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_load: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_ready: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_error: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_load: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_ready: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Script":
|
||||
"""Create an inline or user-defined script.
|
||||
|
@ -25,6 +25,7 @@ import reflex.state
|
||||
from reflex.base import Base
|
||||
from reflex.compiler.templates import STATEFUL_COMPONENT
|
||||
from reflex.components.core.breakpoints import Breakpoints
|
||||
from reflex.components.dynamic import load_dynamic_serializer
|
||||
from reflex.components.tags import Tag
|
||||
from reflex.constants import (
|
||||
Dirs,
|
||||
@ -35,20 +36,30 @@ from reflex.constants import (
|
||||
MemoizationMode,
|
||||
PageNames,
|
||||
)
|
||||
from reflex.constants.compiler import SpecialAttributes
|
||||
from reflex.event import (
|
||||
EventChain,
|
||||
EventChainVar,
|
||||
EventHandler,
|
||||
EventSpec,
|
||||
EventVar,
|
||||
call_event_fn,
|
||||
call_event_handler,
|
||||
empty_event,
|
||||
get_handler_args,
|
||||
)
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.style import Style, format_as_emotion
|
||||
from reflex.utils import format, imports, types
|
||||
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import BaseVar, ImmutableVarData, Var, VarData
|
||||
from reflex.utils.imports import (
|
||||
ImmutableParsedImportDict,
|
||||
ImportDict,
|
||||
ImportVar,
|
||||
ParsedImportDict,
|
||||
parse_imports,
|
||||
)
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
from reflex.vars.sequence import LiteralArrayVar
|
||||
|
||||
|
||||
class BaseComponent(Base, ABC):
|
||||
@ -189,7 +200,7 @@ class Component(BaseComponent, ABC):
|
||||
class_name: Any = None
|
||||
|
||||
# Special component props.
|
||||
special_props: Set[Var] = set()
|
||||
special_props: List[Var] = []
|
||||
|
||||
# Whether the component should take the focus once the page is loaded
|
||||
autofocus: bool = False
|
||||
@ -441,9 +452,17 @@ class Component(BaseComponent, ABC):
|
||||
not passed_types
|
||||
and not types._issubclass(passed_type, expected_type, value)
|
||||
):
|
||||
value_name = value._var_name if isinstance(value, Var) else value
|
||||
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_types or passed_type}."
|
||||
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:
|
||||
@ -457,13 +476,24 @@ class Component(BaseComponent, ABC):
|
||||
for key in kwargs["event_triggers"]:
|
||||
del kwargs[key]
|
||||
|
||||
# Place data_ and aria_ attributes into custom_attrs
|
||||
special_attributes = tuple(
|
||||
key
|
||||
for key in kwargs
|
||||
if key not in fields and SpecialAttributes.is_special(key)
|
||||
)
|
||||
if special_attributes:
|
||||
custom_attrs = kwargs.setdefault("custom_attrs", {})
|
||||
for key in special_attributes:
|
||||
custom_attrs[format.to_kebab_case(key)] = kwargs.pop(key)
|
||||
|
||||
# Add style props to the component.
|
||||
style = kwargs.get("style", {})
|
||||
if isinstance(style, List):
|
||||
# Merge styles, the later ones overriding keys in the earlier ones.
|
||||
style = {k: v for style_dict in style for k, v in style_dict.items()}
|
||||
|
||||
if isinstance(style, Breakpoints):
|
||||
if isinstance(style, (Breakpoints, Var)):
|
||||
style = {
|
||||
# Assign the Breakpoints to the self-referential selector to avoid squashing down to a regular dict.
|
||||
"&": style,
|
||||
@ -476,13 +506,16 @@ class Component(BaseComponent, ABC):
|
||||
**{attr: value for attr, value in kwargs.items() if attr not in fields},
|
||||
}
|
||||
)
|
||||
if "custom_attrs" not in kwargs:
|
||||
kwargs["custom_attrs"] = {}
|
||||
|
||||
# Convert class_name to str if it's list
|
||||
class_name = kwargs.get("class_name", "")
|
||||
if isinstance(class_name, (List, tuple)):
|
||||
kwargs["class_name"] = " ".join(class_name)
|
||||
if any(isinstance(c, Var) for c in class_name):
|
||||
kwargs["class_name"] = LiteralArrayVar.create(
|
||||
class_name, _var_type=List[str]
|
||||
).join(" ")
|
||||
else:
|
||||
kwargs["class_name"] = " ".join(class_name)
|
||||
|
||||
# Construct the component.
|
||||
super().__init__(*args, **kwargs)
|
||||
@ -491,7 +524,11 @@ class Component(BaseComponent, ABC):
|
||||
self,
|
||||
args_spec: Any,
|
||||
value: Union[
|
||||
Var, EventHandler, EventSpec, List[Union[EventHandler, EventSpec]], Callable
|
||||
Var,
|
||||
EventHandler,
|
||||
EventSpec,
|
||||
List[Union[EventHandler, EventSpec, EventVar]],
|
||||
Callable,
|
||||
],
|
||||
) -> Union[EventChain, Var]:
|
||||
"""Create an event chain from a variety of input types.
|
||||
@ -508,11 +545,16 @@ class Component(BaseComponent, ABC):
|
||||
"""
|
||||
# If it's an event chain var, return it.
|
||||
if isinstance(value, Var):
|
||||
if value._var_type is not EventChain:
|
||||
if isinstance(value, EventChainVar):
|
||||
return value
|
||||
elif isinstance(value, EventVar):
|
||||
value = [value]
|
||||
elif issubclass(value._var_type, (EventChain, EventSpec)):
|
||||
return self._create_event_chain(args_spec, value.guess_type())
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Invalid event chain: {repr(value)} of type {type(value)}"
|
||||
f"Invalid event chain: {str(value)} of type {value._var_type}"
|
||||
)
|
||||
return value
|
||||
elif isinstance(value, EventChain):
|
||||
# Trust that the caller knows what they're doing passing an EventChain directly
|
||||
return value
|
||||
@ -523,7 +565,7 @@ class Component(BaseComponent, ABC):
|
||||
|
||||
# If the input is a list of event handlers, create an event chain.
|
||||
if isinstance(value, List):
|
||||
events: list[EventSpec] = []
|
||||
events: List[Union[EventSpec, EventVar]] = []
|
||||
for v in value:
|
||||
if isinstance(v, (EventHandler, EventSpec)):
|
||||
# Call the event handler to get the event.
|
||||
@ -537,6 +579,8 @@ class Component(BaseComponent, ABC):
|
||||
"lambda inside an EventChain list."
|
||||
)
|
||||
events.extend(result)
|
||||
elif isinstance(v, EventVar):
|
||||
events.append(v)
|
||||
else:
|
||||
raise ValueError(f"Invalid event: {v}")
|
||||
|
||||
@ -546,32 +590,30 @@ class Component(BaseComponent, ABC):
|
||||
if isinstance(result, Var):
|
||||
# Recursively call this function if the lambda returned an EventChain Var.
|
||||
return self._create_event_chain(args_spec, result)
|
||||
events = result
|
||||
events = [*result]
|
||||
|
||||
# Otherwise, raise an error.
|
||||
else:
|
||||
raise ValueError(f"Invalid event chain: {value}")
|
||||
|
||||
# Add args to the event specs if necessary.
|
||||
events = [e.with_args(get_handler_args(e)) for e in events]
|
||||
|
||||
# Collect event_actions from each spec
|
||||
event_actions = {}
|
||||
for e in events:
|
||||
event_actions.update(e.event_actions)
|
||||
events = [
|
||||
(e.with_args(get_handler_args(e)) if isinstance(e, EventSpec) else e)
|
||||
for e in events
|
||||
]
|
||||
|
||||
# Return the event chain.
|
||||
if isinstance(args_spec, Var):
|
||||
return EventChain(
|
||||
events=events,
|
||||
args_spec=None,
|
||||
event_actions=event_actions,
|
||||
event_actions={},
|
||||
)
|
||||
else:
|
||||
return EventChain(
|
||||
events=events,
|
||||
args_spec=args_spec,
|
||||
event_actions=event_actions,
|
||||
event_actions={},
|
||||
)
|
||||
|
||||
def get_event_triggers(self) -> Dict[str, Any]:
|
||||
@ -582,21 +624,21 @@ class Component(BaseComponent, ABC):
|
||||
|
||||
"""
|
||||
default_triggers = {
|
||||
EventTriggers.ON_FOCUS: lambda: [],
|
||||
EventTriggers.ON_BLUR: lambda: [],
|
||||
EventTriggers.ON_CLICK: lambda: [],
|
||||
EventTriggers.ON_CONTEXT_MENU: lambda: [],
|
||||
EventTriggers.ON_DOUBLE_CLICK: lambda: [],
|
||||
EventTriggers.ON_MOUSE_DOWN: lambda: [],
|
||||
EventTriggers.ON_MOUSE_ENTER: lambda: [],
|
||||
EventTriggers.ON_MOUSE_LEAVE: lambda: [],
|
||||
EventTriggers.ON_MOUSE_MOVE: lambda: [],
|
||||
EventTriggers.ON_MOUSE_OUT: lambda: [],
|
||||
EventTriggers.ON_MOUSE_OVER: lambda: [],
|
||||
EventTriggers.ON_MOUSE_UP: lambda: [],
|
||||
EventTriggers.ON_SCROLL: lambda: [],
|
||||
EventTriggers.ON_MOUNT: lambda: [],
|
||||
EventTriggers.ON_UNMOUNT: lambda: [],
|
||||
EventTriggers.ON_FOCUS: empty_event,
|
||||
EventTriggers.ON_BLUR: empty_event,
|
||||
EventTriggers.ON_CLICK: empty_event,
|
||||
EventTriggers.ON_CONTEXT_MENU: empty_event,
|
||||
EventTriggers.ON_DOUBLE_CLICK: empty_event,
|
||||
EventTriggers.ON_MOUSE_DOWN: empty_event,
|
||||
EventTriggers.ON_MOUSE_ENTER: empty_event,
|
||||
EventTriggers.ON_MOUSE_LEAVE: empty_event,
|
||||
EventTriggers.ON_MOUSE_MOVE: empty_event,
|
||||
EventTriggers.ON_MOUSE_OUT: empty_event,
|
||||
EventTriggers.ON_MOUSE_OVER: empty_event,
|
||||
EventTriggers.ON_MOUSE_UP: empty_event,
|
||||
EventTriggers.ON_SCROLL: empty_event,
|
||||
EventTriggers.ON_MOUNT: empty_event,
|
||||
EventTriggers.ON_UNMOUNT: empty_event,
|
||||
}
|
||||
|
||||
# Look for component specific triggers,
|
||||
@ -605,9 +647,9 @@ class Component(BaseComponent, ABC):
|
||||
if types._issubclass(field.type_, EventHandler):
|
||||
args_spec = None
|
||||
annotation = field.annotation
|
||||
if hasattr(annotation, "__metadata__"):
|
||||
args_spec = annotation.__metadata__[0]
|
||||
default_triggers[field.name] = args_spec or (lambda: [])
|
||||
if (metadata := getattr(annotation, "__metadata__", None)) is not None:
|
||||
args_spec = metadata[0]
|
||||
default_triggers[field.name] = args_spec or (empty_event) # type: ignore
|
||||
return default_triggers
|
||||
|
||||
def __repr__(self) -> str:
|
||||
@ -647,7 +689,7 @@ class Component(BaseComponent, ABC):
|
||||
"""
|
||||
# Create the base tag.
|
||||
tag = Tag(
|
||||
name=self.tag if not self.alias else self.alias,
|
||||
name=(self.tag if not self.alias else self.alias) or "",
|
||||
special_props=self.special_props,
|
||||
)
|
||||
|
||||
@ -661,7 +703,7 @@ class Component(BaseComponent, ABC):
|
||||
# Add ref to element if `id` is not None.
|
||||
ref = self.get_ref()
|
||||
if ref is not None:
|
||||
props["ref"] = ImmutableVar.create(ref)
|
||||
props["ref"] = Var(_js_expr=ref)
|
||||
else:
|
||||
props = props.copy()
|
||||
|
||||
@ -1006,8 +1048,11 @@ class Component(BaseComponent, ABC):
|
||||
elif isinstance(event, EventChain):
|
||||
event_args = []
|
||||
for spec in event.events:
|
||||
for args in spec.args:
|
||||
event_args.extend(args)
|
||||
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) -> list[Var]:
|
||||
@ -1036,10 +1081,10 @@ class Component(BaseComponent, ABC):
|
||||
# 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(
|
||||
ImmutableVar(
|
||||
_var_name="style",
|
||||
Var(
|
||||
_js_expr="style",
|
||||
_var_type=str,
|
||||
_var_data=ImmutableVarData.merge(self.style._var_data),
|
||||
_var_data=VarData.merge(self.style._var_data),
|
||||
)
|
||||
)
|
||||
|
||||
@ -1081,8 +1126,12 @@ class Component(BaseComponent, ABC):
|
||||
for trigger in self.event_triggers.values():
|
||||
if isinstance(trigger, EventChain):
|
||||
for event in trigger.events:
|
||||
if event.handler.state_full_name:
|
||||
return True
|
||||
if isinstance(event, EventSpec):
|
||||
if event.handler.state_full_name:
|
||||
return True
|
||||
else:
|
||||
if event._var_state:
|
||||
return True
|
||||
elif isinstance(trigger, Var) and trigger._var_state:
|
||||
return True
|
||||
return False
|
||||
@ -1303,9 +1352,15 @@ class Component(BaseComponent, ABC):
|
||||
|
||||
# Collect imports from Vars used directly by this component.
|
||||
var_datas = [var._get_all_var_data() for var in self._get_vars()]
|
||||
var_imports = [
|
||||
var_data.imports for var_data in var_datas if var_data is not None
|
||||
]
|
||||
var_imports: List[ImmutableParsedImportDict] = list(
|
||||
map(
|
||||
lambda var_data: var_data.imports,
|
||||
filter(
|
||||
None,
|
||||
var_datas,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
added_import_dicts: list[ParsedImportDict] = []
|
||||
for clz in self._iter_parent_classes_with_method("add_imports"):
|
||||
@ -1352,9 +1407,9 @@ class Component(BaseComponent, ABC):
|
||||
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 = format.format_event_chain(on_mount)
|
||||
on_mount = str(LiteralVar.create(on_mount)) + "()"
|
||||
if on_unmount is not None:
|
||||
on_unmount = format.format_event_chain(on_unmount)
|
||||
on_unmount = str(LiteralVar.create(on_unmount)) + "()"
|
||||
if on_mount is not None or on_unmount is not None:
|
||||
return f"""
|
||||
useEffect(() => {{
|
||||
@ -1372,7 +1427,7 @@ class Component(BaseComponent, ABC):
|
||||
"""
|
||||
ref = self.get_ref()
|
||||
if ref is not None:
|
||||
return f"const {ref} = useRef(null); {str(ImmutableVar.create_safe(ref).as_ref())} = {ref};"
|
||||
return f"const {ref} = useRef(null); {str(Var(_js_expr=ref).as_ref())} = {ref};"
|
||||
|
||||
def _get_vars_hooks(self) -> dict[str, None]:
|
||||
"""Get the hooks required by vars referenced in this component.
|
||||
@ -1513,7 +1568,7 @@ class Component(BaseComponent, ABC):
|
||||
The ref name.
|
||||
"""
|
||||
# do not create a ref if the id is dynamic or unspecified
|
||||
if self.id is None or isinstance(self.id, (BaseVar, ImmutableVar)):
|
||||
if self.id is None or isinstance(self.id, Var):
|
||||
return None
|
||||
return format.format_ref(self.id)
|
||||
|
||||
@ -1651,7 +1706,7 @@ class CustomComponent(Component):
|
||||
value = self._create_event_chain(
|
||||
value=value,
|
||||
args_spec=event_triggers_in_component_declaration.get(
|
||||
key, lambda: []
|
||||
key, empty_event
|
||||
),
|
||||
)
|
||||
self.props[format.to_camel_case(key)] = value
|
||||
@ -1714,10 +1769,14 @@ class CustomComponent(Component):
|
||||
Args:
|
||||
seen: The tags of the components that have already been seen.
|
||||
|
||||
Raises:
|
||||
ValueError: If the tag is not set.
|
||||
|
||||
Returns:
|
||||
The set of custom components.
|
||||
"""
|
||||
assert self.tag is not None, "The tag must be set."
|
||||
if self.tag is None:
|
||||
raise ValueError("The tag must be set.")
|
||||
|
||||
# Store the seen components in a set to avoid infinite recursion.
|
||||
if seen is None:
|
||||
@ -1752,15 +1811,15 @@ class CustomComponent(Component):
|
||||
"""
|
||||
return super()._render(props=self.props)
|
||||
|
||||
def get_prop_vars(self) -> List[ImmutableVar]:
|
||||
def get_prop_vars(self) -> List[Var]:
|
||||
"""Get the prop vars.
|
||||
|
||||
Returns:
|
||||
The prop vars.
|
||||
"""
|
||||
return [
|
||||
ImmutableVar(
|
||||
_var_name=name,
|
||||
Var(
|
||||
_js_expr=name,
|
||||
_var_type=(
|
||||
prop._var_type if types._isinstance(prop, Var) else type(prop)
|
||||
),
|
||||
@ -1866,19 +1925,6 @@ class NoSSRComponent(Component):
|
||||
return "".join((library_import, mod_import, opts_fragment))
|
||||
|
||||
|
||||
@serializer
|
||||
def serialize_component(comp: Component):
|
||||
"""Serialize a component.
|
||||
|
||||
Args:
|
||||
comp: The component to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized component.
|
||||
"""
|
||||
return str(comp)
|
||||
|
||||
|
||||
class StatefulComponent(BaseComponent):
|
||||
"""A component that depends on state and is rendered outside of the page component.
|
||||
|
||||
@ -2130,9 +2176,7 @@ class StatefulComponent(BaseComponent):
|
||||
|
||||
# Get the actual EventSpec and render it.
|
||||
event = component.event_triggers[event_trigger]
|
||||
rendered_chain = format.format_prop(event)
|
||||
if isinstance(rendered_chain, str):
|
||||
rendered_chain = rendered_chain.strip("{}")
|
||||
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()
|
||||
@ -2141,9 +2185,10 @@ class StatefulComponent(BaseComponent):
|
||||
# Calculate Var dependencies accessed by the handler for useCallback dep array.
|
||||
var_deps = ["addEvents", "Event"]
|
||||
for arg in event_args:
|
||||
if arg._get_all_var_data() is None:
|
||||
var_data = arg._get_all_var_data()
|
||||
if var_data is None:
|
||||
continue
|
||||
for hook in arg._get_all_var_data().hooks:
|
||||
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],
|
||||
@ -2154,7 +2199,7 @@ class StatefulComponent(BaseComponent):
|
||||
|
||||
# Store the memoized function name and hook code for this event trigger.
|
||||
trigger_memo[event_trigger] = (
|
||||
ImmutableVar.create_safe(memo_name)._replace(
|
||||
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)}])",
|
||||
@ -2227,7 +2272,7 @@ class StatefulComponent(BaseComponent):
|
||||
Returns:
|
||||
The tag to render.
|
||||
"""
|
||||
return dict(Tag(name=self.tag))
|
||||
return dict(Tag(name=self.tag or ""))
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Represent the component in React.
|
||||
@ -2292,3 +2337,6 @@ class MemoizationLeaf(Component):
|
||||
update={"disposition": MemoizationDisposition.ALWAYS}
|
||||
)
|
||||
return comp
|
||||
|
||||
|
||||
load_dynamic_serializer()
|
||||
|
@ -18,57 +18,53 @@ from reflex.components.radix.themes.typography.text import Text
|
||||
from reflex.components.sonner.toast import Toaster, ToastProps
|
||||
from reflex.constants import Dirs, Hooks, Imports
|
||||
from reflex.constants.compiler import CompileVars
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.function import FunctionStringVar
|
||||
from reflex.ivars.number import BooleanVar
|
||||
from reflex.ivars.sequence import LiteralArrayVar
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
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( # type: ignore
|
||||
imports=Imports.EVENTS,
|
||||
hooks={Hooks.EVENTS: None},
|
||||
)
|
||||
|
||||
connect_errors: Var = ImmutableVar.create_safe(
|
||||
value=CompileVars.CONNECT_ERROR,
|
||||
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_error: Var = ImmutableVar.create_safe(
|
||||
value="((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
|
||||
)
|
||||
|
||||
connection_errors_count: Var = ImmutableVar.create_safe(
|
||||
value="connectErrors.length",
|
||||
_var_data=connect_error_var_data,
|
||||
)
|
||||
|
||||
has_connection_errors: Var = ImmutableVar.create_safe(
|
||||
value="(connectErrors.length > 0)",
|
||||
_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 = ImmutableVar.create_safe(
|
||||
value="(connectErrors.length >= 2)",
|
||||
_var_data=connect_error_var_data,
|
||||
has_too_many_connection_errors = Var(
|
||||
_js_expr="(connectErrors.length >= 2)", _var_data=connect_error_var_data
|
||||
).to(BooleanVar)
|
||||
|
||||
|
||||
class WebsocketTargetURL(ImmutableVar):
|
||||
class WebsocketTargetURL(Var):
|
||||
"""A component that renders the websocket target URL."""
|
||||
|
||||
@classmethod
|
||||
def create(cls) -> ImmutableVar:
|
||||
def create(cls) -> Var:
|
||||
"""Create a websocket target URL component.
|
||||
|
||||
Returns:
|
||||
The websocket target URL component.
|
||||
"""
|
||||
return ImmutableVar(
|
||||
_var_name="getBackendURL(env.EVENT).href",
|
||||
_var_data=ImmutableVarData(
|
||||
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")],
|
||||
@ -125,8 +121,8 @@ class ConnectionToaster(Toaster):
|
||||
),
|
||||
).call(
|
||||
# TODO: This breaks the assumption that Vars are JS expressions
|
||||
ImmutableVar.create_safe(
|
||||
f"""
|
||||
Var(
|
||||
_js_expr=f"""
|
||||
() => {{
|
||||
if ({str(has_too_many_connection_errors)}) {{
|
||||
if (!userDismissed) {{
|
||||
@ -238,7 +234,7 @@ class WifiOffPulse(Icon):
|
||||
Returns:
|
||||
The icon component with default props applied.
|
||||
"""
|
||||
pulse_var = ImmutableVar.create("pulse")
|
||||
pulse_var = Var(_js_expr="pulse")
|
||||
return super().create(
|
||||
"wifi_off",
|
||||
color=props.pop("color", "crimson"),
|
||||
|
@ -3,28 +3,41 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
|
||||
from typing import Any, Dict, Literal, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.el.elements.typography import Div
|
||||
from reflex.components.lucide.icon import Icon
|
||||
from reflex.components.sonner.toast import Toaster, ToastProps
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.constants.compiler import CompileVars
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import Var, VarData
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import Var
|
||||
from reflex.vars.number import BooleanVar
|
||||
|
||||
connect_error_var_data: VarData
|
||||
connect_errors: Var
|
||||
connection_error: Var
|
||||
connection_errors_count: Var
|
||||
has_connection_errors: Var
|
||||
has_too_many_connection_errors: Var
|
||||
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(ImmutableVar):
|
||||
class WebsocketTargetURL(Var):
|
||||
@classmethod
|
||||
def create(cls) -> ImmutableVar: ... # type: ignore
|
||||
def create(cls) -> Var: ... # type: ignore
|
||||
|
||||
def default_connection_error() -> list[str | Var | Component]: ...
|
||||
|
||||
@ -41,24 +54,24 @@ class ConnectionToaster(Toaster):
|
||||
visible_toasts: Optional[Union[Var[int], int]] = None,
|
||||
position: Optional[
|
||||
Union[
|
||||
Literal[
|
||||
"bottom-center",
|
||||
"bottom-left",
|
||||
"bottom-right",
|
||||
"top-center",
|
||||
"top-left",
|
||||
"top-right",
|
||||
],
|
||||
Var[
|
||||
Literal[
|
||||
"top-left",
|
||||
"top-center",
|
||||
"top-right",
|
||||
"bottom-left",
|
||||
"bottom-center",
|
||||
"bottom-left",
|
||||
"bottom-right",
|
||||
"top-center",
|
||||
"top-left",
|
||||
"top-right",
|
||||
]
|
||||
],
|
||||
Literal[
|
||||
"top-left",
|
||||
"top-center",
|
||||
"top-right",
|
||||
"bottom-left",
|
||||
"bottom-center",
|
||||
"bottom-right",
|
||||
],
|
||||
]
|
||||
] = None,
|
||||
close_button: Optional[Union[Var[bool], bool]] = None,
|
||||
@ -66,9 +79,9 @@ class ConnectionToaster(Toaster):
|
||||
dir: Optional[Union[Var[str], str]] = None,
|
||||
hotkey: Optional[Union[Var[str], str]] = None,
|
||||
invert: Optional[Union[Var[bool], bool]] = None,
|
||||
toast_options: Optional[Union[Var[ToastProps], ToastProps]] = None,
|
||||
toast_options: Optional[Union[ToastProps, Var[ToastProps]]] = None,
|
||||
gap: Optional[Union[Var[int], int]] = None,
|
||||
loading_icon: Optional[Union[Var[Icon], Icon]] = None,
|
||||
loading_icon: Optional[Union[Icon, Var[Icon]]] = None,
|
||||
pause_when_page_is_hidden: Optional[Union[Var[bool], bool]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
@ -76,41 +89,21 @@ class ConnectionToaster(Toaster):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ConnectionToaster":
|
||||
"""Create a connection toaster component.
|
||||
@ -156,41 +149,21 @@ class ConnectionBanner(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ConnectionBanner":
|
||||
"""Create a connection banner component.
|
||||
@ -215,41 +188,21 @@ class ConnectionModal(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ConnectionModal":
|
||||
"""Create a connection banner component.
|
||||
@ -275,41 +228,21 @@ class WifiOffPulse(Icon):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "WifiOffPulse":
|
||||
"""Create a wifi_off icon with an animated opacity pulse.
|
||||
@ -338,71 +271,51 @@ class ConnectionPulser(Div):
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
access_key: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
auto_capitalize: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
content_editable: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
context_menu: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
dir: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
draggable: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
enter_key_hint: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
hidden: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
input_mode: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
item_prop: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
lang: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
role: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
slot: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
spell_check: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
tab_index: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
title: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
title: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ConnectionPulser":
|
||||
"""Create a connection pulser component.
|
||||
|
@ -13,9 +13,9 @@ from __future__ import annotations
|
||||
from reflex import constants
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.core.cond import cond
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
route_not_found: Var = Var.create_safe(constants.ROUTE_NOT_FOUND, _var_is_string=False)
|
||||
route_not_found: Var = Var(_js_expr=constants.ROUTE_NOT_FOUND)
|
||||
|
||||
|
||||
class ClientSideRouting(Component):
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
route_not_found: Var
|
||||
|
||||
@ -26,41 +26,21 @@ class ClientSideRouting(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "ClientSideRouting":
|
||||
"""Create the component.
|
||||
@ -95,41 +75,21 @@ class Default404Page(Component):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Default404Page":
|
||||
"""Create the component.
|
||||
|
@ -2,14 +2,15 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict, List, Union
|
||||
from typing import Dict, List, Tuple, Union
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.tags.tag import Tag
|
||||
from reflex.event import EventChain, EventHandler
|
||||
from reflex.event import EventChain, EventHandler, identity_event
|
||||
from reflex.utils.format import format_prop, wrap
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import Var, get_unique_variable_name
|
||||
from reflex.vars import get_unique_variable_name
|
||||
from reflex.vars.base import Var
|
||||
|
||||
|
||||
class Clipboard(Fragment):
|
||||
@ -19,7 +20,7 @@ class Clipboard(Fragment):
|
||||
targets: Var[List[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[lambda data: [data]]
|
||||
on_paste: EventHandler[identity_event(List[Tuple[str, str]])]
|
||||
|
||||
# Save the original event actions for the on_paste event.
|
||||
on_paste_event_actions: Var[Dict[str, Union[bool, int]]]
|
||||
@ -85,8 +86,8 @@ class Clipboard(Fragment):
|
||||
return [
|
||||
"usePasteHandler(%s, %s, %s)"
|
||||
% (
|
||||
self.targets._var_name_unwrapped,
|
||||
self.on_paste_event_actions._var_name_unwrapped,
|
||||
str(self.targets),
|
||||
str(self.on_paste_event_actions),
|
||||
on_paste,
|
||||
)
|
||||
]
|
||||
|
@ -3,13 +3,13 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, List, Optional, Union, overload
|
||||
from typing import Any, Dict, List, Optional, Union, overload
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class Clipboard(Fragment):
|
||||
@overload
|
||||
@ -17,9 +17,9 @@ class Clipboard(Fragment):
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
targets: Optional[Union[Var[List[str]], List[str]]] = None,
|
||||
targets: Optional[Union[List[str], Var[List[str]]]] = None,
|
||||
on_paste_event_actions: Optional[
|
||||
Union[Var[Dict[str, Union[bool, int]]], Dict[str, Union[bool, int]]]
|
||||
Union[Dict[str, Union[bool, int]], Var[Dict[str, Union[bool, int]]]]
|
||||
] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
@ -27,42 +27,22 @@ class Clipboard(Fragment):
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_paste: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_paste: Optional[EventType[list[tuple[str, str]]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Clipboard":
|
||||
"""Create a Clipboard component.
|
||||
|
@ -8,11 +8,11 @@ from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
||||
from reflex.components.tags import CondTag, Tag
|
||||
from reflex.constants import Dirs
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.ivars.number import ternary_operation
|
||||
from reflex.style import LIGHT_COLOR_MODE, resolved_color_mode
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars import Var, VarData
|
||||
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")],
|
||||
@ -94,7 +94,7 @@ class Cond(MemoizationLeaf):
|
||||
).set(
|
||||
props=tag.format_props(),
|
||||
),
|
||||
cond_state=f"isTrue({self.cond._var_full_name})",
|
||||
cond_state=f"isTrue({str(self.cond)})",
|
||||
)
|
||||
|
||||
def add_imports(self) -> ImportDict:
|
||||
@ -103,10 +103,11 @@ class Cond(MemoizationLeaf):
|
||||
Returns:
|
||||
The import dict for the component.
|
||||
"""
|
||||
cond_imports: dict[str, str | ImportVar | list[str | ImportVar]] = getattr(
|
||||
VarData.merge(self.cond._get_all_var_data()), "imports", {}
|
||||
)
|
||||
return {**cond_imports, **_IS_TRUE_IMPORT}
|
||||
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
|
||||
@ -118,10 +119,10 @@ def cond(condition: Any, c1: Component) -> Component: ...
|
||||
|
||||
|
||||
@overload
|
||||
def cond(condition: Any, c1: Any, c2: Any) -> ImmutableVar: ...
|
||||
def cond(condition: Any, c1: Any, c2: Any) -> Var: ...
|
||||
|
||||
|
||||
def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar:
|
||||
def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
|
||||
"""Create a conditional component or Prop.
|
||||
|
||||
Args:
|
||||
@ -135,17 +136,15 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar:
|
||||
Raises:
|
||||
ValueError: If the arguments are invalid.
|
||||
"""
|
||||
if isinstance(condition, Var) and not isinstance(condition, ImmutableVar):
|
||||
condition._var_is_local = True
|
||||
# Convert the condition to a Var.
|
||||
cond_var = LiteralVar.create(condition)
|
||||
assert cond_var is not None, "The condition must be set."
|
||||
if cond_var is None:
|
||||
raise ValueError("The condition must be set.")
|
||||
|
||||
# If the first component is a component, create a Cond component.
|
||||
if isinstance(c1, BaseComponent):
|
||||
assert c2 is None or isinstance(
|
||||
c2, BaseComponent
|
||||
), "Both arguments must be components."
|
||||
if c2 is not None and not isinstance(c2, BaseComponent):
|
||||
raise ValueError("Both arguments must be components.")
|
||||
return Cond.create(cond_var, c1, c2)
|
||||
|
||||
# Otherwise, create a conditional Var.
|
||||
|
@ -6,8 +6,9 @@ from typing import Any, Type, Union
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.constants import EventTriggers
|
||||
from reflex.event import EventHandler
|
||||
from reflex.vars import Var, VarData
|
||||
from reflex.event import EventHandler, empty_event
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import Var
|
||||
|
||||
DEFAULT_DEBOUNCE_TIMEOUT = 300
|
||||
|
||||
@ -45,7 +46,7 @@ class DebounceInput(Component):
|
||||
element: Var[Type[Component]]
|
||||
|
||||
# Fired when the input value changes
|
||||
on_change: EventHandler[lambda e0: [e0.value]]
|
||||
on_change: EventHandler[empty_event]
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children: Component, **props: Any) -> Component:
|
||||
@ -106,23 +107,20 @@ class DebounceInput(Component):
|
||||
props[field] = getattr(child, field)
|
||||
child_ref = child.get_ref()
|
||||
if props.get("input_ref") is None and child_ref:
|
||||
props["input_ref"] = Var.create_safe(
|
||||
child_ref, _var_is_local=False, _var_is_string=False
|
||||
)
|
||||
props["input_ref"] = Var(_js_expr=child_ref, _var_type=str)
|
||||
props["id"] = child.id
|
||||
|
||||
# Set the child element to wrap, including any imports/hooks from the child.
|
||||
props.setdefault(
|
||||
"element",
|
||||
Var.create_safe(
|
||||
"{%s}" % (child.alias or child.tag),
|
||||
_var_is_local=False,
|
||||
_var_is_string=False,
|
||||
Var(
|
||||
_js_expr=str(child.alias or child.tag),
|
||||
_var_type=Type[Component],
|
||||
_var_data=VarData(
|
||||
imports=child._get_imports(),
|
||||
hooks=child._get_hooks_internal(),
|
||||
),
|
||||
).to(Type[Component]),
|
||||
),
|
||||
)
|
||||
|
||||
component = super().create(**props)
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Type, Union, overload
|
||||
from typing import Any, Dict, Optional, Type, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
DEFAULT_DEBOUNCE_TIMEOUT = 300
|
||||
|
||||
@ -22,51 +22,31 @@ class DebounceInput(Component):
|
||||
debounce_timeout: Optional[Union[Var[int], int]] = None,
|
||||
force_notify_by_enter: Optional[Union[Var[bool], bool]] = None,
|
||||
force_notify_on_blur: Optional[Union[Var[bool], bool]] = None,
|
||||
value: Optional[Union[Var[Union[float, int, str]], str, int, float]] = None,
|
||||
value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None,
|
||||
input_ref: Optional[Union[Var[str], str]] = None,
|
||||
element: Optional[Union[Var[Type[Component]], Type[Component]]] = None,
|
||||
element: Optional[Union[Type[Component], Var[Type[Component]]]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_change: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_change: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "DebounceInput":
|
||||
"""Create a DebounceInput component.
|
||||
|
@ -9,9 +9,8 @@ from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.tags import IterTag
|
||||
from reflex.constants import MemoizationMode
|
||||
from reflex.ivars.base import ImmutableVar
|
||||
from reflex.state import ComponentState
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
|
||||
|
||||
class ForeachVarError(TypeError):
|
||||
@ -52,10 +51,10 @@ class Foreach(Component):
|
||||
ForeachVarError: If the iterable is of type Any.
|
||||
TypeError: If the render function is a ComponentState.
|
||||
"""
|
||||
iterable = ImmutableVar.create_safe(iterable)
|
||||
iterable = LiteralVar.create(iterable)
|
||||
if iterable._var_type == Any:
|
||||
raise ForeachVarError(
|
||||
f"Could not foreach over var `{iterable._var_full_name}` of type Any. "
|
||||
f"Could not foreach over var `{str(iterable)}` 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/"
|
||||
)
|
||||
@ -127,7 +126,7 @@ class Foreach(Component):
|
||||
|
||||
return dict(
|
||||
tag,
|
||||
iterable_state=tag.iterable._var_full_name,
|
||||
iterable_state=str(tag.iterable),
|
||||
arg_name=tag.arg_var_name,
|
||||
arg_index=tag.get_index_var_arg(),
|
||||
iterable_type=tag.iterable._var_type.mro()[0].__name__,
|
||||
|
@ -3,7 +3,7 @@
|
||||
from typing import Dict
|
||||
|
||||
from reflex.components.el.elements.typography import Div
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
|
||||
class Html(Div):
|
||||
|
@ -3,12 +3,12 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
from typing import Any, Callable, Dict, Optional, Union, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.el.elements.typography import Div
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.event import EventType
|
||||
from reflex.style import Style
|
||||
from reflex.vars import Var
|
||||
from reflex.vars.base import Var
|
||||
|
||||
class Html(Div):
|
||||
@overload
|
||||
@ -17,73 +17,53 @@ class Html(Div):
|
||||
cls,
|
||||
*children,
|
||||
dangerouslySetInnerHTML: Optional[
|
||||
Union[Var[Dict[str, str]], Dict[str, str]]
|
||||
Union[Dict[str, str], Var[Dict[str, str]]]
|
||||
] = None,
|
||||
access_key: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
auto_capitalize: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
content_editable: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
context_menu: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
dir: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
draggable: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
enter_key_hint: Optional[
|
||||
Union[Var[Union[bool, int, str]], str, int, bool]
|
||||
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||
] = None,
|
||||
hidden: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
input_mode: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
item_prop: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
lang: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
role: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
slot: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
spell_check: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
tab_index: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
title: Optional[Union[Var[Union[bool, int, str]], str, int, bool]] = None,
|
||||
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
title: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_blur: Optional[EventType[[]]] = None,
|
||||
on_click: Optional[EventType[[]]] = None,
|
||||
on_context_menu: Optional[EventType[[]]] = None,
|
||||
on_double_click: Optional[EventType[[]]] = None,
|
||||
on_focus: Optional[EventType[[]]] = None,
|
||||
on_mount: Optional[EventType[[]]] = None,
|
||||
on_mouse_down: Optional[EventType[[]]] = None,
|
||||
on_mouse_enter: Optional[EventType[[]]] = None,
|
||||
on_mouse_leave: Optional[EventType[[]]] = None,
|
||||
on_mouse_move: Optional[EventType[[]]] = None,
|
||||
on_mouse_out: Optional[EventType[[]]] = None,
|
||||
on_mouse_over: Optional[EventType[[]]] = None,
|
||||
on_mouse_up: Optional[EventType[[]]] = None,
|
||||
on_scroll: Optional[EventType[[]]] = None,
|
||||
on_unmount: Optional[EventType[[]]] = None,
|
||||
**props,
|
||||
) -> "Html":
|
||||
"""Create a html component.
|
||||
|
@ -6,12 +6,12 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from reflex.components.base import Fragment
|
||||
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
||||
from reflex.components.tags import MatchTag, Tag
|
||||
from reflex.ivars.base import ImmutableVar, LiteralVar
|
||||
from reflex.style import Style
|
||||
from reflex.utils import format, types
|
||||
from reflex.utils.exceptions import MatchTypeError
|
||||
from reflex.utils.imports import ImportDict
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
|
||||
|
||||
class Match(MemoizationLeaf):
|
||||
@ -187,7 +187,7 @@ class Match(MemoizationLeaf):
|
||||
if not types._issubclass(type(case[-1]), return_type):
|
||||
raise MatchTypeError(
|
||||
f"Match cases should have the same return types. Case {index} with return "
|
||||
f"value `{case[-1]._var_name if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`"
|
||||
f"value `{case[-1]._js_expr if isinstance(case[-1], Var) else textwrap.shorten(str(case[-1]), width=250)}`"
|
||||
f" of type {type(case[-1])!r} is not {return_type}"
|
||||
)
|
||||
|
||||
@ -232,14 +232,14 @@ class Match(MemoizationLeaf):
|
||||
) or not types._isinstance(default, Var):
|
||||
raise ValueError("Return types of match cases should be Vars.")
|
||||
|
||||
return ImmutableVar(
|
||||
_var_name=format.format_match(
|
||||
cond=match_cond_var._var_name_unwrapped,
|
||||
match_cases=match_cases, # type: ignore
|
||||
return Var(
|
||||
_js_expr=format.format_match(
|
||||
cond=str(match_cond_var),
|
||||
match_cases=match_cases,
|
||||
default=default, # type: ignore
|
||||
),
|
||||
_var_type=default._var_type, # type: ignore
|
||||
_var_data=ImmutableVarData.merge(
|
||||
_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],
|
||||
default._get_all_var_data(), # type: ignore
|
||||
@ -267,7 +267,8 @@ class Match(MemoizationLeaf):
|
||||
Returns:
|
||||
The import dict.
|
||||
"""
|
||||
return getattr(VarData.merge(self.cond._get_all_var_data()), "imports", {})
|
||||
var_data = VarData.merge(self.cond._get_all_var_data())
|
||||
return var_data.old_school_imports() if var_data else {}
|
||||
|
||||
|
||||
match = Match.create
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Callable, ClassVar, Dict, List, Optional
|
||||
from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple
|
||||
|
||||
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
|
||||
from reflex.components.el.elements.forms import Input
|
||||
from reflex.components.radix.themes.layout.box import Box
|
||||
from reflex.config import environment
|
||||
from reflex.constants import Dirs
|
||||
from reflex.event import (
|
||||
CallableEventSpec,
|
||||
@ -19,10 +19,10 @@ from reflex.event import (
|
||||
call_script,
|
||||
parse_args_spec,
|
||||
)
|
||||
from reflex.ivars.base import ImmutableCallableVar, ImmutableVar
|
||||
from reflex.ivars.sequence import LiteralStringVar
|
||||
from reflex.utils.imports import ImportVar
|
||||
from reflex.vars import ImmutableVarData, Var, VarData
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import CallableVar, LiteralVar, Var
|
||||
from reflex.vars.sequence import LiteralStringVar
|
||||
|
||||
DEFAULT_UPLOAD_ID: str = "default"
|
||||
|
||||
@ -37,8 +37,8 @@ upload_files_context_var_data: VarData = VarData(
|
||||
)
|
||||
|
||||
|
||||
@ImmutableCallableVar
|
||||
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
|
||||
@CallableVar
|
||||
def upload_file(id_: 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
|
||||
@ -58,17 +58,17 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
|
||||
}})
|
||||
"""
|
||||
|
||||
return ImmutableVar(
|
||||
_var_name=var_name,
|
||||
return Var(
|
||||
_js_expr=var_name,
|
||||
_var_type=EventChain,
|
||||
_var_data=ImmutableVarData.merge(
|
||||
_var_data=VarData.merge(
|
||||
upload_files_context_var_data, id_var._get_all_var_data()
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@ImmutableCallableVar
|
||||
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
|
||||
@CallableVar
|
||||
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var:
|
||||
"""Get the list of selected files.
|
||||
|
||||
Args:
|
||||
@ -78,10 +78,10 @@ def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
|
||||
A var referencing the list of selected file paths.
|
||||
"""
|
||||
id_var = LiteralStringVar.create(id_)
|
||||
return ImmutableVar(
|
||||
_var_name=f"(filesById[{str(id_var)}] ? filesById[{str(id_var)}].map((f) => (f.path || f.name)) : [])",
|
||||
return Var(
|
||||
_js_expr=f"(filesById[{str(id_var)}] ? filesById[{str(id_var)}].map((f) => (f.path || f.name)) : [])",
|
||||
_var_type=List[str],
|
||||
_var_data=ImmutableVarData.merge(
|
||||
_var_data=VarData.merge(
|
||||
upload_files_context_var_data, id_var._get_all_var_data()
|
||||
),
|
||||
).guess_type()
|
||||
@ -113,7 +113,7 @@ def cancel_upload(upload_id: str) -> EventSpec:
|
||||
An event spec that cancels the upload when triggered.
|
||||
"""
|
||||
return call_script(
|
||||
f"upload_controllers[{Var.create_safe(upload_id, _var_is_string=True)._var_name_unwrapped}]?.abort()"
|
||||
f"upload_controllers[{str(LiteralVar.create(upload_id))}]?.abort()"
|
||||
)
|
||||
|
||||
|
||||
@ -125,23 +125,20 @@ def get_upload_dir() -> Path:
|
||||
"""
|
||||
Upload.is_used = True
|
||||
|
||||
uploaded_files_dir = Path(
|
||||
os.environ.get("REFLEX_UPLOADED_FILES_DIR", "./uploaded_files")
|
||||
)
|
||||
uploaded_files_dir = environment.REFLEX_UPLOADED_FILES_DIR
|
||||
uploaded_files_dir.mkdir(parents=True, exist_ok=True)
|
||||
return uploaded_files_dir
|
||||
|
||||
|
||||
uploaded_files_url_prefix: Var = Var.create_safe(
|
||||
"${getBackendURL(env.UPLOAD)}",
|
||||
_var_is_string=False,
|
||||
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]:
|
||||
@ -155,12 +152,10 @@ def get_upload_url(file_path: str) -> Var[str]:
|
||||
"""
|
||||
Upload.is_used = True
|
||||
|
||||
return Var.create_safe(
|
||||
f"{uploaded_files_url_prefix}/{file_path}", _var_is_string=True
|
||||
)
|
||||
return uploaded_files_url_prefix + "/" + file_path
|
||||
|
||||
|
||||
def _on_drop_spec(files: Var):
|
||||
def _on_drop_spec(files: Var) -> Tuple[Var[Any]]:
|
||||
"""Args spec for the on_drop event trigger.
|
||||
|
||||
Args:
|
||||
@ -169,7 +164,7 @@ def _on_drop_spec(files: Var):
|
||||
Returns:
|
||||
Signature for on_drop handler including the files to upload.
|
||||
"""
|
||||
return [files]
|
||||
return (files,)
|
||||
|
||||
|
||||
class UploadFilesProvider(Component):
|
||||
@ -182,7 +177,7 @@ class UploadFilesProvider(Component):
|
||||
class Upload(MemoizationLeaf):
|
||||
"""A file upload component."""
|
||||
|
||||
library = "react-dropzone@14.2.3"
|
||||
library = "react-dropzone@14.2.10"
|
||||
|
||||
tag = "ReactDropzone"
|
||||
|
||||
@ -250,9 +245,7 @@ class Upload(MemoizationLeaf):
|
||||
}
|
||||
# The file input to use.
|
||||
upload = Input.create(type="file")
|
||||
upload.special_props = {
|
||||
ImmutableVar(_var_name="...getInputProps()", _var_type=None)
|
||||
}
|
||||
upload.special_props = [Var(_js_expr="{...getInputProps()}", _var_type=None)]
|
||||
|
||||
# The dropzone to use.
|
||||
zone = Box.create(
|
||||
@ -260,9 +253,7 @@ class Upload(MemoizationLeaf):
|
||||
*children,
|
||||
**{k: v for k, v in props.items() if k not in supported_props},
|
||||
)
|
||||
zone.special_props = {
|
||||
ImmutableVar(_var_name="...getRootProps()", _var_type=None)
|
||||
}
|
||||
zone.special_props = [Var(_js_expr="{...getRootProps()}", _var_type=None)]
|
||||
|
||||
# Create the component.
|
||||
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
|
||||
@ -299,7 +290,7 @@ class Upload(MemoizationLeaf):
|
||||
Returns:
|
||||
The updated arg_value tuple when arg is "files", otherwise the original arg_value.
|
||||
"""
|
||||
if arg_value[0]._var_name == "files":
|
||||
if arg_value[0]._js_expr == "files":
|
||||
placeholder = parse_args_spec(_on_drop_spec)[0]
|
||||
return (arg_value[0], placeholder)
|
||||
return arg_value
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user