Merge branch 'main' into use-own-cached-templates-release

This commit is contained in:
Khaleel Al-Adhami 2025-01-07 10:37:14 -08:00
commit c54f24f2eb
222 changed files with 5021 additions and 1920 deletions

View File

@ -0,0 +1,19 @@
---
name: Enhancement Request
about: Suggest an enhancement for an existing Reflex feature.
title: ''
labels: 'enhancement'
assignees: ''
---
**Describe the Enhancement you want**
A clear and concise description of what the improvement does.
- Which feature do you want to improve? (and what problem does it have)
- What is the benefit of the enhancement?
- Show an example/usecase were the improvement are needed.
**Additional context**
Add any other context here.

View File

@ -0,0 +1,18 @@
---
name: Feature Request
about: Suggest a new feature for Reflex
title: ''
labels: 'feature request'
assignees: ''
---
**Describe the Features**
A clear and concise description of what the features does.
- What is the purpose of the feature?
- Show an example / use cases for the new feature.
**Additional context**
Add any other context here.

View File

@ -6,7 +6,7 @@
# #
# Exit conditions: # Exit conditions:
# - Python of version `python-version` is ready to be invoked as `python`. # - Python of version `python-version` is ready to be invoked as `python`.
# - Poetry of version `poetry-version` is ready ot be invoked as `poetry`. # - Poetry of version `poetry-version` is ready to be invoked as `poetry`.
# - If `run-poetry-install` is true, deps as defined in `pyproject.toml` will have been installed into the venv at `create-venv-at-path`. # - If `run-poetry-install` is true, deps as defined in `pyproject.toml` will have been installed into the venv at `create-venv-at-path`.
name: 'Setup Reflex build environment' name: 'Setup Reflex build environment'

View File

@ -80,7 +80,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
# Show OS combos first in GUI # Show OS combos first in GUI
os: [ubuntu-latest, windows-latest, macos-12] os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['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: exclude:
- os: windows-latest - os: windows-latest
@ -92,7 +92,7 @@ jobs:
python-version: '3.9.18' python-version: '3.9.18'
- os: macos-latest - os: macos-latest
python-version: '3.10.13' python-version: '3.10.13'
- os: macos-12 - os: macos-latest
python-version: '3.12.0' python-version: '3.12.0'
include: include:
- os: windows-latest - os: windows-latest
@ -155,7 +155,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
# Show OS combos first in GUI # Show OS combos first in GUI
os: [ubuntu-latest, windows-latest, macos-12] os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.11.5'] python-version: ['3.11.5']
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}

View File

@ -58,7 +58,7 @@ jobs:
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run uv pip install -r requirements.txt run: poetry run uv pip install -r requirements.txt
- name: Install additional dependencies for DB access - name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg2-binary run: poetry run uv pip install psycopg
- name: Init Website for reflex-web - name: Init Website for reflex-web
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run reflex init run: poetry run reflex init

View File

@ -22,9 +22,9 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
strategy: strategy:
matrix: matrix:
state_manager: ["redis", "memory"] state_manager: ['redis', 'memory']
python-version: ['3.11.5', '3.12.0', '3.13.0']
split_index: [1, 2] split_index: [1, 2]
python-version: ["3.11.5", "3.12.0"]
fail-fast: false fail-fast: false
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
services: services:
@ -53,7 +53,7 @@ jobs:
SCREENSHOT_DIR: /tmp/screenshots/${{ matrix.state_manager }}/${{ matrix.python-version }}/${{ matrix.split_index }} SCREENSHOT_DIR: /tmp/screenshots/${{ matrix.state_manager }}/${{ matrix.python-version }}/${{ matrix.split_index }}
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
run: | run: |
poetry run playwright install --with-deps poetry run playwright install chromium
poetry run pytest tests/integration --splits 2 --group ${{matrix.split_index}} poetry run pytest tests/integration --splits 2 --group ${{matrix.split_index}}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: Upload failed test screenshots name: Upload failed test screenshots

View File

@ -43,7 +43,7 @@ jobs:
matrix: matrix:
# Show OS combos first in GUI # Show OS combos first in GUI
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest, windows-latest]
python-version: ['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', '3.13.0']
exclude: exclude:
- os: windows-latest - os: windows-latest
python-version: '3.10.13' python-version: '3.10.13'
@ -73,12 +73,14 @@ jobs:
run: | run: |
poetry run uv pip install -r requirements.txt poetry run uv pip install -r requirements.txt
- name: Install additional dependencies for DB access - name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg2-binary run: poetry run uv pip install psycopg
- name: Check export --backend-only before init for counter example - name: Check export --backend-only before init for counter example
working-directory: ./reflex-examples/counter working-directory: ./reflex-examples/counter
run: | run: |
poetry run reflex export --backend-only poetry run reflex export --backend-only
- name: Check run --backend-only before init for counter example - name: Check run --backend-only before init for counter example
env:
WAIT_FOR_LISTENING_PORT_ARGS: --path ping
run: | run: |
poetry run bash scripts/integration.sh ./reflex-examples/counter dev 8001 --backend-only --backend-port 8001 poetry run bash scripts/integration.sh ./reflex-examples/counter dev 8001 --backend-only --backend-port 8001
- name: Init Website for counter example - name: Init Website for counter example
@ -147,7 +149,7 @@ jobs:
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run uv pip install $(grep -ivE "reflex " requirements.txt) run: poetry run uv pip install $(grep -ivE "reflex " requirements.txt)
- name: Install additional dependencies for DB access - name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg2-binary run: poetry run uv pip install psycopg
- name: Init Website for reflex-web - name: Init Website for reflex-web
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run reflex init run: poetry run reflex init
@ -163,13 +165,42 @@ jobs:
--pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --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
rx-shout-from-template:
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env
with:
python-version: '3.11.4'
run-poetry-install: true
create-venv-at-path: .venv
- name: Create app directory
run: mkdir rx-shout-from-template
- name: Init reflex-web from template
run: poetry run reflex init --template https://github.com/masenf/rx_shout
working-directory: ./rx-shout-from-template
- name: ignore reflex pin in requirements
run: sed -i -e '/reflex==/d' requirements.txt
working-directory: ./rx-shout-from-template
- name: Install additional dependencies
run: poetry run uv pip install -r requirements.txt
working-directory: ./rx-shout-from-template
- name: Run Website and Check for errors
run: |
# Check that npm is home
npm -v
poetry run bash scripts/integration.sh ./rx-shout-from-template prod
reflex-web-macos: reflex-web-macos:
if: github.event_name == 'push' && github.ref == 'refs/heads/main' if: github.event_name == 'push' && github.ref == 'refs/heads/main'
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ['3.11.5', '3.12.0'] python-version: ['3.11.5', '3.12.0']
runs-on: macos-12 runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env - uses: ./.github/actions/setup_build_env
@ -187,7 +218,7 @@ jobs:
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run uv pip install -r requirements.txt run: poetry run uv pip install -r requirements.txt
- name: Install additional dependencies for DB access - name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg2-binary run: poetry run uv pip install psycopg
- name: Init Website for reflex-web - name: Init Website for reflex-web
working-directory: ./reflex-web working-directory: ./reflex-web
run: poetry run reflex init run: poetry run reflex init

View File

@ -78,6 +78,7 @@ jobs:
shell: wsl-bash {0} shell: wsl-bash {0}
run: | run: |
export TELEMETRY_ENABLED=false export TELEMETRY_ENABLED=false
export WAIT_FOR_LISTENING_PORT_ARGS="--path ping"
dos2unix scripts/integration.sh dos2unix scripts/integration.sh
poetry run bash scripts/integration.sh ./reflex-examples/counter dev 8001 --backend-only --backend-port 8001 poetry run bash scripts/integration.sh ./reflex-examples/counter dev 8001 --backend-only --backend-port 8001
- name: Init Website for counter example - name: Init Website for counter example

View File

@ -28,7 +28,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest, windows-latest]
python-version: ['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', '3.13.0']
# Windows is a bit behind on Python version availability in Github # Windows is a bit behind on Python version availability in Github
exclude: exclude:
- os: windows-latest - os: windows-latest
@ -88,8 +88,9 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0'] # Note: py39, py310 versions chosen due to available arm64 darwin builds.
runs-on: macos-12 python-version: ['3.9.13', '3.10.11', '3.11.5', '3.12.0', '3.13.0']
runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env - uses: ./.github/actions/setup_build_env

View File

@ -3,7 +3,7 @@ fail_fast: true
repos: repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit - repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.7.4 rev: v0.8.2
hooks: hooks:
- id: ruff-format - id: ruff-format
args: [reflex, tests] args: [reflex, tests]
@ -11,6 +11,12 @@ repos:
args: ["--fix", "--exit-non-zero-on-fix"] args: ["--fix", "--exit-non-zero-on-fix"]
exclude: '^integration/benchmarks/' exclude: '^integration/benchmarks/'
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
args: ["reflex"]
# Run pyi check before pyright because pyright can fail if pyi files are wrong. # Run pyi check before pyright because pyright can fail if pyi files are wrong.
- repo: local - repo: local
hooks: hooks:

View File

@ -5,7 +5,7 @@
We as members, contributors, and leaders pledge to make participation in our We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status, identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, religion, or sexual identity nationality, personal appearance, race, religion, or sexual identity
and orientation. and orientation.

View File

@ -249,7 +249,7 @@ We welcome contributions of any size! Below are some good ways to get started in
- **GitHub Discussions**: A great way to talk about features you want added or things that are confusing/need clarification. - **GitHub Discussions**: A great way to talk about features you want added or things that are confusing/need clarification.
- **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) are an excellent way to report bugs. Additionally, you can try and solve an existing issue and submit a PR. - **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) are an excellent way to report bugs. Additionally, you can try and solve an existing issue and submit a PR.
We are actively looking for contributors, no matter your skill level or experience. To contribute check out [CONTIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) We are actively looking for contributors, no matter your skill level or experience. To contribute check out [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md)
## All Thanks To Our Contributors: ## All Thanks To Our Contributors:

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import argparse import argparse
import json import json
import os import os
from pathlib import Path
from utils import send_data_to_posthog from utils import send_data_to_posthog
@ -18,7 +19,7 @@ def extract_stats_from_json(json_file: str) -> list[dict]:
Returns: Returns:
list[dict]: The stats for each test. list[dict]: The stats for each test.
""" """
with open(json_file, "r") as file: with Path(json_file).open() as file:
json_data = json.load(file) json_data = json.load(file)
# Load the JSON data if it is a string, otherwise assume it's already a dictionary # Load the JSON data if it is a string, otherwise assume it's already a dictionary

View File

@ -5,6 +5,7 @@ from __future__ import annotations
import argparse import argparse
import json import json
import os import os
from pathlib import Path
from utils import send_data_to_posthog from utils import send_data_to_posthog
@ -18,7 +19,7 @@ def extract_stats_from_json(json_file: str) -> dict:
Returns: Returns:
dict: The stats for each test. dict: The stats for each test.
""" """
with open(json_file, "r") as file: with Path(json_file).open() as file:
json_data = json.load(file) json_data = json.load(file)
# Load the JSON data if it is a string, otherwise assume it's already a dictionary # Load the JSON data if it is a string, otherwise assume it's already a dictionary

View File

@ -21,7 +21,7 @@ def get_package_size(venv_path: Path, os_name):
ValueError: when venv does not exist or python version is None. ValueError: when venv does not exist or python version is None.
""" """
python_version = get_python_version(venv_path, os_name) python_version = get_python_version(venv_path, os_name)
print("Python version:", python_version) print("Python version:", python_version) # noqa: T201
if python_version is None: if python_version is None:
raise ValueError("Error: Failed to determine Python version.") raise ValueError("Error: Failed to determine Python version.")

View File

@ -23,11 +23,11 @@
# for example, pass `docker build --platform=linux/amd64 ...` # for example, pass `docker build --platform=linux/amd64 ...`
# Stage 1: init # Stage 1: init
FROM python:3.11 as init FROM python:3.13 as init
ARG uv=/root/.local/bin/uv ARG uv=/root/.local/bin/uv
# Install `uv` for faster package boostrapping # Install `uv` for faster package bootstrapping
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
RUN /install.sh && rm /install.sh RUN /install.sh && rm /install.sh
@ -48,11 +48,11 @@ RUN $uv pip install -r requirements.txt
RUN reflex init RUN reflex init
# Stage 2: copy artifacts into slim image # Stage 2: copy artifacts into slim image
FROM python:3.11-slim FROM python:3.13-slim
WORKDIR /app WORKDIR /app
RUN adduser --disabled-password --home /app reflex RUN adduser --disabled-password --home /app reflex
COPY --chown=reflex --from=init /app /app COPY --chown=reflex --from=init /app /app
# Install libpq-dev for psycopg2 (skip if not using postgres). # Install libpq-dev for psycopg (skip if not using postgres).
RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/*
USER reflex USER reflex
ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1 ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1

View File

@ -2,11 +2,11 @@
# instance of a Reflex app. # instance of a Reflex app.
# Stage 1: init # Stage 1: init
FROM python:3.11 as init FROM python:3.13 as init
ARG uv=/root/.local/bin/uv ARG uv=/root/.local/bin/uv
# Install `uv` for faster package boostrapping # Install `uv` for faster package bootstrapping
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
RUN /install.sh && rm /install.sh RUN /install.sh && rm /install.sh
@ -35,11 +35,11 @@ RUN rm -rf .web && mkdir .web
RUN mv /tmp/_static .web/_static RUN mv /tmp/_static .web/_static
# Stage 2: copy artifacts into slim image # Stage 2: copy artifacts into slim image
FROM python:3.11-slim FROM python:3.13-slim
WORKDIR /app WORKDIR /app
RUN adduser --disabled-password --home /app reflex RUN adduser --disabled-password --home /app reflex
COPY --chown=reflex --from=init /app /app COPY --chown=reflex --from=init /app /app
# Install libpq-dev for psycopg2 (skip if not using postgres). # Install libpq-dev for psycopg (skip if not using postgres).
RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* RUN apt-get update -y && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/*
USER reflex USER reflex
ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1 ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1

View File

@ -15,7 +15,7 @@ services:
app: app:
environment: environment:
DB_URL: postgresql+psycopg2://postgres:secret@db/postgres DB_URL: postgresql+psycopg://postgres:secret@db/postgres
REDIS_URL: redis://redis:6379 REDIS_URL: redis://redis:6379
depends_on: depends_on:
- db - db

View File

@ -0,0 +1,3 @@
.web
!.web/bun.lockb
!.web/package.json

View File

@ -0,0 +1,14 @@
:{$PORT}
encode gzip
@backend_routes path /_event/* /ping /_upload /_upload/*
handle @backend_routes {
reverse_proxy localhost:8000
}
root * /srv
route {
try_files {path} {path}/ /404.html
file_server
}

View File

@ -0,0 +1,62 @@
# This Dockerfile is used to deploy a single-container Reflex app instance
# to services like Render, Railway, Heroku, GCP, and others.
# If the service expects a different port, provide it here (f.e Render expects port 10000)
ARG PORT=8080
# Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend.
ARG API_URL
# It uses a reverse proxy to serve the frontend statically and proxy to backend
# from a single exposed port, expecting TLS termination to be handled at the
# edge by the given platform.
FROM python:3.13 as builder
RUN mkdir -p /app/.web
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
WORKDIR /app
# Install python app requirements and reflex in the container
COPY requirements.txt .
RUN pip install -r requirements.txt
# Install reflex helper utilities like bun/fnm/node
COPY rxconfig.py ./
RUN reflex init
# Install pre-cached frontend dependencies (if exist)
COPY *.web/bun.lockb *.web/package.json .web/
RUN if [ -f .web/bun.lockb ]; then cd .web && ~/.local/share/reflex/bun/bin/bun install --frozen-lockfile; fi
# Copy local context to `/app` inside container (see .dockerignore)
COPY . .
ARG PORT API_URL
# Download other npm dependencies and compile frontend
RUN API_URL=${API_URL:-http://localhost:$PORT} reflex export --loglevel debug --frontend-only --no-zip && mv .web/_static/* /srv/ && rm -rf .web
# Final image with only necessary files
FROM python:3.13-slim
# Install Caddy and redis server inside image
RUN apt-get update -y && apt-get install -y caddy redis-server && rm -rf /var/lib/apt/lists/*
ARG PORT API_URL
ENV PATH="/app/.venv/bin:$PATH" PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} REDIS_URL=redis://localhost PYTHONUNBUFFERED=1
WORKDIR /app
COPY --from=builder /app /app
COPY --from=builder /srv /srv
# Needed until Reflex properly passes SIGTERM on backend.
STOPSIGNAL SIGKILL
EXPOSE $PORT
# Apply migrations before starting the backend.
CMD [ -d alembic ] && reflex db migrate; \
caddy start && \
redis-server --daemonize yes && \
exec reflex run --env prod --backend-only

View File

@ -0,0 +1,37 @@
# production-one-port
This docker deployment runs Reflex in prod mode, exposing a single HTTP port:
* `8080` (`$PORT`) - Caddy server hosting the frontend statically and proxying requests to the backend.
The deployment also runs a local Redis server to store state for each user.
Conceptually it is similar to the `simple-one-port` example except it:
* has layer caching for python, reflex, and node dependencies
* uses multi-stage build to reduce the size of the final image
Using this method may be preferable for deploying in memory constrained
environments, because it serves a static frontend export, rather than running
the NextJS server via node.
## Build
```console
docker build -t reflex-production-one-port .
```
## Run
```console
docker run -p 8080:8080 reflex-production-one-port
```
Note that this container has _no persistence_ and will lose all data when
stopped. You can use bind mounts or named volumes to persist the database and
uploaded_files directories as needed.
## Usage
This container should be used with an existing load balancer or reverse proxy to
terminate TLS.
It is also useful for deploying to simple app platforms, such as Render or Heroku.

View File

@ -4,7 +4,7 @@
# It uses a reverse proxy to serve the frontend statically and proxy to backend # It uses a reverse proxy to serve the frontend statically and proxy to backend
# from a single exposed port, expecting TLS termination to be handled at the # from a single exposed port, expecting TLS termination to be handled at the
# edge by the given platform. # edge by the given platform.
FROM python:3.11 FROM python:3.13
# If the service expects a different port, provide it here (f.e Render expects port 10000) # If the service expects a different port, provide it here (f.e Render expects port 10000)
ARG PORT=8080 ARG PORT=8080

View File

@ -1,5 +1,5 @@
# This Dockerfile is used to deploy a simple single-container Reflex app instance. # This Dockerfile is used to deploy a simple single-container Reflex app instance.
FROM python:3.12 FROM python:3.13
RUN apt-get update && apt-get install -y redis-server && rm -rf /var/lib/apt/lists/* RUN apt-get update && apt-get install -y redis-server && rm -rf /var/lib/apt/lists/*
ENV REDIS_URL=redis://localhost PYTHONUNBUFFERED=1 ENV REDIS_URL=redis://localhost PYTHONUNBUFFERED=1

1218
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "reflex" name = "reflex"
version = "0.6.7dev1" version = "0.6.8dev1"
description = "Web apps in pure Python." description = "Web apps in pure Python."
license = "Apache-2.0" license = "Apache-2.0"
authors = [ authors = [
@ -14,16 +14,8 @@ readme = "README.md"
homepage = "https://reflex.dev" homepage = "https://reflex.dev"
repository = "https://github.com/reflex-dev/reflex" repository = "https://github.com/reflex-dev/reflex"
documentation = "https://reflex.dev/docs/getting-started/introduction" documentation = "https://reflex.dev/docs/getting-started/introduction"
keywords = [ keywords = ["web", "framework"]
"web", classifiers = ["Development Status :: 4 - Beta"]
"framework",
]
classifiers = [
"Development Status :: 4 - Beta",
]
packages = [
{include = "reflex"}
]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "^3.9"
@ -42,21 +34,22 @@ uvicorn = ">=0.20.0"
starlette-admin = ">=0.11.0,<1.0" starlette-admin = ">=0.11.0,<1.0"
alembic = ">=1.11.1,<2.0" alembic = ">=1.11.1,<2.0"
platformdirs = ">=3.10.0,<5.0" platformdirs = ">=3.10.0,<5.0"
distro = {version = ">=1.8.0,<2.0", platform = "linux"} distro = { version = ">=1.8.0,<2.0", platform = "linux" }
python-engineio = "!=4.6.0" python-engineio = "!=4.6.0"
wrapt = [ wrapt = [
{version = ">=1.14.0,<2.0", python = ">=3.11"}, { version = ">=1.14.0,<2.0", python = ">=3.11" },
{version = ">=1.11.0,<2.0", python = "<3.11"}, { version = ">=1.11.0,<2.0", python = "<3.11" },
] ]
packaging = ">=23.1,<25.0" packaging = ">=23.1,<25.0"
reflex-hosting-cli = ">=0.1.17,<2.0" reflex-hosting-cli = ">=0.1.29,<2.0"
charset-normalizer = ">=3.3.2,<4.0" charset-normalizer = ">=3.3.2,<4.0"
wheel = ">=0.42.0,<1.0" wheel = ">=0.42.0,<1.0"
build = ">=1.0.3,<2.0" build = ">=1.0.3,<2.0"
setuptools = ">=75.0" setuptools = ">=75.0"
httpx = ">=0.25.1,<1.0" httpx = ">=0.25.1,<1.0"
twine = ">=4.0.0,<6.0" twine = ">=4.0.0,<7.0"
tomlkit = ">=0.12.4,<1.0" tomlkit = ">=0.12.4,<1.0"
asgiproxy = { version = "==0.1.1", optional = true }
lazy_loader = ">=0.4" lazy_loader = ">=0.4"
reflex-chakra = ">=0.6.0" reflex-chakra = ">=0.6.0"
typing_extensions = ">=4.6.0" typing_extensions = ">=4.6.0"
@ -70,7 +63,7 @@ dill = ">=0.3.8"
toml = ">=0.10.2,<1.0" toml = ">=0.10.2,<1.0"
pytest-asyncio = ">=0.24.0" pytest-asyncio = ">=0.24.0"
pytest-cov = ">=4.0.0,<7.0" pytest-cov = ">=4.0.0,<7.0"
ruff = "0.7.4" ruff = "0.8.2"
pandas = ">=2.1.1,<3.0" pandas = ">=2.1.1,<3.0"
pillow = ">=10.0.0,<12.0" pillow = ">=10.0.0,<12.0"
plotly = ">=5.13.0,<6.0" plotly = ">=5.13.0,<6.0"
@ -80,10 +73,14 @@ selenium = ">=4.11.0,<5.0"
pytest-benchmark = ">=4.0.0,<6.0" pytest-benchmark = ">=4.0.0,<6.0"
playwright = ">=1.46.0" playwright = ">=1.46.0"
pytest-playwright = ">=0.5.1" pytest-playwright = ">=0.5.1"
asgiproxy = "==0.1.1"
[tool.poetry.scripts] [tool.poetry.scripts]
reflex = "reflex.reflex:cli" reflex = "reflex.reflex:cli"
[tool.poetry.extras]
proxy = ["asgiproxy"]
[build-system] [build-system]
requires = ["poetry-core>=1.5.1"] requires = ["poetry-core>=1.5.1"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
@ -92,14 +89,15 @@ build-backend = "poetry.core.masonry.api"
[tool.ruff] [tool.ruff]
target-version = "py39" target-version = "py39"
output-format = "concise"
lint.isort.split-on-trailing-comma = false lint.isort.split-on-trailing-comma = false
lint.select = ["B", "D", "E", "F", "I", "SIM", "W"] lint.select = ["B", "C4", "D", "E", "ERA", "F", "FURB", "I", "PERF", "PTH", "RUF", "SIM", "T", "W"]
lint.ignore = ["B008", "D205", "E501", "F403", "SIM115"] lint.ignore = ["B008", "D205", "E501", "F403", "SIM115", "RUF006", "RUF012"]
lint.pydocstyle.convention = "google" lint.pydocstyle.convention = "google"
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] "__init__.py" = ["F401"]
"tests/*.py" = ["D100", "D103", "D104", "B018"] "tests/*.py" = ["D100", "D103", "D104", "B018", "PERF", "T"]
"reflex/.templates/*.py" = ["D100", "D103", "D104"] "reflex/.templates/*.py" = ["D100", "D103", "D104"]
"*.pyi" = ["D301", "D415", "D417", "D418", "E742"] "*.pyi" = ["D301", "D415", "D417", "D418", "E742"]
"*/blank.py" = ["I001"] "*/blank.py" = ["I001"]
@ -107,3 +105,7 @@ lint.pydocstyle.convention = "google"
[tool.pytest.ini_options] [tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function" asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto" asyncio_mode = "auto"
[tool.codespell]
skip = "docs/*,*.html,examples/*, *.pyi"
ignore-words-list = "te, TreeE"

View File

@ -1,4 +1,5 @@
{% extends "web/pages/base_page.js.jinja2" %} {% extends "web/pages/base_page.js.jinja2" %}
{% from "web/pages/macros.js.jinja2" import renderHooks %}
{% block early_imports %} {% block early_imports %}
import '$/styles/styles.css' import '$/styles/styles.css'
@ -18,10 +19,7 @@ import * as {{library_alias}} from "{{library_path}}";
{% block export %} {% block export %}
function AppWrap({children}) { function AppWrap({children}) {
{{ renderHooks(hooks) }}
{% for hook in hooks %}
{{ hook }}
{% endfor %}
return ( return (
{{utils.render(render, indent_width=0)}} {{utils.render(render, indent_width=0)}}

View File

@ -1,5 +1,5 @@
{% extends "web/pages/base_page.js.jinja2" %} {% extends "web/pages/base_page.js.jinja2" %}
{% from "web/pages/macros.js.jinja2" import renderHooks %}
{% block export %} {% block export %}
{% for component in components %} {% for component in components %}
@ -8,9 +8,8 @@
{% endfor %} {% endfor %}
export const {{component.name}} = memo(({ {{-component.props|join(", ")-}} }) => { export const {{component.name}} = memo(({ {{-component.props|join(", ")-}} }) => {
{% for hook in component.hooks %} {{ renderHooks(component.hooks) }}
{{ hook }}
{% endfor %}
return( return(
{{utils.render(component.render)}} {{utils.render(component.render)}}
) )

View File

@ -1,4 +1,5 @@
{% extends "web/pages/base_page.js.jinja2" %} {% extends "web/pages/base_page.js.jinja2" %}
{% from "web/pages/macros.js.jinja2" import renderHooks %}
{% block declaration %} {% block declaration %}
{% for custom_code in custom_codes %} {% for custom_code in custom_codes %}
@ -8,9 +9,7 @@
{% block export %} {% block export %}
export default function Component() { export default function Component() {
{% for hook in hooks %} {{ renderHooks(hooks)}}
{{ hook }}
{% endfor %}
return ( return (
{{utils.render(render, indent_width=0)}} {{utils.render(render, indent_width=0)}}

View File

@ -0,0 +1,38 @@
{% macro renderHooks(hooks) %}
{% set sorted_hooks = sort_hooks(hooks) %}
{# Render the grouped hooks #}
{% for hook, _ in sorted_hooks[const.hook_position.INTERNAL] %}
{{ hook }}
{% endfor %}
{% for hook, _ in sorted_hooks[const.hook_position.PRE_TRIGGER] %}
{{ hook }}
{% endfor %}
{% for hook, _ in sorted_hooks[const.hook_position.POST_TRIGGER] %}
{{ hook }}
{% endfor %}
{% endmacro %}
{% macro renderHooksWithMemo(hooks, memo)%}
{% set sorted_hooks = sort_hooks(hooks) %}
{# Render the grouped hooks #}
{% for hook, _ in sorted_hooks[const.hook_position.INTERNAL] %}
{{ hook }}
{% endfor %}
{% for hook, _ in sorted_hooks[const.hook_position.PRE_TRIGGER] %}
{{ hook }}
{% endfor %}
{% for hook in memo %}
{{ hook }}
{% endfor %}
{% for hook, _ in sorted_hooks[const.hook_position.POST_TRIGGER] %}
{{ hook }}
{% endfor %}
{% endmacro %}

View File

@ -1,17 +1,9 @@
{% import 'web/pages/utils.js.jinja2' as utils %} {% import 'web/pages/utils.js.jinja2' as utils %}
{% from 'web/pages/macros.js.jinja2' import renderHooksWithMemo %}
{% set all_hooks = component._get_all_hooks() %}
export function {{tag_name}} () { export function {{tag_name}} () {
{% for hook in component._get_all_hooks_internal() %} {{ renderHooksWithMemo(all_hooks, memo_trigger_hooks) }}
{{ hook }}
{% endfor %}
{% for hook in memo_trigger_hooks %}
{{ hook }}
{% endfor %}
{% for hook in component._get_all_hooks() %}
{{ hook }}
{% endfor %}
return ( return (
{{utils.render(component.render(), indent_width=0)}} {{utils.render(component.render(), indent_width=0)}}

View File

@ -28,7 +28,7 @@ export const state_name = "{{state_name}}"
export const exception_state_name = "{{const.frontend_exception_state}}" export const exception_state_name = "{{const.frontend_exception_state}}"
// Theses events are triggered on initial load and each page navigation. // These events are triggered on initial load and each page navigation.
export const onLoadInternalEvent = () => { export const onLoadInternalEvent = () => {
const internal_events = []; const internal_events = [];

View File

@ -40,9 +40,6 @@ let event_processing = false;
// Array holding pending events to be processed. // Array holding pending events to be processed.
const event_queue = []; const event_queue = [];
// Pending upload promises, by id
const upload_controllers = {};
/** /**
* Generate a UUID (Used for session tokens). * Generate a UUID (Used for session tokens).
* Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid * Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
@ -211,11 +208,16 @@ export const applyEvent = async (event, socket) => {
if (event.name == "_download") { if (event.name == "_download") {
const a = document.createElement("a"); const a = document.createElement("a");
a.hidden = true; a.hidden = true;
a.href = event.payload.url;
// Special case when linking to uploaded files // Special case when linking to uploaded files
a.href = event.payload.url.replace( if (a.href.includes("getBackendURL(env.UPLOAD)")) {
"${getBackendURL(env.UPLOAD)}", a.href = eval?.(
getBackendURL(env.UPLOAD) event.payload.url.replace(
"getBackendURL(env.UPLOAD)",
`"${getBackendURL(env.UPLOAD)}"`
)
); );
}
a.download = event.payload.filename; a.download = event.payload.filename;
a.click(); a.click();
a.remove(); a.remove();
@ -300,7 +302,7 @@ export const applyEvent = async (event, socket) => {
if (socket) { if (socket) {
socket.emit( socket.emit(
"event", "event",
JSON.stringify(event, (k, v) => (v === undefined ? null : v)) event,
); );
return true; return true;
} }
@ -407,6 +409,8 @@ export const connect = async (
transports: transports, transports: transports,
autoUnref: false, autoUnref: false,
}); });
// Ensure undefined fields in events are sent as null instead of removed
socket.current.io.encoder.replacer = (k, v) => (v === undefined ? null : v)
function checkVisibility() { function checkVisibility() {
if (document.visibilityState === "visible") { if (document.visibilityState === "visible") {
@ -443,8 +447,7 @@ export const connect = async (
}); });
// On each received message, queue the updates and events. // On each received message, queue the updates and events.
socket.current.on("event", async (message) => { socket.current.on("event", async (update) => {
const update = JSON5.parse(message);
for (const substate in update.delta) { for (const substate in update.delta) {
dispatch[substate](update.delta[substate]); dispatch[substate](update.delta[substate]);
} }
@ -456,7 +459,7 @@ export const connect = async (
}); });
socket.current.on("reload", async (event) => { socket.current.on("reload", async (event) => {
event_processing = false; event_processing = false;
queueEvents([...initialEvents(), JSON5.parse(event)], socket); queueEvents([...initialEvents(), event], socket);
}); });
document.addEventListener("visibilitychange", checkVisibility); document.addEventListener("visibilitychange", checkVisibility);
@ -485,7 +488,9 @@ export const uploadFiles = async (
return false; return false;
} }
if (upload_controllers[upload_id]) { const upload_ref_name = `__upload_controllers_${upload_id}`
if (refs[upload_ref_name]) {
console.log("Upload already in progress for ", upload_id); console.log("Upload already in progress for ", upload_id);
return false; return false;
} }
@ -497,7 +502,9 @@ export const uploadFiles = async (
// Whenever called, responseText will contain the entire response so far. // Whenever called, responseText will contain the entire response so far.
const chunks = progressEvent.event.target.responseText.trim().split("\n"); const chunks = progressEvent.event.target.responseText.trim().split("\n");
// So only process _new_ chunks beyond resp_idx. // So only process _new_ chunks beyond resp_idx.
chunks.slice(resp_idx).map((chunk) => { chunks.slice(resp_idx).map((chunk_json) => {
try {
const chunk = JSON5.parse(chunk_json);
event_callbacks.map((f, ix) => { event_callbacks.map((f, ix) => {
f(chunk) f(chunk)
.then(() => { .then(() => {
@ -509,11 +516,17 @@ export const uploadFiles = async (
.catch((e) => { .catch((e) => {
if (progressEvent.progress === 1) { if (progressEvent.progress === 1) {
// Chunk may be incomplete, so only report errors when full response is available. // Chunk may be incomplete, so only report errors when full response is available.
console.log("Error parsing chunk", chunk, e); console.log("Error processing chunk", chunk, e);
} }
return; return;
}); });
}); });
} catch (e) {
if (progressEvent.progress === 1) {
console.log("Error parsing chunk", chunk_json, e);
}
return;
}
}); });
}; };
@ -537,7 +550,7 @@ export const uploadFiles = async (
}); });
// Send the file to the server. // Send the file to the server.
upload_controllers[upload_id] = controller; refs[upload_ref_name] = controller;
try { try {
return await axios.post(getBackendURL(UPLOADURL), formdata, config); return await axios.post(getBackendURL(UPLOADURL), formdata, config);
@ -557,7 +570,7 @@ export const uploadFiles = async (
} }
return false; return false;
} finally { } finally {
delete upload_controllers[upload_id]; delete refs[upload_ref_name];
} }
}; };
@ -799,7 +812,7 @@ export const useEventLoop = (
connect( connect(
socket, socket,
dispatch, dispatch,
["websocket", "polling"], ["websocket"],
setConnectErrors, setConnectErrors,
client_storage client_storage
); );

View File

@ -331,7 +331,7 @@ _MAPPING: dict = {
"SessionStorage", "SessionStorage",
], ],
"middleware": ["middleware", "Middleware"], "middleware": ["middleware", "Middleware"],
"model": ["session", "Model"], "model": ["asession", "session", "Model"],
"state": [ "state": [
"var", "var",
"ComponentState", "ComponentState",

View File

@ -186,6 +186,7 @@ from .istate.wrappers import get_state as get_state
from .middleware import Middleware as Middleware from .middleware import Middleware as Middleware
from .middleware import middleware as middleware from .middleware import middleware as middleware
from .model import Model as Model from .model import Model as Model
from .model import asession as asession
from .model import session as session from .model import session as session
from .page import page as page from .page import page as page
from .state import ComponentState as ComponentState from .state import ComponentState as ComponentState

View File

@ -17,6 +17,7 @@ import sys
import traceback import traceback
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from types import SimpleNamespace
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
@ -330,6 +331,12 @@ class App(MiddlewareMixin, LifespanMixin):
self.register_lifespan_task(windows_hot_reload_lifespan_hack) self.register_lifespan_task(windows_hot_reload_lifespan_hack)
# Enable proxying to frontend server.
if not environment.REFLEX_BACKEND_ONLY.get():
from reflex.proxy import proxy_middleware
self.register_lifespan_task(proxy_middleware)
def _enable_state(self) -> None: def _enable_state(self) -> None:
"""Enable state for the app.""" """Enable state for the app."""
if not self.state: if not self.state:
@ -363,6 +370,11 @@ class App(MiddlewareMixin, LifespanMixin):
max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE, max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE,
ping_interval=constants.Ping.INTERVAL, ping_interval=constants.Ping.INTERVAL,
ping_timeout=constants.Ping.TIMEOUT, ping_timeout=constants.Ping.TIMEOUT,
json=SimpleNamespace(
dumps=staticmethod(format.json_dumps),
loads=staticmethod(json.loads),
),
transports=["websocket"],
) )
elif getattr(self.sio, "async_mode", "") != "asgi": elif getattr(self.sio, "async_mode", "") != "asgi":
raise RuntimeError( raise RuntimeError(
@ -430,7 +442,7 @@ class App(MiddlewareMixin, LifespanMixin):
allow_credentials=True, allow_credentials=True,
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],
allow_origins=["*"], allow_origins=get_config().cors_allowed_origins,
) )
@property @property
@ -467,7 +479,7 @@ class App(MiddlewareMixin, LifespanMixin):
def add_page( def add_page(
self, self,
component: Component | ComponentCallable, component: Component | ComponentCallable | None = None,
route: str | None = None, route: str | None = None,
title: str | Var | None = None, title: str | Var | None = None,
description: str | Var | None = None, description: str | Var | None = None,
@ -490,17 +502,33 @@ class App(MiddlewareMixin, LifespanMixin):
meta: The metadata of the page. meta: The metadata of the page.
Raises: Raises:
ValueError: When the specified route name already exists. PageValueError: When the component is not set for a non-404 page.
RouteValueError: When the specified route name already exists.
""" """
# If the route is not set, get it from the callable. # If the route is not set, get it from the callable.
if route is None: if route is None:
if not isinstance(component, Callable): if not isinstance(component, Callable):
raise ValueError("Route must be set if component is not a callable.") raise exceptions.RouteValueError(
"Route must be set if component is not a callable."
)
# Format the route. # Format the route.
route = format.format_route(component.__name__) route = format.format_route(component.__name__)
else: else:
route = format.format_route(route, format_case=False) route = format.format_route(route, format_case=False)
if route == constants.Page404.SLUG:
if component is None:
component = Default404Page.create()
component = wait_for_client_redirect(self._generate_component(component))
title = title or constants.Page404.TITLE
description = description or constants.Page404.DESCRIPTION
image = image or constants.Page404.IMAGE
else:
if component is None:
raise exceptions.PageValueError(
"Component must be set for a non-404 page."
)
# Check if the route given is valid # Check if the route given is valid
verify_route_validity(route) verify_route_validity(route)
@ -516,7 +544,7 @@ class App(MiddlewareMixin, LifespanMixin):
if route == constants.PageNames.INDEX_ROUTE if route == constants.PageNames.INDEX_ROUTE
else f"`{route}`" else f"`{route}`"
) )
raise ValueError( raise exceptions.RouteValueError(
f"Duplicate page route {route_name} already exists. Make sure you do not have two" f"Duplicate page route {route_name} already exists. Make sure you do not have two"
f" pages with the same route" f" pages with the same route"
) )
@ -633,10 +661,14 @@ class App(MiddlewareMixin, LifespanMixin):
on_load: The event handler(s) that will be called each time the page load. on_load: The event handler(s) that will be called each time the page load.
meta: The metadata of the page. meta: The metadata of the page.
""" """
if component is None: console.deprecate(
component = Default404Page.create() feature_name="App.add_custom_404_page",
reason=f"Use app.add_page(component, route='/{constants.Page404.SLUG}') instead.",
deprecation_version="0.6.7",
removal_version="0.8.0",
)
self.add_page( self.add_page(
component=wait_for_client_redirect(self._generate_component(component)), component=component,
route=constants.Page404.SLUG, route=constants.Page404.SLUG,
title=title or constants.Page404.TITLE, title=title or constants.Page404.TITLE,
image=image or constants.Page404.IMAGE, image=image or constants.Page404.IMAGE,
@ -837,7 +869,7 @@ class App(MiddlewareMixin, LifespanMixin):
# Render a default 404 page if the user didn't supply one # Render a default 404 page if the user didn't supply one
if constants.Page404.SLUG not in self.unevaluated_pages: if constants.Page404.SLUG not in self.unevaluated_pages:
self.add_custom_404_page() self.add_page(route=constants.Page404.SLUG)
# Fix up the style. # Fix up the style.
self.style = evaluate_style_namespaces(self.style) self.style = evaluate_style_namespaces(self.style)
@ -947,12 +979,12 @@ class App(MiddlewareMixin, LifespanMixin):
is not None is not None
): ):
executor = concurrent.futures.ProcessPoolExecutor( executor = concurrent.futures.ProcessPoolExecutor(
max_workers=number_of_processes, max_workers=number_of_processes or None,
mp_context=multiprocessing.get_context("fork"), mp_context=multiprocessing.get_context("fork"),
) )
else: else:
executor = concurrent.futures.ThreadPoolExecutor( executor = concurrent.futures.ThreadPoolExecutor(
max_workers=environment.REFLEX_COMPILE_THREADS.get() max_workers=environment.REFLEX_COMPILE_THREADS.get() or None
) )
for route, component in zip(self.pages, page_components): for route, component in zip(self.pages, page_components):
@ -965,7 +997,6 @@ class App(MiddlewareMixin, LifespanMixin):
def _submit_work(fn, *args, **kwargs): def _submit_work(fn, *args, **kwargs):
f = executor.submit(fn, *args, **kwargs) f = executor.submit(fn, *args, **kwargs)
# f = executor.apipe(fn, *args, **kwargs)
result_futures.append(f) result_futures.append(f)
# Compile the pre-compiled pages. # Compile the pre-compiled pages.
@ -1157,7 +1188,7 @@ class App(MiddlewareMixin, LifespanMixin):
if hasattr(handler_fn, "__name__"): if hasattr(handler_fn, "__name__"):
_fn_name = handler_fn.__name__ _fn_name = handler_fn.__name__
else: else:
_fn_name = handler_fn.__class__.__name__ _fn_name = type(handler_fn).__name__
if isinstance(handler_fn, functools.partial): if isinstance(handler_fn, functools.partial):
raise ValueError( raise ValueError(
@ -1270,7 +1301,7 @@ async def process(
await asyncio.create_task( await asyncio.create_task(
app.event_namespace.emit( app.event_namespace.emit(
"reload", "reload",
data=format.json_dumps(event), data=event,
to=sid, to=sid,
) )
) )
@ -1331,20 +1362,22 @@ async def health() -> JSONResponse:
health_status = {"status": True} health_status = {"status": True}
status_code = 200 status_code = 200
db_status, redis_status = await asyncio.gather( tasks = []
get_db_status(), prerequisites.get_redis_status()
)
health_status["db"] = db_status if prerequisites.check_db_used():
tasks.append(get_db_status())
if prerequisites.check_redis_used():
tasks.append(prerequisites.get_redis_status())
if redis_status is None: results = await asyncio.gather(*tasks)
for result in results:
health_status |= result
if "redis" in health_status and health_status["redis"] is None:
health_status["redis"] = False health_status["redis"] = False
else:
health_status["redis"] = redis_status
if not health_status["db"] or ( if not all(health_status.values()):
not health_status["redis"] and redis_status is not None
):
health_status["status"] = False health_status["status"] = False
status_code = 503 status_code = 503
@ -1523,7 +1556,7 @@ class EventNamespace(AsyncNamespace):
""" """
# Creating a task prevents the update from being blocked behind other coroutines. # Creating a task prevents the update from being blocked behind other coroutines.
await asyncio.create_task( await asyncio.create_task(
self.emit(str(constants.SocketEvent.EVENT), update.json(), to=sid) self.emit(str(constants.SocketEvent.EVENT), update, to=sid)
) )
async def on_event(self, sid, data): async def on_event(self, sid, data):
@ -1536,7 +1569,7 @@ class EventNamespace(AsyncNamespace):
sid: The Socket.IO session id. sid: The Socket.IO session id.
data: The event data. data: The event data.
""" """
fields = json.loads(data) fields = data
# Get the event. # Get the event.
event = Event( event = Event(
**{k: v for k, v in fields.items() if k not in ("handler", "event_actions")} **{k: v for k, v in fields.items() if k not in ("handler", "event_actions")}

View File

@ -5,7 +5,7 @@ from pathlib import Path
from typing import Optional from typing import Optional
from reflex import constants from reflex import constants
from reflex.utils.exec import is_backend_only from reflex.config import EnvironmentVariables
def asset( def asset(
@ -52,7 +52,7 @@ def asset(
The relative URL to the asset. The relative URL to the asset.
""" """
assets = constants.Dirs.APP_ASSETS assets = constants.Dirs.APP_ASSETS
backend_only = is_backend_only() backend_only = EnvironmentVariables.REFLEX_BACKEND_ONLY.get()
# Local asset handling # Local asset handling
if not shared: if not shared:

View File

@ -30,8 +30,9 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
# can't use reflex.config.environment here cause of circular import # can't use reflex.config.environment here cause of circular import
reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true" reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true"
for base in bases: base = None
try: try:
for base in bases:
if not reload and getattr(base, field_name, None): if not reload and getattr(base, field_name, None):
pass pass
except TypeError as te: except TypeError as te:

View File

@ -75,7 +75,7 @@ def _compile_app(app_root: Component) -> str:
return templates.APP_ROOT.render( return templates.APP_ROOT.render(
imports=utils.compile_imports(app_root._get_all_imports()), imports=utils.compile_imports(app_root._get_all_imports()),
custom_codes=app_root._get_all_custom_code(), custom_codes=app_root._get_all_custom_code(),
hooks={**app_root._get_all_hooks_internal(), **app_root._get_all_hooks()}, hooks=app_root._get_all_hooks(),
window_libraries=window_libraries, window_libraries=window_libraries,
render=app_root.render(), render=app_root.render(),
) )
@ -149,7 +149,7 @@ def _compile_page(
imports=imports, imports=imports,
dynamic_imports=component._get_all_dynamic_imports(), dynamic_imports=component._get_all_dynamic_imports(),
custom_codes=component._get_all_custom_code(), custom_codes=component._get_all_custom_code(),
hooks={**component._get_all_hooks_internal(), **component._get_all_hooks()}, hooks=component._get_all_hooks(),
render=component.render(), render=component.render(),
**kwargs, **kwargs,
) )

View File

@ -1,9 +1,46 @@
"""Templates to use in the reflex compiler.""" """Templates to use in the reflex compiler."""
from __future__ import annotations
from jinja2 import Environment, FileSystemLoader, Template from jinja2 import Environment, FileSystemLoader, Template
from reflex import constants from reflex import constants
from reflex.constants import Hooks
from reflex.utils.format import format_state_name, json_dumps from reflex.utils.format import format_state_name, json_dumps
from reflex.vars.base import VarData
def _sort_hooks(hooks: dict[str, VarData | None]):
"""Sort the hooks by their position.
Args:
hooks: The hooks to sort.
Returns:
The sorted hooks.
"""
sorted_hooks = {
Hooks.HookPosition.INTERNAL: [],
Hooks.HookPosition.PRE_TRIGGER: [],
Hooks.HookPosition.POST_TRIGGER: [],
}
for hook, data in hooks.items():
if data and data.position and data.position == Hooks.HookPosition.INTERNAL:
sorted_hooks[Hooks.HookPosition.INTERNAL].append((hook, data))
elif not data or (
not data.position
or data.position == constants.Hooks.HookPosition.PRE_TRIGGER
):
sorted_hooks[Hooks.HookPosition.PRE_TRIGGER].append((hook, data))
elif (
data
and data.position
and data.position == constants.Hooks.HookPosition.POST_TRIGGER
):
sorted_hooks[Hooks.HookPosition.POST_TRIGGER].append((hook, data))
return sorted_hooks
class ReflexJinjaEnvironment(Environment): class ReflexJinjaEnvironment(Environment):
@ -45,7 +82,9 @@ class ReflexJinjaEnvironment(Environment):
"on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL, "on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,
"update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL, "update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL,
"frontend_exception_state": constants.CompileVars.FRONTEND_EXCEPTION_STATE_FULL, "frontend_exception_state": constants.CompileVars.FRONTEND_EXCEPTION_STATE_FULL,
"hook_position": constants.Hooks.HookPosition,
} }
self.globals["sort_hooks"] = _sort_hooks
def get_template(name: str) -> Template: def get_template(name: str) -> Template:
@ -102,6 +141,9 @@ STYLE = get_template("web/styles/styles.css.jinja2")
# Code that generate the package json file # Code that generate the package json file
PACKAGE_JSON = get_template("web/package.json.jinja2") PACKAGE_JSON = get_template("web/package.json.jinja2")
# Template containing some macros used in the web pages.
MACROS = get_template("web/pages/macros.js.jinja2")
# Code that generate the pyproject.toml file for custom components. # Code that generate the pyproject.toml file for custom components.
CUSTOM_COMPONENTS_PYPROJECT_TOML = get_template( CUSTOM_COMPONENTS_PYPROJECT_TOML = get_template(
"custom_components/pyproject.toml.jinja2" "custom_components/pyproject.toml.jinja2"

View File

@ -115,7 +115,7 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
default, rest = compile_import_statement(fields) default, rest = compile_import_statement(fields)
# prevent lib from being rendered on the page if all imports are non rendered kind # prevent lib from being rendered on the page if all imports are non rendered kind
if not any({f.render for f in fields}): # type: ignore if not any(f.render for f in fields): # type: ignore
continue continue
if not lib: if not lib:
@ -123,8 +123,7 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
raise ValueError("No default field allowed for empty library.") raise ValueError("No default field allowed for empty library.")
if rest is None or len(rest) == 0: if rest is None or len(rest) == 0:
raise ValueError("No fields to import.") raise ValueError("No fields to import.")
for module in sorted(rest): import_dicts.extend(get_import_dict(module) for module in sorted(rest))
import_dicts.append(get_import_dict(module))
continue continue
# remove the version before rendering the package imports # remove the version before rendering the package imports
@ -291,7 +290,7 @@ def compile_custom_component(
"name": component.tag, "name": component.tag,
"props": props, "props": props,
"render": render.render(), "render": render.render(),
"hooks": {**render._get_all_hooks_internal(), **render._get_all_hooks()}, "hooks": render._get_all_hooks(),
"custom_code": render._get_all_custom_code(), "custom_code": render._get_all_custom_code(),
}, },
imports, imports,

View File

@ -9,6 +9,7 @@ from reflex.components.tags import Tag
from reflex.components.tags.tagless import Tagless from reflex.components.tags.tagless import Tagless
from reflex.utils.imports import ParsedImportDict from reflex.utils.imports import ParsedImportDict
from reflex.vars import BooleanVar, ObjectVar, Var from reflex.vars import BooleanVar, ObjectVar, Var
from reflex.vars.base import VarData
class Bare(Component): class Bare(Component):
@ -32,7 +33,7 @@ class Bare(Component):
contents = str(contents) if contents is not None else "" contents = str(contents) if contents is not None else ""
return cls(contents=contents) # type: ignore return cls(contents=contents) # type: ignore
def _get_all_hooks_internal(self) -> dict[str, None]: def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
"""Include the hooks for the component. """Include the hooks for the component.
Returns: Returns:
@ -43,7 +44,7 @@ class Bare(Component):
hooks |= self.contents._var_value._get_all_hooks_internal() hooks |= self.contents._var_value._get_all_hooks_internal()
return hooks return hooks
def _get_all_hooks(self) -> dict[str, None]: def _get_all_hooks(self) -> dict[str, VarData | None]:
"""Include the hooks for the component. """Include the hooks for the component.
Returns: Returns:
@ -103,8 +104,8 @@ class Bare(Component):
def _render(self) -> Tag: def _render(self) -> Tag:
if isinstance(self.contents, Var): if isinstance(self.contents, Var):
if isinstance(self.contents, (BooleanVar, ObjectVar)): if isinstance(self.contents, (BooleanVar, ObjectVar)):
return Tagless(contents=f"{{{str(self.contents.to_string())}}}") return Tagless(contents=f"{{{self.contents.to_string()!s}}}")
return Tagless(contents=f"{{{str(self.contents)}}}") return Tagless(contents=f"{{{self.contents!s}}}")
return Tagless(contents=str(self.contents)) return Tagless(contents=str(self.contents))
def _get_vars(self, include_children: bool = False) -> Iterator[Var]: def _get_vars(self, include_children: bool = False) -> Iterator[Var]:

View File

@ -23,6 +23,8 @@ from typing import (
Union, Union,
) )
from typing_extensions import deprecated
import reflex.state import reflex.state
from reflex.base import Base from reflex.base import Base
from reflex.compiler.templates import STATEFUL_COMPONENT from reflex.compiler.templates import STATEFUL_COMPONENT
@ -43,17 +45,13 @@ from reflex.constants.state import FRONTEND_EVENT_STATE
from reflex.event import ( from reflex.event import (
EventCallback, EventCallback,
EventChain, EventChain,
EventChainVar,
EventHandler, EventHandler,
EventSpec, EventSpec,
EventVar, EventVar,
call_event_fn,
call_event_handler,
get_handler_args,
no_args_event_spec, no_args_event_spec,
) )
from reflex.style import Style, format_as_emotion from reflex.style import Style, format_as_emotion
from reflex.utils import format, imports, types from reflex.utils import console, format, imports, types
from reflex.utils.imports import ( from reflex.utils.imports import (
ImmutableParsedImportDict, ImmutableParsedImportDict,
ImportDict, ImportDict,
@ -104,7 +102,7 @@ class BaseComponent(Base, ABC):
""" """
@abstractmethod @abstractmethod
def _get_all_hooks_internal(self) -> dict[str, None]: def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
"""Get the reflex internal hooks for the component and its children. """Get the reflex internal hooks for the component and its children.
Returns: Returns:
@ -112,7 +110,7 @@ class BaseComponent(Base, ABC):
""" """
@abstractmethod @abstractmethod
def _get_all_hooks(self) -> dict[str, None]: def _get_all_hooks(self) -> dict[str, VarData | None]:
"""Get the React hooks for this component. """Get the React hooks for this component.
Returns: Returns:
@ -161,7 +159,7 @@ class ComponentNamespace(SimpleNamespace):
Returns: Returns:
The hash of the namespace. The hash of the namespace.
""" """
return hash(self.__class__.__name__) return hash(type(self).__name__)
def evaluate_style_namespaces(style: ComponentStyle) -> dict: def evaluate_style_namespaces(style: ComponentStyle) -> dict:
@ -493,8 +491,7 @@ class Component(BaseComponent, ABC):
) )
# Check if the key is an event trigger. # Check if the key is an event trigger.
if key in component_specific_triggers: if key in component_specific_triggers:
# Temporarily disable full control for event triggers. kwargs["event_triggers"][key] = EventChain.create(
kwargs["event_triggers"][key] = self._create_event_chain(
value=value, # type: ignore value=value, # type: ignore
args_spec=component_specific_triggers[key], args_spec=component_specific_triggers[key],
key=key, key=key,
@ -548,6 +545,7 @@ class Component(BaseComponent, ABC):
# Construct the component. # Construct the component.
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@deprecated("Use rx.EventChain.create instead.")
def _create_event_chain( def _create_event_chain(
self, self,
args_spec: types.ArgsSpec | Sequence[types.ArgsSpec], args_spec: types.ArgsSpec | Sequence[types.ArgsSpec],
@ -569,81 +567,17 @@ class Component(BaseComponent, ABC):
Returns: Returns:
The event chain. The event chain.
Raises:
ValueError: If the value is not a valid event chain.
""" """
# If it's an event chain var, return it. console.deprecate(
if isinstance(value, Var): "Component._create_event_chain",
if isinstance(value, EventChainVar): "Use rx.EventChain.create instead.",
return value deprecation_version="0.6.8",
elif isinstance(value, EventVar): removal_version="0.7.0",
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: {str(value)} of type {value._var_type}"
) )
elif isinstance(value, EventChain): return EventChain.create(
# Trust that the caller knows what they're doing passing an EventChain directly value=value, # type: ignore
return value
# If the input is a single event handler, wrap it in a list.
if isinstance(value, (EventHandler, EventSpec)):
value = [value]
# If the input is a list of event handlers, create an event chain.
if isinstance(value, List):
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, key=key))
elif isinstance(v, Callable):
# Call the lambda to get the event chain.
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, key=key)
if isinstance(result, Var):
# Recursively call this function if the lambda returned an EventChain Var.
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)) if isinstance(e, EventSpec) else e)
for e in events
]
# Return the event chain.
if isinstance(args_spec, Var):
return EventChain(
events=events,
args_spec=None,
event_actions={},
)
else:
return EventChain(
events=events,
args_spec=args_spec, args_spec=args_spec,
event_actions={}, key=key,
) )
def get_event_triggers( def get_event_triggers(
@ -653,7 +587,6 @@ class Component(BaseComponent, ABC):
Returns: Returns:
The event triggers. The event triggers.
""" """
default_triggers: Dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]] = { default_triggers: Dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]] = {
EventTriggers.ON_FOCUS: no_args_event_spec, EventTriggers.ON_FOCUS: no_args_event_spec,
@ -1111,7 +1044,7 @@ class Component(BaseComponent, ABC):
vars.append(prop_var) vars.append(prop_var)
# Style keeps track of its own VarData instance, so embed in a temp Var that is yielded. # Style keeps track of its own VarData instance, so embed in a temp Var that is yielded.
if isinstance(self.style, dict) and self.style or isinstance(self.style, Var): if (isinstance(self.style, dict) and self.style) or isinstance(self.style, Var):
vars.append( vars.append(
Var( Var(
_js_expr="style", _js_expr="style",
@ -1209,7 +1142,7 @@ class Component(BaseComponent, ABC):
Yields: Yields:
The parent classes that define the method (differently than the base). The parent classes that define the method (differently than the base).
""" """
seen_methods = set([getattr(Component, method)]) seen_methods = {getattr(Component, method)}
for clz in cls.mro(): for clz in cls.mro():
if clz is Component: if clz is Component:
break break
@ -1339,7 +1272,7 @@ class Component(BaseComponent, ABC):
""" """
_imports = {} _imports = {}
if self._get_ref_hook(): if self._get_ref_hook() is not None:
# Handle hooks needed for attaching react refs to DOM nodes. # Handle hooks needed for attaching react refs to DOM nodes.
_imports.setdefault("react", set()).add(ImportVar(tag="useRef")) _imports.setdefault("react", set()).add(ImportVar(tag="useRef"))
_imports.setdefault(f"$/{Dirs.STATE_PATH}", set()).add( _imports.setdefault(f"$/{Dirs.STATE_PATH}", set()).add(
@ -1369,7 +1302,9 @@ class Component(BaseComponent, ABC):
if user_hooks_data is not None: if user_hooks_data is not None:
other_imports.append(user_hooks_data.imports) other_imports.append(user_hooks_data.imports)
other_imports.extend( other_imports.extend(
hook_imports for hook_imports in self._get_added_hooks().values() hook_vardata.imports
for hook_vardata in self._get_added_hooks().values()
if hook_vardata is not None
) )
return imports.merge_imports(_imports, *other_imports) return imports.merge_imports(_imports, *other_imports)
@ -1391,15 +1326,9 @@ class Component(BaseComponent, ABC):
# Collect imports from Vars used directly by this component. # Collect imports from Vars used directly by this component.
var_datas = [var._get_all_var_data() for var in self._get_vars()] var_datas = [var._get_all_var_data() for var in self._get_vars()]
var_imports: List[ImmutableParsedImportDict] = list( var_imports: List[ImmutableParsedImportDict] = [
map( var_data.imports for var_data in var_datas if var_data is not None
lambda var_data: var_data.imports, ]
filter(
None,
var_datas,
),
)
)
added_import_dicts: list[ParsedImportDict] = [] added_import_dicts: list[ParsedImportDict] = []
for clz in self._iter_parent_classes_with_method("add_imports"): for clz in self._iter_parent_classes_with_method("add_imports"):
@ -1408,8 +1337,9 @@ class Component(BaseComponent, ABC):
if not isinstance(list_of_import_dict, list): if not isinstance(list_of_import_dict, list):
list_of_import_dict = [list_of_import_dict] list_of_import_dict = [list_of_import_dict]
for import_dict in list_of_import_dict: added_import_dicts.extend(
added_import_dicts.append(parse_imports(import_dict)) [parse_imports(import_dict) for import_dict in list_of_import_dict]
)
return imports.merge_imports( return imports.merge_imports(
*self._get_props_imports(), *self._get_props_imports(),
@ -1458,7 +1388,7 @@ class Component(BaseComponent, ABC):
}} }}
}}, []);""" }}, []);"""
def _get_ref_hook(self) -> str | None: def _get_ref_hook(self) -> Var | None:
"""Generate the ref hook for the component. """Generate the ref hook for the component.
Returns: Returns:
@ -1466,9 +1396,12 @@ class Component(BaseComponent, ABC):
""" """
ref = self.get_ref() ref = self.get_ref()
if ref is not None: if ref is not None:
return f"const {ref} = useRef(null); {str(Var(_js_expr=ref)._as_ref())} = {ref};" return Var(
f"const {ref} = useRef(null); {Var(_js_expr=ref)._as_ref()!s} = {ref};",
_var_data=VarData(position=Hooks.HookPosition.INTERNAL),
)
def _get_vars_hooks(self) -> dict[str, None]: def _get_vars_hooks(self) -> dict[str, VarData | None]:
"""Get the hooks required by vars referenced in this component. """Get the hooks required by vars referenced in this component.
Returns: Returns:
@ -1481,27 +1414,38 @@ class Component(BaseComponent, ABC):
vars_hooks.update( vars_hooks.update(
var_data.hooks var_data.hooks
if isinstance(var_data.hooks, dict) if isinstance(var_data.hooks, dict)
else {k: None for k in var_data.hooks} else {
k: VarData(position=Hooks.HookPosition.INTERNAL)
for k in var_data.hooks
}
) )
return vars_hooks return vars_hooks
def _get_events_hooks(self) -> dict[str, None]: def _get_events_hooks(self) -> dict[str, VarData | None]:
"""Get the hooks required by events referenced in this component. """Get the hooks required by events referenced in this component.
Returns: Returns:
The hooks for the events. The hooks for the events.
""" """
return {Hooks.EVENTS: None} if self.event_triggers else {} return (
{Hooks.EVENTS: VarData(position=Hooks.HookPosition.INTERNAL)}
if self.event_triggers
else {}
)
def _get_special_hooks(self) -> dict[str, None]: def _get_special_hooks(self) -> dict[str, VarData | None]:
"""Get the hooks required by special actions referenced in this component. """Get the hooks required by special actions referenced in this component.
Returns: Returns:
The hooks for special actions. The hooks for special actions.
""" """
return {Hooks.AUTOFOCUS: None} if self.autofocus else {} return (
{Hooks.AUTOFOCUS: VarData(position=Hooks.HookPosition.INTERNAL)}
if self.autofocus
else {}
)
def _get_hooks_internal(self) -> dict[str, None]: def _get_hooks_internal(self) -> dict[str, VarData | None]:
"""Get the React hooks for this component managed by the framework. """Get the React hooks for this component managed by the framework.
Downstream components should NOT override this method to avoid breaking Downstream components should NOT override this method to avoid breaking
@ -1512,7 +1456,7 @@ class Component(BaseComponent, ABC):
""" """
return { return {
**{ **{
hook: None str(hook): VarData(position=Hooks.HookPosition.INTERNAL)
for hook in [self._get_ref_hook(), self._get_mount_lifecycle_hook()] for hook in [self._get_ref_hook(), self._get_mount_lifecycle_hook()]
if hook is not None if hook is not None
}, },
@ -1521,7 +1465,7 @@ class Component(BaseComponent, ABC):
**self._get_special_hooks(), **self._get_special_hooks(),
} }
def _get_added_hooks(self) -> dict[str, ImportDict]: def _get_added_hooks(self) -> dict[str, VarData | None]:
"""Get the hooks added via `add_hooks` method. """Get the hooks added via `add_hooks` method.
Returns: Returns:
@ -1530,17 +1474,15 @@ class Component(BaseComponent, ABC):
code = {} code = {}
def extract_var_hooks(hook: Var): def extract_var_hooks(hook: Var):
_imports = {}
var_data = VarData.merge(hook._get_all_var_data()) var_data = VarData.merge(hook._get_all_var_data())
if var_data is not None: if var_data is not None:
for sub_hook in var_data.hooks: for sub_hook in var_data.hooks:
code[sub_hook] = {} code[sub_hook] = None
if var_data.imports:
_imports = var_data.imports
if str(hook) in code: if str(hook) in code:
code[str(hook)] = imports.merge_imports(code[str(hook)], _imports) code[str(hook)] = VarData.merge(var_data, code[str(hook)])
else: else:
code[str(hook)] = _imports code[str(hook)] = var_data
# Add the hook code from add_hooks for each parent class (this is reversed to preserve # Add the hook code from add_hooks for each parent class (this is reversed to preserve
# the order of the hooks in the final output) # the order of the hooks in the final output)
@ -1549,7 +1491,7 @@ class Component(BaseComponent, ABC):
if isinstance(hook, Var): if isinstance(hook, Var):
extract_var_hooks(hook) extract_var_hooks(hook)
else: else:
code[hook] = {} code[hook] = None
return code return code
@ -1563,7 +1505,7 @@ class Component(BaseComponent, ABC):
""" """
return return
def _get_all_hooks_internal(self) -> dict[str, None]: def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
"""Get the reflex internal hooks for the component and its children. """Get the reflex internal hooks for the component and its children.
Returns: Returns:
@ -1578,7 +1520,7 @@ class Component(BaseComponent, ABC):
return code return code
def _get_all_hooks(self) -> dict[str, None]: def _get_all_hooks(self) -> dict[str, VarData | None]:
"""Get the React hooks for this component and its children. """Get the React hooks for this component and its children.
Returns: Returns:
@ -1586,13 +1528,15 @@ class Component(BaseComponent, ABC):
""" """
code = {} code = {}
# Add the internal hooks for this component.
code.update(self._get_hooks_internal())
# Add the hook code for this component. # Add the hook code for this component.
hooks = self._get_hooks() hooks = self._get_hooks()
if hooks is not None: if hooks is not None:
code[hooks] = None code[hooks] = None
for hook in self._get_added_hooks(): code.update(self._get_added_hooks())
code[hook] = None
# Add the hook code for the children. # Add the hook code for the children.
for child in self.children: for child in self.children:
@ -1742,7 +1686,7 @@ class CustomComponent(Component):
# Handle event chains. # Handle event chains.
if types._issubclass(type_, EventChain): if types._issubclass(type_, EventChain):
value = self._create_event_chain( value = EventChain.create(
value=value, value=value,
args_spec=event_triggers_in_component_declaration.get( args_spec=event_triggers_in_component_declaration.get(
key, no_args_event_spec key, no_args_event_spec
@ -2194,6 +2138,31 @@ class StatefulComponent(BaseComponent):
] ]
return [var_name] return [var_name]
@staticmethod
def _get_deps_from_event_trigger(event: EventChain | EventSpec | Var) -> set[str]:
"""Get the dependencies accessed by event triggers.
Args:
event: The event trigger to extract deps from.
Returns:
The dependencies accessed by the event triggers.
"""
events: list = [event]
deps = set()
if isinstance(event, EventChain):
events.extend(event.events)
for ev in events:
if isinstance(ev, EventSpec):
for arg in ev.args:
for a in arg:
var_datas = VarData.merge(a._get_all_var_data())
if var_datas and var_datas.deps is not None:
deps |= {str(dep) for dep in var_datas.deps}
return deps
@classmethod @classmethod
def _get_memoized_event_triggers( def _get_memoized_event_triggers(
cls, cls,
@ -2230,6 +2199,11 @@ class StatefulComponent(BaseComponent):
# Calculate Var dependencies accessed by the handler for useCallback dep array. # Calculate Var dependencies accessed by the handler for useCallback dep array.
var_deps = ["addEvents", "Event"] var_deps = ["addEvents", "Event"]
# Get deps from event trigger var data.
var_deps.extend(cls._get_deps_from_event_trigger(event))
# Get deps from hooks.
for arg in event_args: for arg in event_args:
var_data = arg._get_all_var_data() var_data = arg._get_all_var_data()
if var_data is None: if var_data is None:
@ -2252,7 +2226,7 @@ class StatefulComponent(BaseComponent):
) )
return trigger_memo return trigger_memo
def _get_all_hooks_internal(self) -> dict[str, None]: def _get_all_hooks_internal(self) -> dict[str, VarData | None]:
"""Get the reflex internal hooks for the component and its children. """Get the reflex internal hooks for the component and its children.
Returns: Returns:
@ -2260,7 +2234,7 @@ class StatefulComponent(BaseComponent):
""" """
return {} return {}
def _get_all_hooks(self) -> dict[str, None]: def _get_all_hooks(self) -> dict[str, VarData | None]:
"""Get the React hooks for this component. """Get the React hooks for this component.
Returns: Returns:
@ -2378,7 +2352,7 @@ class MemoizationLeaf(Component):
The memoization leaf The memoization leaf
""" """
comp = super().create(*children, **props) comp = super().create(*children, **props)
if comp._get_all_hooks() or comp._get_all_hooks_internal(): if comp._get_all_hooks():
comp._memoization_mode = cls._memoization_mode.copy( comp._memoization_mode = cls._memoization_mode.copy(
update={"disposition": MemoizationDisposition.ALWAYS} update={"disposition": MemoizationDisposition.ALWAYS}
) )
@ -2563,7 +2537,7 @@ class LiteralComponentVar(CachedVarOperation, LiteralVar, ComponentVar):
Returns: Returns:
The hash of the var. The hash of the var.
""" """
return hash((self.__class__.__name__, self._js_expr)) return hash((type(self).__name__, self._js_expr))
@classmethod @classmethod
def create( def create(

View File

@ -109,7 +109,7 @@ class ConnectionToaster(Toaster):
) )
individual_hooks = [ individual_hooks = [
f"const toast_props = {str(LiteralVar.create(props))};", f"const toast_props = {LiteralVar.create(props)!s};",
"const [userDismissed, setUserDismissed] = useState(false);", "const [userDismissed, setUserDismissed] = useState(false);",
FunctionStringVar( FunctionStringVar(
"useEffect", "useEffect",
@ -124,7 +124,7 @@ class ConnectionToaster(Toaster):
Var( Var(
_js_expr=f""" _js_expr=f"""
() => {{ () => {{
if ({str(has_too_many_connection_errors)}) {{ if ({has_too_many_connection_errors!s}) {{
if (!userDismissed) {{ if (!userDismissed) {{
toast.error( toast.error(
`Cannot connect to server: ${{{connection_error}}}.`, `Cannot connect to server: ${{{connection_error}}}.`,
@ -241,7 +241,7 @@ class WifiOffPulse(Icon):
size=props.pop("size", 32), size=props.pop("size", 32),
z_index=props.pop("z_index", 9999), z_index=props.pop("z_index", 9999),
position=props.pop("position", "fixed"), position=props.pop("position", "fixed"),
bottom=props.pop("botton", "33px"), bottom=props.pop("bottom", "33px"),
right=props.pop("right", "33px"), right=props.pop("right", "33px"),
animation=LiteralVar.create(f"{pulse_var} 1s infinite"), animation=LiteralVar.create(f"{pulse_var} 1s infinite"),
**props, **props,

View File

@ -58,7 +58,7 @@ class Breakpoints(Dict[K, V]):
Args: Args:
custom: Custom mapping using CSS values or variables. custom: Custom mapping using CSS values or variables.
initial: Styling when in the inital width initial: Styling when in the initial width
xs: Styling when in the extra-small width xs: Styling when in the extra-small width
sm: Styling when in the small width sm: Styling when in the small width
md: Styling when in the medium width md: Styling when in the medium width

View File

@ -24,7 +24,7 @@ class ClientSideRouting(Component):
library = "$/utils/client_side_routing" library = "$/utils/client_side_routing"
tag = "useClientSideRouting" tag = "useClientSideRouting"
def add_hooks(self) -> list[str]: def add_hooks(self) -> list[str | Var]:
"""Get the hooks to render. """Get the hooks to render.
Returns: Returns:
@ -66,4 +66,4 @@ class Default404Page(Component):
tag = "Error" tag = "Error"
is_default = True is_default = True
status_code: Var[int] = 404 # type: ignore status_code: Var[int] = Var.create(404)

View File

@ -13,7 +13,7 @@ from reflex.vars.base import Var
route_not_found: Var route_not_found: Var
class ClientSideRouting(Component): class ClientSideRouting(Component):
def add_hooks(self) -> list[str]: ... def add_hooks(self) -> list[str | Var]: ...
def render(self) -> str: ... def render(self) -> str: ...
@overload @overload
@classmethod @classmethod

View File

@ -6,11 +6,12 @@ from typing import Dict, List, Tuple, Union
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.components.tags.tag import Tag from reflex.components.tags.tag import Tag
from reflex.constants.compiler import Hooks
from reflex.event import EventChain, EventHandler, passthrough_event_spec from reflex.event import EventChain, EventHandler, passthrough_event_spec
from reflex.utils.format import format_prop, wrap from reflex.utils.format import format_prop, wrap
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import get_unique_variable_name from reflex.vars import get_unique_variable_name
from reflex.vars.base import Var from reflex.vars.base import Var, VarData
class Clipboard(Fragment): class Clipboard(Fragment):
@ -51,7 +52,7 @@ class Clipboard(Fragment):
return super().create(*children, **props) return super().create(*children, **props)
def _exclude_props(self) -> list[str]: def _exclude_props(self) -> list[str]:
return super()._exclude_props() + ["on_paste", "on_paste_event_actions"] return [*super()._exclude_props(), "on_paste", "on_paste_event_actions"]
def _render(self) -> Tag: def _render(self) -> Tag:
tag = super()._render() tag = super()._render()
@ -72,7 +73,7 @@ class Clipboard(Fragment):
), ),
} }
def add_hooks(self) -> list[str]: def add_hooks(self) -> list[str | Var[str]]:
"""Add hook to register paste event listener. """Add hook to register paste event listener.
Returns: Returns:
@ -83,13 +84,14 @@ class Clipboard(Fragment):
return [] return []
if isinstance(on_paste, EventChain): if isinstance(on_paste, EventChain):
on_paste = wrap(str(format_prop(on_paste)).strip("{}"), "(") on_paste = wrap(str(format_prop(on_paste)).strip("{}"), "(")
hook_expr = f"usePasteHandler({self.targets!s}, {self.on_paste_event_actions!s}, {on_paste!s})"
return [ return [
"usePasteHandler(%s, %s, %s)" Var(
% ( hook_expr,
str(self.targets), _var_type="str",
str(self.on_paste_event_actions), _var_data=VarData(position=Hooks.HookPosition.POST_TRIGGER),
on_paste, ),
)
] ]

View File

@ -71,6 +71,6 @@ class Clipboard(Fragment):
... ...
def add_imports(self) -> dict[str, ImportVar]: ... def add_imports(self) -> dict[str, ImportVar]: ...
def add_hooks(self) -> list[str]: ... def add_hooks(self) -> list[str | Var[str]]: ...
clipboard = Clipboard.create clipboard = Clipboard.create

View File

@ -49,9 +49,9 @@ class Cond(MemoizationLeaf):
The conditional component. The conditional component.
""" """
# Wrap everything in fragments. # Wrap everything in fragments.
if comp1.__class__.__name__ != "Fragment": if type(comp1).__name__ != "Fragment":
comp1 = Fragment.create(comp1) comp1 = Fragment.create(comp1)
if comp2 is None or comp2.__class__.__name__ != "Fragment": if comp2 is None or type(comp2).__name__ != "Fragment":
comp2 = Fragment.create(comp2) if comp2 else Fragment.create() comp2 = Fragment.create(comp2) if comp2 else Fragment.create()
return Fragment.create( return Fragment.create(
cls( cls(
@ -94,7 +94,7 @@ class Cond(MemoizationLeaf):
).set( ).set(
props=tag.format_props(), props=tag.format_props(),
), ),
cond_state=f"isTrue({str(self.cond)})", cond_state=f"isTrue({self.cond!s})",
) )
def add_imports(self) -> ImportDict: def add_imports(self) -> ImportDict:

View File

@ -54,7 +54,7 @@ class Foreach(Component):
iterable = LiteralVar.create(iterable) iterable = LiteralVar.create(iterable)
if iterable._var_type == Any: if iterable._var_type == Any:
raise ForeachVarError( raise ForeachVarError(
f"Could not foreach over var `{str(iterable)}` of type Any. " f"Could not foreach over var `{iterable!s}` of type Any. "
"(If you are trying to foreach over a state var, add a type annotation to the var). " "(If you are trying to foreach over a state var, add a type annotation to the var). "
"See https://reflex.dev/docs/library/dynamic-rendering/foreach/" "See https://reflex.dev/docs/library/dynamic-rendering/foreach/"
) )

View File

@ -29,7 +29,7 @@ from reflex.event import (
from reflex.utils import format from reflex.utils import format
from reflex.utils.imports import ImportVar from reflex.utils.imports import ImportVar
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var, get_unique_variable_name from reflex.vars.base import CallableVar, Var, get_unique_variable_name
from reflex.vars.sequence import LiteralStringVar from reflex.vars.sequence import LiteralStringVar
DEFAULT_UPLOAD_ID: str = "default" DEFAULT_UPLOAD_ID: str = "default"
@ -61,7 +61,7 @@ def upload_file(id_: str = DEFAULT_UPLOAD_ID) -> Var:
id_var = LiteralStringVar.create(id_) id_var = LiteralStringVar.create(id_)
var_name = f"""e => setFilesById(filesById => {{ var_name = f"""e => setFilesById(filesById => {{
const updatedFilesById = Object.assign({{}}, filesById); const updatedFilesById = Object.assign({{}}, filesById);
updatedFilesById[{str(id_var)}] = e; updatedFilesById[{id_var!s}] = e;
return updatedFilesById; return updatedFilesById;
}}) }})
""" """
@ -87,7 +87,7 @@ def selected_files(id_: str = DEFAULT_UPLOAD_ID) -> Var:
""" """
id_var = LiteralStringVar.create(id_) id_var = LiteralStringVar.create(id_)
return Var( return Var(
_js_expr=f"(filesById[{str(id_var)}] ? filesById[{str(id_var)}].map((f) => (f.path || f.name)) : [])", _js_expr=f"(filesById[{id_var!s}] ? filesById[{id_var!s}].map((f) => (f.path || f.name)) : [])",
_var_type=List[str], _var_type=List[str],
_var_data=VarData.merge( _var_data=VarData.merge(
upload_files_context_var_data, id_var._get_all_var_data() upload_files_context_var_data, id_var._get_all_var_data()
@ -108,7 +108,8 @@ def clear_selected_files(id_: str = DEFAULT_UPLOAD_ID) -> EventSpec:
# UploadFilesProvider assigns a special function to clear selected files # UploadFilesProvider assigns a special function to clear selected files
# into the shared global refs object to make it accessible outside a React # into the shared global refs object to make it accessible outside a React
# component via `run_script` (otherwise backend could never clear files). # component via `run_script` (otherwise backend could never clear files).
return run_script(f"refs['__clear_selected_files']({id_!r})") func = Var("__clear_selected_files")._as_ref()
return run_script(f"{func}({id_!r})")
def cancel_upload(upload_id: str) -> EventSpec: def cancel_upload(upload_id: str) -> EventSpec:
@ -120,9 +121,8 @@ def cancel_upload(upload_id: str) -> EventSpec:
Returns: Returns:
An event spec that cancels the upload when triggered. An event spec that cancels the upload when triggered.
""" """
return run_script( controller = Var(f"__upload_controllers_{upload_id}")._as_ref()
f"upload_controllers[{str(LiteralVar.create(upload_id))}]?.abort()" return run_script(f"{controller}?.abort()")
)
def get_upload_dir() -> Path: def get_upload_dir() -> Path:
@ -301,7 +301,7 @@ class Upload(MemoizationLeaf):
) )
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} " left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
right_side = f"useDropzone({str(use_dropzone_arguments)})" right_side = f"useDropzone({use_dropzone_arguments!s})"
var_data = VarData.merge( var_data = VarData.merge(
VarData( VarData(

View File

@ -445,7 +445,7 @@ class CodeBlock(Component, MarkdownComponentMap):
dark=Theme.one_dark, dark=Theme.one_dark,
) )
# react-syntax-highlighter doesnt have an explicit "light" or "dark" theme so we use one-light and one-dark # react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark
# themes respectively to ensure code compatibility. # themes respectively to ensure code compatibility.
if "theme" in props and not isinstance(props["theme"], Var): if "theme" in props and not isinstance(props["theme"], Var):
props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore
@ -519,13 +519,13 @@ class CodeBlock(Component, MarkdownComponentMap):
The hook to register the language. The hook to register the language.
""" """
return f""" return f"""
if ({str(_LANGUAGE)}) {{ if ({_LANGUAGE!s}) {{
(async () => {{ (async () => {{
try {{ try {{
const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${{{str(_LANGUAGE)}}}`); const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${{{_LANGUAGE!s}}}`);
SyntaxHighlighter.registerLanguage({str(_LANGUAGE)}, module.default); SyntaxHighlighter.registerLanguage({_LANGUAGE!s}, module.default);
}} catch (error) {{ }} catch (error) {{
console.error(`Error importing language module for ${{{str(_LANGUAGE)}}}:`, error); console.error(`Error importing language module for ${{{_LANGUAGE!s}}}:`, error);
}} }}
}})(); }})();
}} }}
@ -547,7 +547,7 @@ class CodeBlock(Component, MarkdownComponentMap):
The hooks for the component. The hooks for the component.
""" """
return [ return [
f"const {str(_LANGUAGE)} = {str(self.language)}", f"const {_LANGUAGE!s} = {self.language!s}",
self._get_language_registration_hook(), self._get_language_registration_hook(),
] ]

View File

@ -51,27 +51,6 @@ class GridColumnIcons(Enum):
VideoUri = "video_uri" VideoUri = "video_uri"
# @serializer
# def serialize_gridcolumn_icon(icon: GridColumnIcons) -> str:
# """Serialize grid column icon.
# Args:
# icon: the Icon to serialize.
# Returns:
# The serialized value.
# """
# return "prefix" + str(icon)
# class DataEditorColumn(Base):
# """Column."""
# title: str
# id: Optional[str] = None
# type_: str = "str"
class DataEditorTheme(Base): class DataEditorTheme(Base):
"""The theme for the DataEditor component.""" """The theme for the DataEditor component."""
@ -229,7 +208,7 @@ class DataEditor(NoSSRComponent):
header_height: Var[int] header_height: Var[int]
# Additional header icons: # Additional header icons:
# header_icons: Var[Any] # (TODO: must be a map of name: svg) # header_icons: Var[Any] # (TODO: must be a map of name: svg) #noqa: ERA001
# The maximum width a column can be automatically sized to. # The maximum width a column can be automatically sized to.
max_column_auto_width: Var[int] max_column_auto_width: Var[int]
@ -240,7 +219,7 @@ class DataEditor(NoSSRComponent):
# The minimum width a column can be resized to. # The minimum width a column can be resized to.
min_column_width: Var[int] min_column_width: Var[int]
# Determins the height of each row. # Determines the height of each row.
row_height: Var[int] row_height: Var[int]
# Kind of row markers. # Kind of row markers.
@ -360,6 +339,9 @@ class DataEditor(NoSSRComponent):
editor_id = get_unique_variable_name() editor_id = get_unique_variable_name()
# Define the name of the getData callback associated with this component and assign to get_cell_content. # Define the name of the getData callback associated with this component and assign to get_cell_content.
if self.get_cell_content is not None:
data_callback = self.get_cell_content._js_expr
else:
data_callback = f"getData_{editor_id}" data_callback = f"getData_{editor_id}"
self.get_cell_content = Var(_js_expr=data_callback) # type: ignore self.get_cell_content = Var(_js_expr=data_callback) # type: ignore
@ -406,10 +388,8 @@ class DataEditor(NoSSRComponent):
props["rows"] = data.length() if isinstance(data, Var) else len(data) props["rows"] = data.length() if isinstance(data, Var) else len(data)
if not isinstance(columns, Var) and len(columns): if not isinstance(columns, Var) and len(columns):
if ( if types.is_dataframe(type(data)) or (
types.is_dataframe(type(data)) isinstance(data, Var) and types.is_dataframe(data._var_type)
or isinstance(data, Var)
and types.is_dataframe(data._var_type)
): ):
raise ValueError( raise ValueError(
"Cannot pass in both a pandas dataframe and columns to the data_editor component." "Cannot pass in both a pandas dataframe and columns to the data_editor component."

View File

@ -288,10 +288,10 @@ class DataEditor(NoSSRComponent):
freeze_columns: The number of columns which should remain in place when scrolling horizontally. Doesn't include rowMarkers. freeze_columns: The number of columns which should remain in place when scrolling horizontally. Doesn't include rowMarkers.
group_header_height: Controls the header of the group header row. group_header_height: Controls the header of the group header row.
header_height: Controls the height of the header row. header_height: Controls the height of the header row.
max_column_auto_width: Additional header icons: header_icons: Var[Any] # (TODO: must be a map of name: svg) The maximum width a column can be automatically sized to. max_column_auto_width: The maximum width a column can be automatically sized to.
max_column_width: The maximum width a column can be resized to. max_column_width: The maximum width a column can be resized to.
min_column_width: The minimum width a column can be resized to. min_column_width: The minimum width a column can be resized to.
row_height: Determins the height of each row. row_height: Determines the height of each row.
row_markers: Kind of row markers. row_markers: Kind of row markers.
row_marker_start_index: Changes the starting index for row markers. row_marker_start_index: Changes the starting index for row markers.
row_marker_width: Sets the width of row markers in pixels, if unset row markers will automatically size. row_marker_width: Sets the width of row markers in pixels, if unset row markers will automatically size.

View File

@ -490,17 +490,17 @@ class ShikiJsTransformer(ShikiBaseTransformers):
}, },
# White Space # White Space
# ".tab, .space": { # ".tab, .space": {
# "position": "relative", # "position": "relative", # noqa: ERA001
# }, # },
# ".tab::before": { # ".tab::before": {
# "content": "'⇥'", # "content": "'⇥'", # noqa: ERA001
# "position": "absolute", # "position": "absolute", # noqa: ERA001
# "opacity": "0.3", # "opacity": "0.3",# noqa: ERA001
# }, # },
# ".space::before": { # ".space::before": {
# "content": "'·'", # "content": "'·'", # noqa: ERA001
# "position": "absolute", # "position": "absolute", # noqa: ERA001
# "opacity": "0.3", # "opacity": "0.3", # noqa: ERA001
# }, # },
} }
) )

View File

@ -173,7 +173,7 @@ def load_dynamic_serializer():
f"const [{unique_var_name}, set_{unique_var_name}] = useState(null);": None, f"const [{unique_var_name}, set_{unique_var_name}] = useState(null);": None,
"useEffect(() => {" "useEffect(() => {"
"let isMounted = true;" "let isMounted = true;"
f"evalReactComponent({str(js_string)})" f"evalReactComponent({js_string!s})"
".then((component) => {" ".then((component) => {"
"if (isMounted) {" "if (isMounted) {"
f"set_{unique_var_name}(component);" f"set_{unique_var_name}(component);"
@ -183,7 +183,7 @@ def load_dynamic_serializer():
"isMounted = false;" "isMounted = false;"
"};" "};"
"}" "}"
f", [{str(js_string)}]);": None, f", [{js_string!s}]);": None,
}, },
), ),
), ),

View File

@ -127,7 +127,7 @@ _MAPPING = {
EXCLUDE = ["del_", "Del", "image"] EXCLUDE = ["del_", "Del", "image"]
for _, v in _MAPPING.items(): for v in _MAPPING.values():
v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE]) v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])
_SUBMOD_ATTRS: dict[str, list[str]] = _MAPPING _SUBMOD_ATTRS: dict[str, list[str]] = _MAPPING

View File

@ -339,5 +339,5 @@ _MAPPING = {
], ],
} }
EXCLUDE = ["del_", "Del", "image"] EXCLUDE = ["del_", "Del", "image"]
for _, v in _MAPPING.items(): for v in _MAPPING.values():
v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE]) v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Base classes."""
from typing import Union from typing import Union

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Forms classes."""
from __future__ import annotations from __future__ import annotations
@ -18,6 +18,7 @@ from reflex.event import (
prevent_default, prevent_default,
) )
from reflex.utils.imports import ImportDict from reflex.utils.imports import ImportDict
from reflex.utils.types import is_optional
from reflex.vars import VarData from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var from reflex.vars.base import LiteralVar, Var
@ -84,7 +85,6 @@ class Datalist(BaseHTML):
"""Display the datalist element.""" """Display the datalist element."""
tag = "datalist" tag = "datalist"
# No unique attributes, only common ones are inherited
class Fieldset(Element): class Fieldset(Element):
@ -182,9 +182,7 @@ class Form(BaseHTML):
props["handle_submit_unique_name"] = "" props["handle_submit_unique_name"] = ""
form = super().create(*children, **props) form = super().create(*children, **props)
form.handle_submit_unique_name = md5( form.handle_submit_unique_name = md5(
str({**form._get_all_hooks_internal(), **form._get_all_hooks()}).encode( str(form._get_all_hooks()).encode("utf-8")
"utf-8"
)
).hexdigest() ).hexdigest()
return form return form
@ -241,16 +239,15 @@ class Form(BaseHTML):
if ref.startswith("refs_"): if ref.startswith("refs_"):
ref_var = Var(_js_expr=ref[:-3])._as_ref() ref_var = Var(_js_expr=ref[:-3])._as_ref()
form_refs[ref[len("refs_") : -3]] = Var( form_refs[ref[len("refs_") : -3]] = Var(
_js_expr=f"getRefValues({str(ref_var)})", _js_expr=f"getRefValues({ref_var!s})",
_var_data=VarData.merge(ref_var._get_all_var_data()), _var_data=VarData.merge(ref_var._get_all_var_data()),
) )
else: else:
ref_var = Var(_js_expr=ref)._as_ref() ref_var = Var(_js_expr=ref)._as_ref()
form_refs[ref[4:]] = Var( form_refs[ref[4:]] = Var(
_js_expr=f"getRefValue({str(ref_var)})", _js_expr=f"getRefValue({ref_var!s})",
_var_data=VarData.merge(ref_var._get_all_var_data()), _var_data=VarData.merge(ref_var._get_all_var_data()),
) )
# print(repr(form_refs))
return form_refs return form_refs
def _get_vars(self, include_children: bool = True) -> Iterator[Var]: def _get_vars(self, include_children: bool = True) -> Iterator[Var]:
@ -258,7 +255,8 @@ class Form(BaseHTML):
yield from self._get_form_refs().values() yield from self._get_form_refs().values()
def _exclude_props(self) -> list[str]: def _exclude_props(self) -> list[str]:
return super()._exclude_props() + [ return [
*super()._exclude_props(),
"reset_on_submit", "reset_on_submit",
"handle_submit_unique_name", "handle_submit_unique_name",
] ]
@ -383,6 +381,33 @@ class Input(BaseHTML):
# Fired when a key is released # Fired when a key is released
on_key_up: EventHandler[key_event] on_key_up: EventHandler[key_event]
@classmethod
def create(cls, *children, **props):
"""Create an Input component.
Args:
*children: The children of the component.
**props: The properties of the component.
Returns:
The component.
"""
from reflex.vars.number import ternary_operation
value = props.get("value")
# React expects an empty string(instead of null) for controlled inputs.
if value is not None and is_optional(
(value_var := Var.create(value))._var_type
):
props["value"] = ternary_operation(
(value_var != Var.create(None)) # pyright: ignore [reportGeneralTypeIssues]
& (value_var != Var(_js_expr="undefined")),
value,
Var.create(""),
)
return super().create(*children, **props)
class Label(BaseHTML): class Label(BaseHTML):
"""Display the label element.""" """Display the label element."""
@ -400,7 +425,6 @@ class Legend(BaseHTML):
"""Display the legend element.""" """Display the legend element."""
tag = "legend" tag = "legend"
# No unique attributes, only common ones are inherited
class Meter(BaseHTML): class Meter(BaseHTML):
@ -652,19 +676,20 @@ class Textarea(BaseHTML):
"Cannot combine `enter_key_submit` with `on_key_down`.", "Cannot combine `enter_key_submit` with `on_key_down`.",
) )
custom_attrs["on_key_down"] = Var( custom_attrs["on_key_down"] = Var(
_js_expr=f"(e) => enterKeySubmitOnKeyDown(e, {str(enter_key_submit)})", _js_expr=f"(e) => enterKeySubmitOnKeyDown(e, {enter_key_submit!s})",
_var_data=VarData.merge(enter_key_submit._get_all_var_data()), _var_data=VarData.merge(enter_key_submit._get_all_var_data()),
) )
if auto_height is not None: if auto_height is not None:
auto_height = Var.create(auto_height) auto_height = Var.create(auto_height)
custom_attrs["on_input"] = Var( custom_attrs["on_input"] = Var(
_js_expr=f"(e) => autoHeightOnInput(e, {str(auto_height)})", _js_expr=f"(e) => autoHeightOnInput(e, {auto_height!s})",
_var_data=VarData.merge(auto_height._get_all_var_data()), _var_data=VarData.merge(auto_height._get_all_var_data()),
) )
return super().create(*children, **props) return super().create(*children, **props)
def _exclude_props(self) -> list[str]: def _exclude_props(self) -> list[str]:
return super()._exclude_props() + [ return [
*super()._exclude_props(),
"auto_height", "auto_height",
"enter_key_submit", "enter_key_submit",
] ]

View File

@ -189,7 +189,7 @@ class Datalist(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@ -512,7 +512,7 @@ class Input(BaseHTML):
on_unmount: Optional[EventType[[], BASE_STATE]] = None, on_unmount: Optional[EventType[[], BASE_STATE]] = None,
**props, **props,
) -> "Input": ) -> "Input":
"""Create the component. """Create an Input component.
Args: Args:
*children: The children of the component. *children: The children of the component.
@ -576,7 +576,7 @@ class Input(BaseHTML):
class_name: The class name for the component. class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute custom_attrs: custom attribute
**props: The props of the component. **props: The properties of the component.
Returns: Returns:
The component. The component.
@ -730,7 +730,7 @@ class Legend(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Inline classes."""
from typing import Union from typing import Union

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Media classes."""
from typing import Any, Union from typing import Any, Union
@ -129,7 +129,6 @@ class Img(BaseHTML):
Returns: Returns:
The component. The component.
""" """
return ( return (
super().create(src=children[0], **props) super().create(src=children[0], **props)
@ -274,14 +273,12 @@ class Picture(BaseHTML):
"""Display the picture element.""" """Display the picture element."""
tag = "picture" tag = "picture"
# No unique attributes, only common ones are inherited
class Portal(BaseHTML): class Portal(BaseHTML):
"""Display the portal element.""" """Display the portal element."""
tag = "portal" tag = "portal"
# No unique attributes, only common ones are inherited
class Source(BaseHTML): class Source(BaseHTML):

View File

@ -340,7 +340,6 @@ class Img(BaseHTML):
Returns: Returns:
The component. The component.
""" """
... ...
@ -987,7 +986,7 @@ class Picture(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@ -1073,7 +1072,7 @@ class Portal(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Metadata classes."""
from typing import List, Union from typing import List, Union
@ -8,7 +8,7 @@ from reflex.vars.base import Var
from .base import BaseHTML from .base import BaseHTML
class Base(BaseHTML): # noqa: E742 class Base(BaseHTML):
"""Display the base element.""" """Display the base element."""
tag = "base" tag = "base"
@ -18,13 +18,13 @@ class Base(BaseHTML): # noqa: E742
target: Var[Union[str, int, bool]] target: Var[Union[str, int, bool]]
class Head(BaseHTML): # noqa: E742 class Head(BaseHTML):
"""Display the head element.""" """Display the head element."""
tag = "head" tag = "head"
class Link(BaseHTML): # noqa: E742 class Link(BaseHTML):
"""Display the link element.""" """Display the link element."""
tag = "link" tag = "link"
@ -75,14 +75,14 @@ class Meta(BaseHTML): # Inherits common attributes from BaseHTML
name: Var[Union[str, int, bool]] name: Var[Union[str, int, bool]]
class Title(Element): # noqa: E742 class Title(Element):
"""Display the title element.""" """Display the title element."""
tag = "title" tag = "title"
# Had to be named with an underscore so it doesnt conflict with reflex.style Style in pyi # Had to be named with an underscore so it doesn't conflict with reflex.style Style in pyi
class StyleEl(Element): # noqa: E742 class StyleEl(Element):
"""Display the style element.""" """Display the style element."""
tag = "style" tag = "style"

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Other classes."""
from typing import Union from typing import Union
@ -26,31 +26,39 @@ class Dialog(BaseHTML):
class Summary(BaseHTML): class Summary(BaseHTML):
"""Display the summary element.""" """Display the summary element.
Used as a summary or caption for a <details> element.
"""
tag = "summary" tag = "summary"
# No unique attributes, only common ones are inherited; used as a summary or caption for a <details> element
class Slot(BaseHTML): class Slot(BaseHTML):
"""Display the slot element.""" """Display the slot element.
Used as a placeholder inside a web component.
"""
tag = "slot" tag = "slot"
# No unique attributes, only common ones are inherited; used as a placeholder inside a web component
class Template(BaseHTML): class Template(BaseHTML):
"""Display the template element.""" """Display the template element.
Used for declaring fragments of HTML that can be cloned and inserted in the document.
"""
tag = "template" tag = "template"
# No unique attributes, only common ones are inherited; used for declaring fragments of HTML that can be cloned and inserted in the document
class Math(BaseHTML): class Math(BaseHTML):
"""Display the math element.""" """Display the math element.
Represents a mathematical expression.
"""
tag = "math" tag = "math"
# No unique attributes, only common ones are inherited; used for displaying mathematical expressions
class Html(BaseHTML): class Html(BaseHTML):

View File

@ -244,7 +244,7 @@ class Summary(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited; used as a summary or caption for a <details> element Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@ -330,7 +330,7 @@ class Slot(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited; used as a placeholder inside a web component Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@ -416,7 +416,7 @@ class Template(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited; used for declaring fragments of HTML that can be cloned and inserted in the document Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
@ -502,7 +502,7 @@ class Math(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited; used for displaying mathematical expressions Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Scripts classes."""
from typing import Union from typing import Union
@ -17,7 +17,6 @@ class Noscript(BaseHTML):
"""Display the noscript element.""" """Display the noscript element."""
tag = "noscript" tag = "noscript"
# No unique attributes, only common ones are inherited
class Script(BaseHTML): class Script(BaseHTML):

View File

@ -154,7 +154,7 @@ class Noscript(BaseHTML):
Args: Args:
*children: The children of the component. *children: The children of the component.
access_key: No unique attributes, only common ones are inherited Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu. context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.

View File

@ -1,93 +1,93 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Sectioning classes."""
from .base import BaseHTML from .base import BaseHTML
class Body(BaseHTML): # noqa: E742 class Body(BaseHTML):
"""Display the body element.""" """Display the body element."""
tag = "body" tag = "body"
class Address(BaseHTML): # noqa: E742 class Address(BaseHTML):
"""Display the address element.""" """Display the address element."""
tag = "address" tag = "address"
class Article(BaseHTML): # noqa: E742 class Article(BaseHTML):
"""Display the article element.""" """Display the article element."""
tag = "article" tag = "article"
class Aside(BaseHTML): # noqa: E742 class Aside(BaseHTML):
"""Display the aside element.""" """Display the aside element."""
tag = "aside" tag = "aside"
class Footer(BaseHTML): # noqa: E742 class Footer(BaseHTML):
"""Display the footer element.""" """Display the footer element."""
tag = "footer" tag = "footer"
class Header(BaseHTML): # noqa: E742 class Header(BaseHTML):
"""Display the header element.""" """Display the header element."""
tag = "header" tag = "header"
class H1(BaseHTML): # noqa: E742 class H1(BaseHTML):
"""Display the h1 element.""" """Display the h1 element."""
tag = "h1" tag = "h1"
class H2(BaseHTML): # noqa: E742 class H2(BaseHTML):
"""Display the h1 element.""" """Display the h1 element."""
tag = "h2" tag = "h2"
class H3(BaseHTML): # noqa: E742 class H3(BaseHTML):
"""Display the h1 element.""" """Display the h1 element."""
tag = "h3" tag = "h3"
class H4(BaseHTML): # noqa: E742 class H4(BaseHTML):
"""Display the h1 element.""" """Display the h1 element."""
tag = "h4" tag = "h4"
class H5(BaseHTML): # noqa: E742 class H5(BaseHTML):
"""Display the h1 element.""" """Display the h1 element."""
tag = "h5" tag = "h5"
class H6(BaseHTML): # noqa: E742 class H6(BaseHTML):
"""Display the h1 element.""" """Display the h1 element."""
tag = "h6" tag = "h6"
class Main(BaseHTML): # noqa: E742 class Main(BaseHTML):
"""Display the main element.""" """Display the main element."""
tag = "main" tag = "main"
class Nav(BaseHTML): # noqa: E742 class Nav(BaseHTML):
"""Display the nav element.""" """Display the nav element."""
tag = "nav" tag = "nav"
class Section(BaseHTML): # noqa: E742 class Section(BaseHTML):
"""Display the section element.""" """Display the section element."""
tag = "section" tag = "section"

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Tables classes."""
from typing import Union from typing import Union

View File

@ -1,4 +1,4 @@
"""Element classes. This is an auto-generated file. Do not edit. See ../generate.py.""" """Typography classes."""
from typing import Union from typing import Union

View File

@ -8,7 +8,7 @@ from reflex.vars.base import Var
class LucideIconComponent(Component): class LucideIconComponent(Component):
"""Lucide Icon Component.""" """Lucide Icon Component."""
library = "lucide-react@0.359.0" library = "lucide-react@0.469.0"
class Icon(LucideIconComponent): class Icon(LucideIconComponent):
@ -106,6 +106,7 @@ LUCIDE_ICON_LIST = [
"ambulance", "ambulance",
"ampersand", "ampersand",
"ampersands", "ampersands",
"amphora",
"anchor", "anchor",
"angry", "angry",
"annoyed", "annoyed",
@ -193,6 +194,7 @@ LUCIDE_ICON_LIST = [
"baggage_claim", "baggage_claim",
"ban", "ban",
"banana", "banana",
"bandage",
"banknote", "banknote",
"bar_chart", "bar_chart",
"bar_chart_2", "bar_chart_2",
@ -230,8 +232,10 @@ LUCIDE_ICON_LIST = [
"between_horizontal_start", "between_horizontal_start",
"between_vertical_end", "between_vertical_end",
"between_vertical_start", "between_vertical_start",
"biceps_flexed",
"bike", "bike",
"binary", "binary",
"binoculars",
"biohazard", "biohazard",
"bird", "bird",
"bitcoin", "bitcoin",
@ -278,6 +282,7 @@ LUCIDE_ICON_LIST = [
"boom_box", "boom_box",
"bot", "bot",
"bot_message_square", "bot_message_square",
"bot_off",
"box", "box",
"box_select", "box_select",
"boxes", "boxes",
@ -289,6 +294,7 @@ LUCIDE_ICON_LIST = [
"brick_wall", "brick_wall",
"briefcase", "briefcase",
"briefcase_business", "briefcase_business",
"briefcase_conveyor_belt",
"briefcase_medical", "briefcase_medical",
"bring_to_front", "bring_to_front",
"brush", "brush",
@ -305,9 +311,13 @@ LUCIDE_ICON_LIST = [
"cake_slice", "cake_slice",
"calculator", "calculator",
"calendar", "calendar",
"calendar_1",
"calendar_arrow_down",
"calendar_arrow_up",
"calendar_check", "calendar_check",
"calendar_check_2", "calendar_check_2",
"calendar_clock", "calendar_clock",
"calendar_cog",
"calendar_days", "calendar_days",
"calendar_fold", "calendar_fold",
"calendar_heart", "calendar_heart",
@ -318,6 +328,7 @@ LUCIDE_ICON_LIST = [
"calendar_plus_2", "calendar_plus_2",
"calendar_range", "calendar_range",
"calendar_search", "calendar_search",
"calendar_sync",
"calendar_x", "calendar_x",
"calendar_x_2", "calendar_x_2",
"camera", "camera",
@ -342,6 +353,29 @@ LUCIDE_ICON_LIST = [
"castle", "castle",
"cat", "cat",
"cctv", "cctv",
"chart_area",
"chart_bar",
"chart_bar_big",
"chart_bar_decreasing",
"chart_bar_increasing",
"chart_bar_stacked",
"chart_candlestick",
"chart_column",
"chart_column_big",
"chart_column_decreasing",
"chart_column_increasing",
"chart_column_stacked",
"chart_gantt",
"chart_line",
"chart_network",
"chart_no_axes_column",
"chart_no_axes_column_decreasing",
"chart_no_axes_column_increasing",
"chart_no_axes_combined",
"chart_no_axes_gantt",
"chart_pie",
"chart_scatter",
"chart_spline",
"check", "check",
"check_check", "check_check",
"chef_hat", "chef_hat",
@ -356,6 +390,7 @@ LUCIDE_ICON_LIST = [
"chevrons_down_up", "chevrons_down_up",
"chevrons_left", "chevrons_left",
"chevrons_left_right", "chevrons_left_right",
"chevrons_left_right_ellipsis",
"chevrons_right", "chevrons_right",
"chevrons_right_left", "chevrons_right_left",
"chevrons_up", "chevrons_up",
@ -374,8 +409,8 @@ LUCIDE_ICON_LIST = [
"circle_arrow_out_up_right", "circle_arrow_out_up_right",
"circle_arrow_right", "circle_arrow_right",
"circle_arrow_up", "circle_arrow_up",
"circle_check_big",
"circle_check", "circle_check",
"circle_check_big",
"circle_chevron_down", "circle_chevron_down",
"circle_chevron_left", "circle_chevron_left",
"circle_chevron_right", "circle_chevron_right",
@ -387,13 +422,14 @@ LUCIDE_ICON_LIST = [
"circle_dot_dashed", "circle_dot_dashed",
"circle_ellipsis", "circle_ellipsis",
"circle_equal", "circle_equal",
"circle_fading_arrow_up",
"circle_fading_plus", "circle_fading_plus",
"circle_gauge", "circle_gauge",
"circle_help", "circle_help",
"circle_minus", "circle_minus",
"circle_off", "circle_off",
"circle_parking_off",
"circle_parking", "circle_parking",
"circle_parking_off",
"circle_pause", "circle_pause",
"circle_percent", "circle_percent",
"circle_play", "circle_play",
@ -432,7 +468,11 @@ LUCIDE_ICON_LIST = [
"clock_7", "clock_7",
"clock_8", "clock_8",
"clock_9", "clock_9",
"clock_alert",
"clock_arrow_down",
"clock_arrow_up",
"cloud", "cloud",
"cloud_alert",
"cloud_cog", "cloud_cog",
"cloud_download", "cloud_download",
"cloud_drizzle", "cloud_drizzle",
@ -503,6 +543,7 @@ LUCIDE_ICON_LIST = [
"cup_soda", "cup_soda",
"currency", "currency",
"cylinder", "cylinder",
"dam",
"database", "database",
"database_backup", "database_backup",
"database_zap", "database_zap",
@ -510,7 +551,9 @@ LUCIDE_ICON_LIST = [
"dessert", "dessert",
"diameter", "diameter",
"diamond", "diamond",
"diamond_minus",
"diamond_percent", "diamond_percent",
"diamond_plus",
"dice_1", "dice_1",
"dice_2", "dice_2",
"dice_3", "dice_3",
@ -539,6 +582,7 @@ LUCIDE_ICON_LIST = [
"dribbble", "dribbble",
"drill", "drill",
"droplet", "droplet",
"droplet_off",
"droplets", "droplets",
"drum", "drum",
"drumstick", "drumstick",
@ -554,12 +598,15 @@ LUCIDE_ICON_LIST = [
"ellipsis", "ellipsis",
"ellipsis_vertical", "ellipsis_vertical",
"equal", "equal",
"equal_approximately",
"equal_not", "equal_not",
"eraser", "eraser",
"ethernet_port",
"euro", "euro",
"expand", "expand",
"external_link", "external_link",
"eye", "eye",
"eye_closed",
"eye_off", "eye_off",
"facebook", "facebook",
"factory", "factory",
@ -579,6 +626,10 @@ LUCIDE_ICON_LIST = [
"file_bar_chart", "file_bar_chart",
"file_bar_chart_2", "file_bar_chart_2",
"file_box", "file_box",
"file_chart_column",
"file_chart_column_increasing",
"file_chart_line",
"file_chart_pie",
"file_check", "file_check",
"file_check_2", "file_check_2",
"file_clock", "file_clock",
@ -620,6 +671,7 @@ LUCIDE_ICON_LIST = [
"file_type", "file_type",
"file_type_2", "file_type_2",
"file_up", "file_up",
"file_user",
"file_video", "file_video",
"file_video_2", "file_video_2",
"file_volume", "file_volume",
@ -661,6 +713,7 @@ LUCIDE_ICON_LIST = [
"folder_check", "folder_check",
"folder_clock", "folder_clock",
"folder_closed", "folder_closed",
"folder_code",
"folder_cog", "folder_cog",
"folder_dot", "folder_dot",
"folder_down", "folder_down",
@ -733,7 +786,12 @@ LUCIDE_ICON_LIST = [
"graduation_cap", "graduation_cap",
"grape", "grape",
"grid_2x2", "grid_2x2",
"grid_2x_2",
"grid_2x_2_check",
"grid_2x_2_plus",
"grid_2x_2_x",
"grid_3x3", "grid_3x3",
"grid_3x_3",
"grip", "grip",
"grip_horizontal", "grip_horizontal",
"grip_vertical", "grip_vertical",
@ -762,6 +820,7 @@ LUCIDE_ICON_LIST = [
"heading_4", "heading_4",
"heading_5", "heading_5",
"heading_6", "heading_6",
"headphone_off",
"headphones", "headphones",
"headset", "headset",
"heart", "heart",
@ -779,14 +838,20 @@ LUCIDE_ICON_LIST = [
"hospital", "hospital",
"hotel", "hotel",
"hourglass", "hourglass",
"house",
"house_plug",
"house_plus",
"ice_cream_bowl", "ice_cream_bowl",
"ice_cream_cone", "ice_cream_cone",
"id_card",
"image", "image",
"image_down", "image_down",
"image_minus", "image_minus",
"image_off", "image_off",
"image_play",
"image_plus", "image_plus",
"image_up", "image_up",
"image_upscale",
"images", "images",
"import", "import",
"inbox", "inbox",
@ -808,6 +873,7 @@ LUCIDE_ICON_LIST = [
"key_square", "key_square",
"keyboard", "keyboard",
"keyboard_music", "keyboard_music",
"keyboard_off",
"lamp", "lamp",
"lamp_ceiling", "lamp_ceiling",
"lamp_desk", "lamp_desk",
@ -817,8 +883,9 @@ LUCIDE_ICON_LIST = [
"land_plot", "land_plot",
"landmark", "landmark",
"languages", "languages",
"laptop_minimal",
"laptop", "laptop",
"laptop_minimal",
"laptop_minimal_check",
"lasso", "lasso",
"lasso_select", "lasso_select",
"laugh", "laugh",
@ -833,6 +900,8 @@ LUCIDE_ICON_LIST = [
"layout_template", "layout_template",
"leaf", "leaf",
"leafy_green", "leafy_green",
"lectern",
"letter_text",
"library", "library",
"library_big", "library_big",
"life_buoy", "life_buoy",
@ -845,10 +914,12 @@ LUCIDE_ICON_LIST = [
"link_2_off", "link_2_off",
"linkedin", "linkedin",
"list", "list",
"list_check",
"list_checks", "list_checks",
"list_collapse", "list_collapse",
"list_end", "list_end",
"list_filter", "list_filter",
"list_filter_plus",
"list_minus", "list_minus",
"list_music", "list_music",
"list_ordered", "list_ordered",
@ -861,15 +932,17 @@ LUCIDE_ICON_LIST = [
"list_x", "list_x",
"loader", "loader",
"loader_circle", "loader_circle",
"loader_pinwheel",
"locate", "locate",
"locate_fixed", "locate_fixed",
"locate_off", "locate_off",
"lock", "lock",
"lock_keyhole_open",
"lock_keyhole", "lock_keyhole",
"lock_keyhole_open",
"lock_open", "lock_open",
"log_in", "log_in",
"log_out", "log_out",
"logs",
"lollipop", "lollipop",
"luggage", "luggage",
"magnet", "magnet",
@ -886,7 +959,16 @@ LUCIDE_ICON_LIST = [
"mails", "mails",
"map", "map",
"map_pin", "map_pin",
"map_pin_check",
"map_pin_check_inside",
"map_pin_house",
"map_pin_minus",
"map_pin_minus_inside",
"map_pin_off", "map_pin_off",
"map_pin_plus",
"map_pin_plus_inside",
"map_pin_x",
"map_pin_x_inside",
"map_pinned", "map_pinned",
"martini", "martini",
"maximize", "maximize",
@ -915,6 +997,7 @@ LUCIDE_ICON_LIST = [
"message_square_diff", "message_square_diff",
"message_square_dot", "message_square_dot",
"message_square_heart", "message_square_heart",
"message_square_lock",
"message_square_more", "message_square_more",
"message_square_off", "message_square_off",
"message_square_plus", "message_square_plus",
@ -926,8 +1009,9 @@ LUCIDE_ICON_LIST = [
"message_square_x", "message_square_x",
"messages_square", "messages_square",
"mic", "mic",
"mic_vocal",
"mic_off", "mic_off",
"mic_vocal",
"microchip",
"microscope", "microscope",
"microwave", "microwave",
"milestone", "milestone",
@ -938,6 +1022,7 @@ LUCIDE_ICON_LIST = [
"minus", "minus",
"monitor", "monitor",
"monitor_check", "monitor_check",
"monitor_cog",
"monitor_dot", "monitor_dot",
"monitor_down", "monitor_down",
"monitor_off", "monitor_off",
@ -953,8 +1038,10 @@ LUCIDE_ICON_LIST = [
"mountain", "mountain",
"mountain_snow", "mountain_snow",
"mouse", "mouse",
"mouse_off",
"mouse_pointer", "mouse_pointer",
"mouse_pointer_2", "mouse_pointer_2",
"mouse_pointer_ban",
"mouse_pointer_click", "mouse_pointer_click",
"move", "move",
"move_3d", "move_3d",
@ -991,10 +1078,13 @@ LUCIDE_ICON_LIST = [
"nut_off", "nut_off",
"octagon", "octagon",
"octagon_alert", "octagon_alert",
"octagon_minus",
"octagon_pause", "octagon_pause",
"octagon_x", "octagon_x",
"omega",
"option", "option",
"orbit", "orbit",
"origami",
"package", "package",
"package_2", "package_2",
"package_check", "package_check",
@ -1007,6 +1097,7 @@ LUCIDE_ICON_LIST = [
"paint_roller", "paint_roller",
"paintbrush", "paintbrush",
"paintbrush_2", "paintbrush_2",
"paintbrush_vertical",
"palette", "palette",
"panel_bottom", "panel_bottom",
"panel_bottom_close", "panel_bottom_close",
@ -1036,13 +1127,16 @@ LUCIDE_ICON_LIST = [
"pc_case", "pc_case",
"pen", "pen",
"pen_line", "pen_line",
"pen_off",
"pen_tool", "pen_tool",
"pencil", "pencil",
"pencil_line", "pencil_line",
"pencil_off",
"pencil_ruler", "pencil_ruler",
"pentagon", "pentagon",
"percent", "percent",
"person_standing", "person_standing",
"philippine_peso",
"phone", "phone",
"phone_call", "phone_call",
"phone_forwarded", "phone_forwarded",
@ -1058,7 +1152,10 @@ LUCIDE_ICON_LIST = [
"pie_chart", "pie_chart",
"piggy_bank", "piggy_bank",
"pilcrow", "pilcrow",
"pilcrow_left",
"pilcrow_right",
"pill", "pill",
"pill_bottle",
"pin", "pin",
"pin_off", "pin_off",
"pipette", "pipette",
@ -1084,6 +1181,7 @@ LUCIDE_ICON_LIST = [
"power_off", "power_off",
"presentation", "presentation",
"printer", "printer",
"printer_check",
"projector", "projector",
"proportions", "proportions",
"puzzle", "puzzle",
@ -1158,6 +1256,7 @@ LUCIDE_ICON_LIST = [
"satellite_dish", "satellite_dish",
"save", "save",
"save_all", "save_all",
"save_off",
"scale", "scale",
"scale_3d", "scale_3d",
"scaling", "scaling",
@ -1165,7 +1264,9 @@ LUCIDE_ICON_LIST = [
"scan_barcode", "scan_barcode",
"scan_eye", "scan_eye",
"scan_face", "scan_face",
"scan_heart",
"scan_line", "scan_line",
"scan_qr_code",
"scan_search", "scan_search",
"scan_text", "scan_text",
"scatter_chart", "scatter_chart",
@ -1181,6 +1282,7 @@ LUCIDE_ICON_LIST = [
"search_code", "search_code",
"search_slash", "search_slash",
"search_x", "search_x",
"section",
"send", "send",
"send_horizontal", "send_horizontal",
"send_to_back", "send_to_back",
@ -1225,6 +1327,7 @@ LUCIDE_ICON_LIST = [
"signal_low", "signal_low",
"signal_medium", "signal_medium",
"signal_zero", "signal_zero",
"signature",
"signpost", "signpost",
"signpost_big", "signpost_big",
"siren", "siren",
@ -1234,8 +1337,8 @@ LUCIDE_ICON_LIST = [
"slack", "slack",
"slash", "slash",
"slice", "slice",
"sliders_vertical",
"sliders_horizontal", "sliders_horizontal",
"sliders_vertical",
"smartphone", "smartphone",
"smartphone_charging", "smartphone_charging",
"smartphone_nfc", "smartphone_nfc",
@ -1259,29 +1362,31 @@ LUCIDE_ICON_LIST = [
"sprout", "sprout",
"square", "square",
"square_activity", "square_activity",
"square_arrow_down",
"square_arrow_down_left", "square_arrow_down_left",
"square_arrow_down_right", "square_arrow_down_right",
"square_arrow_down",
"square_arrow_left", "square_arrow_left",
"square_arrow_out_down_left", "square_arrow_out_down_left",
"square_arrow_out_down_right", "square_arrow_out_down_right",
"square_arrow_out_up_left", "square_arrow_out_up_left",
"square_arrow_out_up_right", "square_arrow_out_up_right",
"square_arrow_right", "square_arrow_right",
"square_arrow_up",
"square_arrow_up_left", "square_arrow_up_left",
"square_arrow_up_right", "square_arrow_up_right",
"square_arrow_up",
"square_asterisk", "square_asterisk",
"square_bottom_dashed_scissors", "square_bottom_dashed_scissors",
"square_check_big", "square_chart_gantt",
"square_check", "square_check",
"square_check_big",
"square_chevron_down", "square_chevron_down",
"square_chevron_left", "square_chevron_left",
"square_chevron_right", "square_chevron_right",
"square_chevron_up", "square_chevron_up",
"square_code", "square_code",
"square_dashed_bottom_code", "square_dashed",
"square_dashed_bottom", "square_dashed_bottom",
"square_dashed_bottom_code",
"square_dashed_kanban", "square_dashed_kanban",
"square_dashed_mouse_pointer", "square_dashed_mouse_pointer",
"square_divide", "square_divide",
@ -1295,8 +1400,8 @@ LUCIDE_ICON_LIST = [
"square_menu", "square_menu",
"square_minus", "square_minus",
"square_mouse_pointer", "square_mouse_pointer",
"square_parking_off",
"square_parking", "square_parking",
"square_parking_off",
"square_pen", "square_pen",
"square_percent", "square_percent",
"square_pi", "square_pi",
@ -1310,10 +1415,11 @@ LUCIDE_ICON_LIST = [
"square_slash", "square_slash",
"square_split_horizontal", "square_split_horizontal",
"square_split_vertical", "square_split_vertical",
"square_square",
"square_stack", "square_stack",
"square_terminal", "square_terminal",
"square_user_round",
"square_user", "square_user",
"square_user_round",
"square_x", "square_x",
"squircle", "squircle",
"squirrel", "squirrel",
@ -1350,6 +1456,7 @@ LUCIDE_ICON_LIST = [
"table_cells_merge", "table_cells_merge",
"table_cells_split", "table_cells_split",
"table_columns_split", "table_columns_split",
"table_of_contents",
"table_properties", "table_properties",
"table_rows_split", "table_rows_split",
"tablet", "tablet",
@ -1365,11 +1472,11 @@ LUCIDE_ICON_LIST = [
"tangent", "tangent",
"target", "target",
"telescope", "telescope",
"tent",
"tent_tree", "tent_tree",
"terminal", "terminal",
"test_tube_diagonal",
"test_tube", "test_tube",
"tent", "test_tube_diagonal",
"test_tubes", "test_tubes",
"text", "text",
"text_cursor", "text_cursor",
@ -1390,11 +1497,14 @@ LUCIDE_ICON_LIST = [
"ticket_plus", "ticket_plus",
"ticket_slash", "ticket_slash",
"ticket_x", "ticket_x",
"tickets",
"tickets_plane",
"timer", "timer",
"timer_off", "timer_off",
"timer_reset", "timer_reset",
"toggle_left", "toggle_left",
"toggle_right", "toggle_right",
"toilet",
"tornado", "tornado",
"torus", "torus",
"touchpad", "touchpad",
@ -1416,17 +1526,21 @@ LUCIDE_ICON_LIST = [
"trello", "trello",
"trending_down", "trending_down",
"trending_up", "trending_up",
"trending_up_down",
"triangle", "triangle",
"triangle_right",
"triangle_alert", "triangle_alert",
"triangle_right",
"trophy", "trophy",
"truck", "truck",
"turtle", "turtle",
"tv", "tv",
"tv_2", "tv_2",
"tv_minimal",
"tv_minimal_play",
"twitch", "twitch",
"twitter", "twitter",
"type", "type",
"type_outline",
"umbrella", "umbrella",
"umbrella_off", "umbrella_off",
"underline", "underline",
@ -1437,8 +1551,8 @@ LUCIDE_ICON_LIST = [
"unfold_vertical", "unfold_vertical",
"ungroup", "ungroup",
"university", "university",
"unlink_2",
"unlink", "unlink",
"unlink_2",
"unplug", "unplug",
"upload", "upload",
"usb", "usb",
@ -1446,11 +1560,13 @@ LUCIDE_ICON_LIST = [
"user_check", "user_check",
"user_cog", "user_cog",
"user_minus", "user_minus",
"user_pen",
"user_plus", "user_plus",
"user_round", "user_round",
"user_round_check", "user_round_check",
"user_round_cog", "user_round_cog",
"user_round_minus", "user_round_minus",
"user_round_pen",
"user_round_plus", "user_round_plus",
"user_round_search", "user_round_search",
"user_round_x", "user_round_x",
@ -1472,14 +1588,16 @@ LUCIDE_ICON_LIST = [
"videotape", "videotape",
"view", "view",
"voicemail", "voicemail",
"volleyball",
"volume", "volume",
"volume_1", "volume_1",
"volume_2", "volume_2",
"volume_off",
"volume_x", "volume_x",
"vote", "vote",
"wallet", "wallet",
"wallet_minimal",
"wallet_cards", "wallet_cards",
"wallet_minimal",
"wallpaper", "wallpaper",
"wand", "wand",
"wand_sparkles", "wand_sparkles",
@ -1487,17 +1605,22 @@ LUCIDE_ICON_LIST = [
"washing_machine", "washing_machine",
"watch", "watch",
"waves", "waves",
"waves_ladder",
"waypoints", "waypoints",
"webcam", "webcam",
"webhook_off",
"webhook", "webhook",
"webhook_off",
"weight", "weight",
"wheat", "wheat",
"wheat_off", "wheat_off",
"whole_word", "whole_word",
"wifi", "wifi",
"wifi_high",
"wifi_low",
"wifi_off", "wifi_off",
"wifi_zero",
"wind", "wind",
"wind_arrow_down",
"wine", "wine",
"wine_off", "wine_off",
"workflow", "workflow",

View File

@ -154,6 +154,7 @@ LUCIDE_ICON_LIST = [
"ambulance", "ambulance",
"ampersand", "ampersand",
"ampersands", "ampersands",
"amphora",
"anchor", "anchor",
"angry", "angry",
"annoyed", "annoyed",
@ -241,6 +242,7 @@ LUCIDE_ICON_LIST = [
"baggage_claim", "baggage_claim",
"ban", "ban",
"banana", "banana",
"bandage",
"banknote", "banknote",
"bar_chart", "bar_chart",
"bar_chart_2", "bar_chart_2",
@ -278,8 +280,10 @@ LUCIDE_ICON_LIST = [
"between_horizontal_start", "between_horizontal_start",
"between_vertical_end", "between_vertical_end",
"between_vertical_start", "between_vertical_start",
"biceps_flexed",
"bike", "bike",
"binary", "binary",
"binoculars",
"biohazard", "biohazard",
"bird", "bird",
"bitcoin", "bitcoin",
@ -326,6 +330,7 @@ LUCIDE_ICON_LIST = [
"boom_box", "boom_box",
"bot", "bot",
"bot_message_square", "bot_message_square",
"bot_off",
"box", "box",
"box_select", "box_select",
"boxes", "boxes",
@ -337,6 +342,7 @@ LUCIDE_ICON_LIST = [
"brick_wall", "brick_wall",
"briefcase", "briefcase",
"briefcase_business", "briefcase_business",
"briefcase_conveyor_belt",
"briefcase_medical", "briefcase_medical",
"bring_to_front", "bring_to_front",
"brush", "brush",
@ -353,9 +359,13 @@ LUCIDE_ICON_LIST = [
"cake_slice", "cake_slice",
"calculator", "calculator",
"calendar", "calendar",
"calendar_1",
"calendar_arrow_down",
"calendar_arrow_up",
"calendar_check", "calendar_check",
"calendar_check_2", "calendar_check_2",
"calendar_clock", "calendar_clock",
"calendar_cog",
"calendar_days", "calendar_days",
"calendar_fold", "calendar_fold",
"calendar_heart", "calendar_heart",
@ -366,6 +376,7 @@ LUCIDE_ICON_LIST = [
"calendar_plus_2", "calendar_plus_2",
"calendar_range", "calendar_range",
"calendar_search", "calendar_search",
"calendar_sync",
"calendar_x", "calendar_x",
"calendar_x_2", "calendar_x_2",
"camera", "camera",
@ -390,6 +401,29 @@ LUCIDE_ICON_LIST = [
"castle", "castle",
"cat", "cat",
"cctv", "cctv",
"chart_area",
"chart_bar",
"chart_bar_big",
"chart_bar_decreasing",
"chart_bar_increasing",
"chart_bar_stacked",
"chart_candlestick",
"chart_column",
"chart_column_big",
"chart_column_decreasing",
"chart_column_increasing",
"chart_column_stacked",
"chart_gantt",
"chart_line",
"chart_network",
"chart_no_axes_column",
"chart_no_axes_column_decreasing",
"chart_no_axes_column_increasing",
"chart_no_axes_combined",
"chart_no_axes_gantt",
"chart_pie",
"chart_scatter",
"chart_spline",
"check", "check",
"check_check", "check_check",
"chef_hat", "chef_hat",
@ -404,6 +438,7 @@ LUCIDE_ICON_LIST = [
"chevrons_down_up", "chevrons_down_up",
"chevrons_left", "chevrons_left",
"chevrons_left_right", "chevrons_left_right",
"chevrons_left_right_ellipsis",
"chevrons_right", "chevrons_right",
"chevrons_right_left", "chevrons_right_left",
"chevrons_up", "chevrons_up",
@ -422,8 +457,8 @@ LUCIDE_ICON_LIST = [
"circle_arrow_out_up_right", "circle_arrow_out_up_right",
"circle_arrow_right", "circle_arrow_right",
"circle_arrow_up", "circle_arrow_up",
"circle_check_big",
"circle_check", "circle_check",
"circle_check_big",
"circle_chevron_down", "circle_chevron_down",
"circle_chevron_left", "circle_chevron_left",
"circle_chevron_right", "circle_chevron_right",
@ -435,13 +470,14 @@ LUCIDE_ICON_LIST = [
"circle_dot_dashed", "circle_dot_dashed",
"circle_ellipsis", "circle_ellipsis",
"circle_equal", "circle_equal",
"circle_fading_arrow_up",
"circle_fading_plus", "circle_fading_plus",
"circle_gauge", "circle_gauge",
"circle_help", "circle_help",
"circle_minus", "circle_minus",
"circle_off", "circle_off",
"circle_parking_off",
"circle_parking", "circle_parking",
"circle_parking_off",
"circle_pause", "circle_pause",
"circle_percent", "circle_percent",
"circle_play", "circle_play",
@ -480,7 +516,11 @@ LUCIDE_ICON_LIST = [
"clock_7", "clock_7",
"clock_8", "clock_8",
"clock_9", "clock_9",
"clock_alert",
"clock_arrow_down",
"clock_arrow_up",
"cloud", "cloud",
"cloud_alert",
"cloud_cog", "cloud_cog",
"cloud_download", "cloud_download",
"cloud_drizzle", "cloud_drizzle",
@ -551,6 +591,7 @@ LUCIDE_ICON_LIST = [
"cup_soda", "cup_soda",
"currency", "currency",
"cylinder", "cylinder",
"dam",
"database", "database",
"database_backup", "database_backup",
"database_zap", "database_zap",
@ -558,7 +599,9 @@ LUCIDE_ICON_LIST = [
"dessert", "dessert",
"diameter", "diameter",
"diamond", "diamond",
"diamond_minus",
"diamond_percent", "diamond_percent",
"diamond_plus",
"dice_1", "dice_1",
"dice_2", "dice_2",
"dice_3", "dice_3",
@ -587,6 +630,7 @@ LUCIDE_ICON_LIST = [
"dribbble", "dribbble",
"drill", "drill",
"droplet", "droplet",
"droplet_off",
"droplets", "droplets",
"drum", "drum",
"drumstick", "drumstick",
@ -602,12 +646,15 @@ LUCIDE_ICON_LIST = [
"ellipsis", "ellipsis",
"ellipsis_vertical", "ellipsis_vertical",
"equal", "equal",
"equal_approximately",
"equal_not", "equal_not",
"eraser", "eraser",
"ethernet_port",
"euro", "euro",
"expand", "expand",
"external_link", "external_link",
"eye", "eye",
"eye_closed",
"eye_off", "eye_off",
"facebook", "facebook",
"factory", "factory",
@ -627,6 +674,10 @@ LUCIDE_ICON_LIST = [
"file_bar_chart", "file_bar_chart",
"file_bar_chart_2", "file_bar_chart_2",
"file_box", "file_box",
"file_chart_column",
"file_chart_column_increasing",
"file_chart_line",
"file_chart_pie",
"file_check", "file_check",
"file_check_2", "file_check_2",
"file_clock", "file_clock",
@ -668,6 +719,7 @@ LUCIDE_ICON_LIST = [
"file_type", "file_type",
"file_type_2", "file_type_2",
"file_up", "file_up",
"file_user",
"file_video", "file_video",
"file_video_2", "file_video_2",
"file_volume", "file_volume",
@ -709,6 +761,7 @@ LUCIDE_ICON_LIST = [
"folder_check", "folder_check",
"folder_clock", "folder_clock",
"folder_closed", "folder_closed",
"folder_code",
"folder_cog", "folder_cog",
"folder_dot", "folder_dot",
"folder_down", "folder_down",
@ -781,7 +834,12 @@ LUCIDE_ICON_LIST = [
"graduation_cap", "graduation_cap",
"grape", "grape",
"grid_2x2", "grid_2x2",
"grid_2x_2",
"grid_2x_2_check",
"grid_2x_2_plus",
"grid_2x_2_x",
"grid_3x3", "grid_3x3",
"grid_3x_3",
"grip", "grip",
"grip_horizontal", "grip_horizontal",
"grip_vertical", "grip_vertical",
@ -810,6 +868,7 @@ LUCIDE_ICON_LIST = [
"heading_4", "heading_4",
"heading_5", "heading_5",
"heading_6", "heading_6",
"headphone_off",
"headphones", "headphones",
"headset", "headset",
"heart", "heart",
@ -827,14 +886,20 @@ LUCIDE_ICON_LIST = [
"hospital", "hospital",
"hotel", "hotel",
"hourglass", "hourglass",
"house",
"house_plug",
"house_plus",
"ice_cream_bowl", "ice_cream_bowl",
"ice_cream_cone", "ice_cream_cone",
"id_card",
"image", "image",
"image_down", "image_down",
"image_minus", "image_minus",
"image_off", "image_off",
"image_play",
"image_plus", "image_plus",
"image_up", "image_up",
"image_upscale",
"images", "images",
"import", "import",
"inbox", "inbox",
@ -856,6 +921,7 @@ LUCIDE_ICON_LIST = [
"key_square", "key_square",
"keyboard", "keyboard",
"keyboard_music", "keyboard_music",
"keyboard_off",
"lamp", "lamp",
"lamp_ceiling", "lamp_ceiling",
"lamp_desk", "lamp_desk",
@ -865,8 +931,9 @@ LUCIDE_ICON_LIST = [
"land_plot", "land_plot",
"landmark", "landmark",
"languages", "languages",
"laptop_minimal",
"laptop", "laptop",
"laptop_minimal",
"laptop_minimal_check",
"lasso", "lasso",
"lasso_select", "lasso_select",
"laugh", "laugh",
@ -881,6 +948,8 @@ LUCIDE_ICON_LIST = [
"layout_template", "layout_template",
"leaf", "leaf",
"leafy_green", "leafy_green",
"lectern",
"letter_text",
"library", "library",
"library_big", "library_big",
"life_buoy", "life_buoy",
@ -893,10 +962,12 @@ LUCIDE_ICON_LIST = [
"link_2_off", "link_2_off",
"linkedin", "linkedin",
"list", "list",
"list_check",
"list_checks", "list_checks",
"list_collapse", "list_collapse",
"list_end", "list_end",
"list_filter", "list_filter",
"list_filter_plus",
"list_minus", "list_minus",
"list_music", "list_music",
"list_ordered", "list_ordered",
@ -909,15 +980,17 @@ LUCIDE_ICON_LIST = [
"list_x", "list_x",
"loader", "loader",
"loader_circle", "loader_circle",
"loader_pinwheel",
"locate", "locate",
"locate_fixed", "locate_fixed",
"locate_off", "locate_off",
"lock", "lock",
"lock_keyhole_open",
"lock_keyhole", "lock_keyhole",
"lock_keyhole_open",
"lock_open", "lock_open",
"log_in", "log_in",
"log_out", "log_out",
"logs",
"lollipop", "lollipop",
"luggage", "luggage",
"magnet", "magnet",
@ -934,7 +1007,16 @@ LUCIDE_ICON_LIST = [
"mails", "mails",
"map", "map",
"map_pin", "map_pin",
"map_pin_check",
"map_pin_check_inside",
"map_pin_house",
"map_pin_minus",
"map_pin_minus_inside",
"map_pin_off", "map_pin_off",
"map_pin_plus",
"map_pin_plus_inside",
"map_pin_x",
"map_pin_x_inside",
"map_pinned", "map_pinned",
"martini", "martini",
"maximize", "maximize",
@ -963,6 +1045,7 @@ LUCIDE_ICON_LIST = [
"message_square_diff", "message_square_diff",
"message_square_dot", "message_square_dot",
"message_square_heart", "message_square_heart",
"message_square_lock",
"message_square_more", "message_square_more",
"message_square_off", "message_square_off",
"message_square_plus", "message_square_plus",
@ -974,8 +1057,9 @@ LUCIDE_ICON_LIST = [
"message_square_x", "message_square_x",
"messages_square", "messages_square",
"mic", "mic",
"mic_vocal",
"mic_off", "mic_off",
"mic_vocal",
"microchip",
"microscope", "microscope",
"microwave", "microwave",
"milestone", "milestone",
@ -986,6 +1070,7 @@ LUCIDE_ICON_LIST = [
"minus", "minus",
"monitor", "monitor",
"monitor_check", "monitor_check",
"monitor_cog",
"monitor_dot", "monitor_dot",
"monitor_down", "monitor_down",
"monitor_off", "monitor_off",
@ -1001,8 +1086,10 @@ LUCIDE_ICON_LIST = [
"mountain", "mountain",
"mountain_snow", "mountain_snow",
"mouse", "mouse",
"mouse_off",
"mouse_pointer", "mouse_pointer",
"mouse_pointer_2", "mouse_pointer_2",
"mouse_pointer_ban",
"mouse_pointer_click", "mouse_pointer_click",
"move", "move",
"move_3d", "move_3d",
@ -1039,10 +1126,13 @@ LUCIDE_ICON_LIST = [
"nut_off", "nut_off",
"octagon", "octagon",
"octagon_alert", "octagon_alert",
"octagon_minus",
"octagon_pause", "octagon_pause",
"octagon_x", "octagon_x",
"omega",
"option", "option",
"orbit", "orbit",
"origami",
"package", "package",
"package_2", "package_2",
"package_check", "package_check",
@ -1055,6 +1145,7 @@ LUCIDE_ICON_LIST = [
"paint_roller", "paint_roller",
"paintbrush", "paintbrush",
"paintbrush_2", "paintbrush_2",
"paintbrush_vertical",
"palette", "palette",
"panel_bottom", "panel_bottom",
"panel_bottom_close", "panel_bottom_close",
@ -1084,13 +1175,16 @@ LUCIDE_ICON_LIST = [
"pc_case", "pc_case",
"pen", "pen",
"pen_line", "pen_line",
"pen_off",
"pen_tool", "pen_tool",
"pencil", "pencil",
"pencil_line", "pencil_line",
"pencil_off",
"pencil_ruler", "pencil_ruler",
"pentagon", "pentagon",
"percent", "percent",
"person_standing", "person_standing",
"philippine_peso",
"phone", "phone",
"phone_call", "phone_call",
"phone_forwarded", "phone_forwarded",
@ -1106,7 +1200,10 @@ LUCIDE_ICON_LIST = [
"pie_chart", "pie_chart",
"piggy_bank", "piggy_bank",
"pilcrow", "pilcrow",
"pilcrow_left",
"pilcrow_right",
"pill", "pill",
"pill_bottle",
"pin", "pin",
"pin_off", "pin_off",
"pipette", "pipette",
@ -1132,6 +1229,7 @@ LUCIDE_ICON_LIST = [
"power_off", "power_off",
"presentation", "presentation",
"printer", "printer",
"printer_check",
"projector", "projector",
"proportions", "proportions",
"puzzle", "puzzle",
@ -1206,6 +1304,7 @@ LUCIDE_ICON_LIST = [
"satellite_dish", "satellite_dish",
"save", "save",
"save_all", "save_all",
"save_off",
"scale", "scale",
"scale_3d", "scale_3d",
"scaling", "scaling",
@ -1213,7 +1312,9 @@ LUCIDE_ICON_LIST = [
"scan_barcode", "scan_barcode",
"scan_eye", "scan_eye",
"scan_face", "scan_face",
"scan_heart",
"scan_line", "scan_line",
"scan_qr_code",
"scan_search", "scan_search",
"scan_text", "scan_text",
"scatter_chart", "scatter_chart",
@ -1229,6 +1330,7 @@ LUCIDE_ICON_LIST = [
"search_code", "search_code",
"search_slash", "search_slash",
"search_x", "search_x",
"section",
"send", "send",
"send_horizontal", "send_horizontal",
"send_to_back", "send_to_back",
@ -1273,6 +1375,7 @@ LUCIDE_ICON_LIST = [
"signal_low", "signal_low",
"signal_medium", "signal_medium",
"signal_zero", "signal_zero",
"signature",
"signpost", "signpost",
"signpost_big", "signpost_big",
"siren", "siren",
@ -1282,8 +1385,8 @@ LUCIDE_ICON_LIST = [
"slack", "slack",
"slash", "slash",
"slice", "slice",
"sliders_vertical",
"sliders_horizontal", "sliders_horizontal",
"sliders_vertical",
"smartphone", "smartphone",
"smartphone_charging", "smartphone_charging",
"smartphone_nfc", "smartphone_nfc",
@ -1307,29 +1410,31 @@ LUCIDE_ICON_LIST = [
"sprout", "sprout",
"square", "square",
"square_activity", "square_activity",
"square_arrow_down",
"square_arrow_down_left", "square_arrow_down_left",
"square_arrow_down_right", "square_arrow_down_right",
"square_arrow_down",
"square_arrow_left", "square_arrow_left",
"square_arrow_out_down_left", "square_arrow_out_down_left",
"square_arrow_out_down_right", "square_arrow_out_down_right",
"square_arrow_out_up_left", "square_arrow_out_up_left",
"square_arrow_out_up_right", "square_arrow_out_up_right",
"square_arrow_right", "square_arrow_right",
"square_arrow_up",
"square_arrow_up_left", "square_arrow_up_left",
"square_arrow_up_right", "square_arrow_up_right",
"square_arrow_up",
"square_asterisk", "square_asterisk",
"square_bottom_dashed_scissors", "square_bottom_dashed_scissors",
"square_check_big", "square_chart_gantt",
"square_check", "square_check",
"square_check_big",
"square_chevron_down", "square_chevron_down",
"square_chevron_left", "square_chevron_left",
"square_chevron_right", "square_chevron_right",
"square_chevron_up", "square_chevron_up",
"square_code", "square_code",
"square_dashed_bottom_code", "square_dashed",
"square_dashed_bottom", "square_dashed_bottom",
"square_dashed_bottom_code",
"square_dashed_kanban", "square_dashed_kanban",
"square_dashed_mouse_pointer", "square_dashed_mouse_pointer",
"square_divide", "square_divide",
@ -1343,8 +1448,8 @@ LUCIDE_ICON_LIST = [
"square_menu", "square_menu",
"square_minus", "square_minus",
"square_mouse_pointer", "square_mouse_pointer",
"square_parking_off",
"square_parking", "square_parking",
"square_parking_off",
"square_pen", "square_pen",
"square_percent", "square_percent",
"square_pi", "square_pi",
@ -1358,10 +1463,11 @@ LUCIDE_ICON_LIST = [
"square_slash", "square_slash",
"square_split_horizontal", "square_split_horizontal",
"square_split_vertical", "square_split_vertical",
"square_square",
"square_stack", "square_stack",
"square_terminal", "square_terminal",
"square_user_round",
"square_user", "square_user",
"square_user_round",
"square_x", "square_x",
"squircle", "squircle",
"squirrel", "squirrel",
@ -1398,6 +1504,7 @@ LUCIDE_ICON_LIST = [
"table_cells_merge", "table_cells_merge",
"table_cells_split", "table_cells_split",
"table_columns_split", "table_columns_split",
"table_of_contents",
"table_properties", "table_properties",
"table_rows_split", "table_rows_split",
"tablet", "tablet",
@ -1413,11 +1520,11 @@ LUCIDE_ICON_LIST = [
"tangent", "tangent",
"target", "target",
"telescope", "telescope",
"tent",
"tent_tree", "tent_tree",
"terminal", "terminal",
"test_tube_diagonal",
"test_tube", "test_tube",
"tent", "test_tube_diagonal",
"test_tubes", "test_tubes",
"text", "text",
"text_cursor", "text_cursor",
@ -1438,11 +1545,14 @@ LUCIDE_ICON_LIST = [
"ticket_plus", "ticket_plus",
"ticket_slash", "ticket_slash",
"ticket_x", "ticket_x",
"tickets",
"tickets_plane",
"timer", "timer",
"timer_off", "timer_off",
"timer_reset", "timer_reset",
"toggle_left", "toggle_left",
"toggle_right", "toggle_right",
"toilet",
"tornado", "tornado",
"torus", "torus",
"touchpad", "touchpad",
@ -1464,17 +1574,21 @@ LUCIDE_ICON_LIST = [
"trello", "trello",
"trending_down", "trending_down",
"trending_up", "trending_up",
"trending_up_down",
"triangle", "triangle",
"triangle_right",
"triangle_alert", "triangle_alert",
"triangle_right",
"trophy", "trophy",
"truck", "truck",
"turtle", "turtle",
"tv", "tv",
"tv_2", "tv_2",
"tv_minimal",
"tv_minimal_play",
"twitch", "twitch",
"twitter", "twitter",
"type", "type",
"type_outline",
"umbrella", "umbrella",
"umbrella_off", "umbrella_off",
"underline", "underline",
@ -1485,8 +1599,8 @@ LUCIDE_ICON_LIST = [
"unfold_vertical", "unfold_vertical",
"ungroup", "ungroup",
"university", "university",
"unlink_2",
"unlink", "unlink",
"unlink_2",
"unplug", "unplug",
"upload", "upload",
"usb", "usb",
@ -1494,11 +1608,13 @@ LUCIDE_ICON_LIST = [
"user_check", "user_check",
"user_cog", "user_cog",
"user_minus", "user_minus",
"user_pen",
"user_plus", "user_plus",
"user_round", "user_round",
"user_round_check", "user_round_check",
"user_round_cog", "user_round_cog",
"user_round_minus", "user_round_minus",
"user_round_pen",
"user_round_plus", "user_round_plus",
"user_round_search", "user_round_search",
"user_round_x", "user_round_x",
@ -1520,14 +1636,16 @@ LUCIDE_ICON_LIST = [
"videotape", "videotape",
"view", "view",
"voicemail", "voicemail",
"volleyball",
"volume", "volume",
"volume_1", "volume_1",
"volume_2", "volume_2",
"volume_off",
"volume_x", "volume_x",
"vote", "vote",
"wallet", "wallet",
"wallet_minimal",
"wallet_cards", "wallet_cards",
"wallet_minimal",
"wallpaper", "wallpaper",
"wand", "wand",
"wand_sparkles", "wand_sparkles",
@ -1535,17 +1653,22 @@ LUCIDE_ICON_LIST = [
"washing_machine", "washing_machine",
"watch", "watch",
"waves", "waves",
"waves_ladder",
"waypoints", "waypoints",
"webcam", "webcam",
"webhook_off",
"webhook", "webhook",
"webhook_off",
"weight", "weight",
"wheat", "wheat",
"wheat_off", "wheat_off",
"whole_word", "whole_word",
"wifi", "wifi",
"wifi_high",
"wifi_low",
"wifi_off", "wifi_off",
"wifi_zero",
"wind", "wind",
"wind_arrow_down",
"wine", "wine",
"wine_off", "wine_off",
"workflow", "workflow",

View File

@ -283,7 +283,7 @@ class Markdown(Component):
# Format the code to handle inline and block code. # Format the code to handle inline and block code.
formatted_code = f""" formatted_code = f"""
const match = (className || '').match(/language-(?<lang>.*)/); const match = (className || '').match(/language-(?<lang>.*)/);
const {str(_LANGUAGE)} = match ? match[1] : ''; const {_LANGUAGE!s} = match ? match[1] : '';
{codeblock_custom_code}; {codeblock_custom_code};
return inline ? ( return inline ? (
{self.format_component("code")} {self.format_component("code")}
@ -340,7 +340,7 @@ const {str(_LANGUAGE)} = match ? match[1] : '';
# If the children are set as a prop, don't pass them as children. # If the children are set as a prop, don't pass them as children.
children_prop = props.pop("children", None) children_prop = props.pop("children", None)
if children_prop is not None: if children_prop is not None:
special_props.append(Var(_js_expr=f"children={{{str(children_prop)}}}")) special_props.append(Var(_js_expr=f"children={{{children_prop!s}}}"))
children = [] children = []
# Get the component. # Get the component.
component = self.component_map[tag](*children, **props).set( component = self.component_map[tag](*children, **props).set(
@ -420,16 +420,17 @@ const {str(_LANGUAGE)} = match ? match[1] : '';
def _get_custom_code(self) -> str | None: def _get_custom_code(self) -> str | None:
hooks = {} hooks = {}
from reflex.compiler.templates import MACROS
for _component in self.component_map.values(): for _component in self.component_map.values():
comp = _component(_MOCK_ARG) comp = _component(_MOCK_ARG)
hooks.update(comp._get_all_hooks_internal())
hooks.update(comp._get_all_hooks()) hooks.update(comp._get_all_hooks())
formatted_hooks = "\n".join(hooks.keys()) formatted_hooks = MACROS.module.renderHooks(hooks) # type: ignore
return f""" return f"""
function {self._get_component_map_name()} () {{ function {self._get_component_map_name()} () {{
{formatted_hooks} {formatted_hooks}
return ( return (
{str(LiteralVar.create(self.format_component_map()))} {LiteralVar.create(self.format_component_map())!s}
) )
}} }}
""" """

View File

@ -47,7 +47,7 @@ class Image(NextComponent):
placeholder: Var[str] placeholder: Var[str]
# Allows passing CSS styles to the underlying image element. # Allows passing CSS styles to the underlying image element.
# style: Var[Any] # style: Var[Any] #noqa: ERA001
# The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead. # The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead.
loading: Var[Literal["lazy", "eager"]] loading: Var[Literal["lazy", "eager"]]

View File

@ -70,7 +70,7 @@ class Image(NextComponent):
quality: The quality of the optimized image, an integer between 1 and 100, where 100 is the best quality and therefore largest file size. Defaults to 75. quality: The quality of the optimized image, an integer between 1 and 100, where 100 is the best quality and therefore largest file size. Defaults to 75.
priority: When true, the image will be considered high priority and preload. Lazy loading is automatically disabled for images using priority. priority: When true, the image will be considered high priority and preload. Lazy loading is automatically disabled for images using priority.
placeholder: A placeholder to use while the image is loading. Possible values are blur, empty, or data:image/.... Defaults to empty. placeholder: A placeholder to use while the image is loading. Possible values are blur, empty, or data:image/.... Defaults to empty.
loading: Allows passing CSS styles to the underlying image element. style: Var[Any] The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead. loading: The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead.
blurDataURL: A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur". blurDataURL: A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur".
on_load: Fires when the image has loaded. on_load: Fires when the image has loaded.
on_error: Fires when the image has an error. on_error: Fires when the image has an error.

View File

@ -149,10 +149,10 @@ class Plotly(NoSSRComponent):
# Fired when a plot element is hovered over. # Fired when a plot element is hovered over.
on_hover: EventHandler[_event_points_data_signature] on_hover: EventHandler[_event_points_data_signature]
# Fired after the plot is layed out (zoom, pan, etc). # Fired after the plot is laid out (zoom, pan, etc).
on_relayout: EventHandler[no_args_event_spec] on_relayout: EventHandler[no_args_event_spec]
# Fired while the plot is being layed out. # Fired while the plot is being laid out.
on_relayouting: EventHandler[no_args_event_spec] on_relayouting: EventHandler[no_args_event_spec]
# Fired after the plot style is changed. # Fired after the plot style is changed.
@ -167,7 +167,7 @@ class Plotly(NoSSRComponent):
# Fired while dragging a selection. # Fired while dragging a selection.
on_selecting: EventHandler[_event_points_data_signature] on_selecting: EventHandler[_event_points_data_signature]
# Fired while an animation is occuring. # Fired while an animation is occurring.
on_transitioning: EventHandler[no_args_event_spec] on_transitioning: EventHandler[no_args_event_spec]
# Fired when a transition is stopped early. # Fired when a transition is stopped early.
@ -270,11 +270,11 @@ const extractPoints = (points) => {
tag.special_props.append( tag.special_props.append(
# Merge all dictionaries and spread the result over props. # Merge all dictionaries and spread the result over props.
Var( Var(
_js_expr=f"{{...mergician({str(figure)}," _js_expr=f"{{...mergician({figure!s},"
f"{','.join(str(md) for md in merge_dicts)})}}", f"{','.join(str(md) for md in merge_dicts)})}}",
), ),
) )
else: else:
# Spread the figure dict over props, nothing to merge. # Spread the figure dict over props, nothing to merge.
tag.special_props.append(Var(_js_expr=f"{{...{str(figure)}}}")) tag.special_props.append(Var(_js_expr=f"{{...{figure!s}}}"))
return tag return tag

View File

@ -130,13 +130,13 @@ class Plotly(NoSSRComponent):
on_deselect: Fired when a selection is cleared (via double click). on_deselect: Fired when a selection is cleared (via double click).
on_double_click: Fired when the plot is double clicked. on_double_click: Fired when the plot is double clicked.
on_hover: Fired when a plot element is hovered over. on_hover: Fired when a plot element is hovered over.
on_relayout: Fired after the plot is layed out (zoom, pan, etc). on_relayout: Fired after the plot is laid out (zoom, pan, etc).
on_relayouting: Fired while the plot is being layed out. on_relayouting: Fired while the plot is being laid out.
on_restyle: Fired after the plot style is changed. on_restyle: Fired after the plot style is changed.
on_redraw: Fired after the plot is redrawn. on_redraw: Fired after the plot is redrawn.
on_selected: Fired after selecting plot elements. on_selected: Fired after selecting plot elements.
on_selecting: Fired while dragging a selection. on_selecting: Fired while dragging a selection.
on_transitioning: Fired while an animation is occuring. on_transitioning: Fired while an animation is occurring.
on_transition_interrupted: Fired when a transition is stopped early. on_transition_interrupted: Fired when a transition is stopped early.
on_unhover: Fired when a hovered element is no longer hovered. on_unhover: Fired when a hovered element is no longer hovered.
style: The style of the component. style: The style of the component.

View File

@ -129,7 +129,8 @@ class AccordionRoot(AccordionComponent):
on_value_change: EventHandler[on_value_change] on_value_change: EventHandler[on_value_change]
def _exclude_props(self) -> list[str]: def _exclude_props(self) -> list[str]:
return super()._exclude_props() + [ return [
*super()._exclude_props(),
"radius", "radius",
"duration", "duration",
"easing", "easing",

View File

@ -34,7 +34,7 @@ def on_value_event_spec(
class SliderRoot(SliderComponent): class SliderRoot(SliderComponent):
"""The Slider component comtaining all slider parts.""" """The Slider component containing all slider parts."""
tag = "Root" tag = "Root"
alias = "RadixSliderRoot" alias = "RadixSliderRoot"
@ -188,7 +188,7 @@ class Slider(ComponentNamespace):
else: else:
children = [ children = [
track, track,
# Foreach.create(props.get("value"), lambda e: SliderThumb.create()), # foreach doesn't render Thumbs properly # Foreach.create(props.get("value"), lambda e: SliderThumb.create()), # foreach doesn't render Thumbs properly # noqa: ERA001
] ]
return SliderRoot.create(*children, **props) return SliderRoot.create(*children, **props)

View File

@ -53,7 +53,7 @@ LiteralAccentColor = Literal[
class CommonMarginProps(Component): class CommonMarginProps(Component):
"""Many radix-themes elements accept shorthand margin props.""" """Many radix-themes elements accept shorthand margin props."""
# Margin: "0" - "9" # Margin: "0" - "9" # noqa: ERA001
m: Var[LiteralSpacing] m: Var[LiteralSpacing]
# Margin horizontal: "0" - "9" # Margin horizontal: "0" - "9"
@ -78,7 +78,7 @@ class CommonMarginProps(Component):
class CommonPaddingProps(Component): class CommonPaddingProps(Component):
"""Many radix-themes elements accept shorthand padding props.""" """Many radix-themes elements accept shorthand padding props."""
# Padding: "0" - "9" # Padding: "0" - "9" # noqa: ERA001
p: Var[Responsive[LiteralSpacing]] p: Var[Responsive[LiteralSpacing]]
# Padding horizontal: "0" - "9" # Padding horizontal: "0" - "9"
@ -139,14 +139,7 @@ class RadixThemesComponent(Component):
component = super().create(*children, **props) component = super().create(*children, **props)
if component.library is None: if component.library is None:
component.library = RadixThemesComponent.__fields__["library"].default component.library = RadixThemesComponent.__fields__["library"].default
component.alias = "RadixThemes" + ( component.alias = "RadixThemes" + (component.tag or type(component).__name__)
component.tag or component.__class__.__name__
)
# value = props.get("value")
# if value is not None and component.alias == "RadixThemesSelect.Root":
# lv = LiteralVar.create(value)
# print(repr(lv))
# print(f"Warning: Value {value} is not used in {component.alias}.")
return component return component
@staticmethod @staticmethod
@ -268,6 +261,7 @@ class Theme(RadixThemesComponent):
_js_expr="{...theme.styles.global[':root'], ...theme.styles.global.body}" _js_expr="{...theme.styles.global[':root'], ...theme.styles.global.body}"
), ),
) )
tag.remove_props("appearance")
return tag return tag

View File

@ -427,7 +427,7 @@ class ColorModeSwitch(Switch):
color_scheme: Override theme color for switch color_scheme: Override theme color for switch
high_contrast: Whether to render the switch with higher contrast color against background high_contrast: Whether to render the switch with higher contrast color against background
radius: Override theme radius for switch: "none" | "small" | "full" radius: Override theme radius for switch: "none" | "small" | "full"
on_change: Props to rename Fired when the value of the switch changes on_change: Fired when the value of the switch changes
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -153,7 +153,7 @@ class Checkbox(RadixThemesComponent):
required: Whether the checkbox is required required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form. name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form. value: The value of the checkbox control when submitting the form.
on_change: Props to rename Fired when the checkbox is checked or unchecked. on_change: Fired when the checkbox is checked or unchecked.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -302,7 +302,7 @@ class HighLevelCheckbox(RadixThemesComponent):
required: Whether the checkbox is required required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form. name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form. value: The value of the checkbox control when submitting the form.
on_change: Props to rename Fired when the checkbox is checked or unchecked. on_change: Fired when the checkbox is checked or unchecked.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -449,7 +449,7 @@ class CheckboxNamespace(ComponentNamespace):
required: Whether the checkbox is required required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form. name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form. value: The value of the checkbox control when submitting the form.
on_change: Props to rename Fired when the checkbox is checked or unchecked. on_change: Fired when the checkbox is checked or unchecked.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -8,6 +8,7 @@ from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spe
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import LiteralAccentColor, RadixThemesComponent from ..base import LiteralAccentColor, RadixThemesComponent
from .checkbox import Checkbox
LiteralDirType = Literal["ltr", "rtl"] LiteralDirType = Literal["ltr", "rtl"]
@ -232,6 +233,15 @@ class ContextMenuSeparator(RadixThemesComponent):
tag = "ContextMenu.Separator" tag = "ContextMenu.Separator"
class ContextMenuCheckbox(Checkbox):
"""The component that contains the checkbox."""
tag = "ContextMenu.CheckboxItem"
# Text to render as shortcut.
shortcut: Var[str]
class ContextMenu(ComponentNamespace): class ContextMenu(ComponentNamespace):
"""Menu representing a set of actions, displayed at the origin of a pointer right-click or long-press.""" """Menu representing a set of actions, displayed at the origin of a pointer right-click or long-press."""
@ -243,6 +253,7 @@ class ContextMenu(ComponentNamespace):
sub_content = staticmethod(ContextMenuSubContent.create) sub_content = staticmethod(ContextMenuSubContent.create)
item = staticmethod(ContextMenuItem.create) item = staticmethod(ContextMenuItem.create)
separator = staticmethod(ContextMenuSeparator.create) separator = staticmethod(ContextMenuSeparator.create)
checkbox = staticmethod(ContextMenuCheckbox.create)
context_menu = ContextMenu() context_menu = ContextMenu()

View File

@ -12,6 +12,7 @@ from reflex.style import Style
from reflex.vars.base import Var from reflex.vars.base import Var
from ..base import RadixThemesComponent from ..base import RadixThemesComponent
from .checkbox import Checkbox
LiteralDirType = Literal["ltr", "rtl"] LiteralDirType = Literal["ltr", "rtl"]
LiteralSizeType = Literal["1", "2"] LiteralSizeType = Literal["1", "2"]
@ -672,6 +673,159 @@ class ContextMenuSeparator(RadixThemesComponent):
""" """
... ...
class ContextMenuCheckbox(Checkbox):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
shortcut: Optional[Union[Var[str], str]] = None,
as_child: Optional[Union[Var[bool], bool]] = None,
size: Optional[
Union[
Breakpoints[str, Literal["1", "2", "3"]],
Literal["1", "2", "3"],
Var[
Union[
Breakpoints[str, Literal["1", "2", "3"]], Literal["1", "2", "3"]
]
],
]
] = None,
variant: Optional[
Union[
Literal["classic", "soft", "surface"],
Var[Literal["classic", "soft", "surface"]],
]
] = None,
color_scheme: Optional[
Union[
Literal[
"amber",
"blue",
"bronze",
"brown",
"crimson",
"cyan",
"gold",
"grass",
"gray",
"green",
"indigo",
"iris",
"jade",
"lime",
"mint",
"orange",
"pink",
"plum",
"purple",
"red",
"ruby",
"sky",
"teal",
"tomato",
"violet",
"yellow",
],
Var[
Literal[
"amber",
"blue",
"bronze",
"brown",
"crimson",
"cyan",
"gold",
"grass",
"gray",
"green",
"indigo",
"iris",
"jade",
"lime",
"mint",
"orange",
"pink",
"plum",
"purple",
"red",
"ruby",
"sky",
"teal",
"tomato",
"violet",
"yellow",
]
],
]
] = None,
high_contrast: Optional[Union[Var[bool], bool]] = None,
default_checked: Optional[Union[Var[bool], bool]] = None,
checked: Optional[Union[Var[bool], bool]] = None,
disabled: Optional[Union[Var[bool], bool]] = None,
required: Optional[Union[Var[bool], bool]] = None,
name: Optional[Union[Var[str], str]] = None,
value: Optional[Union[Var[str], str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[[], BASE_STATE]] = None,
on_change: Optional[
Union[EventType[[], BASE_STATE], EventType[[bool], BASE_STATE]]
] = None,
on_click: Optional[EventType[[], BASE_STATE]] = None,
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
on_double_click: Optional[EventType[[], BASE_STATE]] = None,
on_focus: Optional[EventType[[], BASE_STATE]] = None,
on_mount: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
on_scroll: Optional[EventType[[], BASE_STATE]] = None,
on_unmount: Optional[EventType[[], BASE_STATE]] = None,
**props,
) -> "ContextMenuCheckbox":
"""Create a new component instance.
Will prepend "RadixThemes" to the component tag to avoid conflicts with
other UI libraries for common names, like Text and Button.
Args:
*children: Child components.
shortcut: Text to render as shortcut.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
size: Checkbox size "1" - "3"
variant: Variant of checkbox: "classic" | "surface" | "soft"
color_scheme: Override theme color for checkbox
high_contrast: Whether to render the checkbox with higher contrast color against background
default_checked: Whether the checkbox is checked by default
checked: Whether the checkbox is checked
disabled: Whether the checkbox is disabled
required: Whether the checkbox is required
name: The name of the checkbox control when submitting the form.
value: The value of the checkbox control when submitting the form.
on_change: Fired when the checkbox is checked or unchecked.
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: Component properties.
Returns:
A new component instance.
"""
...
class ContextMenu(ComponentNamespace): class ContextMenu(ComponentNamespace):
root = staticmethod(ContextMenuRoot.create) root = staticmethod(ContextMenuRoot.create)
trigger = staticmethod(ContextMenuTrigger.create) trigger = staticmethod(ContextMenuTrigger.create)
@ -681,5 +835,6 @@ class ContextMenu(ComponentNamespace):
sub_content = staticmethod(ContextMenuSubContent.create) sub_content = staticmethod(ContextMenuSubContent.create)
item = staticmethod(ContextMenuItem.create) item = staticmethod(ContextMenuItem.create)
separator = staticmethod(ContextMenuSeparator.create) separator = staticmethod(ContextMenuSeparator.create)
checkbox = staticmethod(ContextMenuCheckbox.create)
context_menu = ContextMenu() context_menu = ContextMenu()

View File

@ -79,7 +79,7 @@ class IconButton(elements.Button, RadixLoadingProp, RadixThemesComponent):
else: else:
size_map_var = Match.create( size_map_var = Match.create(
props["size"], props["size"],
*[(size, px) for size, px in RADIX_TO_LUCIDE_SIZE.items()], *list(RADIX_TO_LUCIDE_SIZE.items()),
12, 12,
) )
if not isinstance(size_map_var, Var): if not isinstance(size_map_var, Var):

View File

@ -140,10 +140,8 @@ class HighLevelRadioGroup(RadixThemesComponent):
color_scheme = props.pop("color_scheme", None) color_scheme = props.pop("color_scheme", None)
default_value = props.pop("default_value", "") default_value = props.pop("default_value", "")
if ( if not isinstance(items, (list, Var)) or (
not isinstance(items, (list, Var)) isinstance(items, Var) and not types._issubclass(items._var_type, list)
or isinstance(items, Var)
and not types._issubclass(items._var_type, list)
): ):
items_type = type(items) if not isinstance(items, Var) else items._var_type items_type = type(items) if not isinstance(items, Var) else items._var_type
raise TypeError( raise TypeError(

View File

@ -148,7 +148,7 @@ class RadioGroupRoot(RadixThemesComponent):
disabled: Whether the radio group is disabled disabled: Whether the radio group is disabled
name: The name of the group. Submitted with its owning form as part of a name/value pair. name: The name of the group. Submitted with its owning form as part of a name/value pair.
required: Whether the radio group is required required: Whether the radio group is required
on_change: Props to rename Fired when the value of the radio group changes. on_change: Fired when the value of the radio group changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -81,7 +81,7 @@ class SelectRoot(RadixThemesComponent):
name: The name of the select control when submitting the form. name: The name of the select control when submitting the form.
disabled: When True, prevents the user from interacting with select. disabled: When True, prevents the user from interacting with select.
required: When True, indicates that the user must select a value before the owning form can be submitted. required: When True, indicates that the user must select a value before the owning form can be submitted.
on_change: Props to rename Fired when the value of the select changes. on_change: Fired when the value of the select changes.
on_open_change: Fired when the select is opened or closed. on_open_change: Fired when the select is opened or closed.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
@ -732,7 +732,7 @@ class HighLevelSelect(SelectRoot):
name: The name of the select control when submitting the form. name: The name of the select control when submitting the form.
disabled: When True, prevents the user from interacting with select. disabled: When True, prevents the user from interacting with select.
required: When True, indicates that the user must select a value before the owning form can be submitted. required: When True, indicates that the user must select a value before the owning form can be submitted.
on_change: Props to rename Fired when the value of the select changes. on_change: Fired when the value of the select changes.
on_open_change: Fired when the select is opened or closed. on_open_change: Fired when the select is opened or closed.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
@ -912,7 +912,7 @@ class Select(ComponentNamespace):
name: The name of the select control when submitting the form. name: The name of the select control when submitting the form.
disabled: When True, prevents the user from interacting with select. disabled: When True, prevents the user from interacting with select.
required: When True, indicates that the user must select a value before the owning form can be submitted. required: When True, indicates that the user must select a value before the owning form can be submitted.
on_change: Props to rename Fired when the value of the select changes. on_change: Fired when the value of the select changes.
on_open_change: Fired when the select is opened or closed. on_open_change: Fired when the select is opened or closed.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.

View File

@ -195,7 +195,7 @@ class Slider(RadixThemesComponent):
step: The step value of the slider. step: The step value of the slider.
disabled: Whether the slider is disabled disabled: Whether the slider is disabled
orientation: The orientation of the slider. orientation: The orientation of the slider.
on_change: Props to rename Fired when the value of the slider changes. on_change: Fired when the value of the slider changes.
on_value_commit: Fired when a thumb is released after being dragged. on_value_commit: Fired when a thumb is released after being dragged.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.

View File

@ -157,7 +157,7 @@ class Switch(RadixThemesComponent):
color_scheme: Override theme color for switch color_scheme: Override theme color for switch
high_contrast: Whether to render the switch with higher contrast color against background high_contrast: Whether to render the switch with higher contrast color against background
radius: Override theme radius for switch: "none" | "small" | "full" radius: Override theme radius for switch: "none" | "small" | "full"
on_change: Props to rename Fired when the value of the switch changes on_change: Fired when the value of the switch changes
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -72,7 +72,7 @@ class TabsRoot(RadixThemesComponent):
orientation: The orientation of the tabs. orientation: The orientation of the tabs.
dir: Reading direction of the tabs. dir: Reading direction of the tabs.
activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked. activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked.
on_change: Props to rename Fired when the value of the tabs changes. on_change: Fired when the value of the tabs changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.
@ -374,7 +374,7 @@ class Tabs(ComponentNamespace):
orientation: The orientation of the tabs. orientation: The orientation of the tabs.
dir: Reading direction of the tabs. dir: Reading direction of the tabs.
activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked. activation_mode: The mode of activation for the tabs. "automatic" will activate the tab when focused. "manual" will activate the tab when clicked.
on_change: Props to rename Fired when the value of the tabs changes. on_change: Fired when the value of the tabs changes.
style: The style of the component. style: The style of the component.
key: A unique key for the component. key: A unique key for the component.
id: The id for the component. id: The id for the component.

View File

@ -9,7 +9,9 @@ from reflex.components.core.breakpoints import Responsive
from reflex.components.core.debounce import DebounceInput from reflex.components.core.debounce import DebounceInput
from reflex.components.el import elements from reflex.components.el import elements
from reflex.event import EventHandler, input_event, key_event from reflex.event import EventHandler, input_event, key_event
from reflex.utils.types import is_optional
from reflex.vars.base import Var from reflex.vars.base import Var
from reflex.vars.number import ternary_operation
from ..base import LiteralAccentColor, LiteralRadius, RadixThemesComponent from ..base import LiteralAccentColor, LiteralRadius, RadixThemesComponent
@ -17,7 +19,7 @@ LiteralTextFieldSize = Literal["1", "2", "3"]
LiteralTextFieldVariant = Literal["classic", "surface", "soft"] LiteralTextFieldVariant = Literal["classic", "surface", "soft"]
class TextFieldRoot(elements.Div, RadixThemesComponent): class TextFieldRoot(elements.Input, RadixThemesComponent):
"""Captures user input with an optional slot for buttons and icons.""" """Captures user input with an optional slot for buttons and icons."""
tag = "TextField.Root" tag = "TextField.Root"
@ -96,6 +98,19 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
Returns: Returns:
The component. The component.
""" """
value = props.get("value")
# React expects an empty string(instead of null) for controlled inputs.
if value is not None and is_optional(
(value_var := Var.create(value))._var_type
):
props["value"] = ternary_operation(
(value_var != Var.create(None)) # pyright: ignore [reportGeneralTypeIssues]
& (value_var != Var(_js_expr="undefined")),
value,
Var.create(""),
)
component = super().create(*children, **props) component = super().create(*children, **props)
if props.get("value") is not None and props.get("on_change") is not None: if props.get("value") is not None and props.get("on_change") is not None:
# create a debounced input if the user requests full control to avoid typing jank # create a debounced input if the user requests full control to avoid typing jank

View File

@ -17,7 +17,7 @@ from ..base import RadixThemesComponent
LiteralTextFieldSize = Literal["1", "2", "3"] LiteralTextFieldSize = Literal["1", "2", "3"]
LiteralTextFieldVariant = Literal["classic", "surface", "soft"] LiteralTextFieldVariant = Literal["classic", "surface", "soft"]
class TextFieldRoot(elements.Div, RadixThemesComponent): class TextFieldRoot(elements.Input, RadixThemesComponent):
@overload @overload
@classmethod @classmethod
def create( # type: ignore def create( # type: ignore
@ -120,6 +120,30 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
type: Optional[Union[Var[str], str]] = None, type: Optional[Union[Var[str], str]] = None,
value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None, value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None,
list: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, list: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
accept: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
alt: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_focus: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
capture: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
checked: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
default_checked: Optional[Union[Var[bool], bool]] = None,
dirname: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form_action: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form_enc_type: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
form_method: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form_no_validate: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
form_target: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
max: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
min: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
multiple: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
pattern: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
src: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
step: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
use_map: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[ auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str] Union[Var[Union[bool, int, str]], bool, int, str]
@ -192,12 +216,12 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
Args: Args:
*children: The children of the component. *children: The children of the component.
size: Text field size "1" - "3" size: Specifies the visible width of a text control
variant: Variant of text field: "classic" | "surface" | "soft" variant: Variant of text field: "classic" | "surface" | "soft"
color_scheme: Override theme color for text field color_scheme: Override theme color for text field
radius: Override theme radius for text field: "none" | "small" | "medium" | "large" | "full" radius: Override theme radius for text field: "none" | "small" | "medium" | "large" | "full"
auto_complete: Whether the input should have autocomplete enabled auto_complete: Whether the input should have autocomplete enabled
default_value: The value of the input when initially rendered. default_value: The initial value for a text field
disabled: Disables the input disabled: Disables the input
max_length: Specifies the maximum number of characters allowed in the input max_length: Specifies the maximum number of characters allowed in the input
min_length: Specifies the minimum number of characters required in the input min_length: Specifies the minimum number of characters required in the input
@ -208,11 +232,31 @@ class TextFieldRoot(elements.Div, RadixThemesComponent):
type: Specifies the type of input type: Specifies the type of input
value: Value of the input value: Value of the input
list: References a datalist for suggested options list: References a datalist for suggested options
on_change: Fired when the value of the textarea changes. on_change: Fired when the input value changes
on_focus: Fired when the textarea is focused. on_focus: Fired when the input gains focus
on_blur: Fired when the textarea is blurred. on_blur: Fired when the input loses focus
on_key_down: Fired when a key is pressed down. on_key_down: Fired when a key is pressed down
on_key_up: Fired when a key is released. on_key_up: Fired when a key is released
accept: Accepted types of files when the input is file type
alt: Alternate text for input type="image"
auto_focus: Automatically focuses the input when the page loads
capture: Captures media from the user (camera or microphone)
checked: Indicates whether the input is checked (for checkboxes and radio buttons)
default_checked: The initial value (for checkboxes and radio buttons)
dirname: Name part of the input to submit in 'dir' and 'name' pair when form is submitted
form: Associates the input with a form (by id)
form_action: URL to send the form data to (for type="submit" buttons)
form_enc_type: How the form data should be encoded when submitting to the server (for type="submit" buttons)
form_method: HTTP method to use for sending form data (for type="submit" buttons)
form_no_validate: Bypasses form validation when submitting (for type="submit" buttons)
form_target: Specifies where to display the response after submitting the form (for type="submit" buttons)
max: Specifies the maximum value for the input
min: Specifies the minimum value for the input
multiple: Indicates whether multiple values can be entered in an input of the type email or file
pattern: Regex pattern the input's value must match to be valid
src: URL for image inputs
step: Specifies the legal number intervals for an input
use_map: Name of the image map used with the input
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.
@ -457,6 +501,30 @@ class TextField(ComponentNamespace):
type: Optional[Union[Var[str], str]] = None, type: Optional[Union[Var[str], str]] = None,
value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None, value: Optional[Union[Var[Union[float, int, str]], float, int, str]] = None,
list: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, list: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
accept: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
alt: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_focus: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
capture: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
checked: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
default_checked: Optional[Union[Var[bool], bool]] = None,
dirname: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form_action: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form_enc_type: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
form_method: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
form_no_validate: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
form_target: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
max: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
min: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
multiple: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
pattern: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
src: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
step: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
use_map: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None, access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[ auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str] Union[Var[Union[bool, int, str]], bool, int, str]
@ -529,12 +597,12 @@ class TextField(ComponentNamespace):
Args: Args:
*children: The children of the component. *children: The children of the component.
size: Text field size "1" - "3" size: Specifies the visible width of a text control
variant: Variant of text field: "classic" | "surface" | "soft" variant: Variant of text field: "classic" | "surface" | "soft"
color_scheme: Override theme color for text field color_scheme: Override theme color for text field
radius: Override theme radius for text field: "none" | "small" | "medium" | "large" | "full" radius: Override theme radius for text field: "none" | "small" | "medium" | "large" | "full"
auto_complete: Whether the input should have autocomplete enabled auto_complete: Whether the input should have autocomplete enabled
default_value: The value of the input when initially rendered. default_value: The initial value for a text field
disabled: Disables the input disabled: Disables the input
max_length: Specifies the maximum number of characters allowed in the input max_length: Specifies the maximum number of characters allowed in the input
min_length: Specifies the minimum number of characters required in the input min_length: Specifies the minimum number of characters required in the input
@ -545,11 +613,31 @@ class TextField(ComponentNamespace):
type: Specifies the type of input type: Specifies the type of input
value: Value of the input value: Value of the input
list: References a datalist for suggested options list: References a datalist for suggested options
on_change: Fired when the value of the textarea changes. on_change: Fired when the input value changes
on_focus: Fired when the textarea is focused. on_focus: Fired when the input gains focus
on_blur: Fired when the textarea is blurred. on_blur: Fired when the input loses focus
on_key_down: Fired when a key is pressed down. on_key_down: Fired when a key is pressed down
on_key_up: Fired when a key is released. on_key_up: Fired when a key is released
accept: Accepted types of files when the input is file type
alt: Alternate text for input type="image"
auto_focus: Automatically focuses the input when the page loads
capture: Captures media from the user (camera or microphone)
checked: Indicates whether the input is checked (for checkboxes and radio buttons)
default_checked: The initial value (for checkboxes and radio buttons)
dirname: Name part of the input to submit in 'dir' and 'name' pair when form is submitted
form: Associates the input with a form (by id)
form_action: URL to send the form data to (for type="submit" buttons)
form_enc_type: How the form data should be encoded when submitting to the server (for type="submit" buttons)
form_method: HTTP method to use for sending form data (for type="submit" buttons)
form_no_validate: Bypasses form validation when submitting (for type="submit" buttons)
form_target: Specifies where to display the response after submitting the form (for type="submit" buttons)
max: Specifies the maximum value for the input
min: Specifies the minimum value for the input
multiple: Indicates whether multiple values can be entered in an input of the type email or file
pattern: Regex pattern the input's value must match to be valid
src: URL for image inputs
step: Specifies the legal number intervals for an input
use_map: Name of the image map used with the input
access_key: Provides a hint for generating a keyboard shortcut for the current element. access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user. auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable. content_editable: Indicates whether the element's content is editable.

View File

@ -150,7 +150,7 @@ class Center(Flex):
Args: Args:
*children: Child components. *children: Child components.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse" direction: How child items are laid out: "row" | "column" | "row-reverse" | "column-reverse"
align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"
justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between"
wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse" wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse"

View File

@ -22,7 +22,7 @@ class Flex(elements.Div, RadixThemesComponent):
# Change the default rendered element for the one passed as a child, merging their props and behavior. # Change the default rendered element for the one passed as a child, merging their props and behavior.
as_child: Var[bool] as_child: Var[bool]
# How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse" # How child items are laid out: "row" | "column" | "row-reverse" | "column-reverse"
direction: Var[Responsive[LiteralFlexDirection]] direction: Var[Responsive[LiteralFlexDirection]]
# Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" # Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"

View File

@ -153,7 +153,7 @@ class Flex(elements.Div, RadixThemesComponent):
Args: Args:
*children: Child components. *children: Child components.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior. as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
direction: How child items are layed out: "row" | "column" | "row-reverse" | "column-reverse" direction: How child items are laid out: "row" | "column" | "row-reverse" | "column-reverse"
align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" align: Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"
justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between" justify: Alignment of children along the cross axis: "start" | "center" | "end" | "between"
wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse" wrap: Whether children should wrap when they reach the end of their container: "nowrap" | "wrap" | "wrap-reverse"

View File

@ -27,7 +27,7 @@ class Grid(elements.Div, RadixThemesComponent):
# Number of rows # Number of rows
rows: Var[Responsive[str]] rows: Var[Responsive[str]]
# How the grid items are layed out: "row" | "column" | "dense" | "row-dense" | "column-dense" # How the grid items are laid out: "row" | "column" | "dense" | "row-dense" | "column-dense"
flow: Var[Responsive[LiteralGridFlow]] flow: Var[Responsive[LiteralGridFlow]]
# Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch" # Alignment of children along the main axis: "start" | "center" | "end" | "baseline" | "stretch"

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