Merge remote-tracking branch 'origin/main' into global-var-cache
This commit is contained in:
commit
c4d077b55f
34
.github/workflows/performance.yml
vendored
Normal file
34
.github/workflows/performance.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
name: performance-tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "main" # or "master"
|
||||||
|
paths-ignore:
|
||||||
|
- "**/*.md"
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
TELEMETRY_ENABLED: false
|
||||||
|
NODE_OPTIONS: "--max_old_space_size=8192"
|
||||||
|
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||||
|
APP_HARNESS_HEADLESS: 1
|
||||||
|
PYTHONUNBUFFERED: 1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
benchmarks:
|
||||||
|
name: Run benchmarks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: ./.github/actions/setup_build_env
|
||||||
|
with:
|
||||||
|
python-version: 3.12.8
|
||||||
|
run-poetry-install: true
|
||||||
|
create-venv-at-path: .venv
|
||||||
|
- name: Run benchmarks
|
||||||
|
uses: CodSpeedHQ/action@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||||
|
run: poetry run pytest benchmarks/test_evaluate.py --codspeed
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ requirements.txt
|
|||||||
.pyi_generator_last_run
|
.pyi_generator_last_run
|
||||||
.pyi_generator_diff
|
.pyi_generator_diff
|
||||||
reflex.db
|
reflex.db
|
||||||
|
.codspeed
|
231
benchmarks/test_evaluate.py
Normal file
231
benchmarks/test_evaluate.py
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import reflex as rx
|
||||||
|
|
||||||
|
|
||||||
|
class SideBarState(rx.State):
|
||||||
|
"""State for the side bar."""
|
||||||
|
|
||||||
|
current_page: rx.Field[str] = rx.field("/")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SideBarPage:
|
||||||
|
"""A page in the side bar."""
|
||||||
|
|
||||||
|
title: str
|
||||||
|
href: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SideBarSection:
|
||||||
|
"""A section in the side bar."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
icon: str
|
||||||
|
pages: tuple[SideBarPage, ...]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Category:
|
||||||
|
"""A category in the side bar."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
href: str
|
||||||
|
sections: tuple[SideBarSection, ...]
|
||||||
|
|
||||||
|
|
||||||
|
SIDE_BAR = (
|
||||||
|
Category(
|
||||||
|
name="General",
|
||||||
|
href="/",
|
||||||
|
sections=(
|
||||||
|
SideBarSection(
|
||||||
|
name="Home",
|
||||||
|
icon="home",
|
||||||
|
pages=(
|
||||||
|
SideBarPage(title="Home", href="/"),
|
||||||
|
SideBarPage(title="Contact", href="/contact"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SideBarSection(
|
||||||
|
name="About",
|
||||||
|
icon="info",
|
||||||
|
pages=(
|
||||||
|
SideBarPage(title="About", href="/about"),
|
||||||
|
SideBarPage(title="FAQ", href="/faq"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Category(
|
||||||
|
name="Projects",
|
||||||
|
href="/projects",
|
||||||
|
sections=(
|
||||||
|
SideBarSection(
|
||||||
|
name="Python",
|
||||||
|
icon="worm",
|
||||||
|
pages=(
|
||||||
|
SideBarPage(title="Python", href="/projects/python"),
|
||||||
|
SideBarPage(title="Django", href="/projects/django"),
|
||||||
|
SideBarPage(title="Flask", href="/projects/flask"),
|
||||||
|
SideBarPage(title="FastAPI", href="/projects/fastapi"),
|
||||||
|
SideBarPage(title="Pyramid", href="/projects/pyramid"),
|
||||||
|
SideBarPage(title="Tornado", href="/projects/tornado"),
|
||||||
|
SideBarPage(title="TurboGears", href="/projects/turbogears"),
|
||||||
|
SideBarPage(title="Web2py", href="/projects/web2py"),
|
||||||
|
SideBarPage(title="Zope", href="/projects/zope"),
|
||||||
|
SideBarPage(title="Plone", href="/projects/plone"),
|
||||||
|
SideBarPage(title="Quixote", href="/projects/quixote"),
|
||||||
|
SideBarPage(title="Bottle", href="/projects/bottle"),
|
||||||
|
SideBarPage(title="CherryPy", href="/projects/cherrypy"),
|
||||||
|
SideBarPage(title="Falcon", href="/projects/falcon"),
|
||||||
|
SideBarPage(title="Sanic", href="/projects/sanic"),
|
||||||
|
SideBarPage(title="Starlette", href="/projects/starlette"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SideBarSection(
|
||||||
|
name="JavaScript",
|
||||||
|
icon="banana",
|
||||||
|
pages=(
|
||||||
|
SideBarPage(title="JavaScript", href="/projects/javascript"),
|
||||||
|
SideBarPage(title="Angular", href="/projects/angular"),
|
||||||
|
SideBarPage(title="React", href="/projects/react"),
|
||||||
|
SideBarPage(title="Vue", href="/projects/vue"),
|
||||||
|
SideBarPage(title="Ember", href="/projects/ember"),
|
||||||
|
SideBarPage(title="Backbone", href="/projects/backbone"),
|
||||||
|
SideBarPage(title="Meteor", href="/projects/meteor"),
|
||||||
|
SideBarPage(title="Svelte", href="/projects/svelte"),
|
||||||
|
SideBarPage(title="Preact", href="/projects/preact"),
|
||||||
|
SideBarPage(title="Mithril", href="/projects/mithril"),
|
||||||
|
SideBarPage(title="Aurelia", href="/projects/aurelia"),
|
||||||
|
SideBarPage(title="Polymer", href="/projects/polymer"),
|
||||||
|
SideBarPage(title="Knockout", href="/projects/knockout"),
|
||||||
|
SideBarPage(title="Dojo", href="/projects/dojo"),
|
||||||
|
SideBarPage(title="Riot", href="/projects/riot"),
|
||||||
|
SideBarPage(title="Alpine", href="/projects/alpine"),
|
||||||
|
SideBarPage(title="Stimulus", href="/projects/stimulus"),
|
||||||
|
SideBarPage(title="Marko", href="/projects/marko"),
|
||||||
|
SideBarPage(title="Sapper", href="/projects/sapper"),
|
||||||
|
SideBarPage(title="Nuxt", href="/projects/nuxt"),
|
||||||
|
SideBarPage(title="Next", href="/projects/next"),
|
||||||
|
SideBarPage(title="Gatsby", href="/projects/gatsby"),
|
||||||
|
SideBarPage(title="Gridsome", href="/projects/gridsome"),
|
||||||
|
SideBarPage(title="Nest", href="/projects/nest"),
|
||||||
|
SideBarPage(title="Express", href="/projects/express"),
|
||||||
|
SideBarPage(title="Koa", href="/projects/koa"),
|
||||||
|
SideBarPage(title="Hapi", href="/projects/hapi"),
|
||||||
|
SideBarPage(title="LoopBack", href="/projects/loopback"),
|
||||||
|
SideBarPage(title="Feathers", href="/projects/feathers"),
|
||||||
|
SideBarPage(title="Sails", href="/projects/sails"),
|
||||||
|
SideBarPage(title="Adonis", href="/projects/adonis"),
|
||||||
|
SideBarPage(title="Meteor", href="/projects/meteor"),
|
||||||
|
SideBarPage(title="Derby", href="/projects/derby"),
|
||||||
|
SideBarPage(title="Socket.IO", href="/projects/socketio"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def side_bar_page(page: SideBarPage):
|
||||||
|
return rx.box(
|
||||||
|
rx.link(
|
||||||
|
page.title,
|
||||||
|
href=page.href,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def side_bar_section(section: SideBarSection):
|
||||||
|
return rx.accordion.item(
|
||||||
|
rx.accordion.header(
|
||||||
|
rx.accordion.trigger(
|
||||||
|
rx.hstack(
|
||||||
|
rx.hstack(
|
||||||
|
rx.icon(section.icon),
|
||||||
|
section.name,
|
||||||
|
align="center",
|
||||||
|
),
|
||||||
|
rx.accordion.icon(),
|
||||||
|
width="100%",
|
||||||
|
justify="between",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
rx.accordion.content(
|
||||||
|
rx.vstack(
|
||||||
|
*map(side_bar_page, section.pages),
|
||||||
|
),
|
||||||
|
border_inline_start="1px solid",
|
||||||
|
padding_inline_start="1em",
|
||||||
|
margin_inline_start="1.5em",
|
||||||
|
),
|
||||||
|
value=section.name,
|
||||||
|
width="100%",
|
||||||
|
variant="ghost",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def side_bar_category(category: Category):
|
||||||
|
selected_section = cast(
|
||||||
|
rx.Var,
|
||||||
|
rx.match(
|
||||||
|
SideBarState.current_page,
|
||||||
|
*[
|
||||||
|
(
|
||||||
|
section.name,
|
||||||
|
section.name,
|
||||||
|
)
|
||||||
|
for section in category.sections
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return rx.vstack(
|
||||||
|
rx.heading(
|
||||||
|
rx.link(
|
||||||
|
category.name,
|
||||||
|
href=category.href,
|
||||||
|
),
|
||||||
|
size="5",
|
||||||
|
),
|
||||||
|
rx.accordion.root(
|
||||||
|
*map(side_bar_section, category.sections),
|
||||||
|
default_value=selected_section.to(str),
|
||||||
|
variant="ghost",
|
||||||
|
width="100%",
|
||||||
|
collapsible=True,
|
||||||
|
type="multiple",
|
||||||
|
),
|
||||||
|
width="100%",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def side_bar():
|
||||||
|
return rx.vstack(
|
||||||
|
*map(side_bar_category, SIDE_BAR),
|
||||||
|
width="fit-content",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LOREM_IPSUM = "Lorem ipsum dolor sit amet, dolor ut dolore pariatur aliqua enim tempor sed. Labore excepteur sed exercitation. Ullamco aliquip lorem sunt enim in incididunt. Magna anim officia sint cillum labore. Ut eu non dolore minim nostrud magna eu, aute ex in incididunt irure eu. Fugiat et magna magna est excepteur eiusmod minim. Quis eiusmod et non pariatur dolor veniam incididunt, eiusmod irure enim sed dolor lorem pariatur do. Occaecat duis irure excepteur dolore. Proident ut laborum pariatur sit sit, nisi nostrud voluptate magna commodo laborum esse velit. Voluptate non minim deserunt adipiscing irure deserunt cupidatat. Laboris veniam commodo incididunt veniam lorem occaecat, fugiat ipsum dolor cupidatat. Ea officia sed eu excepteur culpa adipiscing, tempor consectetur ullamco eu. Anim ex proident nulla sunt culpa, voluptate veniam proident est adipiscing sint elit velit. Laboris adipiscing est culpa cillum magna. Sit veniam nulla nulla, aliqua eiusmod commodo lorem cupidatat commodo occaecat. Fugiat cillum dolor incididunt mollit eiusmod sint. Non lorem dolore labore excepteur minim laborum sed. Irure nisi do lorem nulla sunt commodo, deserunt quis mollit consectetur minim et esse est, proident nostrud officia enim sed reprehenderit. Magna cillum consequat aute reprehenderit duis sunt ullamco. Labore qui mollit voluptate. Duis dolor sint aute amet aliquip officia, est non mollit tempor enim quis fugiat, eu do culpa consectetur magna. Do ullamco aliqua voluptate culpa excepteur reprehenderit reprehenderit. Occaecat nulla sit est magna. Deserunt ea voluptate veniam cillum. Amet cupidatat duis est tempor fugiat ex eu, officia est sunt consectetur labore esse exercitation. Nisi cupidatat irure est nisi. Officia amet eu veniam reprehenderit. In amet incididunt tempor commodo ea labore. Mollit dolor aliquip excepteur, voluptate aute occaecat id officia proident. Ullamco est amet tempor. Proident aliquip proident mollit do aliquip ipsum, culpa quis aute id irure. Velit excepteur cillum cillum ut cupidatat. Occaecat qui elit esse nulla minim. Consequat velit id ad pariatur tempor. Eiusmod deserunt aliqua ex sed quis non. Dolor sint commodo ex in deserunt nostrud excepteur, pariatur ex aliqua anim adipiscing amet proident. Laboris eu laborum magna lorem ipsum fugiat velit."
|
||||||
|
|
||||||
|
|
||||||
|
def complicated_page():
|
||||||
|
return rx.hstack(
|
||||||
|
side_bar(),
|
||||||
|
rx.box(
|
||||||
|
rx.heading("Complicated Page", size="1"),
|
||||||
|
rx.text(LOREM_IPSUM),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.benchmark
|
||||||
|
def test_component_init():
|
||||||
|
complicated_page()
|
49
poetry.lock
generated
49
poetry.lock
generated
@ -251,7 +251,7 @@ files = [
|
|||||||
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
|
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
|
||||||
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
|
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
|
||||||
]
|
]
|
||||||
markers = {main = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "os_name == \"nt\" and implementation_name != \"pypy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")"}
|
markers = {main = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pycparser = "*"
|
pycparser = "*"
|
||||||
@ -495,7 +495,6 @@ files = [
|
|||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
|
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"},
|
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
|
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
|
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
|
||||||
{file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
|
{file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
|
||||||
@ -506,7 +505,6 @@ files = [
|
|||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
|
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"},
|
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
|
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
|
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
|
||||||
{file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
|
{file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
|
||||||
@ -1101,7 +1099,7 @@ version = "3.0.0"
|
|||||||
description = "Python port of markdown-it. Markdown parsing, done right!"
|
description = "Python port of markdown-it. Markdown parsing, done right!"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
groups = ["main"]
|
groups = ["main", "dev"]
|
||||||
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
||||||
files = [
|
files = [
|
||||||
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
|
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
|
||||||
@ -1199,7 +1197,7 @@ version = "0.1.2"
|
|||||||
description = "Markdown URL utilities"
|
description = "Markdown URL utilities"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
groups = ["main"]
|
groups = ["main", "dev"]
|
||||||
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
||||||
files = [
|
files = [
|
||||||
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
|
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
|
||||||
@ -1690,7 +1688,7 @@ files = [
|
|||||||
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
|
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
|
||||||
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
|
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
|
||||||
]
|
]
|
||||||
markers = {main = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "os_name == \"nt\" and implementation_name != \"pypy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")"}
|
markers = {main = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pydantic"
|
name = "pydantic"
|
||||||
@ -1853,7 +1851,7 @@ version = "2.19.1"
|
|||||||
description = "Pygments is a syntax highlighting package written in Python."
|
description = "Pygments is a syntax highlighting package written in Python."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
groups = ["main"]
|
groups = ["main", "dev"]
|
||||||
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
||||||
files = [
|
files = [
|
||||||
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
|
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
|
||||||
@ -1998,6 +1996,39 @@ aspect = ["aspectlib"]
|
|||||||
elasticsearch = ["elasticsearch"]
|
elasticsearch = ["elasticsearch"]
|
||||||
histogram = ["pygal", "pygaljs", "setuptools"]
|
histogram = ["pygal", "pygaljs", "setuptools"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-codspeed"
|
||||||
|
version = "3.1.2"
|
||||||
|
description = "Pytest plugin to create CodSpeed benchmarks"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.9"
|
||||||
|
groups = ["dev"]
|
||||||
|
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
||||||
|
files = [
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aed496f873670ce0ea8f980a7c1a2c6a08f415e0ebdf207bf651b2d922103374"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee45b0b763f6b5fa5d74c7b91d694a9615561c428b320383660672f4471756e3"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c84e591a7a0f67d45e2dc9fd05b276971a3aabcab7478fe43363ebefec1358f4"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6ae6d094247156407770e6b517af70b98862dd59a3c31034aede11d5f71c32c"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0f264991de5b5cdc118b96fc671386cca3f0f34e411482939bf2459dc599097"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0695a4bcd5ff04e8379124dba5d9795ea5e0cadf38be7a0406432fc1467b555"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc356c8dcaaa883af83310f397ac06c96fac9b8a1146e303d4b374b2cb46a18"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc8a5d0366322a75cf562f7d8d672d28c1cf6948695c4dddca50331e08f6b3d5"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c5fe7a19b72f54f217480b3b527102579547b1de9fe3acd9e66cb4629ff46c8"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b67205755a665593f6521a98317d02a9d07d6fdc593f6634de2c94dea47a3055"},
|
||||||
|
{file = "pytest_codspeed-3.1.2-py3-none-any.whl", hash = "sha256:5e7ed0315e33496c5c07dba262b50303b8d0bc4c3d10bf1d422a41e70783f1cb"},
|
||||||
|
{file = "pytest_codspeed-3.1.2.tar.gz", hash = "sha256:09c1733af3aab35e94a621aa510f2d2114f65591e6f644c42ca3f67547edad4b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
cffi = ">=1.17.1"
|
||||||
|
pytest = ">=3.8"
|
||||||
|
rich = ">=13.8.1"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
compat = ["pytest-benchmark (>=5.0.0,<5.1.0)", "pytest-xdist (>=3.6.1,<3.7.0)"]
|
||||||
|
lint = ["mypy (>=1.11.2,<1.12.0)", "ruff (>=0.6.5,<0.7.0)"]
|
||||||
|
test = ["pytest (>=7.0,<8.0)", "pytest-cov (>=4.0.0,<4.1.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-cov"
|
name = "pytest-cov"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
@ -2362,7 +2393,7 @@ version = "13.9.4"
|
|||||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
groups = ["main"]
|
groups = ["main", "dev"]
|
||||||
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
|
||||||
files = [
|
files = [
|
||||||
{file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
|
{file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
|
||||||
@ -3152,4 +3183,4 @@ type = ["pytest-mypy"]
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = ">=3.10, <4.0"
|
python-versions = ">=3.10, <4.0"
|
||||||
content-hash = "35c503a68e87896b4f7d7c209dd3fe6d707ebcc1702377cab0a1339554c6ad77"
|
content-hash = "822150bcbf41e5cbb61da0a059b41d8971e3c6c974c8af4be7ef55126648aea1"
|
||||||
|
@ -71,6 +71,7 @@ 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"
|
||||||
|
pytest-codspeed = "^3.1.2"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
reflex = "reflex.reflex:cli"
|
reflex = "reflex.reflex:cli"
|
||||||
|
@ -86,11 +86,11 @@
|
|||||||
{% for condition in case[:-1] %}
|
{% for condition in case[:-1] %}
|
||||||
case JSON.stringify({{ condition._js_expr }}):
|
case JSON.stringify({{ condition._js_expr }}):
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
return {{ case[-1] }};
|
return {{ render(case[-1]) }};
|
||||||
break;
|
break;
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
default:
|
default:
|
||||||
return {{ component.default }};
|
return {{ render(component.default) }};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
@ -106,6 +106,18 @@ export const getBackendURL = (url_str) => {
|
|||||||
return endpoint;
|
return endpoint;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the backend is disabled.
|
||||||
|
*
|
||||||
|
* @returns True if the backend is disabled, false otherwise.
|
||||||
|
*/
|
||||||
|
export const isBackendDisabled = () => {
|
||||||
|
const cookie = document.cookie
|
||||||
|
.split("; ")
|
||||||
|
.find((row) => row.startsWith("backend-enabled="));
|
||||||
|
return cookie !== undefined && cookie.split("=")[1] == "false";
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if any event in the event queue is stateful.
|
* Determine if any event in the event queue is stateful.
|
||||||
*
|
*
|
||||||
@ -301,10 +313,7 @@ export const applyEvent = async (event, socket) => {
|
|||||||
|
|
||||||
// Send the event to the server.
|
// Send the event to the server.
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.emit(
|
socket.emit("event", event);
|
||||||
"event",
|
|
||||||
event,
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,7 +506,7 @@ export const uploadFiles = async (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upload_ref_name = `__upload_controllers_${upload_id}`
|
const upload_ref_name = `__upload_controllers_${upload_id}`;
|
||||||
|
|
||||||
if (refs[upload_ref_name]) {
|
if (refs[upload_ref_name]) {
|
||||||
console.log("Upload already in progress for ", upload_id);
|
console.log("Upload already in progress for ", upload_id);
|
||||||
@ -815,7 +824,7 @@ export const useEventLoop = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// only use websockets if state is present
|
// only use websockets if state is present
|
||||||
if (Object.keys(initialState).length > 1) {
|
if (Object.keys(initialState).length > 1 && !isBackendDisabled()) {
|
||||||
// Initialize the websocket connection.
|
// Initialize the websocket connection.
|
||||||
if (!socket.current) {
|
if (!socket.current) {
|
||||||
connect(
|
connect(
|
||||||
|
@ -59,7 +59,11 @@ from reflex.components.component import (
|
|||||||
ComponentStyle,
|
ComponentStyle,
|
||||||
evaluate_style_namespaces,
|
evaluate_style_namespaces,
|
||||||
)
|
)
|
||||||
from reflex.components.core.banner import connection_pulser, connection_toaster
|
from reflex.components.core.banner import (
|
||||||
|
backend_disabled,
|
||||||
|
connection_pulser,
|
||||||
|
connection_toaster,
|
||||||
|
)
|
||||||
from reflex.components.core.breakpoints import set_breakpoints
|
from reflex.components.core.breakpoints import set_breakpoints
|
||||||
from reflex.components.core.client_side_routing import (
|
from reflex.components.core.client_side_routing import (
|
||||||
Default404Page,
|
Default404Page,
|
||||||
@ -158,9 +162,12 @@ def default_overlay_component() -> Component:
|
|||||||
Returns:
|
Returns:
|
||||||
The default overlay_component, which is a connection_modal.
|
The default overlay_component, which is a connection_modal.
|
||||||
"""
|
"""
|
||||||
|
config = get_config()
|
||||||
|
|
||||||
return Fragment.create(
|
return Fragment.create(
|
||||||
connection_pulser(),
|
connection_pulser(),
|
||||||
connection_toaster(),
|
connection_toaster(),
|
||||||
|
*([backend_disabled()] if config.is_reflex_cloud else []),
|
||||||
*codespaces.codespaces_auto_redirect(),
|
*codespaces.codespaces_auto_redirect(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1944,7 +1944,7 @@ class StatefulComponent(BaseComponent):
|
|||||||
|
|
||||||
if not should_memoize:
|
if not should_memoize:
|
||||||
# Determine if any Vars have associated data.
|
# Determine if any Vars have associated data.
|
||||||
for prop_var in component._get_vars():
|
for prop_var in component._get_vars(include_children=True):
|
||||||
if prop_var._get_all_var_data():
|
if prop_var._get_all_var_data():
|
||||||
should_memoize = True
|
should_memoize = True
|
||||||
break
|
break
|
||||||
@ -2327,8 +2327,8 @@ class MemoizationLeaf(Component):
|
|||||||
"""
|
"""
|
||||||
comp = super().create(*children, **props)
|
comp = super().create(*children, **props)
|
||||||
if comp._get_all_hooks():
|
if comp._get_all_hooks():
|
||||||
comp._memoization_mode = cls._memoization_mode.copy(
|
comp._memoization_mode = dataclasses.replace(
|
||||||
update={"disposition": MemoizationDisposition.ALWAYS}
|
comp._memoization_mode, disposition=MemoizationDisposition.ALWAYS
|
||||||
)
|
)
|
||||||
return comp
|
return comp
|
||||||
|
|
||||||
@ -2389,7 +2389,7 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) ->
|
|||||||
if tag["name"] == "match":
|
if tag["name"] == "match":
|
||||||
element = tag["cond"]
|
element = tag["cond"]
|
||||||
|
|
||||||
conditionals = tag["default"]
|
conditionals = render_dict_to_var(tag["default"], imported_names)
|
||||||
|
|
||||||
for case in tag["match_cases"][::-1]:
|
for case in tag["match_cases"][::-1]:
|
||||||
condition = case[0].to_string() == element.to_string()
|
condition = case[0].to_string() == element.to_string()
|
||||||
@ -2398,7 +2398,7 @@ def render_dict_to_var(tag: dict | Component | str, imported_names: set[str]) ->
|
|||||||
|
|
||||||
conditionals = ternary_operation(
|
conditionals = ternary_operation(
|
||||||
condition,
|
condition,
|
||||||
case[-1],
|
render_dict_to_var(case[-1], imported_names),
|
||||||
conditionals,
|
conditionals,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,8 +4,10 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from reflex import constants
|
||||||
from reflex.components.component import Component
|
from reflex.components.component import Component
|
||||||
from reflex.components.core.cond import cond
|
from reflex.components.core.cond import cond
|
||||||
|
from reflex.components.datadisplay.logo import svg_logo
|
||||||
from reflex.components.el.elements.typography import Div
|
from reflex.components.el.elements.typography import Div
|
||||||
from reflex.components.lucide.icon import Icon
|
from reflex.components.lucide.icon import Icon
|
||||||
from reflex.components.radix.themes.components.dialog import (
|
from reflex.components.radix.themes.components.dialog import (
|
||||||
@ -293,7 +295,84 @@ class ConnectionPulser(Div):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BackendDisabled(Div):
|
||||||
|
"""A component that displays a message when the backend is disabled."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, **props) -> Component:
|
||||||
|
"""Create a backend disabled component.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
**props: The properties of the component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The backend disabled component.
|
||||||
|
"""
|
||||||
|
import reflex as rx
|
||||||
|
|
||||||
|
is_backend_disabled = Var(
|
||||||
|
"backendDisabled",
|
||||||
|
_var_type=bool,
|
||||||
|
_var_data=VarData(
|
||||||
|
hooks={
|
||||||
|
"const [backendDisabled, setBackendDisabled] = useState(false);": None,
|
||||||
|
"useEffect(() => { setBackendDisabled(isBackendDisabled()); }, []);": None,
|
||||||
|
},
|
||||||
|
imports={
|
||||||
|
f"$/{constants.Dirs.STATE_PATH}": [
|
||||||
|
ImportVar(tag="isBackendDisabled")
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().create(
|
||||||
|
rx.cond(
|
||||||
|
is_backend_disabled,
|
||||||
|
rx.box(
|
||||||
|
rx.box(
|
||||||
|
rx.card(
|
||||||
|
rx.vstack(
|
||||||
|
svg_logo(),
|
||||||
|
rx.text(
|
||||||
|
"You ran out of compute credits.",
|
||||||
|
),
|
||||||
|
rx.callout(
|
||||||
|
rx.fragment(
|
||||||
|
"Please upgrade your plan or raise your compute credits at ",
|
||||||
|
rx.link(
|
||||||
|
"Reflex Cloud.",
|
||||||
|
href="https://cloud.reflex.dev/",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width="100%",
|
||||||
|
icon="info",
|
||||||
|
variant="surface",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
font_size="20px",
|
||||||
|
font_family='"Inter", "Helvetica", "Arial", sans-serif',
|
||||||
|
variant="classic",
|
||||||
|
),
|
||||||
|
position="fixed",
|
||||||
|
top="50%",
|
||||||
|
left="50%",
|
||||||
|
transform="translate(-50%, -50%)",
|
||||||
|
width="40ch",
|
||||||
|
max_width="90vw",
|
||||||
|
),
|
||||||
|
position="fixed",
|
||||||
|
z_index=9999,
|
||||||
|
backdrop_filter="grayscale(1) blur(5px)",
|
||||||
|
width="100dvw",
|
||||||
|
height="100dvh",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
connection_banner = ConnectionBanner.create
|
connection_banner = ConnectionBanner.create
|
||||||
connection_modal = ConnectionModal.create
|
connection_modal = ConnectionModal.create
|
||||||
connection_toaster = ConnectionToaster.create
|
connection_toaster = ConnectionToaster.create
|
||||||
connection_pulser = ConnectionPulser.create
|
connection_pulser = ConnectionPulser.create
|
||||||
|
backend_disabled = BackendDisabled.create
|
||||||
|
@ -350,7 +350,93 @@ class ConnectionPulser(Div):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
class BackendDisabled(Div):
|
||||||
|
@overload
|
||||||
|
@classmethod
|
||||||
|
def create( # type: ignore
|
||||||
|
cls,
|
||||||
|
*children,
|
||||||
|
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
auto_capitalize: Optional[
|
||||||
|
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||||
|
] = None,
|
||||||
|
content_editable: Optional[
|
||||||
|
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||||
|
] = None,
|
||||||
|
context_menu: Optional[
|
||||||
|
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||||
|
] = None,
|
||||||
|
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
enter_key_hint: Optional[
|
||||||
|
Union[Var[Union[bool, int, str]], bool, int, str]
|
||||||
|
] = None,
|
||||||
|
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
title: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
|
||||||
|
style: Optional[Style] = None,
|
||||||
|
key: Optional[Any] = None,
|
||||||
|
id: Optional[Any] = None,
|
||||||
|
class_name: Optional[Any] = None,
|
||||||
|
autofocus: Optional[bool] = None,
|
||||||
|
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
|
||||||
|
on_blur: Optional[EventType[[], 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,
|
||||||
|
) -> "BackendDisabled":
|
||||||
|
"""Create a backend disabled component.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
|
||||||
|
draggable: Defines whether the element can be dragged.
|
||||||
|
enter_key_hint: Hints what media types the media element is able to play.
|
||||||
|
hidden: Defines whether the element is hidden.
|
||||||
|
input_mode: Defines the type of the element.
|
||||||
|
item_prop: Defines the name of the element for metadata purposes.
|
||||||
|
lang: Defines the language used in the element.
|
||||||
|
role: Defines the role of the element.
|
||||||
|
slot: Assigns a slot in a shadow DOM shadow tree to an element.
|
||||||
|
spell_check: Defines whether the element may be checked for spelling errors.
|
||||||
|
tab_index: Defines the position of the current element in the tabbing order.
|
||||||
|
title: Defines a tooltip for the element.
|
||||||
|
style: The style of the component.
|
||||||
|
key: A unique key for the component.
|
||||||
|
id: The id for the component.
|
||||||
|
class_name: The class name for the component.
|
||||||
|
autofocus: Whether the component should take the focus once the page is loaded
|
||||||
|
custom_attrs: custom attribute
|
||||||
|
**props: The properties of the component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The backend disabled component.
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
connection_banner = ConnectionBanner.create
|
connection_banner = ConnectionBanner.create
|
||||||
connection_modal = ConnectionModal.create
|
connection_modal = ConnectionModal.create
|
||||||
connection_toaster = ConnectionToaster.create
|
connection_toaster = ConnectionToaster.create
|
||||||
connection_pulser = ConnectionPulser.create
|
connection_pulser = ConnectionPulser.create
|
||||||
|
backend_disabled = BackendDisabled.create
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Any, Callable, Iterable
|
from typing import Any, Callable, Iterable
|
||||||
|
|
||||||
@ -97,9 +98,20 @@ class Foreach(Component):
|
|||||||
# Determine the index var name based on the params accepted by render_fn.
|
# Determine the index var name based on the params accepted by render_fn.
|
||||||
props["index_var_name"] = params[1].name
|
props["index_var_name"] = params[1].name
|
||||||
else:
|
else:
|
||||||
|
render_fn = self.render_fn
|
||||||
# Otherwise, use a deterministic index, based on the render function bytecode.
|
# Otherwise, use a deterministic index, based on the render function bytecode.
|
||||||
code_hash = (
|
code_hash = (
|
||||||
hash(self.render_fn.__code__)
|
hash(
|
||||||
|
getattr(
|
||||||
|
render_fn,
|
||||||
|
"__code__",
|
||||||
|
(
|
||||||
|
repr(self.render_fn)
|
||||||
|
if not isinstance(render_fn, functools.partial)
|
||||||
|
else render_fn.func.__code__
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
.to_bytes(
|
.to_bytes(
|
||||||
length=8,
|
length=8,
|
||||||
byteorder="big",
|
byteorder="big",
|
||||||
|
@ -10,6 +10,7 @@ from reflex.components.core.cond import cond
|
|||||||
from reflex.components.lucide.icon import Icon
|
from reflex.components.lucide.icon import Icon
|
||||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||||
from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius
|
from reflex.components.radix.themes.base import LiteralAccentColor, LiteralRadius
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler
|
from reflex.event import EventHandler
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.vars import get_uuid_string_var
|
from reflex.vars import get_uuid_string_var
|
||||||
@ -196,8 +197,9 @@ class AccordionItem(AccordionComponent):
|
|||||||
|
|
||||||
# The header of the accordion item.
|
# The header of the accordion item.
|
||||||
header: Var[Union[Component, str]]
|
header: Var[Union[Component, str]]
|
||||||
|
|
||||||
# The content of the accordion item.
|
# The content of the accordion item.
|
||||||
content: Var[Union[Component, str]] = Var.create(None)
|
content: Var[Union[Component, str, None]] = Var.create(None)
|
||||||
|
|
||||||
_valid_children: List[str] = [
|
_valid_children: List[str] = [
|
||||||
"AccordionHeader",
|
"AccordionHeader",
|
||||||
@ -341,6 +343,8 @@ class AccordionTrigger(AccordionComponent):
|
|||||||
|
|
||||||
alias = "RadixAccordionTrigger"
|
alias = "RadixAccordionTrigger"
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, *children, **props) -> Component:
|
def create(cls, *children, **props) -> Component:
|
||||||
"""Create the Accordion trigger component.
|
"""Create the Accordion trigger component.
|
||||||
|
@ -308,7 +308,9 @@ class AccordionItem(AccordionComponent):
|
|||||||
value: Optional[Union[Var[str], str]] = None,
|
value: Optional[Union[Var[str], str]] = None,
|
||||||
disabled: Optional[Union[Var[bool], bool]] = None,
|
disabled: Optional[Union[Var[bool], bool]] = None,
|
||||||
header: Optional[Union[Component, Var[Union[Component, str]], str]] = None,
|
header: Optional[Union[Component, Var[Union[Component, str]], str]] = None,
|
||||||
content: Optional[Union[Component, Var[Union[Component, str]], str]] = None,
|
content: Optional[
|
||||||
|
Union[Component, Var[Optional[Union[Component, str]]], str]
|
||||||
|
] = None,
|
||||||
color_scheme: Optional[
|
color_scheme: Optional[
|
||||||
Union[
|
Union[
|
||||||
Literal[
|
Literal[
|
||||||
|
@ -10,6 +10,7 @@ from reflex.components.component import Component, ComponentNamespace
|
|||||||
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
from reflex.components.radix.primitives.base import RadixPrimitiveComponent
|
||||||
from reflex.components.radix.themes.base import Theme
|
from reflex.components.radix.themes.base import Theme
|
||||||
from reflex.components.radix.themes.layout.flex import Flex
|
from reflex.components.radix.themes.layout.flex import Flex
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -85,6 +86,8 @@ class DrawerTrigger(DrawerComponent):
|
|||||||
# Defaults to true, if the first child acts as the trigger.
|
# Defaults to true, if the first child acts as the trigger.
|
||||||
as_child: Var[bool] = Var.create(True)
|
as_child: Var[bool] = Var.create(True)
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, *children: Any, **props: Any) -> Component:
|
def create(cls, *children: Any, **props: Any) -> Component:
|
||||||
"""Create a new DrawerTrigger instance.
|
"""Create a new DrawerTrigger instance.
|
||||||
|
@ -5,6 +5,7 @@ from typing import Literal
|
|||||||
from reflex.components.component import ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
from reflex.components.el import elements
|
from reflex.components.el import elements
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -33,6 +34,8 @@ class AlertDialogTrigger(RadixThemesTriggerComponent):
|
|||||||
|
|
||||||
tag = "AlertDialog.Trigger"
|
tag = "AlertDialog.Trigger"
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class AlertDialogContent(elements.Div, RadixThemesComponent):
|
class AlertDialogContent(elements.Div, RadixThemesComponent):
|
||||||
"""Contains the content of the dialog. This component is based on the div element."""
|
"""Contains the content of the dialog. This component is based on the div element."""
|
||||||
|
@ -20,7 +20,7 @@ class Card(elements.Div, RadixThemesComponent):
|
|||||||
# Card size: "1" - "5"
|
# Card size: "1" - "5"
|
||||||
size: Var[Responsive[Literal["1", "2", "3", "4", "5"],]]
|
size: Var[Responsive[Literal["1", "2", "3", "4", "5"],]]
|
||||||
|
|
||||||
# Variant of Card: "solid" | "soft" | "outline" | "ghost"
|
# Variant of Card: "surface" | "classic" | "ghost"
|
||||||
variant: Var[Literal["surface", "classic", "ghost"]]
|
variant: Var[Literal["surface", "classic", "ghost"]]
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ class Card(elements.Div, RadixThemesComponent):
|
|||||||
*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.
|
||||||
size: Card size: "1" - "5"
|
size: Card size: "1" - "5"
|
||||||
variant: Variant of Card: "solid" | "soft" | "outline" | "ghost"
|
variant: Variant of Card: "surface" | "classic" | "ghost"
|
||||||
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.
|
||||||
|
@ -4,6 +4,7 @@ from typing import Dict, List, Literal, Union
|
|||||||
|
|
||||||
from reflex.components.component import ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -55,6 +56,8 @@ class ContextMenuTrigger(RadixThemesComponent):
|
|||||||
|
|
||||||
_invalid_children: List[str] = ["ContextMenuContent"]
|
_invalid_children: List[str] = ["ContextMenuContent"]
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class ContextMenuContent(RadixThemesComponent):
|
class ContextMenuContent(RadixThemesComponent):
|
||||||
"""The component that pops out when the context menu is open."""
|
"""The component that pops out when the context menu is open."""
|
||||||
@ -153,6 +156,8 @@ class ContextMenuSubTrigger(RadixThemesComponent):
|
|||||||
|
|
||||||
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSub"]
|
_valid_parents: List[str] = ["ContextMenuContent", "ContextMenuSub"]
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class ContextMenuSubContent(RadixThemesComponent):
|
class ContextMenuSubContent(RadixThemesComponent):
|
||||||
"""The component that pops out when a submenu is open."""
|
"""The component that pops out when a submenu is open."""
|
||||||
|
@ -5,6 +5,7 @@ from typing import Literal
|
|||||||
from reflex.components.component import ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
from reflex.components.el import elements
|
from reflex.components.el import elements
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -31,6 +32,8 @@ class DialogTrigger(RadixThemesTriggerComponent):
|
|||||||
|
|
||||||
tag = "Dialog.Trigger"
|
tag = "Dialog.Trigger"
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class DialogTitle(RadixThemesComponent):
|
class DialogTitle(RadixThemesComponent):
|
||||||
"""Title component to display inside a Dialog modal."""
|
"""Title component to display inside a Dialog modal."""
|
||||||
|
@ -4,6 +4,7 @@ from typing import Dict, List, Literal, Union
|
|||||||
|
|
||||||
from reflex.components.component import ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -60,6 +61,8 @@ class DropdownMenuTrigger(RadixThemesTriggerComponent):
|
|||||||
|
|
||||||
_invalid_children: List[str] = ["DropdownMenuContent"]
|
_invalid_children: List[str] = ["DropdownMenuContent"]
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class DropdownMenuContent(RadixThemesComponent):
|
class DropdownMenuContent(RadixThemesComponent):
|
||||||
"""The Dropdown Menu Content component that pops out when the dropdown menu is open."""
|
"""The Dropdown Menu Content component that pops out when the dropdown menu is open."""
|
||||||
@ -143,6 +146,8 @@ class DropdownMenuSubTrigger(RadixThemesTriggerComponent):
|
|||||||
|
|
||||||
_valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSub"]
|
_valid_parents: List[str] = ["DropdownMenuContent", "DropdownMenuSub"]
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class DropdownMenuSub(RadixThemesComponent):
|
class DropdownMenuSub(RadixThemesComponent):
|
||||||
"""Contains all the parts of a submenu."""
|
"""Contains all the parts of a submenu."""
|
||||||
|
@ -5,6 +5,7 @@ from typing import Dict, Literal, Union
|
|||||||
from reflex.components.component import ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
from reflex.components.el import elements
|
from reflex.components.el import elements
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, passthrough_event_spec
|
from reflex.event import EventHandler, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -37,6 +38,8 @@ class HoverCardTrigger(RadixThemesTriggerComponent):
|
|||||||
|
|
||||||
tag = "HoverCard.Trigger"
|
tag = "HoverCard.Trigger"
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class HoverCardContent(elements.Div, RadixThemesComponent):
|
class HoverCardContent(elements.Div, RadixThemesComponent):
|
||||||
"""Contains the content of the open hover card."""
|
"""Contains the content of the open hover card."""
|
||||||
|
@ -5,6 +5,7 @@ from typing import Dict, Literal, Union
|
|||||||
from reflex.components.component import ComponentNamespace
|
from reflex.components.component import ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
from reflex.components.el import elements
|
from reflex.components.el import elements
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -34,6 +35,8 @@ class PopoverTrigger(RadixThemesTriggerComponent):
|
|||||||
|
|
||||||
tag = "Popover.Trigger"
|
tag = "Popover.Trigger"
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class PopoverContent(elements.Div, RadixThemesComponent):
|
class PopoverContent(elements.Div, RadixThemesComponent):
|
||||||
"""Contains content to be rendered in the open popover."""
|
"""Contains content to be rendered in the open popover."""
|
||||||
|
@ -85,6 +85,8 @@ class RadioCardsItem(RadixThemesComponent):
|
|||||||
# When true, indicates that the user must check the radio item before the owning form can be submitted.
|
# When true, indicates that the user must check the radio item before the owning form can be submitted.
|
||||||
required: Var[bool]
|
required: Var[bool]
|
||||||
|
|
||||||
|
_valid_parents: list[str] = ["RadioCardsRoot"]
|
||||||
|
|
||||||
|
|
||||||
class RadioCards(SimpleNamespace):
|
class RadioCards(SimpleNamespace):
|
||||||
"""RadioCards components namespace."""
|
"""RadioCards components namespace."""
|
||||||
|
@ -5,6 +5,7 @@ from typing import List, Literal, Union
|
|||||||
import reflex as rx
|
import reflex as rx
|
||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import Component, ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import no_args_event_spec, passthrough_event_spec
|
from reflex.event import no_args_event_spec, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -69,6 +70,8 @@ class SelectTrigger(RadixThemesComponent):
|
|||||||
|
|
||||||
_valid_parents: List[str] = ["SelectRoot"]
|
_valid_parents: List[str] = ["SelectRoot"]
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
|
|
||||||
class SelectContent(RadixThemesComponent):
|
class SelectContent(RadixThemesComponent):
|
||||||
"""The component that pops out when the select is open."""
|
"""The component that pops out when the select is open."""
|
||||||
|
@ -7,6 +7,7 @@ from typing import Any, Dict, List, Literal
|
|||||||
from reflex.components.component import Component, ComponentNamespace
|
from reflex.components.component import Component, ComponentNamespace
|
||||||
from reflex.components.core.breakpoints import Responsive
|
from reflex.components.core.breakpoints import Responsive
|
||||||
from reflex.components.core.colors import color
|
from reflex.components.core.colors import color
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import EventHandler, passthrough_event_spec
|
from reflex.event import EventHandler, passthrough_event_spec
|
||||||
from reflex.vars.base import Var
|
from reflex.vars.base import Var
|
||||||
|
|
||||||
@ -95,6 +96,8 @@ class TabsTrigger(RadixThemesComponent):
|
|||||||
|
|
||||||
_valid_parents: List[str] = ["TabsList"]
|
_valid_parents: List[str] = ["TabsList"]
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, *children, **props) -> Component:
|
def create(cls, *children, **props) -> Component:
|
||||||
"""Create a TabsTrigger component.
|
"""Create a TabsTrigger component.
|
||||||
|
@ -96,5 +96,17 @@ class TextArea(RadixThemesComponent, elements.Textarea):
|
|||||||
return DebounceInput.create(super().create(*children, **props))
|
return DebounceInput.create(super().create(*children, **props))
|
||||||
return super().create(*children, **props)
|
return super().create(*children, **props)
|
||||||
|
|
||||||
|
def add_style(self):
|
||||||
|
"""Add the style to the component.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The style of the component.
|
||||||
|
"""
|
||||||
|
added_style: dict[str, dict] = {}
|
||||||
|
added_style.setdefault("& textarea", {})
|
||||||
|
if "padding" in self.style:
|
||||||
|
added_style["& textarea"]["padding"] = self.style.pop("padding")
|
||||||
|
return added_style
|
||||||
|
|
||||||
|
|
||||||
text_area = TextArea.create
|
text_area = TextArea.create
|
||||||
|
@ -268,4 +268,6 @@ class TextArea(RadixThemesComponent, elements.Textarea):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
def add_style(self): ...
|
||||||
|
|
||||||
text_area = TextArea.create
|
text_area = TextArea.create
|
||||||
|
@ -3,13 +3,33 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional, Sequence, Union
|
||||||
|
|
||||||
from reflex.event import EventChain
|
from reflex.event import EventChain
|
||||||
from reflex.utils import format, types
|
from reflex.utils import format, types
|
||||||
from reflex.vars.base import LiteralVar, Var
|
from reflex.vars.base import LiteralVar, Var
|
||||||
|
|
||||||
|
|
||||||
|
def render_prop(value: Any) -> Any:
|
||||||
|
"""Render the prop.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value to render.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The rendered value.
|
||||||
|
"""
|
||||||
|
from reflex.components.component import BaseComponent
|
||||||
|
|
||||||
|
if isinstance(value, BaseComponent):
|
||||||
|
return value.render()
|
||||||
|
if isinstance(value, Sequence) and not isinstance(value, str):
|
||||||
|
return [render_prop(v) for v in value]
|
||||||
|
if callable(value) and not isinstance(value, Var):
|
||||||
|
return None
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass()
|
@dataclasses.dataclass()
|
||||||
class Tag:
|
class Tag:
|
||||||
"""A React tag."""
|
"""A React tag."""
|
||||||
@ -66,7 +86,9 @@ class Tag:
|
|||||||
Tuple[str, Any]: The field name and value.
|
Tuple[str, Any]: The field name and value.
|
||||||
"""
|
"""
|
||||||
for field in dataclasses.fields(self):
|
for field in dataclasses.fields(self):
|
||||||
yield field.name, getattr(self, field.name)
|
rendered_value = render_prop(getattr(self, field.name))
|
||||||
|
if rendered_value is not None:
|
||||||
|
yield field.name, rendered_value
|
||||||
|
|
||||||
def add_props(self, **kwargs: Optional[Any]) -> Tag:
|
def add_props(self, **kwargs: Optional[Any]) -> Tag:
|
||||||
"""Add props to the tag.
|
"""Add props to the tag.
|
||||||
|
@ -703,6 +703,9 @@ class Config(Base):
|
|||||||
# Path to file containing key-values pairs to override in the environment; Dotenv format.
|
# Path to file containing key-values pairs to override in the environment; Dotenv format.
|
||||||
env_file: Optional[str] = None
|
env_file: Optional[str] = None
|
||||||
|
|
||||||
|
# Whether the app is running in the reflex cloud environment.
|
||||||
|
is_reflex_cloud: bool = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize the config values.
|
"""Initialize the config values.
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
"""Compiler variables."""
|
"""Compiler variables."""
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
from reflex.base import Base
|
|
||||||
from reflex.constants import Dirs
|
from reflex.constants import Dirs
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
|
|
||||||
@ -151,7 +151,8 @@ class MemoizationDisposition(enum.Enum):
|
|||||||
NEVER = "never"
|
NEVER = "never"
|
||||||
|
|
||||||
|
|
||||||
class MemoizationMode(Base):
|
@dataclasses.dataclass(frozen=True)
|
||||||
|
class MemoizationMode:
|
||||||
"""The mode for memoizing a Component."""
|
"""The mode for memoizing a Component."""
|
||||||
|
|
||||||
# The conditions under which the component should be memoized.
|
# The conditions under which the component should be memoized.
|
||||||
|
@ -12,6 +12,7 @@ from reflex.components.radix.themes.components.icon_button import IconButton
|
|||||||
from reflex.components.radix.themes.layout.box import Box
|
from reflex.components.radix.themes.layout.box import Box
|
||||||
from reflex.components.radix.themes.layout.container import Container
|
from reflex.components.radix.themes.layout.container import Container
|
||||||
from reflex.components.radix.themes.layout.stack import HStack
|
from reflex.components.radix.themes.layout.stack import HStack
|
||||||
|
from reflex.constants.compiler import MemoizationMode
|
||||||
from reflex.event import run_script
|
from reflex.event import run_script
|
||||||
from reflex.experimental import hooks
|
from reflex.experimental import hooks
|
||||||
from reflex.state import ComponentState
|
from reflex.state import ComponentState
|
||||||
@ -146,6 +147,8 @@ sidebar_trigger_style = {
|
|||||||
class SidebarTrigger(Fragment):
|
class SidebarTrigger(Fragment):
|
||||||
"""A component that renders the sidebar trigger."""
|
"""A component that renders the sidebar trigger."""
|
||||||
|
|
||||||
|
_memoization_mode = MemoizationMode(recursive=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, sidebar: Component, **props):
|
def create(cls, sidebar: Component, **props):
|
||||||
"""Create the sidebar trigger component.
|
"""Create the sidebar trigger component.
|
||||||
|
@ -303,8 +303,16 @@ class Style(dict):
|
|||||||
Returns:
|
Returns:
|
||||||
The combined style.
|
The combined style.
|
||||||
"""
|
"""
|
||||||
_var_data = VarData.merge(self._var_data, getattr(other, "_var_data", None))
|
other_var_data = None
|
||||||
return Style(super().__or__(self, other), _var_data=_var_data) # pyright: ignore [reportGeneralTypeIssues, reportCallIssue]
|
if not isinstance(other, Style):
|
||||||
|
other_dict, other_var_data = convert(other)
|
||||||
|
else:
|
||||||
|
other_dict, other_var_data = other, other._var_data
|
||||||
|
|
||||||
|
new_style = Style(super().__or__(other_dict))
|
||||||
|
if self._var_data or other_var_data:
|
||||||
|
new_style._var_data = VarData.merge(self._var_data, other_var_data)
|
||||||
|
return new_style
|
||||||
|
|
||||||
|
|
||||||
def _format_emotion_style_pseudo_selector(key: str) -> str:
|
def _format_emotion_style_pseudo_selector(key: str) -> str:
|
||||||
|
@ -81,6 +81,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
||||||
OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
|
OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
|
||||||
|
STRING_T = TypeVar("STRING_T", bound=str)
|
||||||
|
|
||||||
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
|
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
|
||||||
|
|
||||||
@ -445,7 +446,12 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
|
|
||||||
_default_var_type: ClassVar[GenericType] = default_type
|
_default_var_type: ClassVar[GenericType] = default_type
|
||||||
|
|
||||||
ToVarOperation.__name__ = f'To{cls.__name__.removesuffix("Var")}Operation'
|
new_to_var_operation_name = f"To{cls.__name__.removesuffix('Var')}Operation"
|
||||||
|
ToVarOperation.__qualname__ = (
|
||||||
|
ToVarOperation.__qualname__.removesuffix(ToVarOperation.__name__)
|
||||||
|
+ new_to_var_operation_name
|
||||||
|
)
|
||||||
|
ToVarOperation.__name__ = new_to_var_operation_name
|
||||||
|
|
||||||
_var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types))
|
_var_subclasses.append(VarSubclassEntry(cls, ToVarOperation, python_types))
|
||||||
|
|
||||||
@ -551,12 +557,60 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
|
|
||||||
return value_with_replaced
|
return value_with_replaced
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@classmethod
|
||||||
|
def create( # type: ignore[override]
|
||||||
|
cls,
|
||||||
|
value: bool,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> BooleanVar: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@classmethod
|
||||||
|
def create( # type: ignore[override]
|
||||||
|
cls,
|
||||||
|
value: int,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> NumberVar[int]: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
value: Any,
|
value: float,
|
||||||
_var_data: VarData | None = None,
|
_var_data: VarData | None = None,
|
||||||
) -> Var:
|
) -> NumberVar[float]: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: STRING_T,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> StringVar[STRING_T]: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: None,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> NoneVar: ...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: OTHER_VAR_TYPE,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> Var[OTHER_VAR_TYPE]: ...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(
|
||||||
|
cls,
|
||||||
|
value: OTHER_VAR_TYPE,
|
||||||
|
_var_data: VarData | None = None,
|
||||||
|
) -> Var[OTHER_VAR_TYPE]:
|
||||||
"""Create a var from a value.
|
"""Create a var from a value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -1337,7 +1391,7 @@ class LiteralVar(Var):
|
|||||||
TypeError: If the value is not a supported type for LiteralVar.
|
TypeError: If the value is not a supported type for LiteralVar.
|
||||||
"""
|
"""
|
||||||
from .object import LiteralObjectVar
|
from .object import LiteralObjectVar
|
||||||
from .sequence import LiteralStringVar
|
from .sequence import ArrayVar, LiteralStringVar
|
||||||
|
|
||||||
if isinstance(value, Var):
|
if isinstance(value, Var):
|
||||||
if _var_data is None:
|
if _var_data is None:
|
||||||
@ -1393,6 +1447,9 @@ class LiteralVar(Var):
|
|||||||
_var_data=_var_data,
|
_var_data=_var_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if isinstance(value, range):
|
||||||
|
return ArrayVar.range(value.start, value.stop, value.step)
|
||||||
|
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
|
f"Unsupported type {type(value)} for LiteralVar. Tried to create a LiteralVar from {value}."
|
||||||
)
|
)
|
||||||
|
@ -31,7 +31,7 @@ from .base import (
|
|||||||
var_operation_return,
|
var_operation_return,
|
||||||
)
|
)
|
||||||
|
|
||||||
NUMBER_T = TypeVar("NUMBER_T", int, float, Union[int, float], bool)
|
NUMBER_T = TypeVar("NUMBER_T", int, float, bool)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .sequence import ArrayVar
|
from .sequence import ArrayVar
|
||||||
|
@ -1593,7 +1593,7 @@ def array_range_operation(
|
|||||||
The range of numbers.
|
The range of numbers.
|
||||||
"""
|
"""
|
||||||
return var_operation_return(
|
return var_operation_return(
|
||||||
js_expression=f"Array.from({{ length: ({stop!s} - {start!s}) / {step!s} }}, (_, i) => {start!s} + i * {step!s})",
|
js_expression=f"Array.from({{ length: Math.ceil(({stop!s} - {start!s}) / {step!s}) }}, (_, i) => {start!s} + i * {step!s})",
|
||||||
var_type=List[int],
|
var_type=List[int],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Test case for displaying the connection banner when the websocket drops."""
|
"""Test case for displaying the connection banner when the websocket drops."""
|
||||||
|
|
||||||
|
import functools
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -11,12 +12,19 @@ from reflex.testing import AppHarness, WebDriver
|
|||||||
from .utils import SessionStorage
|
from .utils import SessionStorage
|
||||||
|
|
||||||
|
|
||||||
def ConnectionBanner():
|
def ConnectionBanner(is_reflex_cloud: bool = False):
|
||||||
"""App with a connection banner."""
|
"""App with a connection banner.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
is_reflex_cloud: The value for config.is_reflex_cloud.
|
||||||
|
"""
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import reflex as rx
|
import reflex as rx
|
||||||
|
|
||||||
|
# Simulate reflex cloud deploy
|
||||||
|
rx.config.get_config().is_reflex_cloud = is_reflex_cloud
|
||||||
|
|
||||||
class State(rx.State):
|
class State(rx.State):
|
||||||
foo: int = 0
|
foo: int = 0
|
||||||
|
|
||||||
@ -40,19 +48,43 @@ def ConnectionBanner():
|
|||||||
app.add_page(index)
|
app.add_page(index)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(
|
||||||
|
params=[False, True], ids=["reflex_cloud_disabled", "reflex_cloud_enabled"]
|
||||||
|
)
|
||||||
|
def simulate_is_reflex_cloud(request) -> bool:
|
||||||
|
"""Fixture to simulate reflex cloud deployment.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: pytest request fixture.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if reflex cloud is enabled, False otherwise.
|
||||||
|
"""
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def connection_banner(tmp_path) -> Generator[AppHarness, None, None]:
|
def connection_banner(
|
||||||
|
tmp_path,
|
||||||
|
simulate_is_reflex_cloud: bool,
|
||||||
|
) -> Generator[AppHarness, None, None]:
|
||||||
"""Start ConnectionBanner app at tmp_path via AppHarness.
|
"""Start ConnectionBanner app at tmp_path via AppHarness.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tmp_path: pytest tmp_path fixture
|
tmp_path: pytest tmp_path fixture
|
||||||
|
simulate_is_reflex_cloud: Whether is_reflex_cloud is set for the app.
|
||||||
|
|
||||||
Yields:
|
Yields:
|
||||||
running AppHarness instance
|
running AppHarness instance
|
||||||
"""
|
"""
|
||||||
with AppHarness.create(
|
with AppHarness.create(
|
||||||
root=tmp_path,
|
root=tmp_path,
|
||||||
app_source=ConnectionBanner,
|
app_source=functools.partial(
|
||||||
|
ConnectionBanner, is_reflex_cloud=simulate_is_reflex_cloud
|
||||||
|
),
|
||||||
|
app_name="connection_banner_reflex_cloud"
|
||||||
|
if simulate_is_reflex_cloud
|
||||||
|
else "connection_banner",
|
||||||
) as harness:
|
) as harness:
|
||||||
yield harness
|
yield harness
|
||||||
|
|
||||||
@ -77,6 +109,38 @@ def has_error_modal(driver: WebDriver) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def has_cloud_banner(driver: WebDriver) -> bool:
|
||||||
|
"""Check if the cloud banner is displayed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
driver: Selenium webdriver instance.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the banner is displayed, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
driver.find_element(
|
||||||
|
By.XPATH, "//*[ contains(text(), 'You ran out of compute credits.') ]"
|
||||||
|
)
|
||||||
|
except NoSuchElementException:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_token(connection_banner, driver):
|
||||||
|
"""Poll for backend to be up.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
connection_banner: AppHarness instance.
|
||||||
|
driver: Selenium webdriver instance.
|
||||||
|
"""
|
||||||
|
ss = SessionStorage(driver)
|
||||||
|
assert connection_banner._poll_for(
|
||||||
|
lambda: ss.get("token") is not None
|
||||||
|
), "token not found"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_connection_banner(connection_banner: AppHarness):
|
async def test_connection_banner(connection_banner: AppHarness):
|
||||||
"""Test that the connection banner is displayed when the websocket drops.
|
"""Test that the connection banner is displayed when the websocket drops.
|
||||||
@ -88,10 +152,7 @@ async def test_connection_banner(connection_banner: AppHarness):
|
|||||||
assert connection_banner.backend is not None
|
assert connection_banner.backend is not None
|
||||||
driver = connection_banner.frontend()
|
driver = connection_banner.frontend()
|
||||||
|
|
||||||
ss = SessionStorage(driver)
|
_assert_token(connection_banner, driver)
|
||||||
assert connection_banner._poll_for(
|
|
||||||
lambda: ss.get("token") is not None
|
|
||||||
), "token not found"
|
|
||||||
|
|
||||||
assert connection_banner._poll_for(lambda: not has_error_modal(driver))
|
assert connection_banner._poll_for(lambda: not has_error_modal(driver))
|
||||||
|
|
||||||
@ -132,3 +193,36 @@ async def test_connection_banner(connection_banner: AppHarness):
|
|||||||
|
|
||||||
# Count should have incremented after coming back up
|
# Count should have incremented after coming back up
|
||||||
assert connection_banner.poll_for_value(counter_element, exp_not_equal="1") == "2"
|
assert connection_banner.poll_for_value(counter_element, exp_not_equal="1") == "2"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_cloud_banner(
|
||||||
|
connection_banner: AppHarness, simulate_is_reflex_cloud: bool
|
||||||
|
):
|
||||||
|
"""Test that the connection banner is displayed when the websocket drops.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
connection_banner: AppHarness instance.
|
||||||
|
simulate_is_reflex_cloud: Whether is_reflex_cloud is set for the app.
|
||||||
|
"""
|
||||||
|
assert connection_banner.app_instance is not None
|
||||||
|
assert connection_banner.backend is not None
|
||||||
|
driver = connection_banner.frontend()
|
||||||
|
|
||||||
|
driver.add_cookie({"name": "backend-enabled", "value": "truly"})
|
||||||
|
driver.refresh()
|
||||||
|
_assert_token(connection_banner, driver)
|
||||||
|
assert connection_banner._poll_for(lambda: not has_cloud_banner(driver))
|
||||||
|
|
||||||
|
driver.add_cookie({"name": "backend-enabled", "value": "false"})
|
||||||
|
driver.refresh()
|
||||||
|
if simulate_is_reflex_cloud:
|
||||||
|
assert connection_banner._poll_for(lambda: has_cloud_banner(driver))
|
||||||
|
else:
|
||||||
|
_assert_token(connection_banner, driver)
|
||||||
|
assert connection_banner._poll_for(lambda: not has_cloud_banner(driver))
|
||||||
|
|
||||||
|
driver.delete_cookie("backend-enabled")
|
||||||
|
driver.refresh()
|
||||||
|
_assert_token(connection_banner, driver)
|
||||||
|
assert connection_banner._poll_for(lambda: not has_cloud_banner(driver))
|
||||||
|
@ -600,6 +600,11 @@ def VarOperations():
|
|||||||
),
|
),
|
||||||
id="foreach_in_match",
|
id="foreach_in_match",
|
||||||
),
|
),
|
||||||
|
# Literal range var in a foreach
|
||||||
|
rx.box(rx.foreach(range(42, 80, 27), rx.text.span), id="range_in_foreach1"),
|
||||||
|
rx.box(rx.foreach(range(42, 80, 3), rx.text.span), id="range_in_foreach2"),
|
||||||
|
rx.box(rx.foreach(range(42, 20, -6), rx.text.span), id="range_in_foreach3"),
|
||||||
|
rx.box(rx.foreach(range(42, 43, 5), rx.text.span), id="range_in_foreach4"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -799,6 +804,11 @@ def test_var_operations(driver, var_operations: AppHarness):
|
|||||||
("memo_comp_nested", "345"),
|
("memo_comp_nested", "345"),
|
||||||
# foreach in a match
|
# foreach in a match
|
||||||
("foreach_in_match", "first\nsecond\nthird"),
|
("foreach_in_match", "first\nsecond\nthird"),
|
||||||
|
# literal range in a foreach
|
||||||
|
("range_in_foreach1", "4269"),
|
||||||
|
("range_in_foreach2", "42454851545760636669727578"),
|
||||||
|
("range_in_foreach3", "42363024"),
|
||||||
|
("range_in_foreach4", "42"),
|
||||||
]
|
]
|
||||||
|
|
||||||
for tag, expected in tests:
|
for tag, expected in tests:
|
||||||
|
@ -42,7 +42,7 @@ def test_match_components():
|
|||||||
|
|
||||||
assert match_cases[0][0]._js_expr == "1"
|
assert match_cases[0][0]._js_expr == "1"
|
||||||
assert match_cases[0][0]._var_type is int
|
assert match_cases[0][0]._var_type is int
|
||||||
first_return_value_render = match_cases[0][1].render()
|
first_return_value_render = match_cases[0][1]
|
||||||
assert first_return_value_render["name"] == "RadixThemesText"
|
assert first_return_value_render["name"] == "RadixThemesText"
|
||||||
assert first_return_value_render["children"][0]["contents"] == '{"first value"}'
|
assert first_return_value_render["children"][0]["contents"] == '{"first value"}'
|
||||||
|
|
||||||
@ -50,35 +50,35 @@ def test_match_components():
|
|||||||
assert match_cases[1][0]._var_type is int
|
assert match_cases[1][0]._var_type is int
|
||||||
assert match_cases[1][1]._js_expr == "3"
|
assert match_cases[1][1]._js_expr == "3"
|
||||||
assert match_cases[1][1]._var_type is int
|
assert match_cases[1][1]._var_type is int
|
||||||
second_return_value_render = match_cases[1][2].render()
|
second_return_value_render = match_cases[1][2]
|
||||||
assert second_return_value_render["name"] == "RadixThemesText"
|
assert second_return_value_render["name"] == "RadixThemesText"
|
||||||
assert second_return_value_render["children"][0]["contents"] == '{"second value"}'
|
assert second_return_value_render["children"][0]["contents"] == '{"second value"}'
|
||||||
|
|
||||||
assert match_cases[2][0]._js_expr == "[1, 2]"
|
assert match_cases[2][0]._js_expr == "[1, 2]"
|
||||||
assert match_cases[2][0]._var_type == List[int]
|
assert match_cases[2][0]._var_type == List[int]
|
||||||
third_return_value_render = match_cases[2][1].render()
|
third_return_value_render = match_cases[2][1]
|
||||||
assert third_return_value_render["name"] == "RadixThemesText"
|
assert third_return_value_render["name"] == "RadixThemesText"
|
||||||
assert third_return_value_render["children"][0]["contents"] == '{"third value"}'
|
assert third_return_value_render["children"][0]["contents"] == '{"third value"}'
|
||||||
|
|
||||||
assert match_cases[3][0]._js_expr == '"random"'
|
assert match_cases[3][0]._js_expr == '"random"'
|
||||||
assert match_cases[3][0]._var_type is str
|
assert match_cases[3][0]._var_type is str
|
||||||
fourth_return_value_render = match_cases[3][1].render()
|
fourth_return_value_render = match_cases[3][1]
|
||||||
assert fourth_return_value_render["name"] == "RadixThemesText"
|
assert fourth_return_value_render["name"] == "RadixThemesText"
|
||||||
assert fourth_return_value_render["children"][0]["contents"] == '{"fourth value"}'
|
assert fourth_return_value_render["children"][0]["contents"] == '{"fourth value"}'
|
||||||
|
|
||||||
assert match_cases[4][0]._js_expr == '({ ["foo"] : "bar" })'
|
assert match_cases[4][0]._js_expr == '({ ["foo"] : "bar" })'
|
||||||
assert match_cases[4][0]._var_type == Mapping[str, str]
|
assert match_cases[4][0]._var_type == Mapping[str, str]
|
||||||
fifth_return_value_render = match_cases[4][1].render()
|
fifth_return_value_render = match_cases[4][1]
|
||||||
assert fifth_return_value_render["name"] == "RadixThemesText"
|
assert fifth_return_value_render["name"] == "RadixThemesText"
|
||||||
assert fifth_return_value_render["children"][0]["contents"] == '{"fifth value"}'
|
assert fifth_return_value_render["children"][0]["contents"] == '{"fifth value"}'
|
||||||
|
|
||||||
assert match_cases[5][0]._js_expr == f"({MatchState.get_name()}.num + 1)"
|
assert match_cases[5][0]._js_expr == f"({MatchState.get_name()}.num + 1)"
|
||||||
assert match_cases[5][0]._var_type is int
|
assert match_cases[5][0]._var_type is int
|
||||||
fifth_return_value_render = match_cases[5][1].render()
|
fifth_return_value_render = match_cases[5][1]
|
||||||
assert fifth_return_value_render["name"] == "RadixThemesText"
|
assert fifth_return_value_render["name"] == "RadixThemesText"
|
||||||
assert fifth_return_value_render["children"][0]["contents"] == '{"sixth value"}'
|
assert fifth_return_value_render["children"][0]["contents"] == '{"sixth value"}'
|
||||||
|
|
||||||
default = match_child["default"].render()
|
default = match_child["default"]
|
||||||
|
|
||||||
assert default["name"] == "RadixThemesText"
|
assert default["name"] == "RadixThemesText"
|
||||||
assert default["children"][0]["contents"] == '{"default value"}'
|
assert default["children"][0]["contents"] == '{"default value"}'
|
||||||
@ -153,7 +153,7 @@ def test_match_on_component_without_default():
|
|||||||
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
match_comp = Match.create(MatchState.value, *match_case_tuples)
|
||||||
default = match_comp.render()["children"][0]["default"]
|
default = match_comp.render()["children"][0]["default"]
|
||||||
|
|
||||||
assert isinstance(default, Fragment)
|
assert isinstance(default, dict) and default["name"] == Fragment.__name__
|
||||||
|
|
||||||
|
|
||||||
def test_match_on_var_no_default():
|
def test_match_on_var_no_default():
|
||||||
|
@ -541,3 +541,7 @@ def test_style_update_with_var_data():
|
|||||||
assert s2._var_data is not None
|
assert s2._var_data is not None
|
||||||
assert "const red = true" in s2._var_data.hooks
|
assert "const red = true" in s2._var_data.hooks
|
||||||
assert "const blue = true" in s2._var_data.hooks
|
assert "const blue = true" in s2._var_data.hooks
|
||||||
|
|
||||||
|
s3 = s1 | s2
|
||||||
|
assert s3._var_data is not None
|
||||||
|
assert "_varData" not in s3
|
||||||
|
@ -1076,19 +1076,19 @@ def test_array_operations():
|
|||||||
assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].slice().reverse()"
|
assert str(array_var.reverse()) == "[1, 2, 3, 4, 5].slice().reverse()"
|
||||||
assert (
|
assert (
|
||||||
str(ArrayVar.range(10))
|
str(ArrayVar.range(10))
|
||||||
== "Array.from({ length: (10 - 0) / 1 }, (_, i) => 0 + i * 1)"
|
== "Array.from({ length: Math.ceil((10 - 0) / 1) }, (_, i) => 0 + i * 1)"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(ArrayVar.range(1, 10))
|
str(ArrayVar.range(1, 10))
|
||||||
== "Array.from({ length: (10 - 1) / 1 }, (_, i) => 1 + i * 1)"
|
== "Array.from({ length: Math.ceil((10 - 1) / 1) }, (_, i) => 1 + i * 1)"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(ArrayVar.range(1, 10, 2))
|
str(ArrayVar.range(1, 10, 2))
|
||||||
== "Array.from({ length: (10 - 1) / 2 }, (_, i) => 1 + i * 2)"
|
== "Array.from({ length: Math.ceil((10 - 1) / 2) }, (_, i) => 1 + i * 2)"
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
str(ArrayVar.range(1, 10, -1))
|
str(ArrayVar.range(1, 10, -1))
|
||||||
== "Array.from({ length: (10 - 1) / -1 }, (_, i) => 1 + i * -1)"
|
== "Array.from({ length: Math.ceil((10 - 1) / -1) }, (_, i) => 1 + i * -1)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user