Merge remote-tracking branch 'upstream/main' into hybrid-properties

This commit is contained in:
Benedikt Bartscher 2024-11-02 00:18:22 +01:00
commit c8527676d4
No known key found for this signature in database
448 changed files with 32810 additions and 34330 deletions

View File

@ -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

View File

@ -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

View File

@ -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
View 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

View 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

View File

@ -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:
@ -51,7 +51,8 @@ jobs:
SCREENSHOT_DIR: /tmp/screenshots
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
run: |
poetry run pytest integration
poetry run playwright install --with-deps
poetry run pytest tests/integration
- uses: actions/upload-artifact@v4
name: Upload failed test screenshots
if: always()

View File

@ -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]
python-version: ['3.10.11', '3.11.4']
env:
@ -165,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

View File

@ -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}

View File

@ -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

View File

@ -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=

View File

@ -3,10 +3,10 @@ fail_fast: true
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.4.10
rev: v0.7.1
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

View File

@ -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

View File

@ -10,7 +10,6 @@
### **✨ Performant, customizable web apps in pure Python. Deploy in seconds. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](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
@ -229,7 +228,7 @@ You can create a multi-page app by adding more pages.
<div align="center">
📑 [Docs](https://reflex.dev/docs/getting-started/introduction) &nbsp; | &nbsp; 🗞️ [Blog](https://reflex.dev/blog) &nbsp; | &nbsp; 📱 [Component Library](https://reflex.dev/docs/library) &nbsp; | &nbsp; 🖼️ [Gallery](https://reflex.dev/docs/gallery) &nbsp; | &nbsp; 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start) &nbsp;
📑 [Docs](https://reflex.dev/docs/getting-started/introduction) &nbsp; | &nbsp; 🗞️ [Blog](https://reflex.dev/blog) &nbsp; | &nbsp; 📱 [Component Library](https://reflex.dev/docs/library) &nbsp; | &nbsp; 🖼️ [Templates](https://reflex.dev/templates/) &nbsp; | &nbsp; 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start) &nbsp;
</div>

View File

@ -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,20 @@ 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":
data = json.loads(filename.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}

View File

@ -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 = {

View File

@ -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 = {

View File

@ -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(

View File

@ -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

View File

@ -10,7 +10,6 @@
### **✨ Performante, anpassbare Web-Apps in purem Python. Bereitstellung in Sekunden. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](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

View File

@ -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

View File

@ -11,7 +11,6 @@ Pynecone की तलाश हैं? आप सही रेपो में
### **✨ प्रदर्शनकारी, अनुकूलित वेब ऐप्स, शुद्ध Python में। सेकंडों में तैनात करें। ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -36,7 +35,7 @@ Reflex के अंदर के कामकाज को जानने क
## ⚙️ इंस्टॉलेशन (Installation)
एक टर्मिनल खोलें और चलाएं (Python 3.8+ की आवश्यकता है):
एक टर्मिनल खोलें और चलाएं (Python 3.9+ की आवश्यकता है):
```bash
pip install reflex

View File

@ -10,7 +10,6 @@
### **✨ App web performanti e personalizzabili in puro Python. Distribuisci in pochi secondi. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](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

View File

@ -11,7 +11,6 @@
### **✨ 即時デプロイが可能な、Pure Python で作ったパフォーマンスと汎用性が高い Web アプリケーション ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -38,7 +37,7 @@ Reflex がどのように動作しているかを知るには、[アーキテク
## ⚙️ インストール
ターミナルを開いて以下のコマンドを実行してください。Python 3.8 以上が必要です。):
ターミナルを開いて以下のコマンドを実行してください。Python 3.9 以上が必要です。):
```bash
pip install reflex

View File

@ -10,7 +10,6 @@
### **✨ 순수 Python으로 고성능 사용자 정의 웹앱을 만들어 보세요. 몇 초만에 배포 가능합니다. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -21,7 +20,7 @@
---
## ⚙️ 설치
터미널을 열고 실행하세요. (Python 3.8+ 필요):
터미널을 열고 실행하세요. (Python 3.9+ 필요):
```bash
pip install reflex

View File

@ -10,7 +10,6 @@
### **✨ برنامه های تحت وب قابل تنظیم، کارآمد تماما پایتونی که در چند ثانیه مستقر(دپلوی) می‎شود. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -35,7 +34,7 @@
## ⚙️ Installation - نصب و راه اندازی
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.8+):
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.9+):
```bash
pip install reflex

View File

@ -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

View File

@ -11,7 +11,6 @@
### **✨ Saf Python'da performanslı, özelleştirilebilir web uygulamaları. Saniyeler içinde dağıtın. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](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
View 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. ✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](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.
&nbsp;
<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>
&nbsp;
Đâ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``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``image_url`.
Có cũng những biến boolean `processing``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) &nbsp; | &nbsp; 🗞️ [Blog](https://reflex.dev/blog) &nbsp; | &nbsp; 📱 [Component Library](https://reflex.dev/docs/library) &nbsp; | &nbsp; 🖼️ [Gallery](https://reflex.dev/docs/gallery) &nbsp; | &nbsp; 🛸 [Deployment](https://reflex.dev/docs/hosting/deploy-quick-start) &nbsp;
</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).

View File

@ -10,7 +10,6 @@
### **✨ 使用 Python 创建高效且可自定义的网页应用程序,几秒钟内即可部署.✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -35,7 +34,7 @@ Reflex 是一个使用纯Python构建全栈web应用的库。
## ⚙️ 安装
打开一个终端并且运行(要求Python3.8+):
打开一个终端并且运行(要求Python3.9+):
```bash
pip install reflex

View File

@ -11,7 +11,6 @@
**✨ 使用 Python 建立高效且可自訂的網頁應用程式,幾秒鐘內即可部署。✨**
[![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
![tests](https://github.com/pynecone-io/pynecone/actions/workflows/integration.yml/badge.svg)
![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
[![Documentaiton](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
[![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
@ -37,7 +36,7 @@ Reflex 是一個可以用純 Python 構建全端網頁應用程式的函式庫
## ⚙️ 安裝
開啟一個終端機並且執行 (需要 Python 3.8+):
開啟一個終端機並且執行 (需要 Python 3.9+):
```bash
pip install reflex

View File

@ -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"

View File

@ -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"

View File

@ -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)

2095
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "reflex"
version = "0.6.0a1"
version = "0.6.5dev1"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [
@ -26,8 +26,7 @@ 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"
@ -54,34 +53,32 @@ 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"
dill = ">=0.3.8"
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.1"
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"
[tool.poetry.scripts]
reflex = "reflex.reflex:cli"
@ -93,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]
@ -104,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"

View File

@ -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"]

View File

@ -1,12 +1,15 @@
{% extends "web/pages/base_page.js.jinja2" %}
{% block early_imports %}
import '/styles/styles.css'
import '$/styles/styles.css'
{% endblock %}
{% block declaration %}
import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js";
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>

View File

@ -36,14 +36,10 @@
{# component: component dictionary #}
{% macro render_tag(component) %}
<{{component.name}} {{- render_props(component.props) }}>
{%- if component.args is not none -%}
{{- render_arg_content(component) }}
{%- else -%}
{{ component.contents }}
{% for child in component.children %}
{{ render(child) }}
{% endfor %}
{%- endif -%}
{{ component.contents }}
{% for child in component.children %}
{{ render(child) }}
{% endfor %}
</{{component.name}}>
{%- endmacro %}
@ -85,10 +81,10 @@
{% macro render_match_tag(component) %}
{
(() => {
switch (JSON.stringify({{ component.cond._var_name }})) {
switch (JSON.stringify({{ component.cond._js_expr }})) {
{% for case in component.match_cases %}
{% for condition in case[:-1] %}
case JSON.stringify({{ condition._var_name }}):
case JSON.stringify({{ condition._js_expr }}):
{% endfor %}
return {{ case[-1] }};
break;

View File

@ -1,5 +1,5 @@
import { createContext, useContext, useMemo, useReducer, useState } from "react"
import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "/utils/state.js"
import { applyDelta, Event, hydrateClientStorage, useEventLoop, refs } from "$/utils/state.js"
{% if initial_state %}
export const initialState = {{ initial_state|json_dumps }}
@ -59,6 +59,8 @@ export const initialEvents = () => [
{% else %}
export const state_name = undefined
export const exception_state_name = undefined
export const onLoadInternalEvent = () => []
export const initialEvents = () => []

View File

@ -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>
);
}

View File

@ -4,8 +4,8 @@ import {
ColorModeContext,
defaultColorMode,
isDevMode,
lastCompiledTimeStamp
} from "/utils/context.js";
lastCompiledTimeStamp,
} from "$/utils/context.js";
export default function RadixThemesColorModeProvider({ children }) {
const { theme, resolvedTheme, setTheme } = useTheme();
@ -37,7 +37,7 @@ export default function RadixThemesColorModeProvider({ children }) {
const allowedModes = ["light", "dark", "system"];
if (!allowedModes.includes(mode)) {
console.error(
`Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`,
`Invalid color mode "${mode}". Defaulting to "${defaultColorMode}".`
);
mode = defaultColorMode;
}

View File

@ -0,0 +1,34 @@
import { useEffect, useState } from "react"
import { codeToHtml} from "shiki"
/**
* Code component that uses Shiki to convert code to HTML and render it.
*
* @param code - The code to be highlighted.
* @param theme - The theme to be used for highlighting.
* @param language - The language of the code.
* @param transformers - The transformers to be applied to the code.
* @param decorations - The decorations to be applied to the code.
* @param divProps - Additional properties to be passed to the div element.
* @returns The rendered code block.
*/
export function Code ({code, theme, language, transformers, decorations, ...divProps}) {
const [codeResult, setCodeResult] = useState("")
useEffect(() => {
async function fetchCode() {
const result = await codeToHtml(code, {
lang: language,
theme,
transformers,
decorations
});
setCodeResult(result);
}
fetchCode();
}, [code, language, theme, transformers, decorations]
)
return (
<div dangerouslySetInnerHTML={{__html: codeResult}} {...divProps} ></div>
)
}

View File

@ -2,7 +2,8 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"$/*": ["*"],
"@/*": ["public/*"]
}
}
}
}

View File

@ -2,7 +2,7 @@
import axios from "axios";
import io from "socket.io-client";
import JSON5 from "json5";
import env from "/env.json";
import env from "$/env.json";
import Cookies from "universal-cookie";
import { useEffect, useRef, useState } from "react";
import Router, { useRouter } from "next/router";
@ -12,9 +12,9 @@ import {
onLoadInternalEvent,
state_name,
exception_state_name,
} from "utils/context.js";
import debounce from "/utils/helpers/debounce";
import throttle from "/utils/helpers/throttle";
} from "$/utils/context.js";
import debounce from "$/utils/helpers/debounce";
import throttle from "$/utils/helpers/throttle";
// Endpoint URLs.
const EVENTURL = env.EVENT;
@ -117,8 +117,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 +129,21 @@ export const applyDelta = (state, delta) => {
return { ...state, ...delta };
};
/**
* Evaluate a dynamic component.
* @param component The component to evaluate.
* @returns The evaluated component.
*/
export const evalReactComponent = async (component) => {
if (!window.React && window.__reflex) {
window.React = window.__reflex.react;
}
const encodedJs = encodeURIComponent(component);
const dataUri = "data:text/javascript;charset=utf-8," + encodedJs;
const module = await eval(`import(dataUri)`);
return module.default;
};
/**
* Only Queue and process events when websocket connection exists.
* @param event The event to queue.
@ -141,7 +156,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 +223,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 +267,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 +308,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 +414,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 +433,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 +542,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 +597,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 +641,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 +680,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 +706,7 @@ export const useEventLoop = (
debounce(
combined_name,
() => queueEvents(events, socket),
event_actions.debounce,
event_actions.debounce
);
} else {
queueEvents(events, socket);
@ -696,30 +731,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 +821,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 +844,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);
};
/**

View File

@ -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",
}

View File

@ -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

View File

@ -6,9 +6,11 @@ import asyncio
import concurrent.futures
import contextlib
import copy
import dataclasses
import functools
import inspect
import io
import json
import multiprocessing
import os
import platform
@ -17,6 +19,7 @@ import traceback
from datetime import datetime
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
AsyncIterator,
Callable,
@ -43,10 +46,12 @@ from starlette_admin.contrib.sqla.view import ModelView
from reflex import constants
from reflex.admin import AdminDash
from reflex.app_mixins import AppMixin, LifespanMixin, MiddlewareMixin
from reflex.base import Base
from reflex.compiler import compiler
from reflex.compiler import utils as compiler_utils
from reflex.compiler.compiler import ExecutorSafeFunctions
from reflex.compiler.compiler import (
ExecutorSafeFunctions,
compile_theme,
)
from reflex.components.base.app_wrap import AppWrap
from reflex.components.base.error_boundary import ErrorBoundary
from reflex.components.base.fragment import Fragment
@ -63,8 +68,15 @@ 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.event import Event, EventHandler, EventSpec, window_alert
from reflex.config import environment, get_config
from reflex.event import (
Event,
EventHandler,
EventSpec,
EventType,
IndividualEventType,
window_alert,
)
from reflex.model import Model, get_db_status
from reflex.page import (
DECORATED_PAGES,
@ -87,6 +99,9 @@ from reflex.utils import codespaces, console, exceptions, format, prerequisites,
from reflex.utils.exec import is_prod_mode, is_testing_env, should_skip_compile
from reflex.utils.imports import ImportVar
if TYPE_CHECKING:
from reflex.vars import Var
# Define custom types.
ComponentCallable = Callable[[], Component]
Reducer = Callable[[Event], Coroutine[Any, Any, StateUpdate]]
@ -169,7 +184,23 @@ class OverlayFragment(Fragment):
pass
class App(MiddlewareMixin, LifespanMixin, Base):
@dataclasses.dataclass(
frozen=True,
)
class UnevaluatedPage:
"""An uncompiled page."""
component: Union[Component, ComponentCallable]
route: str
title: Union[Var, str, None]
description: Union[Var, str, None]
image: str
on_load: Union[EventType[[]], None]
meta: List[Dict[str, str]]
@dataclasses.dataclass()
class App(MiddlewareMixin, LifespanMixin):
"""The main Reflex app that encapsulates the backend and frontend.
Every Reflex app needs an app defined in its main module.
@ -191,24 +222,26 @@ class App(MiddlewareMixin, LifespanMixin, Base):
"""
# The global [theme](https://reflex.dev/docs/styling/theming/#theme) for the entire app.
theme: Optional[Component] = themes.theme(accent_color="blue")
theme: Optional[Component] = dataclasses.field(
default_factory=lambda: themes.theme(accent_color="blue")
)
# The [global style](https://reflex.dev/docs/styling/overview/#global-styles}) for the app.
style: ComponentStyle = {}
style: ComponentStyle = dataclasses.field(default_factory=dict)
# A list of URLs to [stylesheets](https://reflex.dev/docs/styling/custom-stylesheets/) to include in the app.
stylesheets: List[str] = []
stylesheets: List[str] = dataclasses.field(default_factory=list)
# A component that is present on every page (defaults to the Connection Error banner).
overlay_component: Optional[Union[Component, ComponentCallable]] = (
default_overlay_component()
dataclasses.field(default_factory=default_overlay_component)
)
# Error boundary component to wrap the app with.
error_boundary: Optional[ComponentCallable] = default_error_boundary
# Components to add to the head of every page.
head_components: List[Component] = []
head_components: List[Component] = dataclasses.field(default_factory=list)
# The Socket.IO AsyncServer instance.
sio: Optional[AsyncServer] = None
@ -219,8 +252,13 @@ class App(MiddlewareMixin, LifespanMixin, Base):
# Attributes to add to the html root tag of every page.
html_custom_attrs: Optional[Dict[str, str]] = None
# A map from a route to an unevaluated page. PRIVATE.
unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
default_factory=dict
)
# A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
pages: Dict[str, Component] = {}
pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
# The backend API object. PRIVATE.
api: FastAPI = None # type: ignore
@ -232,7 +270,9 @@ class App(MiddlewareMixin, LifespanMixin, Base):
_state_manager: Optional[StateManager] = None
# Mapping from a route to event handlers to trigger when the page loads. PRIVATE.
load_events: Dict[str, List[Union[EventHandler, EventSpec]]] = {}
load_events: Dict[str, List[IndividualEventType[[]]]] = dataclasses.field(
default_factory=dict
)
# Admin dashboard to view and manage the database. PRIVATE.
admin_dash: Optional[AdminDash] = None
@ -241,7 +281,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
event_namespace: Optional[EventNamespace] = None
# Background tasks that are currently running. PRIVATE.
background_tasks: Set[asyncio.Task] = set()
background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
# Frontend Error Handler Function
frontend_exception_handler: Callable[[Exception], None] = (
@ -253,23 +293,14 @@ class App(MiddlewareMixin, LifespanMixin, Base):
[Exception], Union[EventSpec, List[EventSpec], None]
] = default_backend_exception_handler
def __init__(self, **kwargs):
def __post_init__(self):
"""Initialize the app.
Args:
**kwargs: Kwargs to initialize the app with.
Raises:
ValueError: If the event namespace is not provided in the config.
Also, if there are multiple client subclasses of rx.BaseState(Subclasses of rx.BaseState should consist
of the DefaultState and the client app state).
"""
if "connect_error_component" in kwargs:
raise ValueError(
"`connect_error_component` is deprecated, use `overlay_component` instead"
)
super().__init__(**kwargs)
# Special case to allow test cases have multiple subclasses of rx.BaseState.
if not is_testing_env() and BaseState.__subclasses__() != [State]:
# Only rx.State is allowed as Base State subclass.
@ -380,8 +411,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
def _add_optional_endpoints(self):
"""Add optional api endpoints (_upload)."""
# To upload files.
if Upload.is_used:
# To upload files.
self.api.post(str(constants.Endpoint.UPLOAD))(upload(self))
# To access uploaded files.
@ -430,36 +461,21 @@ 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,
component: Component | ComponentCallable,
route: str | None = None,
title: str | None = None,
description: str | None = None,
title: str | Var | None = None,
description: str | Var | None = None,
image: str = constants.DefaultPage.IMAGE,
on_load: (
EventHandler | EventSpec | list[EventHandler | EventSpec] | None
) = None,
on_load: EventType[[]] | None = None,
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
):
"""Add a page to the app.
@ -481,9 +497,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:
@ -492,13 +507,13 @@ class App(MiddlewareMixin, LifespanMixin, Base):
# Check if the route given is valid
verify_route_validity(route)
if route in self.pages and os.getenv(constants.RELOAD_CONFIG):
if route in self.unevaluated_pages and os.getenv(constants.RELOAD_CONFIG):
# when the app is reloaded(typically for app harness tests), we should maintain
# the latest render function of a route.This applies typically to decorated pages
# since they are only added when app._compile is called.
self.pages.pop(route)
self.unevaluated_pages.pop(route)
if route in self.pages:
if route in self.unevaluated_pages:
route_name = (
f"`{route}` or `/`"
if route == constants.PageNames.INDEX_ROUTE
@ -514,59 +529,39 @@ class App(MiddlewareMixin, LifespanMixin, Base):
state = self.state if self.state else State
state.setup_dynamic_args(get_route_args(route))
# Generate the component if it is a callable.
component = self._generate_component(component)
if on_load:
self.load_events[route] = (
on_load if isinstance(on_load, list) else [on_load]
)
# unpack components that return tuples in an rx.fragment.
if isinstance(component, tuple):
component = Fragment.create(*component)
# Ensure state is enabled if this page uses state.
if self.state is None:
if on_load or component._has_stateful_event_triggers():
self._enable_state()
else:
for var in component._get_vars(include_children=True):
var_data = var._get_all_var_data()
if not var_data:
continue
if not var_data.state:
continue
self._enable_state()
break
component = OverlayFragment.create(component)
meta_args = {
"title": (
title
if title is not None
else format.make_default_page_title(get_config().app_name, route)
),
"image": image,
"meta": meta,
}
if description is not None:
meta_args["description"] = description
# Add meta information to the component.
compiler_utils.add_meta(
component,
**meta_args,
self.unevaluated_pages[route] = UnevaluatedPage(
component=component,
route=route,
title=title,
description=description,
image=image,
on_load=on_load,
meta=meta,
)
def _compile_page(self, route: str):
"""Compile a page.
Args:
route: The route of the page to compile.
"""
component, enable_state = compiler.compile_unevaluated_page(
route, self.unevaluated_pages[route], self.state, self.style, self.theme
)
if enable_state:
self._enable_state()
# Add the page.
self._check_routes_conflict(route)
self.pages[route] = component
# Add the load events.
if on_load:
if not isinstance(on_load, list):
on_load = [on_load]
self.load_events[route] = on_load
def get_load_events(self, route: str) -> list[EventHandler | EventSpec]:
def get_load_events(self, route: str) -> list[IndividualEventType[[]]]:
"""Get the load events for a route.
Args:
@ -625,9 +620,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
title: str = constants.Page404.TITLE,
image: str = constants.Page404.IMAGE,
description: str = constants.Page404.DESCRIPTION,
on_load: (
EventHandler | EventSpec | list[EventHandler | EventSpec] | None
) = None,
on_load: EventType[[]] | None = None,
meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
):
"""Define a custom 404 page for any url having no match.
@ -692,7 +685,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
for i, tags in imports.items()
if i not in constants.PackageJson.DEPENDENCIES
and i not in constants.PackageJson.DEV_DEPENDENCIES
and not any(i.startswith(prefix) for prefix in ["/", ".", "next/"])
and not any(i.startswith(prefix) for prefix in ["/", "$/", ".", "next/"])
and i != ""
and any(tag.install for tag in tags)
}
@ -823,7 +816,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:
@ -840,13 +833,33 @@ class App(MiddlewareMixin, LifespanMixin, Base):
"""
from reflex.utils.exceptions import ReflexRuntimeError
self.pages = {}
def get_compilation_time() -> str:
return str(datetime.now().time()).split(".")[0]
# Render a default 404 page if the user didn't supply one
if constants.Page404.SLUG not in self.pages:
if constants.Page404.SLUG not in self.unevaluated_pages:
self.add_custom_404_page()
# Fix up the style.
self.style = evaluate_style_namespaces(self.style)
# Add the app wrappers.
app_wrappers: Dict[tuple[int, str], Component] = {
# Default app wrap component renders {children}
(0, "AppWrap"): AppWrap.create()
}
if self.theme is not None:
# If a theme component was provided, wrap the app with it
app_wrappers[(20, "Theme")] = self.theme
# Fix #2992 by removing the top-level appearance prop
self.theme.appearance = None
for route in self.unevaluated_pages:
self._compile_page(route)
# Add the optional endpoints (_upload)
self._add_optional_endpoints()
@ -881,28 +894,15 @@ class App(MiddlewareMixin, LifespanMixin, Base):
# Store the compile results.
compile_results = []
# Add the app wrappers.
app_wrappers: Dict[tuple[int, str], Component] = {
# Default app wrap component renders {children}
(0, "AppWrap"): AppWrap.create()
}
if self.theme is not None:
# If a theme component was provided, wrap the app with it
app_wrappers[(20, "Theme")] = self.theme
progress.advance(task)
# Fix up the style.
self.style = evaluate_style_namespaces(self.style)
# Track imports and custom components found.
all_imports = {}
custom_components = set()
for _route, component in self.pages.items():
# Merge the component style with the app style.
component._add_style_recursive(self.style, self.theme)
# This has to happen before compiling stateful components as that
# prevents recursive functions from reaching all components.
for component in self.pages.values():
# Add component._get_all_imports() to all_imports.
all_imports.update(component._get_all_imports())
@ -912,8 +912,6 @@ class App(MiddlewareMixin, LifespanMixin, Base):
# Add the custom components from the page to the set.
custom_components |= component._get_all_custom_components()
progress.advance(task)
# Perform auto-memoization of stateful components.
(
stateful_components_path,
@ -931,6 +929,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
)
compile_results.append((stateful_components_path, stateful_components_code))
progress.advance(task)
# Compile the root document before fork.
compile_results.append(
compiler.compile_document_root(
@ -940,77 +940,50 @@ class App(MiddlewareMixin, LifespanMixin, Base):
)
)
# Compile the contexts before fork.
compile_results.append(
compiler.compile_contexts(self.state, self.theme),
)
# Fix #2992 by removing the top-level appearance prop
if self.theme is not None:
self.theme.appearance = None
app_root = self._app_root(app_wrappers=app_wrappers)
progress.advance(task)
# Prepopulate the global ExecutorSafeFunctions class with input data required by the compile functions.
# This is required for multiprocessing to work, in presence of non-picklable inputs.
for route, component in zip(self.pages, page_components):
ExecutorSafeFunctions.COMPILE_PAGE_ARGS_BY_ROUTE[route] = (
route,
component,
self.state,
)
ExecutorSafeFunctions.COMPILE_APP_APP_ROOT = app_root
ExecutorSafeFunctions.CUSTOM_COMPONENTS = custom_components
ExecutorSafeFunctions.STYLE = self.style
# Use a forking process pool, if possible. Much faster, especially for large sites.
# Fallback to ThreadPoolExecutor as something that will always work.
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
)
for route, component in zip(self.pages, page_components):
ExecutorSafeFunctions.COMPONENTS[route] = component
ExecutorSafeFunctions.STATE = self.state
with executor:
result_futures = []
custom_components_future = None
def _mark_complete(_=None):
progress.advance(task)
def _submit_work(fn, *args, **kwargs):
f = executor.submit(fn, *args, **kwargs)
f.add_done_callback(_mark_complete)
# f = executor.apipe(fn, *args, **kwargs)
result_futures.append(f)
# Compile all page components.
# Compile the pre-compiled pages.
for route in self.pages:
_submit_work(ExecutorSafeFunctions.compile_page, route)
# Compile the app wrapper.
_submit_work(ExecutorSafeFunctions.compile_app)
# Compile the custom components.
custom_components_future = executor.submit(
ExecutorSafeFunctions.compile_custom_components,
)
custom_components_future.add_done_callback(_mark_complete)
_submit_work(
ExecutorSafeFunctions.compile_page,
route,
)
# Compile the root stylesheet with base styles.
_submit_work(compiler.compile_root_stylesheet, self.stylesheets)
# Compile the theme.
_submit_work(ExecutorSafeFunctions.compile_theme)
_submit_work(compile_theme, self.style)
# Compile the Tailwind config.
if config.tailwind is not None:
@ -1024,21 +997,34 @@ class App(MiddlewareMixin, LifespanMixin, Base):
# Wait for all compilation tasks to complete.
for future in concurrent.futures.as_completed(result_futures):
compile_results.append(future.result())
progress.advance(task)
# Special case for custom_components, since we need the compiled imports
# to install proper frontend packages.
(
*custom_components_result,
custom_components_imports,
) = custom_components_future.result()
compile_results.append(custom_components_result)
all_imports.update(custom_components_imports)
app_root = self._app_root(app_wrappers=app_wrappers)
# Get imports from AppWrap components.
all_imports.update(app_root._get_all_imports())
progress.advance(task)
# Compile the contexts.
compile_results.append(
compiler.compile_contexts(self.state, self.theme),
)
progress.advance(task)
# Compile the app root.
compile_results.append(
compiler.compile_app(app_root),
)
progress.advance(task)
# Compile custom components.
*custom_components_result, custom_components_imports = (
compiler.compile_components(custom_components)
)
compile_results.append(custom_components_result)
all_imports.update(custom_components_imports)
progress.advance(task)
progress.stop()
@ -1405,7 +1391,7 @@ def upload(app: App):
if isinstance(func, EventHandler):
if func.is_background:
raise UploadTypeError(
f"@rx.background is not supported for upload handler `{handler}`.",
f"@rx.event(background=True) is not supported for upload handler `{handler}`.",
)
func = func.fn
if isinstance(func, functools.partial):
@ -1527,20 +1513,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 = {

View File

@ -4,21 +4,27 @@ from __future__ import annotations
import asyncio
import contextlib
import dataclasses
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
@dataclasses.dataclass
class LifespanMixin(AppMixin):
"""A Mixin that allow tasks to run during the whole app lifespan."""
# Lifespan tasks that are planned to run.
lifespan_tasks: Set[Union[asyncio.Task, Callable]] = set()
lifespan_tasks: Set[Union[asyncio.Task, Callable]] = dataclasses.field(
default_factory=set
)
@contextlib.asynccontextmanager
async def _run_lifespan_tasks(self, app: FastAPI):
@ -26,6 +32,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 +42,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 +62,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

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import asyncio
import dataclasses
from typing import List
from reflex.event import Event
@ -12,11 +13,12 @@ from reflex.state import BaseState, StateUpdate
from .mixin import AppMixin
@dataclasses.dataclass
class MiddlewareMixin(AppMixin):
"""Middleware Mixin that allow to add middleware to the app."""
# Middleware to add to the app. Users should use `add_middleware`. PRIVATE.
middleware: List[Middleware] = []
middleware: List[Middleware] = dataclasses.field(default_factory=list)
def _init_mixin(self):
self.middleware.append(HydrateMiddleware())

View File

@ -1,9 +1,10 @@
"""Default mixin for all app mixins."""
from reflex.base import Base
import dataclasses
class AppMixin(Base):
@dataclasses.dataclass
class AppMixin:
"""Define the base class for all app mixins."""
def _init_mixin(self):

View File

@ -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)

View File

@ -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

View File

@ -2,13 +2,13 @@
from __future__ import annotations
import os
from datetime import datetime
from pathlib import Path
from typing import Dict, Iterable, Optional, Type, Union
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union
from reflex import constants
from reflex.compiler import templates, utils
from reflex.components.base.fragment import Fragment
from reflex.components.component import (
BaseComponent,
Component,
@ -16,13 +16,13 @@ from reflex.components.component import (
CustomComponent,
StatefulComponent,
)
from reflex.config import get_config
from reflex.ivars.base import ImmutableVar, 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.base import LiteralVar, Var
def _compile_document_root(root: Component) -> str:
@ -40,6 +40,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.
@ -49,10 +63,20 @@ 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(),
)
@ -103,8 +127,8 @@ def _compile_contexts(state: Optional[Type[BaseState]], theme: Component | None)
def _compile_page(
component: Component,
state: Type[BaseState],
component: BaseComponent,
state: Type[BaseState] | None,
) -> str:
"""Compile the component given the app state.
@ -119,7 +143,7 @@ def _compile_page(
imports = utils.compile_imports(imports)
# Compile the code to render the component.
kwargs = {"state_name": state.get_name()} if state else {}
kwargs = {"state_name": state.get_name()} if state is not None else {}
return templates.PAGE.render(
imports=imports,
@ -171,7 +195,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."
)
@ -205,7 +229,7 @@ def _compile_components(
"""
imports = {
"react": [ImportVar(tag="memo")],
f"/{constants.Dirs.STATE_PATH}": [ImportVar(tag="E"), ImportVar(tag="isTrue")],
f"$/{constants.Dirs.STATE_PATH}": [ImportVar(tag="E"), ImportVar(tag="isTrue")],
}
component_renders = []
@ -292,7 +316,7 @@ def _compile_stateful_components(
# Don't import from the file that we're about to create.
all_imports = utils.merge_imports(*all_import_dicts)
all_imports.pop(
f"/{constants.Dirs.UTILS}/{constants.PageNames.STATEFUL_COMPONENTS}", None
f"$/{constants.Dirs.UTILS}/{constants.PageNames.STATEFUL_COMPONENTS}", None
)
return templates.STATEFUL_COMPONENTS.render(
@ -320,7 +344,7 @@ def _compile_tailwind(
def compile_document_root(
head_components: list[Component],
html_lang: Optional[str] = None,
html_custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
html_custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
) -> tuple[str, str]:
"""Compile the document root.
@ -401,7 +425,7 @@ def compile_contexts(
def compile_page(
path: str, component: Component, state: Type[BaseState]
path: str, component: BaseComponent, state: Type[BaseState] | None
) -> tuple[str, str]:
"""Compile a single page.
@ -503,7 +527,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
@ -511,6 +535,81 @@ def purge_web_pages_dir():
utils.empty_dir(get_web_dir() / constants.Dirs.PAGES, keep_files=["_app.js"])
if TYPE_CHECKING:
from reflex.app import UnevaluatedPage
def compile_unevaluated_page(
route: str,
page: UnevaluatedPage,
state: Type[BaseState] | None = None,
style: ComponentStyle | None = None,
theme: Component | None = None,
) -> Tuple[Component, bool]:
"""Compiles an uncompiled page into a component and adds meta information.
Args:
route: The route of the page.
page: The uncompiled page object.
state: The state of the app.
style: The style of the page.
theme: The theme of the page.
Returns:
The compiled component and whether state should be enabled.
"""
# Generate the component if it is a callable.
component = page.component
component = component if isinstance(component, Component) else component()
# unpack components that return tuples in an rx.fragment.
if isinstance(component, tuple):
component = Fragment.create(*component)
component._add_style_recursive(style or {}, theme)
enable_state = False
# Ensure state is enabled if this page uses state.
if state is None:
if page.on_load or component._has_stateful_event_triggers():
enable_state = True
else:
for var in component._get_vars(include_children=True):
var_data = var._get_all_var_data()
if not var_data:
continue
if not var_data.state:
continue
enable_state = True
break
from reflex.app import OverlayFragment
from reflex.utils.format import make_default_page_title
component = OverlayFragment.create(component)
meta_args = {
"title": (
page.title
if page.title is not None
else make_default_page_title(get_config().app_name, route)
),
"image": page.image,
"meta": page.meta,
}
if page.description is not None:
meta_args["description"] = page.description
# Add meta information to the component.
utils.add_meta(
component,
**meta_args,
)
return component, enable_state
class ExecutorSafeFunctions:
"""Helper class to allow parallelisation of parts of the compilation process.
@ -536,13 +635,12 @@ class ExecutorSafeFunctions:
"""
COMPILE_PAGE_ARGS_BY_ROUTE = {}
COMPILE_APP_APP_ROOT: Component | None = None
CUSTOM_COMPONENTS: set[CustomComponent] | None = None
STYLE: ComponentStyle | None = None
COMPONENTS: Dict[str, BaseComponent] = {}
UNCOMPILED_PAGES: Dict[str, UnevaluatedPage] = {}
STATE: Optional[Type[BaseState]] = None
@classmethod
def compile_page(cls, route: str):
def compile_page(cls, route: str) -> tuple[str, str]:
"""Compile a page.
Args:
@ -551,46 +649,45 @@ class ExecutorSafeFunctions:
Returns:
The path and code of the compiled page.
"""
return compile_page(*cls.COMPILE_PAGE_ARGS_BY_ROUTE[route])
return compile_page(route, cls.COMPONENTS[route], cls.STATE)
@classmethod
def compile_app(cls):
"""Compile the app.
def compile_unevaluated_page(
cls,
route: str,
style: ComponentStyle,
theme: Component | None,
) -> tuple[str, Component, tuple[str, str]]:
"""Compile an unevaluated page.
Args:
route: The route of the page to compile.
style: The style of the page.
theme: The theme of the page.
Returns:
The path and code of the compiled app.
Raises:
ValueError: If the app root is not set.
The route, compiled component, and compiled page.
"""
if cls.COMPILE_APP_APP_ROOT is None:
raise ValueError("COMPILE_APP_APP_ROOT should be set")
return compile_app(cls.COMPILE_APP_APP_ROOT)
component, enable_state = compile_unevaluated_page(
route, cls.UNCOMPILED_PAGES[route]
)
component = component if isinstance(component, Component) else component()
component._add_style_recursive(style, theme)
return route, component, compile_page(route, component, cls.STATE)
@classmethod
def compile_custom_components(cls):
"""Compile the custom components.
Returns:
The path and code of the compiled custom components.
Raises:
ValueError: If the custom components are not set.
"""
if cls.CUSTOM_COMPONENTS is None:
raise ValueError("CUSTOM_COMPONENTS should be set")
return compile_components(cls.CUSTOM_COMPONENTS)
@classmethod
def compile_theme(cls):
def compile_theme(cls, style: ComponentStyle | None) -> tuple[str, str]:
"""Compile the theme.
Args:
style: The style to compile.
Returns:
The path and code of the compiled theme.
Raises:
ValueError: If the style is not set.
"""
if cls.STYLE is None:
if style is None:
raise ValueError("STYLE should be set")
return compile_theme(cls.STYLE)
return compile_theme(style)

View File

@ -2,13 +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.ivars.base import ImmutableVar
from reflex.utils.prerequisites import get_web_dir
from reflex.vars.base import Var
try:
from pydantic.v1.fields import ModelField
@ -29,7 +28,8 @@ 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
@ -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}), "")
@ -79,6 +83,12 @@ def validate_imports(import_dict: ParsedImportDict):
f"{_import.tag}/{_import.alias}" if _import.alias else _import.tag
)
if import_name in used_tags:
already_imported = used_tags[import_name]
if (already_imported[0] == "$" and already_imported[1:] == lib) or (
lib[0] == "$" and lib[1:] == already_imported
):
used_tags[import_name] = lib if lib[0] == "$" else already_imported
continue
raise ValueError(
f"Can not compile, the tag {import_name} is used multiple time from {lib} and {used_tags[import_name]}"
)
@ -92,6 +102,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 +119,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
@ -155,7 +170,7 @@ def compile_state(state: Type[BaseState]) -> dict:
initial_state = state(_reflex_internal_init=True).dict(
initial=True, include_computed=False
)
return format.format_state(initial_state)
return initial_state
def _compile_client_storage_field(
@ -268,7 +283,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 (
@ -286,7 +301,7 @@ def compile_custom_component(
def create_document_root(
head_components: list[Component] | None = None,
html_lang: Optional[str] = None,
html_custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
html_custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
) -> Component:
"""Create the document root.
@ -429,11 +444,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))
@ -448,16 +463,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):

View File

@ -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"))

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class AppWrap(Fragment):
@overload
@ -21,52 +21,22 @@ class AppWrap(Fragment):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -4,11 +4,11 @@ from __future__ import annotations
from typing import Any, Iterator
from reflex.components.component import Component
from reflex.components.component import Component, LiteralComponentVar
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.utils.imports import ParsedImportDict
from reflex.vars import BooleanVar, ObjectVar, Var
class Bare(Component):
@ -26,18 +26,88 @@ class Bare(Component):
Returns:
The component.
"""
if isinstance(contents, ImmutableVar):
if isinstance(contents, Var):
return cls(contents=contents)
else:
contents = str(contents) if contents is not None else ""
return cls(contents=contents) # type: ignore
def _get_all_hooks_internal(self) -> dict[str, None]:
"""Include the hooks for the component.
Returns:
The hooks for the component.
"""
hooks = super()._get_all_hooks_internal()
if isinstance(self.contents, LiteralComponentVar):
hooks |= self.contents._var_value._get_all_hooks_internal()
return hooks
def _get_all_hooks(self) -> dict[str, None]:
"""Include the hooks for the component.
Returns:
The hooks for the component.
"""
hooks = super()._get_all_hooks()
if isinstance(self.contents, LiteralComponentVar):
hooks |= self.contents._var_value._get_all_hooks()
return hooks
def _get_all_imports(self) -> ParsedImportDict:
"""Include the imports for the component.
Returns:
The imports for the component.
"""
imports = super()._get_all_imports()
if isinstance(self.contents, LiteralComponentVar):
var_data = self.contents._get_all_var_data()
if var_data:
imports |= {k: list(v) for k, v in var_data.imports}
return imports
def _get_all_dynamic_imports(self) -> set[str]:
"""Get dynamic imports for the component.
Returns:
The dynamic imports.
"""
dynamic_imports = super()._get_all_dynamic_imports()
if isinstance(self.contents, LiteralComponentVar):
dynamic_imports |= self.contents._var_value._get_all_dynamic_imports()
return dynamic_imports
def _get_all_custom_code(self) -> set[str]:
"""Get custom code for the component.
Returns:
The custom code.
"""
custom_code = super()._get_all_custom_code()
if isinstance(self.contents, LiteralComponentVar):
custom_code |= self.contents._var_value._get_all_custom_code()
return custom_code
def _get_all_refs(self) -> set[str]:
"""Get the refs for the children of the component.
Returns:
The refs for the children.
"""
refs = super()._get_all_refs()
if isinstance(self.contents, LiteralComponentVar):
refs |= self.contents._var_value._get_all_refs()
return refs
def _render(self) -> Tag:
if isinstance(self.contents, ImmutableVar):
if isinstance(self.contents, Var):
if isinstance(self.contents, (BooleanVar, ObjectVar)):
return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
return Tagless(contents=f"{{{str(self.contents)}}}")
return Tagless(contents=str(self.contents))
def _get_vars(self, include_children: bool = False) -> Iterator[ImmutableVar]:
def _get_vars(self, include_children: bool = False) -> Iterator[Var]:
"""Walk all Vars used in this component.
Args:

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class Body(Component):
@overload
@ -21,52 +21,22 @@ class Body(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class NextDocumentLib(Component):
@overload
@ -21,52 +21,22 @@ class NextDocumentLib(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -98,52 +68,22 @@ class Html(NextDocumentLib):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -174,52 +114,22 @@ class DocumentHead(NextDocumentLib):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -250,52 +160,22 @@ class Main(NextDocumentLib):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -326,52 +206,22 @@ class NextScript(NextDocumentLib):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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 | ImmutableVar]:
"""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

View File

@ -3,85 +3,54 @@
# ------------------- 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.ivars.base import ImmutableVar
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 | ImmutableVar]: ...
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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_error: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
on_error: Fired when the boundary catches an error.
Fallback_component: Rendered instead of the children when an error is caught.
style: The style of the component.
key: A unique key for the component.
@ -92,7 +61,7 @@ class ErrorBoundary(Component):
**props: The props of the component.
Returns:
The component.
The ErrorBoundary component.
"""
...

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class Fragment(Component):
@overload
@ -21,52 +21,22 @@ class Fragment(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class NextHeadLib(Component):
@overload
@ -21,52 +21,22 @@ class NextHeadLib(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -97,52 +67,22 @@ class Head(NextHeadLib, MemoizationLeaf):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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):

View File

@ -3,13 +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.ivars.base import ImmutableVar
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,52 +23,22 @@ class RawLink(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -109,52 +78,22 @@ class ScriptTag(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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()

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class Title(Component):
def render(self) -> dict: ...
@ -22,52 +22,22 @@ class Title(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -103,52 +73,22 @@ class Meta(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -189,52 +129,22 @@ class Description(Meta):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -275,52 +185,22 @@ class Image(Meta):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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:

View File

@ -3,13 +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.ivars.base import ImmutableVar
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
@ -20,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,61 +28,25 @@ class Script(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_error: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_load: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_ready: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -102,6 +65,9 @@ class Script(Component):
*children: The children of the component.
src: Required unless inline script is used
strategy: When the script will execute: afterInteractive (defer-like behavior) | beforeInteractive | lazyOnload (async-like behavior)
on_load: Triggered when the script is loading
on_ready: Triggered when the script has loaded
on_error: Triggered when the script has errored
style: The style of the component.
key: A unique key for the component.
id: The id for the component.

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import copy
import dataclasses
import typing
from abc import ABC, abstractmethod
from functools import lru_cache, wraps
@ -25,6 +26,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,15 +37,19 @@ from reflex.constants import (
MemoizationMode,
PageNames,
)
from reflex.constants.compiler import SpecialAttributes
from reflex.event import (
EventCallback,
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 (
@ -53,8 +59,17 @@ from reflex.utils.imports import (
ParsedImportDict,
parse_imports,
)
from reflex.utils.serializers import serializer
from reflex.vars import Var, VarData
from reflex.vars import VarData
from reflex.vars.base import (
CachedVarOperation,
LiteralVar,
Var,
cached_property_no_lock,
)
from reflex.vars.function import ArgsFunctionOperation, FunctionStringVar
from reflex.vars.number import ternary_operation
from reflex.vars.object import ObjectVar
from reflex.vars.sequence import LiteralArrayVar
class BaseComponent(Base, ABC):
@ -177,7 +192,7 @@ class Component(BaseComponent, ABC):
style: Style = Style()
# A mapping from event triggers to event chains.
event_triggers: Dict[str, Union[EventChain, ImmutableVar]] = {}
event_triggers: Dict[str, Union[EventChain, Var]] = {}
# The alias for the tag.
alias: Optional[str] = None
@ -195,7 +210,7 @@ class Component(BaseComponent, ABC):
class_name: Any = None
# Special component props.
special_props: Set[ImmutableVar] = set()
special_props: List[Var] = []
# Whether the component should take the focus once the page is loaded
autofocus: bool = False
@ -213,7 +228,7 @@ class Component(BaseComponent, ABC):
_rename_props: Dict[str, str] = {}
# custom attribute
custom_attrs: Dict[str, Union[ImmutableVar, str]] = {}
custom_attrs: Dict[str, Union[Var, str]] = {}
# When to memoize this component and its children.
_memoization_mode: MemoizationMode = MemoizationMode()
@ -249,7 +264,7 @@ class Component(BaseComponent, ABC):
"""
return {}
def add_hooks(self) -> list[str | ImmutableVar]:
def add_hooks(self) -> list[str | Var]:
"""Add hooks inside the component function.
Hooks are pieces of literal Javascript code that is inserted inside the
@ -404,7 +419,7 @@ class Component(BaseComponent, ABC):
passed_types = None
try:
# Try to create a var from the value.
if isinstance(value, ImmutableVar):
if isinstance(value, Var):
kwargs[key] = value
else:
kwargs[key] = LiteralVar.create(value)
@ -447,11 +462,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, ImmutableVar) 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:
@ -459,19 +480,31 @@ class Component(BaseComponent, ABC):
kwargs["event_triggers"][key] = self._create_event_chain(
value=value, # type: ignore
args_spec=component_specific_triggers[key],
key=key,
)
# Remove any keys that were added as events.
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,
@ -484,13 +517,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)
@ -499,14 +535,20 @@ 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, ImmutableVar]:
key: Optional[str] = None,
) -> Union[EventChain, Var]:
"""Create an event chain from a variety of input types.
Args:
args_spec: The args_spec of the event trigger being bound.
value: The value to create the event chain from.
key: The key of the event trigger being bound.
Returns:
The event chain.
@ -515,12 +557,17 @@ class Component(BaseComponent, ABC):
ValueError: If the value is not a valid event chain.
"""
# If it's an event chain var, return it.
if isinstance(value, ImmutableVar):
if value._var_type is not EventChain:
if isinstance(value, Var):
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(), key=key)
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
@ -531,55 +578,55 @@ 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.
events.append(call_event_handler(v, args_spec))
events.append(call_event_handler(v, args_spec, key=key))
elif isinstance(v, Callable):
# Call the lambda to get the event chain.
result = call_event_fn(v, args_spec)
if isinstance(result, ImmutableVar):
result = call_event_fn(v, args_spec, key=key)
if isinstance(result, Var):
raise ValueError(
f"Invalid event chain: {v}. Cannot use a Var-returning "
"lambda inside an EventChain list."
)
events.extend(result)
elif isinstance(v, EventVar):
events.append(v)
else:
raise ValueError(f"Invalid event: {v}")
# If the input is a callable, create an event chain.
elif isinstance(value, Callable):
result = call_event_fn(value, args_spec)
if isinstance(result, ImmutableVar):
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
return self._create_event_chain(args_spec, result, key=key)
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, ImmutableVar):
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]:
@ -590,32 +637,32 @@ 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,
# e.g. variable declared as EventHandler types.
for field in self.get_fields().values():
if types._issubclass(field.type_, EventHandler):
if types._issubclass(field.outer_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:
@ -655,7 +702,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,
)
@ -669,7 +716,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()
@ -882,7 +929,7 @@ class Component(BaseComponent, ABC):
Returns:
The dictionary of the component style as value and the style notation as key.
"""
if isinstance(self.style, ImmutableVar):
if isinstance(self.style, Var):
return {"css": self.style}
emotion_style = format_as_emotion(self.style)
return (
@ -998,8 +1045,8 @@ class Component(BaseComponent, ABC):
@staticmethod
def _get_vars_from_event_triggers(
event_triggers: dict[str, EventChain | ImmutableVar],
) -> Iterator[tuple[str, list[ImmutableVar]]]:
event_triggers: dict[str, EventChain | Var],
) -> Iterator[tuple[str, list[Var]]]:
"""Get the Vars associated with each event trigger.
Args:
@ -1009,16 +1056,19 @@ class Component(BaseComponent, ABC):
tuple of (event_name, event_vars)
"""
for event_trigger, event in event_triggers.items():
if isinstance(event, ImmutableVar):
if isinstance(event, Var):
yield event_trigger, [event]
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[ImmutableVar]:
def _get_vars(self, include_children: bool = False) -> list[Var]:
"""Walk all Vars used in this component.
Args:
@ -1038,18 +1088,14 @@ class Component(BaseComponent, ABC):
# Get Vars associated with component props.
for prop in self.get_props():
prop_var = getattr(self, prop)
if isinstance(prop_var, ImmutableVar):
if isinstance(prop_var, Var):
vars.append(prop_var)
# Style keeps track of its own VarData instance, so embed in a temp Var that is yielded.
if (
isinstance(self.style, dict)
and self.style
or isinstance(self.style, ImmutableVar)
):
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=VarData.merge(self.style._var_data),
)
@ -1066,7 +1112,7 @@ class Component(BaseComponent, ABC):
self.autofocus,
*self.custom_attrs.values(),
):
if isinstance(comp_prop, ImmutableVar):
if isinstance(comp_prop, Var):
vars.append(comp_prop)
elif isinstance(comp_prop, str):
# Collapse VarData encoded in f-strings.
@ -1093,9 +1139,15 @@ 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
elif isinstance(trigger, ImmutableVar) and trigger._var_state:
if isinstance(event, EventCallback):
continue
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
@ -1268,7 +1320,9 @@ class Component(BaseComponent, ABC):
if self._get_ref_hook():
# Handle hooks needed for attaching react refs to DOM nodes.
_imports.setdefault("react", set()).add(ImportVar(tag="useRef"))
_imports.setdefault(f"/{Dirs.STATE_PATH}", set()).add(ImportVar(tag="refs"))
_imports.setdefault(f"$/{Dirs.STATE_PATH}", set()).add(
ImportVar(tag="refs")
)
if self._get_mount_lifecycle_hook():
# Handle hooks for `on_mount` / `on_unmount`.
@ -1287,7 +1341,7 @@ class Component(BaseComponent, ABC):
user_hooks = self._get_hooks()
user_hooks_data = (
VarData.merge(user_hooks._get_all_var_data())
if user_hooks is not None and isinstance(user_hooks, ImmutableVar)
if user_hooks is not None and isinstance(user_hooks, Var)
else None
)
if user_hooks_data is not None:
@ -1390,7 +1444,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.
@ -1453,7 +1507,7 @@ class Component(BaseComponent, ABC):
"""
code = {}
def extract_var_hooks(hook: ImmutableVar):
def extract_var_hooks(hook: Var):
_imports = {}
var_data = VarData.merge(hook._get_all_var_data())
if var_data is not None:
@ -1470,7 +1524,7 @@ class Component(BaseComponent, ABC):
# the order of the hooks in the final output)
for clz in reversed(tuple(self._iter_parent_classes_with_method("add_hooks"))):
for hook in clz.add_hooks(self):
if isinstance(hook, ImmutableVar):
if isinstance(hook, Var):
extract_var_hooks(hook)
else:
code[hook] = {}
@ -1531,7 +1585,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, ImmutableVar):
if self.id is None or isinstance(self.id, Var):
return None
return format.format_ref(self.id)
@ -1625,7 +1679,7 @@ class CustomComponent(Component):
"""A custom user-defined component."""
# Use the components library.
library = f"/{Dirs.COMPONENTS_PATH}"
library = f"$/{Dirs.COMPONENTS_PATH}"
# The function that creates the component.
component_fn: Callable[..., Component] = Component.create
@ -1669,8 +1723,9 @@ class CustomComponent(Component):
value = self._create_event_chain(
value=value,
args_spec=event_triggers_in_component_declaration.get(
key, lambda: []
key, empty_event
),
key=key,
)
self.props[format.to_camel_case(key)] = value
continue
@ -1732,10 +1787,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:
@ -1770,15 +1829,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)
),
@ -1786,7 +1845,7 @@ class CustomComponent(Component):
for name, prop in self.props.items()
]
def _get_vars(self, include_children: bool = False) -> list[ImmutableVar]:
def _get_vars(self, include_children: bool = False) -> list[Var]:
"""Walk all Vars used in this component.
Args:
@ -1797,7 +1856,7 @@ class CustomComponent(Component):
"""
return (
super()._get_vars(include_children=include_children)
+ [prop for prop in self.props.values() if isinstance(prop, ImmutableVar)]
+ [prop for prop in self.props.values() if isinstance(prop, Var)]
+ self.get_component(self)._get_vars(include_children=include_children)
)
@ -1884,19 +1943,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.
@ -1968,7 +2014,7 @@ class StatefulComponent(BaseComponent):
should_memoize = True
break
child = cls._child_var(child)
if isinstance(child, ImmutableVar) and child._get_all_var_data():
if isinstance(child, Var) and child._get_all_var_data():
should_memoize = True
break
@ -2124,7 +2170,7 @@ class StatefulComponent(BaseComponent):
def _get_memoized_event_triggers(
cls,
component: Component,
) -> dict[str, tuple[ImmutableVar, str]]:
) -> dict[str, tuple[Var, str]]:
"""Memoize event handler functions with useCallback to avoid unnecessary re-renders.
Args:
@ -2171,7 +2217,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)}])",
@ -2202,7 +2248,7 @@ class StatefulComponent(BaseComponent):
"""
if self.rendered_as_shared:
return {
f"/{Dirs.UTILS}/{PageNames.STATEFUL_COMPONENTS}": [
f"$/{Dirs.UTILS}/{PageNames.STATEFUL_COMPONENTS}": [
ImportVar(tag=self.tag)
]
}
@ -2244,7 +2290,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.
@ -2309,3 +2355,206 @@ class MemoizationLeaf(Component):
update={"disposition": MemoizationDisposition.ALWAYS}
)
return comp
load_dynamic_serializer()
class ComponentVar(Var[Component], python_types=BaseComponent):
"""A Var that represents a Component."""
def empty_component() -> Component:
"""Create an empty component.
Returns:
An empty component.
"""
from reflex.components.base.bare import Bare
return Bare.create("")
def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) -> Var:
"""Convert a render dict to a Var.
Args:
tag: The render dict.
imported_names: The names of the imported components.
Returns:
The Var.
"""
if not isinstance(tag, dict):
if isinstance(tag, Component):
return render_dict_to_var(tag.render(), imported_names)
return Var.create(tag)
if "iterable" in tag:
function_return = Var.create(
[
render_dict_to_var(child.render(), imported_names)
for child in tag["children"]
]
)
func = ArgsFunctionOperation.create(
(tag["arg_var_name"], tag["index_var_name"]),
function_return,
)
return FunctionStringVar.create("Array.prototype.map.call").call(
tag["iterable"]
if not isinstance(tag["iterable"], ObjectVar)
else tag["iterable"].items(),
func,
)
if tag["name"] == "match":
element = tag["cond"]
conditionals = tag["default"]
for case in tag["match_cases"][::-1]:
condition = case[0].to_string() == element.to_string()
for pattern in case[1:-1]:
condition = condition | (pattern.to_string() == element.to_string())
conditionals = ternary_operation(
condition,
case[-1],
conditionals,
)
return conditionals
if "cond" in tag:
return ternary_operation(
tag["cond"],
render_dict_to_var(tag["true_value"], imported_names),
render_dict_to_var(tag["false_value"], imported_names)
if tag["false_value"] is not None
else Var.create(None),
)
props = {}
special_props = []
for prop_str in tag["props"]:
if "=" not in prop_str:
special_props.append(Var(prop_str).to(ObjectVar))
continue
prop = prop_str.index("=")
key = prop_str[:prop]
value = prop_str[prop + 2 : -1]
props[key] = value
props = Var.create({Var.create(k): Var(v) for k, v in props.items()})
for prop in special_props:
props = props.merge(prop)
contents = tag["contents"][1:-1] if tag["contents"] else None
raw_tag_name = tag.get("name")
tag_name = Var(raw_tag_name or "Fragment")
tag_name = (
Var.create(raw_tag_name)
if raw_tag_name
and raw_tag_name.split(".")[0] not in imported_names
and raw_tag_name.lower() == raw_tag_name
else tag_name
)
return FunctionStringVar.create(
"jsx",
).call(
tag_name,
props,
*([Var(contents)] if contents is not None else []),
*[render_dict_to_var(child, imported_names) for child in tag["children"]],
)
@dataclasses.dataclass(
eq=False,
frozen=True,
)
class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar):
"""A Var that represents a Component."""
_var_value: BaseComponent = dataclasses.field(default_factory=empty_component)
@cached_property_no_lock
def _cached_var_name(self) -> str:
"""Get the name of the var.
Returns:
The name of the var.
"""
var_data = self._get_all_var_data()
if var_data is not None:
# flatten imports
imported_names = {j.alias or j.name for i in var_data.imports for j in i[1]}
else:
imported_names = set()
return str(render_dict_to_var(self._var_value.render(), imported_names))
@cached_property_no_lock
def _cached_get_all_var_data(self) -> VarData | None:
"""Get the VarData for the var.
Returns:
The VarData for the var.
"""
return VarData.merge(
VarData(
imports={
"@emotion/react": [
ImportVar(tag="jsx"),
],
}
),
VarData(
imports=self._var_value._get_all_imports(),
),
VarData(
imports={
"react": [
ImportVar(tag="Fragment"),
],
}
),
)
def __hash__(self) -> int:
"""Get the hash of the var.
Returns:
The hash of the var.
"""
return hash((self.__class__.__name__, self._js_expr))
@classmethod
def create(
cls,
value: Component,
_var_data: VarData | None = None,
):
"""Create a var from a value.
Args:
value: The value of the var.
_var_data: Additional hooks and imports associated with the Var.
Returns:
The var.
"""
return LiteralComponentVar(
_js_expr="",
_var_type=type(value),
_var_data=_var_data,
_var_value=value,
)

View File

@ -18,60 +18,56 @@ 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 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",
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")],
"$/env.json": [ImportVar(tag="env", is_default=True)],
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="getBackendURL")],
},
),
_var_type=WebsocketTargetURL,
@ -95,7 +91,7 @@ def default_connection_error() -> list[str | Var | Component]:
class ConnectionToaster(Toaster):
"""A connection toaster component."""
def add_hooks(self) -> list[str | ImmutableVar]:
def add_hooks(self) -> list[str | Var]:
"""Add the hooks for the connection toaster.
Returns:
@ -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"),

View File

@ -3,33 +3,46 @@
# ------------------- 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]: ...
class ConnectionToaster(Toaster):
def add_hooks(self) -> list[str | ImmutableVar]: ...
def add_hooks(self) -> list[str | Var]: ...
@overload
@classmethod
def create( # type: ignore
@ -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,61 +79,31 @@ 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,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -165,52 +148,22 @@ class ConnectionBanner(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -234,52 +187,22 @@ class ConnectionModal(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -304,52 +227,22 @@ class WifiOffPulse(Icon):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -378,81 +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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -13,16 +13,15 @@ from __future__ import annotations
from reflex import constants
from reflex.components.component import Component
from reflex.components.core.cond import cond
from reflex.ivars.base import ImmutableVar
from reflex.vars import Var
from reflex.vars.base import Var
route_not_found: Var = ImmutableVar.create_safe(constants.ROUTE_NOT_FOUND)
route_not_found: Var = Var(_js_expr=constants.ROUTE_NOT_FOUND)
class ClientSideRouting(Component):
"""The client-side routing component."""
library = "/utils/client_side_routing"
library = "$/utils/client_side_routing"
tag = "useClientSideRouting"
def add_hooks(self) -> list[str]:

View File

@ -3,13 +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.ivars.base import ImmutableVar
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,52 +25,22 @@ class ClientSideRouting(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -105,52 +74,22 @@ class Default404Page(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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]]]
@ -66,7 +67,7 @@ class Clipboard(Fragment):
The import dict for the component.
"""
return {
"/utils/helpers/paste.js": ImportVar(
"$/utils/helpers/paste.js": ImportVar(
tag="usePasteHandler", is_default=True
),
}

View File

@ -3,14 +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.ivars.base import ImmutableVar
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
@ -18,64 +17,32 @@ 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,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_paste: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.
@ -83,6 +50,7 @@ class Clipboard(Fragment):
Args:
*children: The children of the component.
targets: The element ids to attach the event listener to. Defaults to all child components or the document.
on_paste: 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_event_actions: Save the original event actions for the on_paste event.
style: The style of the component.
key: A unique key for the component.

View File

@ -8,14 +8,14 @@ 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")],
f"$/{Dirs.STATE_PATH}": [ImportVar(tag="isTrue")],
}
@ -119,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:
@ -138,13 +138,13 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | ImmutableVar:
"""
# 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.

View File

@ -6,9 +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.ivars.base import ImmutableVar
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
@ -46,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:
@ -107,18 +107,18 @@ 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"] = ImmutableVar.create_safe(child_ref)
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",
ImmutableVar(
_var_name=str(child.alias or child.tag),
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(),
hooks=child._get_all_hooks(),
),
),
)
@ -128,6 +128,10 @@ class DebounceInput(Component):
component.event_triggers.update(child.event_triggers)
component.children = child.children
component._rename_props = child._rename_props
outer_get_all_custom_code = component._get_all_custom_code
component._get_all_custom_code = lambda: outer_get_all_custom_code().union(
child._get_all_custom_code()
)
return component
def _render(self):

View File

@ -3,13 +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.ivars.base import ImmutableVar
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
@ -23,63 +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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_change: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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,7 +51,7 @@ 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 `{str(iterable)}` of type Any. "
@ -130,7 +129,7 @@ class Foreach(Component):
iterable_state=str(tag.iterable),
arg_name=tag.arg_var_name,
arg_index=tag.get_index_var_arg(),
iterable_type=tag.iterable.upcast()._var_type.mro()[0].__name__,
iterable_type=tag.iterable._var_type.mro()[0].__name__,
)

View File

@ -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):

View File

@ -3,13 +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.ivars.base import ImmutableVar
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
@ -18,83 +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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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.

View File

@ -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 Var, VarData
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
class Match(MemoizationLeaf):
@ -27,7 +27,7 @@ class Match(MemoizationLeaf):
default: Any
@classmethod
def create(cls, cond: Any, *cases) -> Union[Component, ImmutableVar]:
def create(cls, cond: Any, *cases) -> Union[Component, Var]:
"""Create a Match Component.
Args:
@ -56,7 +56,7 @@ class Match(MemoizationLeaf):
)
@classmethod
def _create_condition_var(cls, cond: Any) -> ImmutableVar:
def _create_condition_var(cls, cond: Any) -> Var:
"""Convert the condition to a Var.
Args:
@ -77,7 +77,7 @@ class Match(MemoizationLeaf):
@classmethod
def _process_cases(
cls, cases: List
) -> Tuple[List, Optional[Union[ImmutableVar, BaseComponent]]]:
) -> Tuple[List, Optional[Union[Var, BaseComponent]]]:
"""Process the list of match cases and the catchall default case.
Args:
@ -125,7 +125,7 @@ class Match(MemoizationLeaf):
return case_element
@classmethod
def _process_match_cases(cls, cases: List) -> List[List[ImmutableVar]]:
def _process_match_cases(cls, cases: List) -> List[List[Var]]:
"""Process the individual match cases.
Args:
@ -157,7 +157,7 @@ class Match(MemoizationLeaf):
if not isinstance(element, BaseComponent)
else element
)
if not isinstance(el, (ImmutableVar, BaseComponent)):
if not isinstance(el, (Var, BaseComponent)):
raise ValueError("Case element must be a var or component")
case_list.append(el)
@ -166,7 +166,7 @@ class Match(MemoizationLeaf):
return match_cases
@classmethod
def _validate_return_types(cls, match_cases: List[List[ImmutableVar]]) -> None:
def _validate_return_types(cls, match_cases: List[List[Var]]) -> None:
"""Validate that match cases have the same return types.
Args:
@ -180,24 +180,24 @@ class Match(MemoizationLeaf):
if types._isinstance(first_case_return, BaseComponent):
return_type = BaseComponent
elif types._isinstance(first_case_return, ImmutableVar):
return_type = ImmutableVar
elif types._isinstance(first_case_return, Var):
return_type = Var
for index, case in enumerate(match_cases):
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], ImmutableVar) 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}"
)
@classmethod
def _create_match_cond_var_or_component(
cls,
match_cond_var: ImmutableVar,
match_cases: List[List[ImmutableVar]],
default: Optional[Union[ImmutableVar, BaseComponent]],
) -> Union[Component, ImmutableVar]:
match_cond_var: Var,
match_cases: List[List[Var]],
default: Optional[Union[Var, BaseComponent]],
) -> Union[Component, Var]:
"""Create and return the match condition var or component.
Args:
@ -232,8 +232,8 @@ 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(
return Var(
_js_expr=format.format_match(
cond=str(match_cond_var),
match_cases=match_cases,
default=default, # type: ignore

View File

@ -2,14 +2,20 @@
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.component import (
Component,
ComponentNamespace,
MemoizationLeaf,
StatefulComponent,
)
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.constants.compiler import Imports
from reflex.event import (
CallableEventSpec,
EventChain,
@ -19,17 +25,18 @@ from reflex.event import (
call_script,
parse_args_spec,
)
from reflex.ivars.base import ImmutableCallableVar, ImmutableVar, LiteralVar
from reflex.ivars.sequence import LiteralStringVar
from reflex.utils import format
from reflex.utils.imports import ImportVar
from reflex.vars import Var, VarData
from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name
from reflex.vars.sequence import LiteralStringVar
DEFAULT_UPLOAD_ID: str = "default"
upload_files_context_var_data: VarData = VarData(
imports={
"react": "useContext",
f"/{Dirs.CONTEXTS_PATH}": "UploadFilesContext",
f"$/{Dirs.CONTEXTS_PATH}": "UploadFilesContext",
},
hooks={
"const [filesById, setFilesById] = useContext(UploadFilesContext);": None,
@ -37,8 +44,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,8 +65,8 @@ 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=VarData.merge(
upload_files_context_var_data, id_var._get_all_var_data()
@ -67,8 +74,8 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar:
)
@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,8 +85,8 @@ 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=VarData.merge(
upload_files_context_var_data, id_var._get_all_var_data()
@ -125,19 +132,17 @@ 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 = ImmutableVar(
_var_name="getBackendURL(env.UPLOAD)",
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),
f"$/{Dirs.STATE_PATH}": "getBackendURL",
"$/env.json": ImportVar(tag="env", is_default=True),
}
),
).to(str)
@ -157,7 +162,7 @@ def get_upload_url(file_path: str) -> Var[str]:
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:
@ -166,24 +171,22 @@ def _on_drop_spec(files: Var):
Returns:
Signature for on_drop handler including the files to upload.
"""
return [files]
return (files,)
class UploadFilesProvider(Component):
"""AppWrap component that provides a dict of selected files by ID via useContext."""
library = f"/{Dirs.CONTEXTS_PATH}"
library = f"$/{Dirs.CONTEXTS_PATH}"
tag = "UploadFilesProvider"
class Upload(MemoizationLeaf):
"""A file upload component."""
library = "react-dropzone@14.2.3"
library = "react-dropzone@14.2.10"
tag = "ReactDropzone"
is_default = True
tag = ""
# The list of accepted file types. This should be a dictionary of MIME types as keys and array of file formats as
# values.
@ -203,7 +206,7 @@ class Upload(MemoizationLeaf):
min_size: Var[int]
# Whether to allow multiple files to be uploaded.
multiple: Var[bool] = True # type: ignore
multiple: Var[bool]
# Whether to disable click to upload.
no_click: Var[bool]
@ -234,6 +237,8 @@ class Upload(MemoizationLeaf):
# Mark the Upload component as used in the app.
cls.is_used = True
props.setdefault("multiple", True)
# Apply the default classname
given_class_name = props.pop("class_name", [])
if isinstance(given_class_name, str):
@ -245,21 +250,6 @@ class Upload(MemoizationLeaf):
upload_props = {
key: value for key, value in props.items() if key in supported_props
}
# The file input to use.
upload = Input.create(type="file")
upload.special_props = {
ImmutableVar(_var_name="{...getInputProps()}", _var_type=None)
}
# The dropzone to use.
zone = Box.create(
upload,
*children,
**{k: v for k, v in props.items() if k not in supported_props},
)
zone.special_props = {
ImmutableVar(_var_name="{...getRootProps()}", _var_type=None)
}
# Create the component.
upload_props["id"] = props.get("id", DEFAULT_UPLOAD_ID)
@ -281,15 +271,78 @@ class Upload(MemoizationLeaf):
),
)
upload_props["on_drop"] = on_drop
input_props_unique_name = get_unique_variable_name()
root_props_unique_name = get_unique_variable_name()
event_var, callback_str = StatefulComponent._get_memoized_event_triggers(
Box.create(on_click=upload_props["on_drop"]) # type: ignore
)["on_click"]
upload_props["on_drop"] = event_var
upload_props = {
format.to_camel_case(key): value for key, value in upload_props.items()
}
use_dropzone_arguements = {
"onDrop": event_var,
**upload_props,
}
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
right_side = f"useDropzone({str(Var.create(use_dropzone_arguements))})"
var_data = VarData.merge(
VarData(
imports=Imports.EVENTS,
hooks={
"const [addEvents, connectError] = useContext(EventLoopContext);": None
},
),
event_var._get_all_var_data(),
VarData(
hooks={
callback_str: None,
f"{left_side} = {right_side};": None,
},
imports={
"react-dropzone": "useDropzone",
**Imports.EVENTS,
},
),
)
# The file input to use.
upload = Input.create(type="file")
upload.special_props = [
Var(
_js_expr=f"{{...{input_props_unique_name}()}}",
_var_type=None,
_var_data=var_data,
)
]
# The dropzone to use.
zone = Box.create(
upload,
*children,
**{k: v for k, v in props.items() if k not in supported_props},
)
zone.special_props = [
Var(
_js_expr=f"{{...{root_props_unique_name}()}}",
_var_type=None,
_var_data=var_data,
)
]
return super().create(
zone,
**upload_props,
)
@classmethod
def _update_arg_tuple_for_on_drop(
cls, arg_value: tuple[ImmutableVar, ImmutableVar]
):
def _update_arg_tuple_for_on_drop(cls, arg_value: tuple[Var, Var]):
"""Helper to update caller-provided EventSpec args for direct use with on_drop.
Args:
@ -298,16 +351,11 @@ 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
def _render(self):
out = super()._render()
out.args = ("getRootProps", "getInputProps")
return out
@staticmethod
def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
return {

View File

@ -4,38 +4,42 @@
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
from pathlib import Path
from typing import Any, Callable, ClassVar, Dict, List, Optional, Union, overload
from typing import Any, ClassVar, Dict, List, Optional, Union, overload
from reflex.components.component import Component, ComponentNamespace, MemoizationLeaf
from reflex.components.component import (
Component,
ComponentNamespace,
MemoizationLeaf,
)
from reflex.constants import Dirs
from reflex.event import (
CallableEventSpec,
EventHandler,
EventSpec,
EventType,
)
from reflex.ivars.base import ImmutableCallableVar, ImmutableVar
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 CallableVar, Var
DEFAULT_UPLOAD_ID: str
upload_files_context_var_data: VarData
@ImmutableCallableVar
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar: ...
@ImmutableCallableVar
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> ImmutableVar: ...
@CallableVar
def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var: ...
@CallableVar
def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var: ...
@CallableEventSpec
def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec: ...
def cancel_upload(upload_id: str) -> EventSpec: ...
def get_upload_dir() -> Path: ...
uploaded_files_url_prefix = ImmutableVar(
_var_name="getBackendURL(env.UPLOAD)",
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),
f"$/{Dirs.STATE_PATH}": "getBackendURL",
"$/env.json": ImportVar(tag="env", is_default=True),
}
),
).to(str)
@ -53,52 +57,22 @@ class UploadFilesProvider(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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,
) -> "UploadFilesProvider":
"""Create the component.
@ -126,7 +100,7 @@ class Upload(MemoizationLeaf):
def create( # type: ignore
cls,
*children,
accept: Optional[Union[Var[Optional[Dict[str, List]]], Dict[str, List]]] = None,
accept: Optional[Union[Dict[str, List], Var[Optional[Dict[str, List]]]]] = None,
disabled: Optional[Union[Var[bool], bool]] = None,
max_files: Optional[Union[Var[int], int]] = None,
max_size: Optional[Union[Var[int], int]] = None,
@ -140,55 +114,23 @@ class Upload(MemoizationLeaf):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_drop: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[EventType[[]]] = None,
on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[EventType[[]]] = None,
on_double_click: Optional[EventType[[]]] = None,
on_drop: Optional[EventType[Any]] = 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,
) -> "Upload":
"""Create an upload component.
@ -204,6 +146,7 @@ class Upload(MemoizationLeaf):
no_click: Whether to disable click to upload.
no_drag: Whether to disable drag and drop.
no_keyboard: Whether to disable using the space/enter keys to upload.
on_drop: Fired when files are dropped.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
@ -223,7 +166,7 @@ class StyledUpload(Upload):
def create( # type: ignore
cls,
*children,
accept: Optional[Union[Var[Optional[Dict[str, List]]], Dict[str, List]]] = None,
accept: Optional[Union[Dict[str, List], Var[Optional[Dict[str, List]]]]] = None,
disabled: Optional[Union[Var[bool], bool]] = None,
max_files: Optional[Union[Var[int], int]] = None,
max_size: Optional[Union[Var[int], int]] = None,
@ -237,55 +180,23 @@ class StyledUpload(Upload):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_drop: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[EventType[[]]] = None,
on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[EventType[[]]] = None,
on_double_click: Optional[EventType[[]]] = None,
on_drop: Optional[EventType[Any]] = 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,
) -> "StyledUpload":
"""Create the styled upload component.
@ -301,6 +212,7 @@ class StyledUpload(Upload):
no_click: Whether to disable click to upload.
no_drag: Whether to disable drag and drop.
no_keyboard: Whether to disable using the space/enter keys to upload.
on_drop: Fired when files are dropped.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
@ -320,7 +232,7 @@ class UploadNamespace(ComponentNamespace):
@staticmethod
def __call__(
*children,
accept: Optional[Union[Var[Optional[Dict[str, List]]], Dict[str, List]]] = None,
accept: Optional[Union[Dict[str, List], Var[Optional[Dict[str, List]]]]] = None,
disabled: Optional[Union[Var[bool], bool]] = None,
max_files: Optional[Union[Var[int], int]] = None,
max_size: Optional[Union[Var[int], int]] = None,
@ -334,55 +246,23 @@ class UploadNamespace(ComponentNamespace):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_drop: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[EventType[[]]] = None,
on_click: Optional[EventType[[]]] = None,
on_context_menu: Optional[EventType[[]]] = None,
on_double_click: Optional[EventType[[]]] = None,
on_drop: Optional[EventType[Any]] = 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,
) -> "StyledUpload":
"""Create the styled upload component.
@ -398,6 +278,7 @@ class UploadNamespace(ComponentNamespace):
no_click: Whether to disable click to upload.
no_drag: Whether to disable drag and drop.
no_keyboard: Whether to disable using the space/enter keys to upload.
on_drop: Fired when files are dropped.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.

View File

@ -8,7 +8,6 @@ _SUBMOD_ATTRS: dict[str, list[str]] = {
"code": [
"CodeBlock",
"code_block",
"LiteralCodeBlockTheme",
"LiteralCodeLanguage",
],
"dataeditor": ["data_editor", "data_editor_theme", "DataEditorTheme"],

View File

@ -4,7 +4,6 @@
# ------------------------------------------------------
from .code import CodeBlock as CodeBlock
from .code import LiteralCodeBlockTheme as LiteralCodeBlockTheme
from .code import LiteralCodeLanguage as LiteralCodeLanguage
from .code import code_block as code_block
from .dataeditor import DataEditorTheme as DataEditorTheme

View File

@ -2,71 +2,20 @@
from __future__ import annotations
from typing import Dict, Literal, Optional, Union
import dataclasses
from typing import ClassVar, Dict, Literal, Optional, Union
from typing_extensions import get_args
from reflex.components.component import Component
from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.cond import color_mode_cond
from reflex.components.lucide.icon import Icon
from reflex.components.radix.themes.components.button import Button
from reflex.components.radix.themes.layout.box import Box
from reflex.constants.colors import Color
from reflex.event import set_clipboard
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.style import Style
from reflex.utils import format
from reflex.utils import console, format
from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars import Var
LiteralCodeBlockTheme = Literal[
"a11y-dark",
"atom-dark",
"cb",
"coldark-cold",
"coldark-dark",
"coy",
"coy-without-shadows",
"darcula",
"dark",
"dracula",
"duotone-dark",
"duotone-earth",
"duotone-forest",
"duotone-light",
"duotone-sea",
"duotone-space",
"funky",
"ghcolors",
"gruvbox-dark",
"gruvbox-light",
"holi-theme",
"hopscotch",
"light", # not present in react-syntax-highlighter styles
"lucario",
"material-dark",
"material-light",
"material-oceanic",
"night-owl",
"nord",
"okaidia",
"one-dark",
"one-light",
"pojoaque",
"prism",
"shades-of-purple",
"solarized-dark-atom",
"solarizedlight",
"synthwave84",
"tomorrow",
"twilight",
"vs",
"vs-dark",
"vsc-dark-plus",
"xonokai",
"z-touch",
]
from reflex.vars.base import LiteralVar, Var, VarData
LiteralCodeLanguage = Literal[
"abap",
@ -351,34 +300,98 @@ LiteralCodeLanguage = Literal[
]
def replace_quotes_with_camel_case(value: str) -> str:
"""Replaces quotes in the given string with camel case format.
def construct_theme_var(theme: str) -> Var[Theme]:
"""Construct a theme var.
Args:
value (str): The string to be processed.
theme: The theme to construct.
Returns:
str: The processed string with quotes replaced by camel case.
The constructed theme var.
"""
for theme in get_args(LiteralCodeBlockTheme):
value = value.replace(f'"{theme}"', format.to_camel_case(theme))
return value
return Var(
theme,
_var_data=VarData(
imports={
f"react-syntax-highlighter/dist/cjs/styles/prism/{format.to_kebab_case(theme)}": [
ImportVar(tag=theme, is_default=True, install=False)
]
}
),
)
@dataclasses.dataclass(init=False)
class Theme:
"""Themes for the CodeBlock component."""
a11y_dark: ClassVar[Var[Theme]] = construct_theme_var("a11yDark")
atom_dark: ClassVar[Var[Theme]] = construct_theme_var("atomDark")
cb: ClassVar[Var[Theme]] = construct_theme_var("cb")
coldark_cold: ClassVar[Var[Theme]] = construct_theme_var("coldarkCold")
coldark_dark: ClassVar[Var[Theme]] = construct_theme_var("coldarkDark")
coy: ClassVar[Var[Theme]] = construct_theme_var("coy")
coy_without_shadows: ClassVar[Var[Theme]] = construct_theme_var("coyWithoutShadows")
darcula: ClassVar[Var[Theme]] = construct_theme_var("darcula")
dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
dracula: ClassVar[Var[Theme]] = construct_theme_var("dracula")
duotone_dark: ClassVar[Var[Theme]] = construct_theme_var("duotoneDark")
duotone_earth: ClassVar[Var[Theme]] = construct_theme_var("duotoneEarth")
duotone_forest: ClassVar[Var[Theme]] = construct_theme_var("duotoneForest")
duotone_light: ClassVar[Var[Theme]] = construct_theme_var("duotoneLight")
duotone_sea: ClassVar[Var[Theme]] = construct_theme_var("duotoneSea")
duotone_space: ClassVar[Var[Theme]] = construct_theme_var("duotoneSpace")
funky: ClassVar[Var[Theme]] = construct_theme_var("funky")
ghcolors: ClassVar[Var[Theme]] = construct_theme_var("ghcolors")
gruvbox_dark: ClassVar[Var[Theme]] = construct_theme_var("gruvboxDark")
gruvbox_light: ClassVar[Var[Theme]] = construct_theme_var("gruvboxLight")
holi_theme: ClassVar[Var[Theme]] = construct_theme_var("holiTheme")
hopscotch: ClassVar[Var[Theme]] = construct_theme_var("hopscotch")
light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
lucario: ClassVar[Var[Theme]] = construct_theme_var("lucario")
material_dark: ClassVar[Var[Theme]] = construct_theme_var("materialDark")
material_light: ClassVar[Var[Theme]] = construct_theme_var("materialLight")
material_oceanic: ClassVar[Var[Theme]] = construct_theme_var("materialOceanic")
night_owl: ClassVar[Var[Theme]] = construct_theme_var("nightOwl")
nord: ClassVar[Var[Theme]] = construct_theme_var("nord")
okaidia: ClassVar[Var[Theme]] = construct_theme_var("okaidia")
one_dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
one_light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
pojoaque: ClassVar[Var[Theme]] = construct_theme_var("pojoaque")
prism: ClassVar[Var[Theme]] = construct_theme_var("prism")
shades_of_purple: ClassVar[Var[Theme]] = construct_theme_var("shadesOfPurple")
solarized_dark_atom: ClassVar[Var[Theme]] = construct_theme_var("solarizedDarkAtom")
solarizedlight: ClassVar[Var[Theme]] = construct_theme_var("solarizedlight")
synthwave84: ClassVar[Var[Theme]] = construct_theme_var("synthwave84")
tomorrow: ClassVar[Var[Theme]] = construct_theme_var("tomorrow")
twilight: ClassVar[Var[Theme]] = construct_theme_var("twilight")
vs: ClassVar[Var[Theme]] = construct_theme_var("vs")
vs_dark: ClassVar[Var[Theme]] = construct_theme_var("vsDark")
vsc_dark_plus: ClassVar[Var[Theme]] = construct_theme_var("vscDarkPlus")
xonokai: ClassVar[Var[Theme]] = construct_theme_var("xonokai")
z_touch: ClassVar[Var[Theme]] = construct_theme_var("zTouch")
for theme_name in dir(Theme):
if theme_name.startswith("_"):
continue
setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme))
class CodeBlock(Component):
"""A code block."""
library = "react-syntax-highlighter@15.5.0"
library = "react-syntax-highlighter@15.6.1"
tag = "PrismAsyncLight"
alias = "SyntaxHighlighter"
# The theme to use ("light" or "dark").
theme: Var[LiteralCodeBlockTheme] = "one-light" # type: ignore
theme: Var[Union[Theme, str]] = Theme.one_light
# The language to use.
language: Var[LiteralCodeLanguage] = "python" # type: ignore
language: Var[LiteralCodeLanguage] = Var.create("python")
# The code to display.
code: Var[str]
@ -398,6 +411,12 @@ class CodeBlock(Component):
# Props passed down to the code tag.
code_tag_props: Var[Dict[str, str]]
# Whether a copy button should appear.
can_copy: Optional[bool] = False
# A custom copy button to override the default one.
copy_button: Optional[Union[bool, Component]] = None
def add_imports(self) -> ImportDict:
"""Add imports for the CodeBlock component.
@ -406,31 +425,6 @@ class CodeBlock(Component):
"""
imports_: ImportDict = {}
themeString = str(self.theme)
selected_themes = []
for possibleTheme in get_args(LiteralCodeBlockTheme):
if format.to_camel_case(possibleTheme) in themeString:
selected_themes.append(possibleTheme)
if possibleTheme in themeString:
selected_themes.append(possibleTheme)
selected_themes = sorted(set(map(self.convert_theme_name, selected_themes)))
imports_.update(
{
f"react-syntax-highlighter/dist/cjs/styles/prism/{theme}": [
ImportVar(
tag=format.to_camel_case(theme),
is_default=True,
install=False,
)
]
for theme in selected_themes
}
)
if (
self.language is not None
and (language_without_quotes := str(self.language).replace('"', ""))
@ -460,16 +454,12 @@ class CodeBlock(Component):
def create(
cls,
*children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[bool, Component]] = None,
**props,
):
"""Create a text component.
Args:
*children: The children of the component.
can_copy: Whether a copy button should appears.
copy_button: A custom copy button to override the default one.
**props: The props to pass to the component.
Returns:
@ -477,18 +467,26 @@ class CodeBlock(Component):
"""
# This component handles style in a special prop.
custom_style = props.pop("custom_style", {})
can_copy = props.pop("can_copy", False)
copy_button = props.pop("copy_button", None)
if "theme" not in props:
# Default color scheme responds to global color mode.
props["theme"] = color_mode_cond(
light=ImmutableVar.create_safe("oneLight"),
dark=ImmutableVar.create_safe("oneDark"),
light=Theme.one_light,
dark=Theme.one_dark,
)
# react-syntax-highlighter doesnt have an explicit "light" or "dark" theme so we use one-light and one-dark
# themes respectively to ensure code compatibility.
if "theme" in props and not isinstance(props["theme"], ImmutableVar):
props["theme"] = cls.convert_theme_name(props["theme"])
if "theme" in props and not isinstance(props["theme"], Var):
props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore
console.deprecate(
feature_name="theme prop as string",
reason="Use code_block.themes instead.",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
if can_copy:
code = children[0]
@ -513,7 +511,7 @@ class CodeBlock(Component):
# Carry the children (code) via props
if children:
props["code"] = children[0]
if not isinstance(props["code"], ImmutableVar):
if not isinstance(props["code"], Var):
props["code"] = LiteralVar.create(props["code"])
# Create the component.
@ -534,9 +532,7 @@ class CodeBlock(Component):
def _render(self):
out = super()._render()
theme = self.theme.upcast()._replace(
_var_name=replace_quotes_with_camel_case(str(self.theme))
)
theme = self.theme
out.add_props(style=theme).remove_props("theme", "code").add_props(
children=self.code
@ -544,19 +540,16 @@ class CodeBlock(Component):
return out
@staticmethod
def convert_theme_name(theme) -> str:
"""Convert theme names to appropriate names.
Args:
theme: The theme name.
Returns:
The right theme name.
"""
if theme in ["light", "dark"]:
return f"one-{theme}"
return theme
def _exclude_props(self) -> list[str]:
return ["can_copy", "copy_button"]
code_block = CodeBlock.create
class CodeblockNamespace(ComponentNamespace):
"""Namespace for the CodeBlock component."""
themes = Theme
__call__ = CodeBlock.create
code_block = CodeblockNamespace()

View File

@ -3,63 +3,16 @@
# ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
import dataclasses
from typing import Any, ClassVar, Dict, Literal, Optional, Union, overload
from reflex.components.component import Component
from reflex.components.component import Component, ComponentNamespace
from reflex.constants.colors import Color
from reflex.event import EventHandler, EventSpec
from reflex.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.utils.imports import ImportDict
from reflex.vars import Var
from reflex.vars.base import Var
LiteralCodeBlockTheme = Literal[
"a11y-dark",
"atom-dark",
"cb",
"coldark-cold",
"coldark-dark",
"coy",
"coy-without-shadows",
"darcula",
"dark",
"dracula",
"duotone-dark",
"duotone-earth",
"duotone-forest",
"duotone-light",
"duotone-sea",
"duotone-space",
"funky",
"ghcolors",
"gruvbox-dark",
"gruvbox-light",
"holi-theme",
"hopscotch",
"light",
"lucario",
"material-dark",
"material-light",
"material-oceanic",
"night-owl",
"nord",
"okaidia",
"one-dark",
"one-light",
"pojoaque",
"prism",
"shades-of-purple",
"solarized-dark-atom",
"solarizedlight",
"synthwave84",
"tomorrow",
"twilight",
"vs",
"vs-dark",
"vsc-dark-plus",
"xonokai",
"z-touch",
]
LiteralCodeLanguage = Literal[
"abap",
"abnf",
@ -342,7 +295,59 @@ LiteralCodeLanguage = Literal[
"zig",
]
def replace_quotes_with_camel_case(value: str) -> str: ...
def construct_theme_var(theme: str) -> Var[Theme]: ...
@dataclasses.dataclass(init=False)
class Theme:
a11y_dark: ClassVar[Var[Theme]] = construct_theme_var("a11yDark")
atom_dark: ClassVar[Var[Theme]] = construct_theme_var("atomDark")
cb: ClassVar[Var[Theme]] = construct_theme_var("cb")
coldark_cold: ClassVar[Var[Theme]] = construct_theme_var("coldarkCold")
coldark_dark: ClassVar[Var[Theme]] = construct_theme_var("coldarkDark")
coy: ClassVar[Var[Theme]] = construct_theme_var("coy")
coy_without_shadows: ClassVar[Var[Theme]] = construct_theme_var("coyWithoutShadows")
darcula: ClassVar[Var[Theme]] = construct_theme_var("darcula")
dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
dracula: ClassVar[Var[Theme]] = construct_theme_var("dracula")
duotone_dark: ClassVar[Var[Theme]] = construct_theme_var("duotoneDark")
duotone_earth: ClassVar[Var[Theme]] = construct_theme_var("duotoneEarth")
duotone_forest: ClassVar[Var[Theme]] = construct_theme_var("duotoneForest")
duotone_light: ClassVar[Var[Theme]] = construct_theme_var("duotoneLight")
duotone_sea: ClassVar[Var[Theme]] = construct_theme_var("duotoneSea")
duotone_space: ClassVar[Var[Theme]] = construct_theme_var("duotoneSpace")
funky: ClassVar[Var[Theme]] = construct_theme_var("funky")
ghcolors: ClassVar[Var[Theme]] = construct_theme_var("ghcolors")
gruvbox_dark: ClassVar[Var[Theme]] = construct_theme_var("gruvboxDark")
gruvbox_light: ClassVar[Var[Theme]] = construct_theme_var("gruvboxLight")
holi_theme: ClassVar[Var[Theme]] = construct_theme_var("holiTheme")
hopscotch: ClassVar[Var[Theme]] = construct_theme_var("hopscotch")
light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
lucario: ClassVar[Var[Theme]] = construct_theme_var("lucario")
material_dark: ClassVar[Var[Theme]] = construct_theme_var("materialDark")
material_light: ClassVar[Var[Theme]] = construct_theme_var("materialLight")
material_oceanic: ClassVar[Var[Theme]] = construct_theme_var("materialOceanic")
night_owl: ClassVar[Var[Theme]] = construct_theme_var("nightOwl")
nord: ClassVar[Var[Theme]] = construct_theme_var("nord")
okaidia: ClassVar[Var[Theme]] = construct_theme_var("okaidia")
one_dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
one_light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
pojoaque: ClassVar[Var[Theme]] = construct_theme_var("pojoaque")
prism: ClassVar[Var[Theme]] = construct_theme_var("prism")
shades_of_purple: ClassVar[Var[Theme]] = construct_theme_var("shadesOfPurple")
solarized_dark_atom: ClassVar[Var[Theme]] = construct_theme_var("solarizedDarkAtom")
solarizedlight: ClassVar[Var[Theme]] = construct_theme_var("solarizedlight")
synthwave84: ClassVar[Var[Theme]] = construct_theme_var("synthwave84")
tomorrow: ClassVar[Var[Theme]] = construct_theme_var("tomorrow")
twilight: ClassVar[Var[Theme]] = construct_theme_var("twilight")
vs: ClassVar[Var[Theme]] = construct_theme_var("vs")
vs_dark: ClassVar[Var[Theme]] = construct_theme_var("vsDark")
vsc_dark_plus: ClassVar[Var[Theme]] = construct_theme_var("vscDarkPlus")
xonokai: ClassVar[Var[Theme]] = construct_theme_var("xonokai")
z_touch: ClassVar[Var[Theme]] = construct_theme_var("zTouch")
for theme_name in dir(Theme):
if theme_name.startswith("_"):
continue
setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme))
class CodeBlock(Component):
def add_imports(self) -> ImportDict: ...
@ -351,110 +356,290 @@ class CodeBlock(Component):
def create( # type: ignore
cls,
*children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[Component, bool]] = None,
theme: Optional[
Union[
Var[
Literal[
"a11y-dark",
"atom-dark",
"cb",
"coldark-cold",
"coldark-dark",
"coy",
"coy-without-shadows",
"darcula",
"dark",
"dracula",
"duotone-dark",
"duotone-earth",
"duotone-forest",
"duotone-light",
"duotone-sea",
"duotone-space",
"funky",
"ghcolors",
"gruvbox-dark",
"gruvbox-light",
"holi-theme",
"hopscotch",
"light",
"lucario",
"material-dark",
"material-light",
"material-oceanic",
"night-owl",
"nord",
"okaidia",
"one-dark",
"one-light",
"pojoaque",
"prism",
"shades-of-purple",
"solarized-dark-atom",
"solarizedlight",
"synthwave84",
"tomorrow",
"twilight",
"vs",
"vs-dark",
"vsc-dark-plus",
"xonokai",
"z-touch",
]
],
Literal[
"a11y-dark",
"atom-dark",
"cb",
"coldark-cold",
"coldark-dark",
"coy",
"coy-without-shadows",
"darcula",
"dark",
"dracula",
"duotone-dark",
"duotone-earth",
"duotone-forest",
"duotone-light",
"duotone-sea",
"duotone-space",
"funky",
"ghcolors",
"gruvbox-dark",
"gruvbox-light",
"holi-theme",
"hopscotch",
"light",
"lucario",
"material-dark",
"material-light",
"material-oceanic",
"night-owl",
"nord",
"okaidia",
"one-dark",
"one-light",
"pojoaque",
"prism",
"shades-of-purple",
"solarized-dark-atom",
"solarizedlight",
"synthwave84",
"tomorrow",
"twilight",
"vs",
"vs-dark",
"vsc-dark-plus",
"xonokai",
"z-touch",
],
]
] = None,
theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None,
language: Optional[
Union[
Literal[
"abap",
"abnf",
"actionscript",
"ada",
"agda",
"al",
"antlr4",
"apacheconf",
"apex",
"apl",
"applescript",
"aql",
"arduino",
"arff",
"asciidoc",
"asm6502",
"asmatmel",
"aspnet",
"autohotkey",
"autoit",
"avisynth",
"avro-idl",
"bash",
"basic",
"batch",
"bbcode",
"bicep",
"birb",
"bison",
"bnf",
"brainfuck",
"brightscript",
"bro",
"bsl",
"c",
"cfscript",
"chaiscript",
"cil",
"clike",
"clojure",
"cmake",
"cobol",
"coffeescript",
"concurnas",
"coq",
"core",
"cpp",
"crystal",
"csharp",
"cshtml",
"csp",
"css",
"css-extras",
"csv",
"cypher",
"d",
"dart",
"dataweave",
"dax",
"dhall",
"diff",
"django",
"dns-zone-file",
"docker",
"dot",
"ebnf",
"editorconfig",
"eiffel",
"ejs",
"elixir",
"elm",
"erb",
"erlang",
"etlua",
"excel-formula",
"factor",
"false",
"firestore-security-rules",
"flow",
"fortran",
"fsharp",
"ftl",
"gap",
"gcode",
"gdscript",
"gedcom",
"gherkin",
"git",
"glsl",
"gml",
"gn",
"go",
"go-module",
"graphql",
"groovy",
"haml",
"handlebars",
"haskell",
"haxe",
"hcl",
"hlsl",
"hoon",
"hpkp",
"hsts",
"http",
"ichigojam",
"icon",
"icu-message-format",
"idris",
"iecst",
"ignore",
"index",
"inform7",
"ini",
"io",
"j",
"java",
"javadoc",
"javadoclike",
"javascript",
"javastacktrace",
"jexl",
"jolie",
"jq",
"js-extras",
"js-templates",
"jsdoc",
"json",
"json5",
"jsonp",
"jsstacktrace",
"jsx",
"julia",
"keepalived",
"keyman",
"kotlin",
"kumir",
"kusto",
"latex",
"latte",
"less",
"lilypond",
"liquid",
"lisp",
"livescript",
"llvm",
"log",
"lolcode",
"lua",
"magma",
"makefile",
"markdown",
"markup",
"markup-templating",
"matlab",
"maxscript",
"mel",
"mermaid",
"mizar",
"mongodb",
"monkey",
"moonscript",
"n1ql",
"n4js",
"nand2tetris-hdl",
"naniscript",
"nasm",
"neon",
"nevod",
"nginx",
"nim",
"nix",
"nsis",
"objectivec",
"ocaml",
"opencl",
"openqasm",
"oz",
"parigp",
"parser",
"pascal",
"pascaligo",
"pcaxis",
"peoplecode",
"perl",
"php",
"php-extras",
"phpdoc",
"plsql",
"powerquery",
"powershell",
"processing",
"prolog",
"promql",
"properties",
"protobuf",
"psl",
"pug",
"puppet",
"pure",
"purebasic",
"purescript",
"python",
"q",
"qml",
"qore",
"qsharp",
"r",
"racket",
"reason",
"regex",
"rego",
"renpy",
"rest",
"rip",
"roboconf",
"robotframework",
"ruby",
"rust",
"sas",
"sass",
"scala",
"scheme",
"scss",
"shell-session",
"smali",
"smalltalk",
"smarty",
"sml",
"solidity",
"solution-file",
"soy",
"sparql",
"splunk-spl",
"sqf",
"sql",
"squirrel",
"stan",
"stylus",
"swift",
"systemd",
"t4-cs",
"t4-templating",
"t4-vb",
"tap",
"tcl",
"textile",
"toml",
"tremor",
"tsx",
"tt2",
"turtle",
"twig",
"typescript",
"typoscript",
"unrealscript",
"uorazor",
"uri",
"v",
"vala",
"vbnet",
"velocity",
"verilog",
"vhdl",
"vim",
"visual-basic",
"warpscript",
"wasm",
"web-idl",
"wiki",
"wolfram",
"wren",
"xeora",
"xml-doc",
"xojo",
"xquery",
"yaml",
"yang",
"zig",
],
Var[
Literal[
"abap",
@ -738,6 +923,77 @@ class CodeBlock(Component):
"zig",
]
],
]
] = None,
code: Optional[Union[Var[str], str]] = None,
show_line_numbers: Optional[Union[Var[bool], bool]] = None,
starting_line_number: Optional[Union[Var[int], int]] = None,
wrap_long_lines: Optional[Union[Var[bool], bool]] = None,
custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None,
code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None,
can_copy: Optional[bool] = None,
copy_button: Optional[Union[Component, bool]] = 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[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,
) -> "CodeBlock":
"""Create a text component.
Args:
*children: The children of the component.
theme: The theme to use ("light" or "dark").
language: The language to use.
code: The code to display.
show_line_numbers: If this is enabled line numbers will be shown next to the code block.
starting_line_number: The starting line number to use.
wrap_long_lines: Whether to wrap long lines.
custom_style: A custom style for the code block.
code_tag_props: Props passed down to the code tag.
can_copy: Whether a copy button should appear.
copy_button: A custom copy button to override the default one.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: The props to pass to the component.
Returns:
The text component.
"""
...
def add_style(self): ...
class CodeblockNamespace(ComponentNamespace):
themes = Theme
@staticmethod
def __call__(
*children,
theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None,
language: Optional[
Union[
Literal[
"abap",
"abnf",
@ -1019,6 +1275,289 @@ class CodeBlock(Component):
"yang",
"zig",
],
Var[
Literal[
"abap",
"abnf",
"actionscript",
"ada",
"agda",
"al",
"antlr4",
"apacheconf",
"apex",
"apl",
"applescript",
"aql",
"arduino",
"arff",
"asciidoc",
"asm6502",
"asmatmel",
"aspnet",
"autohotkey",
"autoit",
"avisynth",
"avro-idl",
"bash",
"basic",
"batch",
"bbcode",
"bicep",
"birb",
"bison",
"bnf",
"brainfuck",
"brightscript",
"bro",
"bsl",
"c",
"cfscript",
"chaiscript",
"cil",
"clike",
"clojure",
"cmake",
"cobol",
"coffeescript",
"concurnas",
"coq",
"core",
"cpp",
"crystal",
"csharp",
"cshtml",
"csp",
"css",
"css-extras",
"csv",
"cypher",
"d",
"dart",
"dataweave",
"dax",
"dhall",
"diff",
"django",
"dns-zone-file",
"docker",
"dot",
"ebnf",
"editorconfig",
"eiffel",
"ejs",
"elixir",
"elm",
"erb",
"erlang",
"etlua",
"excel-formula",
"factor",
"false",
"firestore-security-rules",
"flow",
"fortran",
"fsharp",
"ftl",
"gap",
"gcode",
"gdscript",
"gedcom",
"gherkin",
"git",
"glsl",
"gml",
"gn",
"go",
"go-module",
"graphql",
"groovy",
"haml",
"handlebars",
"haskell",
"haxe",
"hcl",
"hlsl",
"hoon",
"hpkp",
"hsts",
"http",
"ichigojam",
"icon",
"icu-message-format",
"idris",
"iecst",
"ignore",
"index",
"inform7",
"ini",
"io",
"j",
"java",
"javadoc",
"javadoclike",
"javascript",
"javastacktrace",
"jexl",
"jolie",
"jq",
"js-extras",
"js-templates",
"jsdoc",
"json",
"json5",
"jsonp",
"jsstacktrace",
"jsx",
"julia",
"keepalived",
"keyman",
"kotlin",
"kumir",
"kusto",
"latex",
"latte",
"less",
"lilypond",
"liquid",
"lisp",
"livescript",
"llvm",
"log",
"lolcode",
"lua",
"magma",
"makefile",
"markdown",
"markup",
"markup-templating",
"matlab",
"maxscript",
"mel",
"mermaid",
"mizar",
"mongodb",
"monkey",
"moonscript",
"n1ql",
"n4js",
"nand2tetris-hdl",
"naniscript",
"nasm",
"neon",
"nevod",
"nginx",
"nim",
"nix",
"nsis",
"objectivec",
"ocaml",
"opencl",
"openqasm",
"oz",
"parigp",
"parser",
"pascal",
"pascaligo",
"pcaxis",
"peoplecode",
"perl",
"php",
"php-extras",
"phpdoc",
"plsql",
"powerquery",
"powershell",
"processing",
"prolog",
"promql",
"properties",
"protobuf",
"psl",
"pug",
"puppet",
"pure",
"purebasic",
"purescript",
"python",
"q",
"qml",
"qore",
"qsharp",
"r",
"racket",
"reason",
"regex",
"rego",
"renpy",
"rest",
"rip",
"roboconf",
"robotframework",
"ruby",
"rust",
"sas",
"sass",
"scala",
"scheme",
"scss",
"shell-session",
"smali",
"smalltalk",
"smarty",
"sml",
"solidity",
"solution-file",
"soy",
"sparql",
"splunk-spl",
"sqf",
"sql",
"squirrel",
"stan",
"stylus",
"swift",
"systemd",
"t4-cs",
"t4-templating",
"t4-vb",
"tap",
"tcl",
"textile",
"toml",
"tremor",
"tsx",
"tt2",
"turtle",
"twig",
"typescript",
"typoscript",
"unrealscript",
"uorazor",
"uri",
"v",
"vala",
"vbnet",
"velocity",
"verilog",
"vhdl",
"vim",
"visual-basic",
"warpscript",
"wasm",
"web-idl",
"wiki",
"wolfram",
"wren",
"xeora",
"xml-doc",
"xojo",
"xquery",
"yaml",
"yang",
"zig",
]
],
]
] = None,
code: Optional[Union[Var[str], str]] = None,
@ -1026,66 +1565,36 @@ class CodeBlock(Component):
starting_line_number: Optional[Union[Var[int], int]] = None,
wrap_long_lines: Optional[Union[Var[bool], bool]] = None,
custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None,
code_tag_props: Optional[Union[Var[Dict[str, str]], Dict[str, str]]] = None,
code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None,
can_copy: Optional[bool] = None,
copy_button: Optional[Union[Component, bool]] = 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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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,
) -> "CodeBlock":
"""Create a text component.
Args:
*children: The children of the component.
can_copy: Whether a copy button should appears.
copy_button: A custom copy button to override the default one.
theme: The theme to use ("light" or "dark").
language: The language to use.
code: The code to display.
@ -1094,6 +1603,8 @@ class CodeBlock(Component):
wrap_long_lines: Whether to wrap long lines.
custom_style: A custom style for the code block.
code_tag_props: Props passed down to the code tag.
can_copy: Whether a copy button should appear.
copy_button: A custom copy button to override the default one.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
@ -1107,8 +1618,4 @@ class CodeBlock(Component):
"""
...
def add_style(self): ...
@staticmethod
def convert_theme_name(theme) -> str: ...
code_block = CodeBlock.create
code_block = CodeblockNamespace()

View File

@ -3,18 +3,20 @@
from __future__ import annotations
from enum import Enum
from typing import Any, Dict, List, Literal, Optional, Union
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
from typing_extensions import TypedDict
from reflex.base import Base
from reflex.components.component import Component, NoSSRComponent
from reflex.components.literals import LiteralRowMarker
from reflex.event import EventHandler
from reflex.ivars.base import ImmutableVar
from reflex.ivars.sequence import ArrayVar
from reflex.event import EventHandler, empty_event, identity_event
from reflex.utils import console, format, types
from reflex.utils.imports import ImportDict, ImportVar
from reflex.utils.serializers import serializer
from reflex.vars import Var, get_unique_variable_name
from reflex.vars import get_unique_variable_name
from reflex.vars.base import Var
from reflex.vars.sequence import ArrayVar
# TODO: Fix the serialization issue for custom types.
@ -107,17 +109,76 @@ class DataEditorTheme(Base):
text_medium: Optional[str] = None
def on_edit_spec(pos, data: dict[str, Any]):
"""The on edit spec function.
class Bounds(TypedDict):
"""The bounds of the group header."""
Args:
pos: The position of the edit event.
data: The data of the edit event.
x: int
y: int
width: int
height: int
Returns:
The position and data.
"""
return [pos, data]
class CompatSelection(TypedDict):
"""The selection."""
items: list
class Rectangle(TypedDict):
"""The bounds of the group header."""
x: int
y: int
width: int
height: int
class GridSelectionCurrent(TypedDict):
"""The current selection."""
cell: tuple[int, int]
range: Rectangle
rangeStack: list[Rectangle]
class GridSelection(TypedDict):
"""The grid selection."""
current: Optional[GridSelectionCurrent]
columns: CompatSelection
rows: CompatSelection
class GroupHeaderClickedEventArgs(TypedDict):
"""The arguments for the group header clicked event."""
kind: str
group: str
location: tuple[int, int]
bounds: Bounds
isEdge: bool
shiftKey: bool
ctrlKey: bool
metaKey: bool
isTouch: bool
localEventX: int
localEventY: int
button: int
buttons: int
scrollEdge: tuple[int, int]
class GridCell(TypedDict):
"""The grid cell."""
span: Optional[List[int]]
class GridColumn(TypedDict):
"""The grid column."""
title: str
group: Optional[str]
class DataEditor(NoSSRComponent):
@ -125,10 +186,10 @@ class DataEditor(NoSSRComponent):
tag = "DataEditor"
is_default = True
library: str = "@glideapps/glide-data-grid@^5.3.0"
library: str = "@glideapps/glide-data-grid@^6.0.3"
lib_dependencies: List[str] = [
"lodash@^4.17.21",
"marked@^4.0.10",
"marked@^14.1.2",
"react-responsive-carousel@^3.2.7",
]
@ -145,7 +206,7 @@ class DataEditor(NoSSRComponent):
get_cell_content: Var[str]
# Allow selection for copying.
get_cell_for_selection: Var[bool]
get_cells_for_selection: Var[bool]
# Allow paste.
on_paste: Var[bool]
@ -223,52 +284,56 @@ class DataEditor(NoSSRComponent):
theme: Var[Union[DataEditorTheme, Dict]]
# Fired when a cell is activated.
on_cell_activated: EventHandler[lambda pos: [pos]]
on_cell_activated: EventHandler[identity_event(Tuple[int, int])]
# Fired when a cell is clicked.
on_cell_clicked: EventHandler[lambda pos: [pos]]
on_cell_clicked: EventHandler[identity_event(Tuple[int, int])]
# Fired when a cell is right-clicked.
on_cell_context_menu: EventHandler[lambda pos: [pos]]
on_cell_context_menu: EventHandler[identity_event(Tuple[int, int])]
# Fired when a cell is edited.
on_cell_edited: EventHandler[on_edit_spec]
on_cell_edited: EventHandler[identity_event(Tuple[int, int], GridCell)]
# Fired when a group header is clicked.
on_group_header_clicked: EventHandler[on_edit_spec]
on_group_header_clicked: EventHandler[identity_event(Tuple[int, int], GridCell)]
# Fired when a group header is right-clicked.
on_group_header_context_menu: EventHandler[lambda grp_idx, data: [grp_idx, data]]
on_group_header_context_menu: EventHandler[
identity_event(int, GroupHeaderClickedEventArgs)
]
# Fired when a group header is renamed.
on_group_header_renamed: EventHandler[lambda idx, val: [idx, val]]
on_group_header_renamed: EventHandler[identity_event(str, str)]
# Fired when a header is clicked.
on_header_clicked: EventHandler[lambda pos: [pos]]
on_header_clicked: EventHandler[identity_event(Tuple[int, int])]
# Fired when a header is right-clicked.
on_header_context_menu: EventHandler[lambda pos: [pos]]
on_header_context_menu: EventHandler[identity_event(Tuple[int, int])]
# Fired when a header menu item is clicked.
on_header_menu_click: EventHandler[lambda col, pos: [col, pos]]
on_header_menu_click: EventHandler[identity_event(int, Rectangle)]
# Fired when an item is hovered.
on_item_hovered: EventHandler[lambda pos: [pos]]
on_item_hovered: EventHandler[identity_event(Tuple[int, int])]
# Fired when a selection is deleted.
on_delete: EventHandler[lambda selection: [selection]]
on_delete: EventHandler[identity_event(GridSelection)]
# Fired when editing is finished.
on_finished_editing: EventHandler[lambda new_value, movement: [new_value, movement]]
on_finished_editing: EventHandler[
identity_event(Union[GridCell, None], tuple[int, int])
]
# Fired when a row is appended.
on_row_appended: EventHandler[lambda: []]
on_row_appended: EventHandler[empty_event]
# Fired when the selection is cleared.
on_selection_cleared: EventHandler[lambda: []]
on_selection_cleared: EventHandler[empty_event]
# Fired when a column is resized.
on_column_resize: EventHandler[lambda col, width: [col, width]]
on_column_resize: EventHandler[identity_event(GridColumn, int)]
def add_imports(self) -> ImportDict:
"""Add imports for the component.
@ -279,7 +344,7 @@ class DataEditor(NoSSRComponent):
return {
"": f"{format.format_library_name(self.library)}/dist/index.css",
self.library: "GridCellKind",
"/utils/helpers/dataeditor.js": ImportVar(
"$/utils/helpers/dataeditor.js": ImportVar(
tag="formatDataEditorCells", is_default=False, install=False
),
}
@ -295,7 +360,7 @@ class DataEditor(NoSSRComponent):
# Define the name of the getData callback associated with this component and assign to get_cell_content.
data_callback = f"getData_{editor_id}"
self.get_cell_content = ImmutableVar.create(data_callback) # type: ignore
self.get_cell_content = Var(_js_expr=data_callback) # type: ignore
code = [f"function {data_callback}([col, row])" "{"]
@ -329,22 +394,20 @@ class DataEditor(NoSSRComponent):
columns = props.get("columns", [])
data = props.get("data", [])
rows = props.get("rows", None)
rows = props.get("rows")
# If rows is not provided, determine from data.
if rows is None:
if isinstance(data, ImmutableVar) and not isinstance(data, ArrayVar):
if isinstance(data, Var) and not isinstance(data, ArrayVar):
raise ValueError(
"DataEditor data must be an ArrayVar if rows is not provided."
)
props["rows"] = (
data.length() if isinstance(data, ImmutableVar) else len(data)
)
props["rows"] = data.length() if isinstance(data, Var) else len(data)
if not isinstance(columns, ImmutableVar) and len(columns):
if not isinstance(columns, Var) and len(columns):
if (
types.is_dataframe(type(data))
or isinstance(data, ImmutableVar)
or isinstance(data, Var)
and types.is_dataframe(data._var_type)
):
raise ValueError(
@ -361,7 +424,7 @@ class DataEditor(NoSSRComponent):
props["theme"] = DataEditorTheme(**theme)
# Allow by default to select a region of cells in the grid.
props.setdefault("get_cell_for_selection", True)
props.setdefault("get_cells_for_selection", True)
# Disable on_paste by default if not provided.
props.setdefault("on_paste", False)

View File

@ -4,16 +4,17 @@
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
from enum import Enum
from typing import Any, Callable, Dict, List, Literal, Optional, Union, overload
from typing import Any, Dict, List, Literal, Optional, Union, overload
from typing_extensions import TypedDict
from reflex.base import Base
from reflex.components.component import NoSSRComponent
from reflex.event import EventHandler, EventSpec
from reflex.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.utils.imports import ImportDict
from reflex.utils.serializers import serializer
from reflex.vars import Var
from reflex.vars.base import Var
class GridColumnIcons(Enum):
Array = "array"
@ -77,7 +78,53 @@ class DataEditorTheme(Base):
text_light: Optional[str]
text_medium: Optional[str]
def on_edit_spec(pos, data: dict[str, Any]): ...
class Bounds(TypedDict):
x: int
y: int
width: int
height: int
class CompatSelection(TypedDict):
items: list
class Rectangle(TypedDict):
x: int
y: int
width: int
height: int
class GridSelectionCurrent(TypedDict):
cell: tuple[int, int]
range: Rectangle
rangeStack: list[Rectangle]
class GridSelection(TypedDict):
current: Optional[GridSelectionCurrent]
columns: CompatSelection
rows: CompatSelection
class GroupHeaderClickedEventArgs(TypedDict):
kind: str
group: str
location: tuple[int, int]
bounds: Bounds
isEdge: bool
shiftKey: bool
ctrlKey: bool
metaKey: bool
isTouch: bool
localEventX: int
localEventY: int
button: int
buttons: int
scrollEdge: tuple[int, int]
class GridCell(TypedDict):
span: Optional[List[int]]
class GridColumn(TypedDict):
title: str
group: Optional[str]
class DataEditor(NoSSRComponent):
def add_imports(self) -> ImportDict: ...
@ -89,11 +136,11 @@ class DataEditor(NoSSRComponent):
*children,
rows: Optional[Union[Var[int], int]] = None,
columns: Optional[
Union[Var[List[Dict[str, Any]]], List[Dict[str, Any]]]
Union[List[Dict[str, Any]], Var[List[Dict[str, Any]]]]
] = None,
data: Optional[Union[Var[List[List[Any]]], List[List[Any]]]] = None,
data: Optional[Union[List[List[Any]], Var[List[List[Any]]]]] = None,
get_cell_content: Optional[Union[Var[str], str]] = None,
get_cell_for_selection: Optional[Union[Var[bool], bool]] = None,
get_cells_for_selection: Optional[Union[Var[bool], bool]] = None,
on_paste: Optional[Union[Var[bool], bool]] = None,
draw_focus_ring: Optional[Union[Var[bool], bool]] = None,
fixed_shadow_x: Optional[Union[Var[bool], bool]] = None,
@ -107,8 +154,8 @@ class DataEditor(NoSSRComponent):
row_height: Optional[Union[Var[int], int]] = None,
row_markers: Optional[
Union[
Var[Literal["none", "number", "checkbox", "both", "clickable-number"]],
Literal["none", "number", "checkbox", "both", "clickable-number"],
Literal["both", "checkbox", "clickable-number", "none", "number"],
Var[Literal["both", "checkbox", "clickable-number", "none", "number"]],
]
] = None,
row_marker_start_index: Optional[Union[Var[int], int]] = None,
@ -118,8 +165,8 @@ class DataEditor(NoSSRComponent):
vertical_border: Optional[Union[Var[bool], bool]] = None,
column_select: Optional[
Union[
Var[Literal["none", "single", "multi"]],
Literal["none", "single", "multi"],
Literal["multi", "none", "single"],
Var[Literal["multi", "none", "single"]],
]
] = None,
prevent_diagonal_scrolling: Optional[Union[Var[bool], bool]] = None,
@ -128,107 +175,49 @@ class DataEditor(NoSSRComponent):
scroll_offset_x: Optional[Union[Var[int], int]] = None,
scroll_offset_y: Optional[Union[Var[int], int]] = None,
theme: Optional[
Union[Var[Union[DataEditorTheme, Dict]], DataEditorTheme, Dict]
Union[DataEditorTheme, Dict, Var[Union[DataEditorTheme, Dict]]]
] = 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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_cell_activated: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_cell_clicked: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_cell_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_cell_edited: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_column_resize: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_delete: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
on_blur: Optional[EventType[[]]] = None,
on_cell_activated: Optional[EventType[tuple[int, int]]] = None,
on_cell_clicked: Optional[EventType[tuple[int, int]]] = None,
on_cell_context_menu: Optional[EventType[tuple[int, int]]] = None,
on_cell_edited: Optional[EventType[tuple[int, int], GridCell]] = None,
on_click: Optional[EventType[[]]] = None,
on_column_resize: Optional[EventType[GridColumn, int]] = None,
on_context_menu: Optional[EventType[[]]] = None,
on_delete: Optional[EventType[GridSelection]] = None,
on_double_click: Optional[EventType[[]]] = None,
on_finished_editing: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_group_header_clicked: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
EventType[Union[GridCell, None], tuple[int, int]]
] = None,
on_focus: Optional[EventType[[]]] = None,
on_group_header_clicked: Optional[EventType[tuple[int, int], GridCell]] = None,
on_group_header_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_group_header_renamed: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_header_clicked: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_header_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_header_menu_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_item_hovered: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_row_appended: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_selection_cleared: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
EventType[int, GroupHeaderClickedEventArgs]
] = None,
on_group_header_renamed: Optional[EventType[str, str]] = None,
on_header_clicked: Optional[EventType[tuple[int, int]]] = None,
on_header_context_menu: Optional[EventType[tuple[int, int]]] = None,
on_header_menu_click: Optional[EventType[int, Rectangle]] = None,
on_item_hovered: Optional[EventType[tuple[int, int]]] = 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_row_appended: Optional[EventType[[]]] = None,
on_scroll: Optional[EventType[[]]] = None,
on_selection_cleared: Optional[EventType[[]]] = None,
on_unmount: Optional[EventType[[]]] = None,
**props,
) -> "DataEditor":
"""Create the DataEditor component.
@ -239,7 +228,7 @@ class DataEditor(NoSSRComponent):
columns: Headers of the columns for the data grid.
data: The data.
get_cell_content: The name of the callback used to find the data to display.
get_cell_for_selection: Allow selection for copying.
get_cells_for_selection: Allow selection for copying.
on_paste: Allow paste.
draw_focus_ring: Controls the drawing of the focus ring.
fixed_shadow_x: Enables or disables the overlay shadow when scrolling horizontally.
@ -264,6 +253,22 @@ class DataEditor(NoSSRComponent):
scroll_offset_x: Initial scroll offset on the horizontal axis.
scroll_offset_y: Initial scroll offset on the vertical axis.
theme: global theme
on_cell_activated: Fired when a cell is activated.
on_cell_clicked: Fired when a cell is clicked.
on_cell_context_menu: Fired when a cell is right-clicked.
on_cell_edited: Fired when a cell is edited.
on_group_header_clicked: Fired when a group header is clicked.
on_group_header_context_menu: Fired when a group header is right-clicked.
on_group_header_renamed: Fired when a group header is renamed.
on_header_clicked: Fired when a header is clicked.
on_header_context_menu: Fired when a header is right-clicked.
on_header_menu_click: Fired when a header menu item is clicked.
on_item_hovered: Fired when an item is hovered.
on_delete: Fired when a selection is deleted.
on_finished_editing: Fired when editing is finished.
on_row_appended: Fired when a row is appended.
on_selection_cleared: Fired when the selection is cleared.
on_column_resize: Fired when a column is resized.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.

View File

@ -12,31 +12,33 @@ def logo(**props):
Returns:
The logo component.
"""
light_mode_logo = """<svg width="56" height="12" viewBox="0 0 56 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11.6V0.400024H8.96V4.88002H6.72V2.64002H2.24V4.88002H6.72V7.12002H2.24V11.6H0ZM6.72 11.6V7.12002H8.96V11.6H6.72Z" fill="#110F1F"/>
<path d="M11.2 11.6V0.400024H17.92V2.64002H13.44V4.88002H17.92V7.12002H13.44V9.36002H17.92V11.6H11.2Z" fill="#110F1F"/>
<path d="M20.16 11.6V0.400024H26.88V2.64002H22.4V4.88002H26.88V7.12002H22.4V11.6H20.16Z" fill="#110F1F"/>
<path d="M29.12 11.6V0.400024H31.36V9.36002H35.84V11.6H29.12Z" fill="#110F1F"/>
<path d="M38.08 11.6V0.400024H44.8V2.64002H40.32V4.88002H44.8V7.12002H40.32V9.36002H44.8V11.6H38.08Z" fill="#110F1F"/>
<path d="M47.04 4.88002V0.400024H49.28V4.88002H47.04ZM53.76 4.88002V0.400024H56V4.88002H53.76ZM49.28 7.12002V4.88002H53.76V7.12002H49.28ZM47.04 11.6V7.12002H49.28V11.6H47.04ZM53.76 11.6V7.12002H56V11.6H53.76Z" fill="#110F1F"/>
</svg>"""
dark_mode_logo = """<svg width="56" height="12" viewBox="0 0 56 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z" fill="white"/>
<path d="M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z" fill="white"/>
<path d="M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z" fill="white"/>
<path d="M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z" fill="white"/>
<path d="M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z" fill="white"/>
<path d="M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z" fill="white"/>
</svg>"""
def logo_path(d):
return rx.el.svg.path(
d=d,
fill=rx.color_mode_cond("#110F1F", "white"),
)
paths = [
"M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z",
"M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z",
"M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z",
"M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z",
"M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z",
"M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z",
]
return rx.center(
rx.link(
rx.hstack(
"Built with ",
rx.color_mode_cond(
rx.html(light_mode_logo),
rx.html(dark_mode_logo),
rx.el.svg(
*[logo_path(d) for d in paths],
width="56",
height="12",
viewBox="0 0 56 12",
fill="none",
xmlns="http://www.w3.org/2000/svg",
),
text_align="center",
align="center",

View File

@ -0,0 +1,846 @@
"""Shiki syntax hghlighter component."""
from __future__ import annotations
import re
from collections import defaultdict
from typing import Any, Literal, Optional, Union
from reflex.base import Base
from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.colors import color
from reflex.components.core.cond import color_mode_cond
from reflex.components.el.elements.forms import Button
from reflex.components.lucide.icon import Icon
from reflex.components.props import NoExtrasAllowedProps
from reflex.components.radix.themes.layout.box import Box
from reflex.event import call_script, set_clipboard
from reflex.style import Style
from reflex.utils.exceptions import VarTypeError
from reflex.utils.imports import ImportVar
from reflex.vars.base import LiteralVar, Var
from reflex.vars.function import FunctionStringVar
from reflex.vars.sequence import StringVar, string_replace_operation
def copy_script() -> Any:
"""Copy script for the code block and modify the child SVG element.
Returns:
Any: The result of calling the script.
"""
return call_script(
f"""
// Event listener for the parent click
document.addEventListener('click', function(event) {{
// Find the closest button (parent element)
const parent = event.target.closest('button');
// If the parent is found
if (parent) {{
// Find the SVG element within the parent
const svgIcon = parent.querySelector('svg');
// If the SVG exists, proceed with the script
if (svgIcon) {{
const originalPath = svgIcon.innerHTML;
const checkmarkPath = '<polyline points="20 6 9 17 4 12"></polyline>'; // Checkmark SVG path
function transition(element, scale, opacity) {{
element.style.transform = `scale(${{scale}})`;
element.style.opacity = opacity;
}}
// Animate the SVG
transition(svgIcon, 0, '0');
setTimeout(() => {{
svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark
svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary
transition(svgIcon, 1, '1');
setTimeout(() => {{
transition(svgIcon, 0, '0');
setTimeout(() => {{
svgIcon.innerHTML = originalPath; // Restore original SVG content
transition(svgIcon, 1, '1');
}}, 125);
}}, 600);
}}, 125);
}} else {{
// console.error('SVG element not found within the parent.');
}}
}} else {{
// console.error('Parent element not found.');
}}
}});
"""
)
SHIKIJS_TRANSFORMER_FNS = {
"transformerNotationDiff",
"transformerNotationHighlight",
"transformerNotationWordHighlight",
"transformerNotationFocus",
"transformerNotationErrorLevel",
"transformerRenderWhitespace",
"transformerMetaHighlight",
"transformerMetaWordHighlight",
"transformerCompactLineOptions",
# TODO: this transformer when included adds a weird behavior which removes other code lines. Need to figure out why.
# "transformerRemoveLineBreak",
"transformerRemoveNotationEscape",
}
LINE_NUMBER_STYLING = {
"code": {
"counter-reset": "step",
"counter-increment": "step 0",
"display": "grid",
"line-height": "1.7",
"font-size": "0.875em",
},
"code .line::before": {
"content": "counter(step)",
"counter-increment": "step",
"width": "1rem",
"margin-right": "1.5rem",
"display": "inline-block",
"text-align": "right",
"color": "rgba(115,138,148,.4)",
},
}
BOX_PARENT_STYLING = {
"pre": {
"margin": "0",
"padding": "24px",
"background": "transparent",
"overflow-x": "auto",
"border-radius": "6px",
},
}
THEME_MAPPING = {
"light": "one-light",
"dark": "one-dark-pro",
"a11y-dark": "github-dark",
}
LANGUAGE_MAPPING = {"bash": "shellscript"}
LiteralCodeLanguage = Literal[
"abap",
"actionscript-3",
"ada",
"angular-html",
"angular-ts",
"apache",
"apex",
"apl",
"applescript",
"ara",
"asciidoc",
"asm",
"astro",
"awk",
"ballerina",
"bat",
"beancount",
"berry",
"bibtex",
"bicep",
"blade",
"c",
"cadence",
"clarity",
"clojure",
"cmake",
"cobol",
"codeowners",
"codeql",
"coffee",
"common-lisp",
"coq",
"cpp",
"crystal",
"csharp",
"css",
"csv",
"cue",
"cypher",
"d",
"dart",
"dax",
"desktop",
"diff",
"docker",
"dotenv",
"dream-maker",
"edge",
"elixir",
"elm",
"emacs-lisp",
"erb",
"erlang",
"fennel",
"fish",
"fluent",
"fortran-fixed-form",
"fortran-free-form",
"fsharp",
"gdresource",
"gdscript",
"gdshader",
"genie",
"gherkin",
"git-commit",
"git-rebase",
"gleam",
"glimmer-js",
"glimmer-ts",
"glsl",
"gnuplot",
"go",
"graphql",
"groovy",
"hack",
"haml",
"handlebars",
"haskell",
"haxe",
"hcl",
"hjson",
"hlsl",
"html",
"html-derivative",
"http",
"hxml",
"hy",
"imba",
"ini",
"java",
"javascript",
"jinja",
"jison",
"json",
"json5",
"jsonc",
"jsonl",
"jsonnet",
"jssm",
"jsx",
"julia",
"kotlin",
"kusto",
"latex",
"lean",
"less",
"liquid",
"log",
"logo",
"lua",
"luau",
"make",
"markdown",
"marko",
"matlab",
"mdc",
"mdx",
"mermaid",
"mojo",
"move",
"narrat",
"nextflow",
"nginx",
"nim",
"nix",
"nushell",
"objective-c",
"objective-cpp",
"ocaml",
"pascal",
"perl",
"php",
"plain",
"plsql",
"po",
"postcss",
"powerquery",
"powershell",
"prisma",
"prolog",
"proto",
"pug",
"puppet",
"purescript",
"python",
"qml",
"qmldir",
"qss",
"r",
"racket",
"raku",
"razor",
"reg",
"regexp",
"rel",
"riscv",
"rst",
"ruby",
"rust",
"sas",
"sass",
"scala",
"scheme",
"scss",
"shaderlab",
"shellscript",
"shellsession",
"smalltalk",
"solidity",
"soy",
"sparql",
"splunk",
"sql",
"ssh-config",
"stata",
"stylus",
"svelte",
"swift",
"system-verilog",
"systemd",
"tasl",
"tcl",
"templ",
"terraform",
"tex",
"toml",
"ts-tags",
"tsv",
"tsx",
"turtle",
"twig",
"typescript",
"typespec",
"typst",
"v",
"vala",
"vb",
"verilog",
"vhdl",
"viml",
"vue",
"vue-html",
"vyper",
"wasm",
"wenyan",
"wgsl",
"wikitext",
"wolfram",
"xml",
"xsl",
"yaml",
"zenscript",
"zig",
]
LiteralCodeTheme = Literal[
"andromeeda",
"aurora-x",
"ayu-dark",
"catppuccin-frappe",
"catppuccin-latte",
"catppuccin-macchiato",
"catppuccin-mocha",
"dark-plus",
"dracula",
"dracula-soft",
"everforest-dark",
"everforest-light",
"github-dark",
"github-dark-default",
"github-dark-dimmed",
"github-dark-high-contrast",
"github-light",
"github-light-default",
"github-light-high-contrast",
"houston",
"laserwave",
"light-plus",
"material-theme",
"material-theme-darker",
"material-theme-lighter",
"material-theme-ocean",
"material-theme-palenight",
"min-dark",
"min-light",
"monokai",
"night-owl",
"nord",
"one-dark-pro",
"one-light",
"plastic",
"poimandres",
"red",
# rose-pine themes dont work with the current version of shikijs transformers
# https://github.com/shikijs/shiki/issues/730
"rose-pine",
"rose-pine-dawn",
"rose-pine-moon",
"slack-dark",
"slack-ochin",
"snazzy-light",
"solarized-dark",
"solarized-light",
"synthwave-84",
"tokyo-night",
"vesper",
"vitesse-black",
"vitesse-dark",
"vitesse-light",
]
class Position(NoExtrasAllowedProps):
"""Position of the decoration."""
line: int
character: int
class ShikiDecorations(NoExtrasAllowedProps):
"""Decorations for the code block."""
start: Union[int, Position]
end: Union[int, Position]
tag_name: str = "span"
properties: dict[str, Any] = {}
always_wrap: bool = False
class ShikiBaseTransformers(Base):
"""Base for creating transformers."""
library: str
fns: list[FunctionStringVar]
style: Optional[Style]
class ShikiJsTransformer(ShikiBaseTransformers):
"""A Wrapped shikijs transformer."""
library: str = "@shikijs/transformers"
fns: list[FunctionStringVar] = [
FunctionStringVar.create(fn) for fn in SHIKIJS_TRANSFORMER_FNS
]
style: Optional[Style] = Style(
{
"code": {"line-height": "1.7", "font-size": "0.875em", "display": "grid"},
# Diffs
".diff": {
"margin": "0 -24px",
"padding": "0 24px",
"width": "calc(100% + 48px)",
"display": "inline-block",
},
".diff.add": {
"background-color": "rgba(16, 185, 129, .14)",
"position": "relative",
},
".diff.remove": {
"background-color": "rgba(244, 63, 94, .14)",
"opacity": "0.7",
"position": "relative",
},
".diff.remove:after": {
"position": "absolute",
"left": "10px",
"content": "'-'",
"color": "#b34e52",
},
".diff.add:after": {
"position": "absolute",
"left": "10px",
"content": "'+'",
"color": "#18794e",
},
# Highlight
".highlighted": {
"background-color": "rgba(142, 150, 170, .14)",
"margin": "0 -24px",
"padding": "0 24px",
"width": "calc(100% + 48px)",
"display": "inline-block",
},
".highlighted.error": {
"background-color": "rgba(244, 63, 94, .14)",
},
".highlighted.warning": {
"background-color": "rgba(234, 179, 8, .14)",
},
# Highlighted Word
".highlighted-word": {
"background-color": color("gray", 2),
"border": f"1px solid {color('gray', 5)}",
"padding": "1px 3px",
"margin": "-1px -3px",
"border-radius": "4px",
},
# Focused Lines
".has-focused .line:not(.focused)": {
"opacity": "0.7",
"filter": "blur(0.095rem)",
"transition": "filter .35s, opacity .35s",
},
".has-focused:hover .line:not(.focused)": {
"opacity": "1",
"filter": "none",
},
# White Space
# ".tab, .space": {
# "position": "relative",
# },
# ".tab::before": {
# "content": "'⇥'",
# "position": "absolute",
# "opacity": "0.3",
# },
# ".space::before": {
# "content": "'·'",
# "position": "absolute",
# "opacity": "0.3",
# },
}
)
def __init__(self, **kwargs):
"""Initialize the transformer.
Args:
kwargs: Kwargs to initialize the props.
"""
fns = kwargs.pop("fns", None)
style = kwargs.pop("style", None)
if fns:
kwargs["fns"] = [
(
FunctionStringVar.create(x)
if not isinstance(x, FunctionStringVar)
else x
)
for x in fns
]
if style:
kwargs["style"] = Style(style)
super().__init__(**kwargs)
class ShikiCodeBlock(Component):
"""A Code block."""
library = "/components/shiki/code"
tag = "Code"
alias = "ShikiCode"
lib_dependencies: list[str] = ["shiki"]
# The language to use.
language: Var[LiteralCodeLanguage] = Var.create("python")
# The theme to use ("light" or "dark").
theme: Var[LiteralCodeTheme] = Var.create("one-light")
# The set of themes to use for different modes.
themes: Var[Union[list[dict[str, Any]], dict[str, str]]]
# The code to display.
code: Var[str]
# The transformers to use for the syntax highlighter.
transformers: Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]] = Var.create(
[]
)
# The decorations to use for the syntax highlighter.
decorations: Var[list[ShikiDecorations]] = Var.create([])
@classmethod
def create(
cls,
*children,
**props,
) -> Component:
"""Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/).
Args:
*children: The children of the component.
**props: The props to pass to the component.
Returns:
The code block component.
"""
# Separate props for the code block and the wrapper
code_block_props = {}
code_wrapper_props = {}
decorations = props.pop("decorations", [])
class_props = cls.get_props()
# Distribute props between the code block and wrapper
for key, value in props.items():
(code_block_props if key in class_props else code_wrapper_props)[key] = (
value
)
# cast decorations into ShikiDecorations.
decorations = [
ShikiDecorations(**decoration)
if not isinstance(decoration, ShikiDecorations)
else decoration
for decoration in decorations
]
code_block_props["decorations"] = decorations
code_block_props["code"] = children[0]
code_block = super().create(**code_block_props)
transformer_styles = {}
# Collect styles from transformers and wrapper
for transformer in code_block.transformers._var_value: # type: ignore
if isinstance(transformer, ShikiBaseTransformers) and transformer.style:
transformer_styles.update(transformer.style)
transformer_styles.update(code_wrapper_props.pop("style", {}))
return Box.create(
code_block,
*children[1:],
style=Style({**transformer_styles, **BOX_PARENT_STYLING}),
**code_wrapper_props,
)
def add_imports(self) -> dict[str, list[str]]:
"""Add the necessary imports.
We add all referenced transformer functions as imports from their corresponding
libraries.
Returns:
Imports for the component.
"""
imports = defaultdict(list)
for transformer in self.transformers._var_value:
if isinstance(transformer, ShikiBaseTransformers):
imports[transformer.library].extend(
[ImportVar(tag=str(fn)) for fn in transformer.fns]
)
(
self.lib_dependencies.append(transformer.library)
if transformer.library not in self.lib_dependencies
else None
)
return imports
@classmethod
def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers:
"""Create a transformer from a third party library.
Args:
library: The name of the library.
fns: The str names of the functions/callables to invoke from the library.
Returns:
A transformer for the specified library.
Raises:
ValueError: If a supplied function name is not valid str.
"""
if any(not isinstance(fn_name, str) for fn_name in fns):
raise ValueError(
f"the function names should be str names of functions in the specified transformer: {library!r}"
)
return ShikiBaseTransformers( # type: ignore
library=library, fns=[FunctionStringVar.create(fn) for fn in fns]
)
def _render(self, props: dict[str, Any] | None = None):
"""Renders the component with the given properties, processing transformers if present.
Args:
props: Optional properties to pass to the render function.
Returns:
Rendered component output.
"""
# Ensure props is initialized from class attributes if not provided
props = props or {
attr.rstrip("_"): getattr(self, attr) for attr in self.get_props()
}
# Extract transformers and apply transformations
transformers = props.get("transformers")
if transformers is not None:
transformed_values = self._process_transformers(transformers._var_value)
props["transformers"] = LiteralVar.create(transformed_values)
return super()._render(props)
def _process_transformers(self, transformer_list: list) -> list:
"""Processes a list of transformers, applying transformations where necessary.
Args:
transformer_list: List of transformer objects or values.
Returns:
list: A list of transformed values.
"""
processed = []
for transformer in transformer_list:
if isinstance(transformer, ShikiBaseTransformers):
processed.extend(fn.call() for fn in transformer.fns)
else:
processed.append(transformer)
return processed
class ShikiHighLevelCodeBlock(ShikiCodeBlock):
"""High level component for the shiki syntax highlighter."""
# If this is enabled, the default transformers(shikijs transformer) will be used.
use_transformers: Var[bool]
# If this is enabled line numbers will be shown next to the code block.
show_line_numbers: Var[bool]
# Whether a copy button should appear.
can_copy: bool = False
# copy_button: A custom copy button to override the default one.
copy_button: Optional[Union[Component, bool]] = None
@classmethod
def create(
cls,
*children,
**props,
) -> Component:
"""Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/).
Args:
*children: The children of the component.
**props: The props to pass to the component.
Returns:
The code block component.
"""
use_transformers = props.pop("use_transformers", False)
show_line_numbers = props.pop("show_line_numbers", False)
language = props.pop("language", None)
can_copy = props.pop("can_copy", False)
copy_button = props.pop("copy_button", None)
if use_transformers:
props["transformers"] = [ShikiJsTransformer()]
if language is not None:
props["language"] = cls._map_languages(language)
# line numbers are generated via css
if show_line_numbers:
props["style"] = {**LINE_NUMBER_STYLING, **props.get("style", {})}
theme = props.pop("theme", None)
props["theme"] = props["theme"] = (
cls._map_themes(theme)
if theme is not None
else color_mode_cond( # Default color scheme responds to global color mode.
light="one-light",
dark="one-dark-pro",
)
)
if can_copy:
code = children[0]
copy_button = ( # type: ignore
copy_button
if copy_button is not None
else Button.create(
Icon.create(tag="copy", size=16, color=color("gray", 11)),
on_click=[
set_clipboard(cls._strip_transformer_triggers(code)), # type: ignore
copy_script(),
],
style=Style(
{
"position": "absolute",
"top": "4px",
"right": "4px",
"background": color("gray", 3),
"border": "1px solid",
"border-color": color("gray", 5),
"border-radius": "6px",
"padding": "5px",
"opacity": "1",
"cursor": "pointer",
"_hover": {
"background": color("gray", 4),
},
"transition": "background 0.250s ease-out",
"&>svg": {
"transition": "transform 0.250s ease-out, opacity 0.250s ease-out",
},
"_active": {
"background": color("gray", 5),
},
}
),
)
)
if copy_button:
return ShikiCodeBlock.create(
children[0], copy_button, position="relative", **props
)
else:
return ShikiCodeBlock.create(children[0], **props)
@staticmethod
def _map_themes(theme: str) -> str:
if isinstance(theme, str) and theme in THEME_MAPPING:
return THEME_MAPPING[theme]
return theme
@staticmethod
def _map_languages(language: str) -> str:
if isinstance(language, str) and language in LANGUAGE_MAPPING:
return LANGUAGE_MAPPING[language]
return language
@staticmethod
def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str:
if not isinstance(code, (StringVar, str)):
raise VarTypeError(
f"code should be string literal or a StringVar type. Got {type(code)} instead."
)
regex_pattern = r"[\/#]+ *\[!code.*?\]"
if isinstance(code, Var):
return string_replace_operation(
code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), ""
)
if isinstance(code, str):
return re.sub(regex_pattern, "", code)
class TransformerNamespace(ComponentNamespace):
"""Namespace for the Transformers."""
shikijs = ShikiJsTransformer
class CodeblockNamespace(ComponentNamespace):
"""Namespace for the CodeBlock component."""
root = staticmethod(ShikiCodeBlock.create)
create_transformer = staticmethod(ShikiCodeBlock.create_transformer)
transformers = TransformerNamespace()
__call__ = staticmethod(ShikiHighLevelCodeBlock.create)
code_block = CodeblockNamespace()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,190 @@
"""Components that are dynamically generated on the backend."""
from typing import TYPE_CHECKING, Union
from reflex import constants
from reflex.utils import imports
from reflex.utils.exceptions import DynamicComponentMissingLibrary
from reflex.utils.format import format_library_name
from reflex.utils.serializers import serializer
from reflex.vars import Var, get_unique_variable_name
from reflex.vars.base import VarData, transform
if TYPE_CHECKING:
from reflex.components.component import Component
def get_cdn_url(lib: str) -> str:
"""Get the CDN URL for a library.
Args:
lib: The library to get the CDN URL for.
Returns:
The CDN URL for the library.
"""
return f"https://cdn.jsdelivr.net/npm/{lib}" + "/+esm"
bundled_libraries = {"react", "@radix-ui/themes", "@emotion/react", "next/link"}
def bundle_library(component: Union["Component", str]):
"""Bundle a library with the component.
Args:
component: The component to bundle the library with.
Raises:
DynamicComponentMissingLibrary: Raised when a dynamic component is missing a library.
"""
if isinstance(component, str):
bundled_libraries.add(component)
return
if component.library is None:
raise DynamicComponentMissingLibrary("Component must have a library to bundle.")
bundled_libraries.add(format_library_name(component.library))
def load_dynamic_serializer():
"""Load the serializer for dynamic components."""
# Causes a circular import, so we import here.
from reflex.components.component import Component
@serializer
def make_component(component: Component) -> str:
"""Generate the code for a dynamic component.
Args:
component: The component to generate code for.
Returns:
The generated code
"""
# Causes a circular import, so we import here.
from reflex.compiler import templates, utils
from reflex.components.base.bare import Bare
component = Bare.create(Var.create(component))
rendered_components = {}
# Include dynamic imports in the shared component.
if dynamic_imports := component._get_all_dynamic_imports():
rendered_components.update(
{dynamic_import: None for dynamic_import in dynamic_imports}
)
# Include custom code in the shared component.
rendered_components.update(
{code: None for code in component._get_all_custom_code()},
)
rendered_components[
templates.STATEFUL_COMPONENT.render(
tag_name="MySSRComponent",
memo_trigger_hooks=[],
component=component,
)
] = None
libs_in_window = bundled_libraries
imports = {}
for lib, names in component._get_all_imports().items():
formatted_lib_name = format_library_name(lib)
if (
not lib.startswith((".", "/", "$/"))
and not lib.startswith("http")
and formatted_lib_name not in libs_in_window
):
imports[get_cdn_url(lib)] = names
else:
imports[lib] = names
module_code_lines = templates.STATEFUL_COMPONENTS.render(
imports=utils.compile_imports(imports),
memoized_code="\n".join(rendered_components),
).splitlines()[1:]
# Rewrite imports from `/` to destructure from window
for ix, line in enumerate(module_code_lines[:]):
if line.startswith("import "):
if 'from "$/' in line or 'from "/' in line:
module_code_lines[ix] = (
line.replace("import ", "const ", 1).replace(
" from ", " = window['__reflex'][", 1
)
+ "]"
)
else:
for lib in libs_in_window:
if f'from "{lib}"' in line:
module_code_lines[ix] = (
line.replace("import ", "const ", 1)
.replace(
f' from "{lib}"', f" = window.__reflex['{lib}']", 1
)
.replace(" as ", ": ")
)
if line.startswith("export function"):
module_code_lines[ix] = line.replace(
"export function", "export default function", 1
)
line_stripped = line.strip()
if line_stripped.startswith("{") and line_stripped.endswith("}"):
module_code_lines[ix] = line_stripped[1:-1]
module_code_lines.insert(0, "const React = window.__reflex.react;")
return "\n".join(
[
"//__reflex_evaluate",
*module_code_lines,
]
)
@transform
def evaluate_component(js_string: Var[str]) -> Var[Component]:
"""Evaluate a component.
Args:
js_string: The JavaScript string to evaluate.
Returns:
The evaluated JavaScript string.
"""
unique_var_name = get_unique_variable_name()
return js_string._replace(
_js_expr=unique_var_name,
_var_type=Component,
merge_var_data=VarData.merge(
VarData(
imports={
f"$/{constants.Dirs.STATE_PATH}": [
imports.ImportVar(tag="evalReactComponent"),
],
"react": [
imports.ImportVar(tag="useState"),
imports.ImportVar(tag="useEffect"),
],
},
hooks={
f"const [{unique_var_name}, set_{unique_var_name}] = useState(null);": None,
"useEffect(() => {"
"let isMounted = true;"
f"evalReactComponent({str(js_string)})"
".then((component) => {"
"if (isMounted) {"
f"set_{unique_var_name}(component);"
"}"
"});"
"return () => {"
"isMounted = false;"
"};"
"}"
f", [{str(js_string)}]);": None,
},
),
),
)

View File

@ -3,6 +3,7 @@
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
from . import elements as elements
from .elements.forms import Button as Button
from .elements.forms import Fieldset as Fieldset
from .elements.forms import Form as Form

View File

@ -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.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars.base import Var
class Element(Component):
@overload
@ -21,52 +21,22 @@ class Element(Component):
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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,
) -> "Element":
"""Create the component.

View File

@ -3,7 +3,7 @@
from typing import Union
from reflex.components.el.element import Element
from reflex.vars import Var as Var
from reflex.vars.base import Var
class BaseHTML(Element):

View File

@ -3,13 +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.element import Element
from reflex.event import EventHandler, EventSpec
from reflex.ivars.base import ImmutableVar
from reflex.event import EventType
from reflex.style import Style
from reflex.vars import Var
from reflex.vars.base import Var
class BaseHTML(Element):
@overload
@ -17,81 +16,51 @@ class BaseHTML(Element):
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[ImmutableVar, str]]] = None,
on_blur: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_focus: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_scroll: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, ImmutableVar]
] = None,
custom_attrs: Optional[Dict[str, Union[Var, str]]] = 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,
) -> "BaseHTML":
"""Create the component.

View File

@ -3,21 +3,27 @@
from __future__ import annotations
from hashlib import md5
from typing import Any, Dict, Iterator, Set, Union
from typing import Any, Dict, Iterator, Set, Tuple, Union
from jinja2 import Environment
from reflex.components.el.element import Element
from reflex.components.tags.tag import Tag
from reflex.constants import Dirs, EventTriggers
from reflex.event import EventChain, EventHandler
from reflex.ivars.base import ImmutableVar, LiteralVar
from reflex.event import (
EventChain,
EventHandler,
input_event,
key_event,
prevent_default,
)
from reflex.utils.imports import ImportDict
from reflex.vars import Var, VarData
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
from .base import BaseHTML
FORM_DATA = ImmutableVar.create("form_data")
FORM_DATA = Var(_js_expr="form_data")
HANDLE_SUBMIT_JS_JINJA2 = Environment().from_string(
"""
const handleSubmit_{{ handle_submit_unique_name }} = useCallback((ev) => {
@ -96,6 +102,24 @@ class Fieldset(Element):
name: Var[Union[str, int, bool]]
def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
"""Event handler spec for the on_submit event.
Returns:
The event handler spec.
"""
return (FORM_DATA,)
def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]:
"""Event handler spec for the on_submit event.
Returns:
The event handler spec.
"""
return (FORM_DATA,)
class Form(BaseHTML):
"""Display the form element."""
@ -135,7 +159,7 @@ class Form(BaseHTML):
handle_submit_unique_name: Var[str]
# Fired when the form is submitted
on_submit: EventHandler[lambda e0: [FORM_DATA]]
on_submit: EventHandler[on_submit_event_spec, on_submit_string_event_spec]
@classmethod
def create(cls, *children, **props):
@ -148,6 +172,9 @@ class Form(BaseHTML):
Returns:
The form component.
"""
if "on_submit" not in props:
props["on_submit"] = prevent_default
if "handle_submit_unique_name" in props:
return super().create(*children, **props)
@ -169,7 +196,7 @@ class Form(BaseHTML):
"""
return {
"react": "useCallback",
f"/{Dirs.STATE_PATH}": ["getRefValue", "getRefValues"],
f"$/{Dirs.STATE_PATH}": ["getRefValue", "getRefValues"],
}
def add_hooks(self) -> list[str]:
@ -197,8 +224,8 @@ class Form(BaseHTML):
if EventTriggers.ON_SUBMIT in self.event_triggers:
render_tag.add_props(
**{
EventTriggers.ON_SUBMIT: ImmutableVar(
_var_name=f"handleSubmit_{self.handle_submit_unique_name}",
EventTriggers.ON_SUBMIT: Var(
_js_expr=f"handleSubmit_{self.handle_submit_unique_name}",
_var_type=EventChain,
)
}
@ -212,21 +239,21 @@ class Form(BaseHTML):
# when ref start with refs_ it's an array of refs, so we need different method
# to collect data
if ref.startswith("refs_"):
ref_var = ImmutableVar.create_safe(ref[:-3]).as_ref()
form_refs[ref[len("refs_") : -3]] = ImmutableVar.create_safe(
f"getRefValues({str(ref_var)})",
ref_var = Var(_js_expr=ref[:-3]).as_ref()
form_refs[ref[len("refs_") : -3]] = Var(
_js_expr=f"getRefValues({str(ref_var)})",
_var_data=VarData.merge(ref_var._get_all_var_data()),
)
else:
ref_var = ImmutableVar.create_safe(ref).as_ref()
form_refs[ref[4:]] = ImmutableVar.create_safe(
f"getRefValue({str(ref_var)})",
ref_var = Var(_js_expr=ref).as_ref()
form_refs[ref[4:]] = Var(
_js_expr=f"getRefValue({str(ref_var)})",
_var_data=VarData.merge(ref_var._get_all_var_data()),
)
# print(repr(form_refs))
return form_refs
def _get_vars(self, include_children: bool = True) -> Iterator[ImmutableVar]:
def _get_vars(self, include_children: bool = True) -> Iterator[Var]:
yield from super()._get_vars(include_children=include_children)
yield from self._get_form_refs().values()
@ -342,19 +369,19 @@ class Input(BaseHTML):
value: Var[Union[str, int, float]]
# Fired when the input value changes
on_change: EventHandler[lambda e0: [e0.target.value]]
on_change: EventHandler[input_event]
# Fired when the input gains focus
on_focus: EventHandler[lambda e0: [e0.target.value]]
on_focus: EventHandler[input_event]
# Fired when the input loses focus
on_blur: EventHandler[lambda e0: [e0.target.value]]
on_blur: EventHandler[input_event]
# Fired when a key is pressed down
on_key_down: EventHandler[lambda e0: [e0.key]]
on_key_down: EventHandler[key_event]
# Fired when a key is released
on_key_up: EventHandler[lambda e0: [e0.key]]
on_key_up: EventHandler[key_event]
class Label(BaseHTML):
@ -493,7 +520,7 @@ class Select(BaseHTML):
size: Var[Union[str, int, bool]]
# Fired when the select value changes
on_change: EventHandler[lambda e0: [e0.target.value]]
on_change: EventHandler[input_event]
AUTO_HEIGHT_JS = """
@ -583,19 +610,55 @@ class Textarea(BaseHTML):
wrap: Var[Union[str, int, bool]]
# Fired when the input value changes
on_change: EventHandler[lambda e0: [e0.target.value]]
on_change: EventHandler[input_event]
# Fired when the input gains focus
on_focus: EventHandler[lambda e0: [e0.target.value]]
on_focus: EventHandler[input_event]
# Fired when the input loses focus
on_blur: EventHandler[lambda e0: [e0.target.value]]
on_blur: EventHandler[input_event]
# Fired when a key is pressed down
on_key_down: EventHandler[lambda e0: [e0.key]]
on_key_down: EventHandler[key_event]
# Fired when a key is released
on_key_up: EventHandler[lambda e0: [e0.key]]
on_key_up: EventHandler[key_event]
@classmethod
def create(cls, *children, **props):
"""Create a textarea component.
Args:
*children: The children of the textarea.
**props: The properties of the textarea.
Returns:
The textarea component.
Raises:
ValueError: when `enter_key_submit` is combined with `on_key_down`.
"""
enter_key_submit = props.get("enter_key_submit")
auto_height = props.get("auto_height")
custom_attrs = props.setdefault("custom_attrs", {})
if enter_key_submit is not None:
enter_key_submit = Var.create(enter_key_submit)
if "on_key_down" in props:
raise ValueError(
"Cannot combine `enter_key_submit` with `on_key_down`.",
)
custom_attrs["on_key_down"] = Var(
_js_expr=f"(e) => enterKeySubmitOnKeyDown(e, {str(enter_key_submit)})",
_var_data=VarData.merge(enter_key_submit._get_all_var_data()),
)
if auto_height is not None:
auto_height = Var.create(auto_height)
custom_attrs["on_input"] = Var(
_js_expr=f"(e) => autoHeightOnInput(e, {str(auto_height)})",
_var_data=VarData.merge(auto_height._get_all_var_data()),
)
return super().create(*children, **props)
def _exclude_props(self) -> list[str]:
return super()._exclude_props() + [
@ -616,28 +679,6 @@ class Textarea(BaseHTML):
custom_code.add(ENTER_KEY_SUBMIT_JS)
return custom_code
def _render(self) -> Tag:
tag = super()._render()
if self.enter_key_submit is not None:
if "on_key_down" in self.event_triggers:
raise ValueError(
"Cannot combine `enter_key_submit` with `on_key_down`.",
)
tag.add_props(
on_key_down=ImmutableVar.create_safe(
f"(e) => enterKeySubmitOnKeyDown(e, {str(self.enter_key_submit)})",
_var_data=VarData.merge(self.enter_key_submit._get_all_var_data()),
)
)
if self.auto_height is not None:
tag.add_props(
on_input=ImmutableVar.create_safe(
f"(e) => autoHeightOnInput(e, {str(self.auto_height)})",
_var_data=VarData.merge(self.auto_height._get_all_var_data()),
)
)
return tag
button = Button.create
fieldset = Fieldset.create

Some files were not shown because too many files have changed in this diff Show More