Component as Var type (#3732)
* [WiP] Support UI components returned from a computed var * Get rid of nasty react hooks warning * include @babel/standalone in the base to avoid CDN * put window variables behind an object * use jsx * implement the thing * cleanup dead test code (#3909) * override dict in propsbase to use camelCase (#3910) * override dict in propsbase to use camelCase * fix underscore in dict * dang it darglint * [REF-3562][REF-3563] Replace chakra usage (#3872) * [ENG-3717] [flexgen] Initialize app from refactored code (#3918) * Remove Pydantic from some classes (#3907) * half of the way there * add dataclass support * Forbid Computed var shadowing (#3843) * get it right pyright * fix unit tests * rip out more pydantic * fix weird issues with merge_imports * add missing docstring * make special props a list instead of a set * fix moment pyi * actually ignore the runtime error * it's ruff out there --------- Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com> * Merging * fixss * fix field_name * always import react * move func to file * do some weird things * it's really ruff out there * add docs * how does this work * dang it darglint * fix the silly * don't remove computed guy * silly goose, don't ignore var types :D * update code * put f string on one line * make it deprecated instead of outright killing it * i hate it * add imports from react * assert it has evalReactComponent * do things ig * move get field to global context * ooops --------- Co-authored-by: Khaleel Al-Adhami <khaleel.aladhami@gmail.com> Co-authored-by: benedikt-bartscher <31854409+benedikt-bartscher@users.noreply.github.com> Co-authored-by: Elijah Ahianyo <elijahahianyo@gmail.com>
This commit is contained in:
parent
ef38ac29ea
commit
bca49d3537
@ -7,6 +7,10 @@ import '/styles/styles.css'
|
||||
{% block declaration %}
|
||||
import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js";
|
||||
import { ThemeProvider } from 'next-themes'
|
||||
import * as React from "react";
|
||||
import * as utils_context from "/utils/context.js";
|
||||
import * as utils_state from "/utils/state.js";
|
||||
import * as radix from "@radix-ui/themes";
|
||||
|
||||
{% for custom_code in custom_codes %}
|
||||
{{custom_code}}
|
||||
@ -26,6 +30,16 @@ function AppWrap({children}) {
|
||||
}
|
||||
|
||||
export default function MyApp({ Component, pageProps }) {
|
||||
React.useEffect(() => {
|
||||
// Make contexts and state objects available globally for dynamic eval'd components
|
||||
let windowImports = {
|
||||
"react": React,
|
||||
"@radix-ui/themes": radix,
|
||||
"/utils/context": utils_context,
|
||||
"/utils/state": utils_state,
|
||||
};
|
||||
window["__reflex"] = windowImports;
|
||||
}, []);
|
||||
return (
|
||||
<ThemeProvider defaultTheme={ defaultColorMode } attribute="class">
|
||||
<AppWrap>
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
} from "utils/context.js";
|
||||
import debounce from "/utils/helpers/debounce";
|
||||
import throttle from "/utils/helpers/throttle";
|
||||
import * as Babel from "@babel/standalone";
|
||||
|
||||
// Endpoint URLs.
|
||||
const EVENTURL = env.EVENT;
|
||||
@ -117,8 +118,8 @@ export const isStateful = () => {
|
||||
if (event_queue.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return event_queue.some(event => event.name.startsWith("reflex___state"));
|
||||
}
|
||||
return event_queue.some((event) => event.name.startsWith("reflex___state"));
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply a delta to the state.
|
||||
@ -129,6 +130,22 @@ export const applyDelta = (state, delta) => {
|
||||
return { ...state, ...delta };
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a dynamic component.
|
||||
* @param component The component to evaluate.
|
||||
* @returns The evaluated component.
|
||||
*/
|
||||
export const evalReactComponent = async (component) => {
|
||||
if (!window.React && window.__reflex) {
|
||||
window.React = window.__reflex.react;
|
||||
}
|
||||
const output = Babel.transform(component, { presets: ["react"] }).code;
|
||||
const encodedJs = encodeURIComponent(output);
|
||||
const dataUri = "data:text/javascript;charset=utf-8," + encodedJs;
|
||||
const module = await eval(`import(dataUri)`);
|
||||
return module.default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Only Queue and process events when websocket connection exists.
|
||||
* @param event The event to queue.
|
||||
@ -141,7 +158,7 @@ export const queueEventIfSocketExists = async (events, socket) => {
|
||||
return;
|
||||
}
|
||||
await queueEvents(events, socket);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle frontend event or send the event to the backend via Websocket.
|
||||
@ -208,7 +225,10 @@ export const applyEvent = async (event, socket) => {
|
||||
const a = document.createElement("a");
|
||||
a.hidden = true;
|
||||
// Special case when linking to uploaded files
|
||||
a.href = event.payload.url.replace("${getBackendURL(env.UPLOAD)}", getBackendURL(env.UPLOAD))
|
||||
a.href = event.payload.url.replace(
|
||||
"${getBackendURL(env.UPLOAD)}",
|
||||
getBackendURL(env.UPLOAD)
|
||||
);
|
||||
a.download = event.payload.filename;
|
||||
a.click();
|
||||
a.remove();
|
||||
@ -249,7 +269,7 @@ export const applyEvent = async (event, socket) => {
|
||||
} catch (e) {
|
||||
console.log("_call_script", e);
|
||||
if (window && window?.onerror) {
|
||||
window.onerror(e.message, null, null, null, e)
|
||||
window.onerror(e.message, null, null, null, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -290,10 +310,9 @@ export const applyEvent = async (event, socket) => {
|
||||
export const applyRestEvent = async (event, socket) => {
|
||||
let eventSent = false;
|
||||
if (event.handler === "uploadFiles") {
|
||||
|
||||
if (event.payload.files === undefined || event.payload.files.length === 0) {
|
||||
// Submit the event over the websocket to trigger the event handler.
|
||||
return await applyEvent(Event(event.name), socket)
|
||||
return await applyEvent(Event(event.name), socket);
|
||||
}
|
||||
|
||||
// Start upload, but do not wait for it, which would block other events.
|
||||
@ -397,7 +416,7 @@ export const connect = async (
|
||||
console.log("Disconnect backend before bfcache on navigation");
|
||||
socket.current.disconnect();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Once the socket is open, hydrate the page.
|
||||
socket.current.on("connect", () => {
|
||||
@ -416,7 +435,7 @@ export const connect = async (
|
||||
});
|
||||
|
||||
// On each received message, queue the updates and events.
|
||||
socket.current.on("event", (message) => {
|
||||
socket.current.on("event", async (message) => {
|
||||
const update = JSON5.parse(message);
|
||||
for (const substate in update.delta) {
|
||||
dispatch[substate](update.delta[substate]);
|
||||
@ -574,7 +593,11 @@ export const hydrateClientStorage = (client_storage) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client_storage.cookies || client_storage.local_storage || client_storage.session_storage) {
|
||||
if (
|
||||
client_storage.cookies ||
|
||||
client_storage.local_storage ||
|
||||
client_storage.session_storage
|
||||
) {
|
||||
return client_storage_values;
|
||||
}
|
||||
return {};
|
||||
@ -614,15 +637,17 @@ const applyClientStorageDelta = (client_storage, delta) => {
|
||||
) {
|
||||
const options = client_storage.local_storage[state_key];
|
||||
localStorage.setItem(options.name || state_key, delta[substate][key]);
|
||||
} else if(
|
||||
} else if (
|
||||
client_storage.session_storage &&
|
||||
state_key in client_storage.session_storage &&
|
||||
typeof window !== "undefined"
|
||||
) {
|
||||
const session_options = client_storage.session_storage[state_key];
|
||||
sessionStorage.setItem(session_options.name || state_key, delta[substate][key]);
|
||||
sessionStorage.setItem(
|
||||
session_options.name || state_key,
|
||||
delta[substate][key]
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -651,7 +676,7 @@ export const useEventLoop = (
|
||||
if (!(args instanceof Array)) {
|
||||
args = [args];
|
||||
}
|
||||
const _e = args.filter((o) => o?.preventDefault !== undefined)[0]
|
||||
const _e = args.filter((o) => o?.preventDefault !== undefined)[0];
|
||||
|
||||
if (event_actions?.preventDefault && _e?.preventDefault) {
|
||||
_e.preventDefault();
|
||||
@ -671,7 +696,7 @@ export const useEventLoop = (
|
||||
debounce(
|
||||
combined_name,
|
||||
() => queueEvents(events, socket),
|
||||
event_actions.debounce,
|
||||
event_actions.debounce
|
||||
);
|
||||
} else {
|
||||
queueEvents(events, socket);
|
||||
@ -696,30 +721,32 @@ export const useEventLoop = (
|
||||
}
|
||||
}, [router.isReady]);
|
||||
|
||||
// Handle frontend errors and send them to the backend via websocket.
|
||||
useEffect(() => {
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: error.stack,
|
||||
})])
|
||||
return false;
|
||||
}
|
||||
// Handle frontend errors and send them to the backend via websocket.
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
//NOTE: Only works in Chrome v49+
|
||||
//https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events
|
||||
window.onunhandledrejection = function (event) {
|
||||
addEvents([Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: event.reason.stack,
|
||||
})])
|
||||
return false;
|
||||
}
|
||||
|
||||
},[])
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
addEvents([
|
||||
Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: error.stack,
|
||||
}),
|
||||
]);
|
||||
return false;
|
||||
};
|
||||
|
||||
//NOTE: Only works in Chrome v49+
|
||||
//https://github.com/mknichel/javascript-errors?tab=readme-ov-file#promise-rejection-events
|
||||
window.onunhandledrejection = function (event) {
|
||||
addEvents([
|
||||
Event(`${exception_state_name}.handle_frontend_exception`, {
|
||||
stack: event.reason.stack,
|
||||
}),
|
||||
]);
|
||||
return false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Main event loop.
|
||||
useEffect(() => {
|
||||
@ -782,11 +809,11 @@ export const useEventLoop = (
|
||||
// Route after the initial page hydration.
|
||||
useEffect(() => {
|
||||
const change_start = () => {
|
||||
const main_state_dispatch = dispatch["reflex___state____state"]
|
||||
const main_state_dispatch = dispatch["reflex___state____state"];
|
||||
if (main_state_dispatch !== undefined) {
|
||||
main_state_dispatch({ is_hydrated: false })
|
||||
main_state_dispatch({ is_hydrated: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
const change_complete = () => addEvents(onLoadInternalEvent());
|
||||
router.events.on("routeChangeStart", change_start);
|
||||
router.events.on("routeChangeComplete", change_complete);
|
||||
|
@ -47,6 +47,9 @@ def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None
|
||||
# shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
|
||||
pydantic_main.validate_field_name = validate_field_name # type: ignore
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from reflex.vars import Var
|
||||
|
||||
|
||||
class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
"""The base class subclassed by all Reflex classes.
|
||||
@ -92,7 +95,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def get_fields(cls) -> dict[str, Any]:
|
||||
def get_fields(cls) -> dict[str, ModelField]:
|
||||
"""Get the fields of the object.
|
||||
|
||||
Returns:
|
||||
@ -101,7 +104,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
return cls.__fields__
|
||||
|
||||
@classmethod
|
||||
def add_field(cls, var: Any, default_value: Any):
|
||||
def add_field(cls, var: Var, default_value: Any):
|
||||
"""Add a pydantic field after class definition.
|
||||
|
||||
Used by State.add_var() to correctly handle the new variable.
|
||||
@ -110,7 +113,7 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
var: The variable to add a pydantic field for.
|
||||
default_value: The default value of the field
|
||||
"""
|
||||
var_name = var._js_expr.split(".")[-1]
|
||||
var_name = var._var_field_name
|
||||
new_field = ModelField.infer(
|
||||
name=var_name,
|
||||
value=default_value,
|
||||
@ -133,13 +136,4 @@ class Base(BaseModel): # pyright: ignore [reportUnboundVariable]
|
||||
# Seems like this function signature was wrong all along?
|
||||
# If the user wants a field that we know of, get it and pass it off to _get_value
|
||||
key = getattr(self, key)
|
||||
return self._get_value(
|
||||
key,
|
||||
to_dict=True,
|
||||
by_alias=False,
|
||||
include=None,
|
||||
exclude=None,
|
||||
exclude_unset=False,
|
||||
exclude_defaults=False,
|
||||
exclude_none=False,
|
||||
)
|
||||
return key
|
||||
|
@ -25,6 +25,7 @@ import reflex.state
|
||||
from reflex.base import Base
|
||||
from reflex.compiler.templates import STATEFUL_COMPONENT
|
||||
from reflex.components.core.breakpoints import Breakpoints
|
||||
from reflex.components.dynamic import load_dynamic_serializer
|
||||
from reflex.components.tags import Tag
|
||||
from reflex.constants import (
|
||||
Dirs,
|
||||
@ -52,7 +53,6 @@ from reflex.utils.imports import (
|
||||
ParsedImportDict,
|
||||
parse_imports,
|
||||
)
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import VarData
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
|
||||
@ -615,8 +615,8 @@ class Component(BaseComponent, ABC):
|
||||
if types._issubclass(field.type_, EventHandler):
|
||||
args_spec = None
|
||||
annotation = field.annotation
|
||||
if hasattr(annotation, "__metadata__"):
|
||||
args_spec = annotation.__metadata__[0]
|
||||
if (metadata := getattr(annotation, "__metadata__", None)) is not None:
|
||||
args_spec = metadata[0]
|
||||
default_triggers[field.name] = args_spec or (lambda: [])
|
||||
return default_triggers
|
||||
|
||||
@ -1882,19 +1882,6 @@ class NoSSRComponent(Component):
|
||||
return "".join((library_import, mod_import, opts_fragment))
|
||||
|
||||
|
||||
@serializer
|
||||
def serialize_component(comp: Component):
|
||||
"""Serialize a component.
|
||||
|
||||
Args:
|
||||
comp: The component to serialize.
|
||||
|
||||
Returns:
|
||||
The serialized component.
|
||||
"""
|
||||
return str(comp)
|
||||
|
||||
|
||||
class StatefulComponent(BaseComponent):
|
||||
"""A component that depends on state and is rendered outside of the page component.
|
||||
|
||||
@ -2307,3 +2294,6 @@ class MemoizationLeaf(Component):
|
||||
update={"disposition": MemoizationDisposition.ALWAYS}
|
||||
)
|
||||
return comp
|
||||
|
||||
|
||||
load_dynamic_serializer()
|
||||
|
@ -2,11 +2,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
from typing import Any, Dict, Literal, Optional, Union
|
||||
|
||||
from typing_extensions import get_args
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.component import Component, ComponentNamespace
|
||||
from reflex.components.core.cond import color_mode_cond
|
||||
from reflex.components.lucide.icon import Icon
|
||||
from reflex.components.radix.themes.components.button import Button
|
||||
@ -14,9 +15,9 @@ from reflex.components.radix.themes.layout.box import Box
|
||||
from reflex.constants.colors import Color
|
||||
from reflex.event import set_clipboard
|
||||
from reflex.style import Style
|
||||
from reflex.utils import format
|
||||
from reflex.utils import console, format
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
from reflex.vars.base import LiteralVar, Var
|
||||
from reflex.vars.base import LiteralVar, Var, VarData
|
||||
|
||||
LiteralCodeBlockTheme = Literal[
|
||||
"a11y-dark",
|
||||
@ -405,31 +406,6 @@ class CodeBlock(Component):
|
||||
"""
|
||||
imports_: ImportDict = {}
|
||||
|
||||
themeString = str(self.theme)
|
||||
|
||||
selected_themes = []
|
||||
|
||||
for possibleTheme in get_args(LiteralCodeBlockTheme):
|
||||
if format.to_camel_case(possibleTheme) in themeString:
|
||||
selected_themes.append(possibleTheme)
|
||||
if possibleTheme in themeString:
|
||||
selected_themes.append(possibleTheme)
|
||||
|
||||
selected_themes = sorted(set(map(self.convert_theme_name, selected_themes)))
|
||||
|
||||
imports_.update(
|
||||
{
|
||||
f"react-syntax-highlighter/dist/cjs/styles/prism/{theme}": [
|
||||
ImportVar(
|
||||
tag=format.to_camel_case(theme),
|
||||
is_default=True,
|
||||
install=False,
|
||||
)
|
||||
]
|
||||
for theme in selected_themes
|
||||
}
|
||||
)
|
||||
|
||||
if (
|
||||
self.language is not None
|
||||
and (language_without_quotes := str(self.language).replace('"', ""))
|
||||
@ -480,14 +456,20 @@ class CodeBlock(Component):
|
||||
if "theme" not in props:
|
||||
# Default color scheme responds to global color mode.
|
||||
props["theme"] = color_mode_cond(
|
||||
light=Var(_js_expr="oneLight"),
|
||||
dark=Var(_js_expr="oneDark"),
|
||||
light=Theme.one_light,
|
||||
dark=Theme.one_dark,
|
||||
)
|
||||
|
||||
# react-syntax-highlighter doesnt have an explicit "light" or "dark" theme so we use one-light and one-dark
|
||||
# themes respectively to ensure code compatibility.
|
||||
if "theme" in props and not isinstance(props["theme"], Var):
|
||||
props["theme"] = cls.convert_theme_name(props["theme"])
|
||||
props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore
|
||||
console.deprecate(
|
||||
feature_name="theme prop as string",
|
||||
reason="Use code_block.themes instead.",
|
||||
deprecation_version="0.6.0",
|
||||
removal_version="0.7.0",
|
||||
)
|
||||
|
||||
if can_copy:
|
||||
code = children[0]
|
||||
@ -533,9 +515,7 @@ class CodeBlock(Component):
|
||||
def _render(self):
|
||||
out = super()._render()
|
||||
|
||||
theme = self.theme._replace(
|
||||
_js_expr=replace_quotes_with_camel_case(str(self.theme))
|
||||
)
|
||||
theme = self.theme
|
||||
|
||||
out.add_props(style=theme).remove_props("theme", "code").add_props(
|
||||
children=self.code
|
||||
@ -558,4 +538,83 @@ class CodeBlock(Component):
|
||||
return theme
|
||||
|
||||
|
||||
code_block = CodeBlock.create
|
||||
def construct_theme_var(theme: str) -> Var:
|
||||
"""Construct a theme var.
|
||||
|
||||
Args:
|
||||
theme: The theme to construct.
|
||||
|
||||
Returns:
|
||||
The constructed theme var.
|
||||
"""
|
||||
return Var(
|
||||
theme,
|
||||
_var_data=VarData(
|
||||
imports={
|
||||
f"react-syntax-highlighter/dist/cjs/styles/prism/{format.to_kebab_case(theme)}": [
|
||||
ImportVar(tag=theme, is_default=True, install=False)
|
||||
]
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class Theme(enum.Enum):
|
||||
"""Themes for the CodeBlock component."""
|
||||
|
||||
a11y_dark = construct_theme_var("a11yDark")
|
||||
atom_dark = construct_theme_var("atomDark")
|
||||
cb = construct_theme_var("cb")
|
||||
coldark_cold = construct_theme_var("coldarkCold")
|
||||
coldark_dark = construct_theme_var("coldarkDark")
|
||||
coy = construct_theme_var("coy")
|
||||
coy_without_shadows = construct_theme_var("coyWithoutShadows")
|
||||
darcula = construct_theme_var("darcula")
|
||||
dark = construct_theme_var("oneDark")
|
||||
dracula = construct_theme_var("dracula")
|
||||
duotone_dark = construct_theme_var("duotoneDark")
|
||||
duotone_earth = construct_theme_var("duotoneEarth")
|
||||
duotone_forest = construct_theme_var("duotoneForest")
|
||||
duotone_light = construct_theme_var("duotoneLight")
|
||||
duotone_sea = construct_theme_var("duotoneSea")
|
||||
duotone_space = construct_theme_var("duotoneSpace")
|
||||
funky = construct_theme_var("funky")
|
||||
ghcolors = construct_theme_var("ghcolors")
|
||||
gruvbox_dark = construct_theme_var("gruvboxDark")
|
||||
gruvbox_light = construct_theme_var("gruvboxLight")
|
||||
holi_theme = construct_theme_var("holiTheme")
|
||||
hopscotch = construct_theme_var("hopscotch")
|
||||
light = construct_theme_var("oneLight")
|
||||
lucario = construct_theme_var("lucario")
|
||||
material_dark = construct_theme_var("materialDark")
|
||||
material_light = construct_theme_var("materialLight")
|
||||
material_oceanic = construct_theme_var("materialOceanic")
|
||||
night_owl = construct_theme_var("nightOwl")
|
||||
nord = construct_theme_var("nord")
|
||||
okaidia = construct_theme_var("okaidia")
|
||||
one_dark = construct_theme_var("oneDark")
|
||||
one_light = construct_theme_var("oneLight")
|
||||
pojoaque = construct_theme_var("pojoaque")
|
||||
prism = construct_theme_var("prism")
|
||||
shades_of_purple = construct_theme_var("shadesOfPurple")
|
||||
solarized_dark_atom = construct_theme_var("solarizedDarkAtom")
|
||||
solarizedlight = construct_theme_var("solarizedlight")
|
||||
synthwave84 = construct_theme_var("synthwave84")
|
||||
tomorrow = construct_theme_var("tomorrow")
|
||||
twilight = construct_theme_var("twilight")
|
||||
vs = construct_theme_var("vs")
|
||||
vs_dark = construct_theme_var("vsDark")
|
||||
vsc_dark_plus = construct_theme_var("vscDarkPlus")
|
||||
xonokai = construct_theme_var("xonokai")
|
||||
z_touch = construct_theme_var("zTouch")
|
||||
|
||||
|
||||
class CodeblockNamespace(ComponentNamespace):
|
||||
"""Namespace for the CodeBlock component."""
|
||||
|
||||
themes = Theme
|
||||
|
||||
__call__ = CodeBlock.create
|
||||
|
||||
|
||||
code_block = CodeblockNamespace()
|
||||
|
@ -3,9 +3,10 @@
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `reflex/utils/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
import enum
|
||||
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
|
||||
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.component import Component, ComponentNamespace
|
||||
from reflex.constants.colors import Color
|
||||
from reflex.event import EventHandler, EventSpec
|
||||
from reflex.style import Style
|
||||
@ -1001,4 +1002,706 @@ class CodeBlock(Component):
|
||||
@staticmethod
|
||||
def convert_theme_name(theme) -> str: ...
|
||||
|
||||
code_block = CodeBlock.create
|
||||
def construct_theme_var(theme: str) -> Var: ...
|
||||
|
||||
class Theme(enum.Enum):
|
||||
a11y_dark = construct_theme_var("a11yDark")
|
||||
atom_dark = construct_theme_var("atomDark")
|
||||
cb = construct_theme_var("cb")
|
||||
coldark_cold = construct_theme_var("coldarkCold")
|
||||
coldark_dark = construct_theme_var("coldarkDark")
|
||||
coy = construct_theme_var("coy")
|
||||
coy_without_shadows = construct_theme_var("coyWithoutShadows")
|
||||
darcula = construct_theme_var("darcula")
|
||||
dark = construct_theme_var("oneDark")
|
||||
dracula = construct_theme_var("dracula")
|
||||
duotone_dark = construct_theme_var("duotoneDark")
|
||||
duotone_earth = construct_theme_var("duotoneEarth")
|
||||
duotone_forest = construct_theme_var("duotoneForest")
|
||||
duotone_light = construct_theme_var("duotoneLight")
|
||||
duotone_sea = construct_theme_var("duotoneSea")
|
||||
duotone_space = construct_theme_var("duotoneSpace")
|
||||
funky = construct_theme_var("funky")
|
||||
ghcolors = construct_theme_var("ghcolors")
|
||||
gruvbox_dark = construct_theme_var("gruvboxDark")
|
||||
gruvbox_light = construct_theme_var("gruvboxLight")
|
||||
holi_theme = construct_theme_var("holiTheme")
|
||||
hopscotch = construct_theme_var("hopscotch")
|
||||
light = construct_theme_var("oneLight")
|
||||
lucario = construct_theme_var("lucario")
|
||||
material_dark = construct_theme_var("materialDark")
|
||||
material_light = construct_theme_var("materialLight")
|
||||
material_oceanic = construct_theme_var("materialOceanic")
|
||||
night_owl = construct_theme_var("nightOwl")
|
||||
nord = construct_theme_var("nord")
|
||||
okaidia = construct_theme_var("okaidia")
|
||||
one_dark = construct_theme_var("oneDark")
|
||||
one_light = construct_theme_var("oneLight")
|
||||
pojoaque = construct_theme_var("pojoaque")
|
||||
prism = construct_theme_var("prism")
|
||||
shades_of_purple = construct_theme_var("shadesOfPurple")
|
||||
solarized_dark_atom = construct_theme_var("solarizedDarkAtom")
|
||||
solarizedlight = construct_theme_var("solarizedlight")
|
||||
synthwave84 = construct_theme_var("synthwave84")
|
||||
tomorrow = construct_theme_var("tomorrow")
|
||||
twilight = construct_theme_var("twilight")
|
||||
vs = construct_theme_var("vs")
|
||||
vs_dark = construct_theme_var("vsDark")
|
||||
vsc_dark_plus = construct_theme_var("vscDarkPlus")
|
||||
xonokai = construct_theme_var("xonokai")
|
||||
z_touch = construct_theme_var("zTouch")
|
||||
|
||||
class CodeblockNamespace(ComponentNamespace):
|
||||
themes = Theme
|
||||
|
||||
@staticmethod
|
||||
def __call__(
|
||||
*children,
|
||||
can_copy: Optional[bool] = False,
|
||||
copy_button: Optional[Union[Component, bool]] = None,
|
||||
theme: Optional[Union[Any, Var[Any]]] = None,
|
||||
language: Optional[
|
||||
Union[
|
||||
Literal[
|
||||
"abap",
|
||||
"abnf",
|
||||
"actionscript",
|
||||
"ada",
|
||||
"agda",
|
||||
"al",
|
||||
"antlr4",
|
||||
"apacheconf",
|
||||
"apex",
|
||||
"apl",
|
||||
"applescript",
|
||||
"aql",
|
||||
"arduino",
|
||||
"arff",
|
||||
"asciidoc",
|
||||
"asm6502",
|
||||
"asmatmel",
|
||||
"aspnet",
|
||||
"autohotkey",
|
||||
"autoit",
|
||||
"avisynth",
|
||||
"avro-idl",
|
||||
"bash",
|
||||
"basic",
|
||||
"batch",
|
||||
"bbcode",
|
||||
"bicep",
|
||||
"birb",
|
||||
"bison",
|
||||
"bnf",
|
||||
"brainfuck",
|
||||
"brightscript",
|
||||
"bro",
|
||||
"bsl",
|
||||
"c",
|
||||
"cfscript",
|
||||
"chaiscript",
|
||||
"cil",
|
||||
"clike",
|
||||
"clojure",
|
||||
"cmake",
|
||||
"cobol",
|
||||
"coffeescript",
|
||||
"concurnas",
|
||||
"coq",
|
||||
"core",
|
||||
"cpp",
|
||||
"crystal",
|
||||
"csharp",
|
||||
"cshtml",
|
||||
"csp",
|
||||
"css",
|
||||
"css-extras",
|
||||
"csv",
|
||||
"cypher",
|
||||
"d",
|
||||
"dart",
|
||||
"dataweave",
|
||||
"dax",
|
||||
"dhall",
|
||||
"diff",
|
||||
"django",
|
||||
"dns-zone-file",
|
||||
"docker",
|
||||
"dot",
|
||||
"ebnf",
|
||||
"editorconfig",
|
||||
"eiffel",
|
||||
"ejs",
|
||||
"elixir",
|
||||
"elm",
|
||||
"erb",
|
||||
"erlang",
|
||||
"etlua",
|
||||
"excel-formula",
|
||||
"factor",
|
||||
"false",
|
||||
"firestore-security-rules",
|
||||
"flow",
|
||||
"fortran",
|
||||
"fsharp",
|
||||
"ftl",
|
||||
"gap",
|
||||
"gcode",
|
||||
"gdscript",
|
||||
"gedcom",
|
||||
"gherkin",
|
||||
"git",
|
||||
"glsl",
|
||||
"gml",
|
||||
"gn",
|
||||
"go",
|
||||
"go-module",
|
||||
"graphql",
|
||||
"groovy",
|
||||
"haml",
|
||||
"handlebars",
|
||||
"haskell",
|
||||
"haxe",
|
||||
"hcl",
|
||||
"hlsl",
|
||||
"hoon",
|
||||
"hpkp",
|
||||
"hsts",
|
||||
"http",
|
||||
"ichigojam",
|
||||
"icon",
|
||||
"icu-message-format",
|
||||
"idris",
|
||||
"iecst",
|
||||
"ignore",
|
||||
"index",
|
||||
"inform7",
|
||||
"ini",
|
||||
"io",
|
||||
"j",
|
||||
"java",
|
||||
"javadoc",
|
||||
"javadoclike",
|
||||
"javascript",
|
||||
"javastacktrace",
|
||||
"jexl",
|
||||
"jolie",
|
||||
"jq",
|
||||
"js-extras",
|
||||
"js-templates",
|
||||
"jsdoc",
|
||||
"json",
|
||||
"json5",
|
||||
"jsonp",
|
||||
"jsstacktrace",
|
||||
"jsx",
|
||||
"julia",
|
||||
"keepalived",
|
||||
"keyman",
|
||||
"kotlin",
|
||||
"kumir",
|
||||
"kusto",
|
||||
"latex",
|
||||
"latte",
|
||||
"less",
|
||||
"lilypond",
|
||||
"liquid",
|
||||
"lisp",
|
||||
"livescript",
|
||||
"llvm",
|
||||
"log",
|
||||
"lolcode",
|
||||
"lua",
|
||||
"magma",
|
||||
"makefile",
|
||||
"markdown",
|
||||
"markup",
|
||||
"markup-templating",
|
||||
"matlab",
|
||||
"maxscript",
|
||||
"mel",
|
||||
"mermaid",
|
||||
"mizar",
|
||||
"mongodb",
|
||||
"monkey",
|
||||
"moonscript",
|
||||
"n1ql",
|
||||
"n4js",
|
||||
"nand2tetris-hdl",
|
||||
"naniscript",
|
||||
"nasm",
|
||||
"neon",
|
||||
"nevod",
|
||||
"nginx",
|
||||
"nim",
|
||||
"nix",
|
||||
"nsis",
|
||||
"objectivec",
|
||||
"ocaml",
|
||||
"opencl",
|
||||
"openqasm",
|
||||
"oz",
|
||||
"parigp",
|
||||
"parser",
|
||||
"pascal",
|
||||
"pascaligo",
|
||||
"pcaxis",
|
||||
"peoplecode",
|
||||
"perl",
|
||||
"php",
|
||||
"php-extras",
|
||||
"phpdoc",
|
||||
"plsql",
|
||||
"powerquery",
|
||||
"powershell",
|
||||
"processing",
|
||||
"prolog",
|
||||
"promql",
|
||||
"properties",
|
||||
"protobuf",
|
||||
"psl",
|
||||
"pug",
|
||||
"puppet",
|
||||
"pure",
|
||||
"purebasic",
|
||||
"purescript",
|
||||
"python",
|
||||
"q",
|
||||
"qml",
|
||||
"qore",
|
||||
"qsharp",
|
||||
"r",
|
||||
"racket",
|
||||
"reason",
|
||||
"regex",
|
||||
"rego",
|
||||
"renpy",
|
||||
"rest",
|
||||
"rip",
|
||||
"roboconf",
|
||||
"robotframework",
|
||||
"ruby",
|
||||
"rust",
|
||||
"sas",
|
||||
"sass",
|
||||
"scala",
|
||||
"scheme",
|
||||
"scss",
|
||||
"shell-session",
|
||||
"smali",
|
||||
"smalltalk",
|
||||
"smarty",
|
||||
"sml",
|
||||
"solidity",
|
||||
"solution-file",
|
||||
"soy",
|
||||
"sparql",
|
||||
"splunk-spl",
|
||||
"sqf",
|
||||
"sql",
|
||||
"squirrel",
|
||||
"stan",
|
||||
"stylus",
|
||||
"swift",
|
||||
"systemd",
|
||||
"t4-cs",
|
||||
"t4-templating",
|
||||
"t4-vb",
|
||||
"tap",
|
||||
"tcl",
|
||||
"textile",
|
||||
"toml",
|
||||
"tremor",
|
||||
"tsx",
|
||||
"tt2",
|
||||
"turtle",
|
||||
"twig",
|
||||
"typescript",
|
||||
"typoscript",
|
||||
"unrealscript",
|
||||
"uorazor",
|
||||
"uri",
|
||||
"v",
|
||||
"vala",
|
||||
"vbnet",
|
||||
"velocity",
|
||||
"verilog",
|
||||
"vhdl",
|
||||
"vim",
|
||||
"visual-basic",
|
||||
"warpscript",
|
||||
"wasm",
|
||||
"web-idl",
|
||||
"wiki",
|
||||
"wolfram",
|
||||
"wren",
|
||||
"xeora",
|
||||
"xml-doc",
|
||||
"xojo",
|
||||
"xquery",
|
||||
"yaml",
|
||||
"yang",
|
||||
"zig",
|
||||
],
|
||||
Var[
|
||||
Literal[
|
||||
"abap",
|
||||
"abnf",
|
||||
"actionscript",
|
||||
"ada",
|
||||
"agda",
|
||||
"al",
|
||||
"antlr4",
|
||||
"apacheconf",
|
||||
"apex",
|
||||
"apl",
|
||||
"applescript",
|
||||
"aql",
|
||||
"arduino",
|
||||
"arff",
|
||||
"asciidoc",
|
||||
"asm6502",
|
||||
"asmatmel",
|
||||
"aspnet",
|
||||
"autohotkey",
|
||||
"autoit",
|
||||
"avisynth",
|
||||
"avro-idl",
|
||||
"bash",
|
||||
"basic",
|
||||
"batch",
|
||||
"bbcode",
|
||||
"bicep",
|
||||
"birb",
|
||||
"bison",
|
||||
"bnf",
|
||||
"brainfuck",
|
||||
"brightscript",
|
||||
"bro",
|
||||
"bsl",
|
||||
"c",
|
||||
"cfscript",
|
||||
"chaiscript",
|
||||
"cil",
|
||||
"clike",
|
||||
"clojure",
|
||||
"cmake",
|
||||
"cobol",
|
||||
"coffeescript",
|
||||
"concurnas",
|
||||
"coq",
|
||||
"core",
|
||||
"cpp",
|
||||
"crystal",
|
||||
"csharp",
|
||||
"cshtml",
|
||||
"csp",
|
||||
"css",
|
||||
"css-extras",
|
||||
"csv",
|
||||
"cypher",
|
||||
"d",
|
||||
"dart",
|
||||
"dataweave",
|
||||
"dax",
|
||||
"dhall",
|
||||
"diff",
|
||||
"django",
|
||||
"dns-zone-file",
|
||||
"docker",
|
||||
"dot",
|
||||
"ebnf",
|
||||
"editorconfig",
|
||||
"eiffel",
|
||||
"ejs",
|
||||
"elixir",
|
||||
"elm",
|
||||
"erb",
|
||||
"erlang",
|
||||
"etlua",
|
||||
"excel-formula",
|
||||
"factor",
|
||||
"false",
|
||||
"firestore-security-rules",
|
||||
"flow",
|
||||
"fortran",
|
||||
"fsharp",
|
||||
"ftl",
|
||||
"gap",
|
||||
"gcode",
|
||||
"gdscript",
|
||||
"gedcom",
|
||||
"gherkin",
|
||||
"git",
|
||||
"glsl",
|
||||
"gml",
|
||||
"gn",
|
||||
"go",
|
||||
"go-module",
|
||||
"graphql",
|
||||
"groovy",
|
||||
"haml",
|
||||
"handlebars",
|
||||
"haskell",
|
||||
"haxe",
|
||||
"hcl",
|
||||
"hlsl",
|
||||
"hoon",
|
||||
"hpkp",
|
||||
"hsts",
|
||||
"http",
|
||||
"ichigojam",
|
||||
"icon",
|
||||
"icu-message-format",
|
||||
"idris",
|
||||
"iecst",
|
||||
"ignore",
|
||||
"index",
|
||||
"inform7",
|
||||
"ini",
|
||||
"io",
|
||||
"j",
|
||||
"java",
|
||||
"javadoc",
|
||||
"javadoclike",
|
||||
"javascript",
|
||||
"javastacktrace",
|
||||
"jexl",
|
||||
"jolie",
|
||||
"jq",
|
||||
"js-extras",
|
||||
"js-templates",
|
||||
"jsdoc",
|
||||
"json",
|
||||
"json5",
|
||||
"jsonp",
|
||||
"jsstacktrace",
|
||||
"jsx",
|
||||
"julia",
|
||||
"keepalived",
|
||||
"keyman",
|
||||
"kotlin",
|
||||
"kumir",
|
||||
"kusto",
|
||||
"latex",
|
||||
"latte",
|
||||
"less",
|
||||
"lilypond",
|
||||
"liquid",
|
||||
"lisp",
|
||||
"livescript",
|
||||
"llvm",
|
||||
"log",
|
||||
"lolcode",
|
||||
"lua",
|
||||
"magma",
|
||||
"makefile",
|
||||
"markdown",
|
||||
"markup",
|
||||
"markup-templating",
|
||||
"matlab",
|
||||
"maxscript",
|
||||
"mel",
|
||||
"mermaid",
|
||||
"mizar",
|
||||
"mongodb",
|
||||
"monkey",
|
||||
"moonscript",
|
||||
"n1ql",
|
||||
"n4js",
|
||||
"nand2tetris-hdl",
|
||||
"naniscript",
|
||||
"nasm",
|
||||
"neon",
|
||||
"nevod",
|
||||
"nginx",
|
||||
"nim",
|
||||
"nix",
|
||||
"nsis",
|
||||
"objectivec",
|
||||
"ocaml",
|
||||
"opencl",
|
||||
"openqasm",
|
||||
"oz",
|
||||
"parigp",
|
||||
"parser",
|
||||
"pascal",
|
||||
"pascaligo",
|
||||
"pcaxis",
|
||||
"peoplecode",
|
||||
"perl",
|
||||
"php",
|
||||
"php-extras",
|
||||
"phpdoc",
|
||||
"plsql",
|
||||
"powerquery",
|
||||
"powershell",
|
||||
"processing",
|
||||
"prolog",
|
||||
"promql",
|
||||
"properties",
|
||||
"protobuf",
|
||||
"psl",
|
||||
"pug",
|
||||
"puppet",
|
||||
"pure",
|
||||
"purebasic",
|
||||
"purescript",
|
||||
"python",
|
||||
"q",
|
||||
"qml",
|
||||
"qore",
|
||||
"qsharp",
|
||||
"r",
|
||||
"racket",
|
||||
"reason",
|
||||
"regex",
|
||||
"rego",
|
||||
"renpy",
|
||||
"rest",
|
||||
"rip",
|
||||
"roboconf",
|
||||
"robotframework",
|
||||
"ruby",
|
||||
"rust",
|
||||
"sas",
|
||||
"sass",
|
||||
"scala",
|
||||
"scheme",
|
||||
"scss",
|
||||
"shell-session",
|
||||
"smali",
|
||||
"smalltalk",
|
||||
"smarty",
|
||||
"sml",
|
||||
"solidity",
|
||||
"solution-file",
|
||||
"soy",
|
||||
"sparql",
|
||||
"splunk-spl",
|
||||
"sqf",
|
||||
"sql",
|
||||
"squirrel",
|
||||
"stan",
|
||||
"stylus",
|
||||
"swift",
|
||||
"systemd",
|
||||
"t4-cs",
|
||||
"t4-templating",
|
||||
"t4-vb",
|
||||
"tap",
|
||||
"tcl",
|
||||
"textile",
|
||||
"toml",
|
||||
"tremor",
|
||||
"tsx",
|
||||
"tt2",
|
||||
"turtle",
|
||||
"twig",
|
||||
"typescript",
|
||||
"typoscript",
|
||||
"unrealscript",
|
||||
"uorazor",
|
||||
"uri",
|
||||
"v",
|
||||
"vala",
|
||||
"vbnet",
|
||||
"velocity",
|
||||
"verilog",
|
||||
"vhdl",
|
||||
"vim",
|
||||
"visual-basic",
|
||||
"warpscript",
|
||||
"wasm",
|
||||
"web-idl",
|
||||
"wiki",
|
||||
"wolfram",
|
||||
"wren",
|
||||
"xeora",
|
||||
"xml-doc",
|
||||
"xojo",
|
||||
"xquery",
|
||||
"yaml",
|
||||
"yang",
|
||||
"zig",
|
||||
]
|
||||
],
|
||||
]
|
||||
] = None,
|
||||
code: Optional[Union[Var[str], str]] = None,
|
||||
show_line_numbers: Optional[Union[Var[bool], bool]] = None,
|
||||
starting_line_number: Optional[Union[Var[int], int]] = None,
|
||||
wrap_long_lines: Optional[Union[Var[bool], bool]] = None,
|
||||
custom_style: Optional[Dict[str, Union[str, Var, Color]]] = None,
|
||||
code_tag_props: Optional[Union[Dict[str, str], Var[Dict[str, str]]]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
class_name: Optional[Any] = None,
|
||||
autofocus: Optional[bool] = None,
|
||||
custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
|
||||
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_context_menu: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_double_click: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_mouse_down: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_enter: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_leave: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_move: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_out: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_over: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_mouse_up: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
|
||||
on_unmount: Optional[
|
||||
Union[EventHandler, EventSpec, list, Callable, Var]
|
||||
] = None,
|
||||
**props,
|
||||
) -> "CodeBlock":
|
||||
"""Create a text component.
|
||||
|
||||
Args:
|
||||
*children: The children of the component.
|
||||
can_copy: Whether a copy button should appears.
|
||||
copy_button: A custom copy button to override the default one.
|
||||
theme: The theme to use ("light" or "dark").
|
||||
language: The language to use.
|
||||
code: The code to display.
|
||||
show_line_numbers: If this is enabled line numbers will be shown next to the code block.
|
||||
starting_line_number: The starting line number to use.
|
||||
wrap_long_lines: Whether to wrap long lines.
|
||||
custom_style: A custom style for the code block.
|
||||
code_tag_props: Props passed down to the code tag.
|
||||
style: The style of the component.
|
||||
key: A unique key for the component.
|
||||
id: The id for the component.
|
||||
class_name: The class name for the component.
|
||||
autofocus: Whether the component should take the focus once the page is loaded
|
||||
custom_attrs: custom attribute
|
||||
**props: The props to pass to the component.
|
||||
|
||||
Returns:
|
||||
The text component.
|
||||
"""
|
||||
...
|
||||
|
||||
code_block = CodeblockNamespace()
|
||||
|
143
reflex/components/dynamic.py
Normal file
143
reflex/components/dynamic.py
Normal file
@ -0,0 +1,143 @@
|
||||
"""Components that are dynamically generated on the backend."""
|
||||
|
||||
from reflex import constants
|
||||
from reflex.utils import imports
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import Var, get_unique_variable_name
|
||||
from reflex.vars.base import VarData, transform
|
||||
|
||||
|
||||
def get_cdn_url(lib: str) -> str:
|
||||
"""Get the CDN URL for a library.
|
||||
|
||||
Args:
|
||||
lib: The library to get the CDN URL for.
|
||||
|
||||
Returns:
|
||||
The CDN URL for the library.
|
||||
"""
|
||||
return f"https://cdn.jsdelivr.net/npm/{lib}" + "/+esm"
|
||||
|
||||
|
||||
def load_dynamic_serializer():
|
||||
"""Load the serializer for dynamic components."""
|
||||
# Causes a circular import, so we import here.
|
||||
from reflex.components.component import Component
|
||||
|
||||
@serializer
|
||||
def make_component(component: Component) -> str:
|
||||
"""Generate the code for a dynamic component.
|
||||
|
||||
Args:
|
||||
component: The component to generate code for.
|
||||
|
||||
Returns:
|
||||
The generated code
|
||||
"""
|
||||
# Causes a circular import, so we import here.
|
||||
from reflex.compiler import templates, utils
|
||||
|
||||
rendered_components = {}
|
||||
# Include dynamic imports in the shared component.
|
||||
if dynamic_imports := component._get_all_dynamic_imports():
|
||||
rendered_components.update(
|
||||
{dynamic_import: None for dynamic_import in dynamic_imports}
|
||||
)
|
||||
|
||||
# Include custom code in the shared component.
|
||||
rendered_components.update(
|
||||
{code: None for code in component._get_all_custom_code()},
|
||||
)
|
||||
|
||||
rendered_components[
|
||||
templates.STATEFUL_COMPONENT.render(
|
||||
tag_name="MySSRComponent",
|
||||
memo_trigger_hooks=[],
|
||||
component=component,
|
||||
)
|
||||
] = None
|
||||
|
||||
imports = {}
|
||||
for lib, names in component._get_all_imports().items():
|
||||
if (
|
||||
not lib.startswith((".", "/"))
|
||||
and not lib.startswith("http")
|
||||
and lib != "react"
|
||||
):
|
||||
imports[get_cdn_url(lib)] = names
|
||||
else:
|
||||
imports[lib] = names
|
||||
|
||||
module_code_lines = templates.STATEFUL_COMPONENTS.render(
|
||||
imports=utils.compile_imports(imports),
|
||||
memoized_code="\n".join(rendered_components),
|
||||
).splitlines()[1:]
|
||||
|
||||
# Rewrite imports from `/` to destructure from window
|
||||
for ix, line in enumerate(module_code_lines[:]):
|
||||
if line.startswith("import "):
|
||||
if 'from "/' in line:
|
||||
module_code_lines[ix] = (
|
||||
line.replace("import ", "const ", 1).replace(
|
||||
" from ", " = window['__reflex'][", 1
|
||||
)
|
||||
+ "]"
|
||||
)
|
||||
elif 'from "react"' in line:
|
||||
module_code_lines[ix] = line.replace(
|
||||
"import ", "const ", 1
|
||||
).replace(' from "react"', " = window.__reflex.react", 1)
|
||||
if line.startswith("export function"):
|
||||
module_code_lines[ix] = line.replace(
|
||||
"export function", "export default function", 1
|
||||
)
|
||||
|
||||
module_code_lines.insert(0, "const React = window.__reflex.react;")
|
||||
|
||||
return "//__reflex_evaluate\n" + "\n".join(module_code_lines)
|
||||
|
||||
@transform
|
||||
def evaluate_component(js_string: Var[str]) -> Var[Component]:
|
||||
"""Evaluate a component.
|
||||
|
||||
Args:
|
||||
js_string: The JavaScript string to evaluate.
|
||||
|
||||
Returns:
|
||||
The evaluated JavaScript string.
|
||||
"""
|
||||
unique_var_name = get_unique_variable_name()
|
||||
|
||||
return js_string._replace(
|
||||
_js_expr=unique_var_name,
|
||||
_var_type=Component,
|
||||
merge_var_data=VarData.merge(
|
||||
VarData(
|
||||
imports={
|
||||
f"/{constants.Dirs.STATE_PATH}": [
|
||||
imports.ImportVar(tag="evalReactComponent"),
|
||||
],
|
||||
"react": [
|
||||
imports.ImportVar(tag="useState"),
|
||||
imports.ImportVar(tag="useEffect"),
|
||||
],
|
||||
},
|
||||
hooks={
|
||||
f"const [{unique_var_name}, set_{unique_var_name}] = useState(null);": None,
|
||||
"useEffect(() => {"
|
||||
"let isMounted = true;"
|
||||
f"evalReactComponent({str(js_string)})"
|
||||
".then((component) => {"
|
||||
"if (isMounted) {"
|
||||
f"set_{unique_var_name}(component);"
|
||||
"}"
|
||||
"});"
|
||||
"return () => {"
|
||||
"isMounted = false;"
|
||||
"};"
|
||||
"}"
|
||||
f", [{str(js_string)}]);": None,
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
@ -111,6 +111,7 @@ class PackageJson(SimpleNamespace):
|
||||
PATH = "package.json"
|
||||
|
||||
DEPENDENCIES = {
|
||||
"@babel/standalone": "7.25.3",
|
||||
"@emotion/react": "11.11.1",
|
||||
"axios": "1.6.0",
|
||||
"json5": "2.2.3",
|
||||
|
@ -40,6 +40,7 @@ from reflex.vars.base import (
|
||||
DynamicRouteVar,
|
||||
Var,
|
||||
computed_var,
|
||||
dispatch,
|
||||
is_computed_var,
|
||||
)
|
||||
|
||||
@ -336,6 +337,29 @@ class EventHandlerSetVar(EventHandler):
|
||||
return super().__call__(*args)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pydantic.v1.fields import ModelField
|
||||
|
||||
|
||||
def get_var_for_field(cls: Type[BaseState], f: ModelField):
|
||||
"""Get a Var instance for a Pydantic field.
|
||||
|
||||
Args:
|
||||
cls: The state class.
|
||||
f: The Pydantic field.
|
||||
|
||||
Returns:
|
||||
The Var instance.
|
||||
"""
|
||||
field_name = format.format_state_name(cls.get_full_name()) + "." + f.name
|
||||
|
||||
return dispatch(
|
||||
field_name=field_name,
|
||||
var_data=VarData.from_state(cls, f.name),
|
||||
result_var_type=f.outer_type_,
|
||||
)
|
||||
|
||||
|
||||
class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
"""The state of the app."""
|
||||
|
||||
@ -556,11 +580,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
|
||||
# Set the base and computed vars.
|
||||
cls.base_vars = {
|
||||
f.name: Var(
|
||||
_js_expr=format.format_state_name(cls.get_full_name()) + "." + f.name,
|
||||
_var_type=f.outer_type_,
|
||||
_var_data=VarData.from_state(cls),
|
||||
).guess_type()
|
||||
f.name: get_var_for_field(cls, f)
|
||||
for f in cls.get_fields().values()
|
||||
if f.name not in cls.get_skip_vars()
|
||||
}
|
||||
@ -948,7 +968,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
var = Var(
|
||||
_js_expr=format.format_state_name(cls.get_full_name()) + "." + name,
|
||||
_var_type=type_,
|
||||
_var_data=VarData.from_state(cls),
|
||||
_var_data=VarData.from_state(cls, name),
|
||||
).guess_type()
|
||||
|
||||
# add the pydantic field dynamically (must be done before _init_var)
|
||||
@ -974,10 +994,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
Args:
|
||||
prop: The var instance to set.
|
||||
"""
|
||||
acutal_var_name = (
|
||||
prop._js_expr if "." not in prop._js_expr else prop._js_expr.split(".")[-1]
|
||||
)
|
||||
setattr(cls, acutal_var_name, prop)
|
||||
setattr(cls, prop._var_field_name, prop)
|
||||
|
||||
@classmethod
|
||||
def _create_event_handler(cls, fn):
|
||||
@ -1017,10 +1034,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
prop: The var to set the default value for.
|
||||
"""
|
||||
# Get the pydantic field for the var.
|
||||
if "." in prop._js_expr:
|
||||
field = cls.get_fields()[prop._js_expr.split(".")[-1]]
|
||||
else:
|
||||
field = cls.get_fields()[prop._js_expr]
|
||||
field = cls.get_fields()[prop._var_field_name]
|
||||
if field.required:
|
||||
default_value = prop.get_default_value()
|
||||
if default_value is not None:
|
||||
@ -1761,11 +1775,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
.union(self._always_dirty_computed_vars)
|
||||
)
|
||||
|
||||
subdelta = {
|
||||
prop: getattr(self, prop)
|
||||
subdelta: Dict[str, Any] = {
|
||||
prop: self.get_value(getattr(self, prop))
|
||||
for prop in delta_vars
|
||||
if not types.is_backend_base_variable(prop, type(self))
|
||||
}
|
||||
|
||||
if len(subdelta) > 0:
|
||||
delta[self.get_full_name()] = subdelta
|
||||
|
||||
|
@ -672,6 +672,8 @@ def format_library_name(library_fullname: str):
|
||||
Returns:
|
||||
The name without the @version if it was part of the name
|
||||
"""
|
||||
if library_fullname.startswith("https://"):
|
||||
return library_fullname
|
||||
lib, at, version = library_fullname.rpartition("@")
|
||||
if not lib:
|
||||
lib = at + version
|
||||
|
@ -20,6 +20,7 @@ from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
FrozenSet,
|
||||
Generic,
|
||||
Iterable,
|
||||
List,
|
||||
@ -72,6 +73,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
VAR_TYPE = TypeVar("VAR_TYPE", covariant=True)
|
||||
OTHER_VAR_TYPE = TypeVar("OTHER_VAR_TYPE")
|
||||
|
||||
warnings.filterwarnings("ignore", message="fields may not start with an underscore")
|
||||
|
||||
@ -119,6 +121,17 @@ class Var(Generic[VAR_TYPE]):
|
||||
"""
|
||||
return self._js_expr
|
||||
|
||||
@property
|
||||
def _var_field_name(self) -> str:
|
||||
"""The name of the field.
|
||||
|
||||
Returns:
|
||||
The name of the field.
|
||||
"""
|
||||
var_data = self._get_all_var_data()
|
||||
field_name = var_data.field_name if var_data else None
|
||||
return field_name or self._js_expr
|
||||
|
||||
@property
|
||||
@deprecated("Use `_js_expr` instead.")
|
||||
def _var_name_unwrapped(self) -> str:
|
||||
@ -181,7 +194,19 @@ class Var(Generic[VAR_TYPE]):
|
||||
and self._get_all_var_data() == other._get_all_var_data()
|
||||
)
|
||||
|
||||
def _replace(self, merge_var_data=None, **kwargs: Any):
|
||||
@overload
|
||||
def _replace(
|
||||
self, _var_type: Type[OTHER_VAR_TYPE], merge_var_data=None, **kwargs: Any
|
||||
) -> Var[OTHER_VAR_TYPE]: ...
|
||||
|
||||
@overload
|
||||
def _replace(
|
||||
self, _var_type: GenericType | None = None, merge_var_data=None, **kwargs: Any
|
||||
) -> Self: ...
|
||||
|
||||
def _replace(
|
||||
self, _var_type: GenericType | None = None, merge_var_data=None, **kwargs: Any
|
||||
) -> Self | Var:
|
||||
"""Make a copy of this Var with updated fields.
|
||||
|
||||
Args:
|
||||
@ -205,14 +230,20 @@ class Var(Generic[VAR_TYPE]):
|
||||
"The _var_full_name_needs_state_prefix argument is not supported for Var."
|
||||
)
|
||||
|
||||
return dataclasses.replace(
|
||||
value_with_replaced = dataclasses.replace(
|
||||
self,
|
||||
_var_type=_var_type or self._var_type,
|
||||
_var_data=VarData.merge(
|
||||
kwargs.get("_var_data", self._var_data), merge_var_data
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
if (js_expr := kwargs.get("_js_expr", None)) is not None:
|
||||
object.__setattr__(value_with_replaced, "_js_expr", js_expr)
|
||||
|
||||
return value_with_replaced
|
||||
|
||||
@classmethod
|
||||
def create(
|
||||
cls,
|
||||
@ -566,8 +597,7 @@ class Var(Generic[VAR_TYPE]):
|
||||
Returns:
|
||||
The name of the setter function.
|
||||
"""
|
||||
var_name_parts = self._js_expr.split(".")
|
||||
setter = constants.SETTER_PREFIX + var_name_parts[-1]
|
||||
setter = constants.SETTER_PREFIX + self._var_field_name
|
||||
var_data = self._get_all_var_data()
|
||||
if var_data is None:
|
||||
return setter
|
||||
@ -581,7 +611,7 @@ class Var(Generic[VAR_TYPE]):
|
||||
Returns:
|
||||
A function that that creates a setter for the var.
|
||||
"""
|
||||
actual_name = self._js_expr.split(".")[-1]
|
||||
actual_name = self._var_field_name
|
||||
|
||||
def setter(state: BaseState, value: Any):
|
||||
"""Get the setter for the var.
|
||||
@ -623,7 +653,9 @@ class Var(Generic[VAR_TYPE]):
|
||||
return StateOperation.create(
|
||||
formatted_state_name,
|
||||
self,
|
||||
_var_data=VarData.merge(VarData.from_state(state), self._var_data),
|
||||
_var_data=VarData.merge(
|
||||
VarData.from_state(state, self._js_expr), self._var_data
|
||||
),
|
||||
).guess_type()
|
||||
|
||||
def __eq__(self, other: Var | Any) -> BooleanVar:
|
||||
@ -1706,12 +1738,18 @@ class ComputedVar(Var[RETURN_TYPE]):
|
||||
while self._js_expr in state_where_defined.inherited_vars:
|
||||
state_where_defined = state_where_defined.get_parent_state()
|
||||
|
||||
return self._replace(
|
||||
_js_expr=format_state_name(state_where_defined.get_full_name())
|
||||
field_name = (
|
||||
format_state_name(state_where_defined.get_full_name())
|
||||
+ "."
|
||||
+ self._js_expr,
|
||||
merge_var_data=VarData.from_state(state_where_defined),
|
||||
).guess_type()
|
||||
+ self._js_expr
|
||||
)
|
||||
|
||||
return dispatch(
|
||||
field_name,
|
||||
var_data=VarData.from_state(state_where_defined, self._js_expr),
|
||||
result_var_type=self._var_type,
|
||||
existing_var=self,
|
||||
)
|
||||
|
||||
if not self._cache:
|
||||
return self.fget(instance)
|
||||
@ -2339,6 +2377,9 @@ class VarData:
|
||||
# The name of the enclosing state.
|
||||
state: str = dataclasses.field(default="")
|
||||
|
||||
# The name of the field in the state.
|
||||
field_name: str = dataclasses.field(default="")
|
||||
|
||||
# Imports needed to render this var
|
||||
imports: ImmutableParsedImportDict = dataclasses.field(default_factory=tuple)
|
||||
|
||||
@ -2348,6 +2389,7 @@ class VarData:
|
||||
def __init__(
|
||||
self,
|
||||
state: str = "",
|
||||
field_name: str = "",
|
||||
imports: ImportDict | ParsedImportDict | None = None,
|
||||
hooks: dict[str, None] | None = None,
|
||||
):
|
||||
@ -2355,6 +2397,7 @@ class VarData:
|
||||
|
||||
Args:
|
||||
state: The name of the enclosing state.
|
||||
field_name: The name of the field in the state.
|
||||
imports: Imports needed to render this var.
|
||||
hooks: Hooks that need to be present in the component to render this var.
|
||||
"""
|
||||
@ -2364,6 +2407,7 @@ class VarData:
|
||||
)
|
||||
)
|
||||
object.__setattr__(self, "state", state)
|
||||
object.__setattr__(self, "field_name", field_name)
|
||||
object.__setattr__(self, "imports", immutable_imports)
|
||||
object.__setattr__(self, "hooks", tuple(hooks or {}))
|
||||
|
||||
@ -2386,12 +2430,14 @@ class VarData:
|
||||
The merged var data object.
|
||||
"""
|
||||
state = ""
|
||||
field_name = ""
|
||||
_imports = {}
|
||||
hooks = {}
|
||||
for var_data in others:
|
||||
if var_data is None:
|
||||
continue
|
||||
state = state or var_data.state
|
||||
field_name = field_name or var_data.field_name
|
||||
_imports = imports.merge_imports(_imports, var_data.imports)
|
||||
hooks.update(
|
||||
var_data.hooks
|
||||
@ -2399,9 +2445,10 @@ class VarData:
|
||||
else {k: None for k in var_data.hooks}
|
||||
)
|
||||
|
||||
if state or _imports or hooks:
|
||||
if state or _imports or hooks or field_name:
|
||||
return VarData(
|
||||
state=state,
|
||||
field_name=field_name,
|
||||
imports=_imports,
|
||||
hooks=hooks,
|
||||
)
|
||||
@ -2413,38 +2460,15 @@ class VarData:
|
||||
Returns:
|
||||
True if any field is set to a non-default value.
|
||||
"""
|
||||
return bool(self.state or self.imports or self.hooks)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
"""Check if two var data objects are equal.
|
||||
|
||||
Args:
|
||||
other: The other var data object to compare.
|
||||
|
||||
Returns:
|
||||
True if all fields are equal and collapsed imports are equal.
|
||||
"""
|
||||
if not isinstance(other, VarData):
|
||||
return False
|
||||
|
||||
# Don't compare interpolations - that's added in by the decoder, and
|
||||
# not part of the vardata itself.
|
||||
return (
|
||||
self.state == other.state
|
||||
and self.hooks
|
||||
== (
|
||||
other.hooks if isinstance(other, VarData) else tuple(other.hooks.keys())
|
||||
)
|
||||
and imports.collapse_imports(self.imports)
|
||||
== imports.collapse_imports(other.imports)
|
||||
)
|
||||
return bool(self.state or self.imports or self.hooks or self.field_name)
|
||||
|
||||
@classmethod
|
||||
def from_state(cls, state: Type[BaseState] | str) -> VarData:
|
||||
def from_state(cls, state: Type[BaseState] | str, field_name: str = "") -> VarData:
|
||||
"""Set the state of the var.
|
||||
|
||||
Args:
|
||||
state: The state to set or the full name of the state.
|
||||
field_name: The name of the field in the state. Optional.
|
||||
|
||||
Returns:
|
||||
The var with the set state.
|
||||
@ -2452,8 +2476,9 @@ class VarData:
|
||||
from reflex.utils import format
|
||||
|
||||
state_name = state if isinstance(state, str) else state.get_full_name()
|
||||
new_var_data = VarData(
|
||||
return VarData(
|
||||
state=state_name,
|
||||
field_name=field_name,
|
||||
hooks={
|
||||
"const {0} = useContext(StateContexts.{0})".format(
|
||||
format.format_state_name(state_name)
|
||||
@ -2464,7 +2489,6 @@ class VarData:
|
||||
"react": [ImportVar(tag="useContext")],
|
||||
},
|
||||
)
|
||||
return new_var_data
|
||||
|
||||
|
||||
def _decode_var_immutable(value: str) -> tuple[VarData | None, str]:
|
||||
@ -2561,3 +2585,238 @@ REPLACED_NAMES = {
|
||||
"set_state": "_var_set_state",
|
||||
"deps": "_deps",
|
||||
}
|
||||
|
||||
|
||||
dispatchers: Dict[GenericType, Callable[[Var], Var]] = {}
|
||||
|
||||
|
||||
def transform(fn: Callable[[Var], Var]) -> Callable[[Var], Var]:
|
||||
"""Register a function to transform a Var.
|
||||
|
||||
Args:
|
||||
fn: The function to register.
|
||||
|
||||
Returns:
|
||||
The decorator.
|
||||
|
||||
Raises:
|
||||
TypeError: If the return type of the function is not a Var.
|
||||
TypeError: If the Var return type does not have a generic type.
|
||||
ValueError: If a function for the generic type is already registered.
|
||||
"""
|
||||
return_type = fn.__annotations__["return"]
|
||||
|
||||
origin = get_origin(return_type)
|
||||
|
||||
if origin is not Var:
|
||||
raise TypeError(
|
||||
f"Expected return type of {fn.__name__} to be a Var, got {origin}."
|
||||
)
|
||||
|
||||
generic_args = get_args(return_type)
|
||||
|
||||
if not generic_args:
|
||||
raise TypeError(
|
||||
f"Expected Var return type of {fn.__name__} to have a generic type."
|
||||
)
|
||||
|
||||
generic_type = get_origin(generic_args[0]) or generic_args[0]
|
||||
|
||||
if generic_type in dispatchers:
|
||||
raise ValueError(f"Function for {generic_type} already registered.")
|
||||
|
||||
dispatchers[generic_type] = fn
|
||||
|
||||
return fn
|
||||
|
||||
|
||||
def generic_type_to_actual_type_map(
|
||||
generic_type: GenericType, actual_type: GenericType
|
||||
) -> Dict[TypeVar, GenericType]:
|
||||
"""Map the generic type to the actual type.
|
||||
|
||||
Args:
|
||||
generic_type: The generic type.
|
||||
actual_type: The actual type.
|
||||
|
||||
Returns:
|
||||
The mapping of type variables to actual types.
|
||||
|
||||
Raises:
|
||||
TypeError: If the generic type and actual type do not match.
|
||||
TypeError: If the number of generic arguments and actual arguments do not match.
|
||||
"""
|
||||
generic_origin = get_origin(generic_type) or generic_type
|
||||
actual_origin = get_origin(actual_type) or actual_type
|
||||
|
||||
if generic_origin is not actual_origin:
|
||||
if isinstance(generic_origin, TypeVar):
|
||||
return {generic_origin: actual_origin}
|
||||
raise TypeError(
|
||||
f"Type mismatch: expected {generic_origin}, got {actual_origin}."
|
||||
)
|
||||
|
||||
generic_args = get_args(generic_type)
|
||||
actual_args = get_args(actual_type)
|
||||
|
||||
if len(generic_args) != len(actual_args):
|
||||
raise TypeError(
|
||||
f"Number of generic arguments mismatch: expected {len(generic_args)}, got {len(actual_args)}."
|
||||
)
|
||||
|
||||
# call recursively for nested generic types and merge the results
|
||||
return {
|
||||
k: v
|
||||
for generic_arg, actual_arg in zip(generic_args, actual_args)
|
||||
for k, v in generic_type_to_actual_type_map(generic_arg, actual_arg).items()
|
||||
}
|
||||
|
||||
|
||||
def resolve_generic_type_with_mapping(
|
||||
generic_type: GenericType, type_mapping: Dict[TypeVar, GenericType]
|
||||
):
|
||||
"""Resolve a generic type with a type mapping.
|
||||
|
||||
Args:
|
||||
generic_type: The generic type.
|
||||
type_mapping: The type mapping.
|
||||
|
||||
Returns:
|
||||
The resolved generic type.
|
||||
"""
|
||||
if isinstance(generic_type, TypeVar):
|
||||
return type_mapping.get(generic_type, generic_type)
|
||||
|
||||
generic_origin = get_origin(generic_type) or generic_type
|
||||
|
||||
generic_args = get_args(generic_type)
|
||||
|
||||
if not generic_args:
|
||||
return generic_type
|
||||
|
||||
mapping_for_older_python = {
|
||||
list: List,
|
||||
set: Set,
|
||||
dict: Dict,
|
||||
tuple: Tuple,
|
||||
frozenset: FrozenSet,
|
||||
}
|
||||
|
||||
return mapping_for_older_python.get(generic_origin, generic_origin)[
|
||||
tuple(
|
||||
resolve_generic_type_with_mapping(arg, type_mapping) for arg in generic_args
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def resolve_arg_type_from_return_type(
|
||||
arg_type: GenericType, return_type: GenericType, actual_return_type: GenericType
|
||||
) -> GenericType:
|
||||
"""Resolve the argument type from the return type.
|
||||
|
||||
Args:
|
||||
arg_type: The argument type.
|
||||
return_type: The return type.
|
||||
actual_return_type: The requested return type.
|
||||
|
||||
Returns:
|
||||
The argument type without the generics that are resolved.
|
||||
"""
|
||||
return resolve_generic_type_with_mapping(
|
||||
arg_type, generic_type_to_actual_type_map(return_type, actual_return_type)
|
||||
)
|
||||
|
||||
|
||||
def dispatch(
|
||||
field_name: str,
|
||||
var_data: VarData,
|
||||
result_var_type: GenericType,
|
||||
existing_var: Var | None = None,
|
||||
) -> Var:
|
||||
"""Dispatch a Var to the appropriate transformation function.
|
||||
|
||||
Args:
|
||||
field_name: The name of the field.
|
||||
var_data: The VarData associated with the Var.
|
||||
result_var_type: The type of the Var.
|
||||
existing_var: The existing Var to transform. Optional.
|
||||
|
||||
Returns:
|
||||
The transformed Var.
|
||||
|
||||
Raises:
|
||||
TypeError: If the return type of the function is not a Var.
|
||||
TypeError: If the Var return type does not have a generic type.
|
||||
TypeError: If the first argument of the function is not a Var.
|
||||
TypeError: If the first argument of the function does not have a generic type
|
||||
"""
|
||||
result_origin_var_type = get_origin(result_var_type) or result_var_type
|
||||
|
||||
if result_origin_var_type in dispatchers:
|
||||
fn = dispatchers[result_origin_var_type]
|
||||
fn_first_arg_type = list(inspect.signature(fn).parameters.values())[
|
||||
0
|
||||
].annotation
|
||||
|
||||
fn_return = inspect.signature(fn).return_annotation
|
||||
|
||||
fn_return_origin = get_origin(fn_return) or fn_return
|
||||
|
||||
if fn_return_origin is not Var:
|
||||
raise TypeError(
|
||||
f"Expected return type of {fn.__name__} to be a Var, got {fn_return}."
|
||||
)
|
||||
|
||||
fn_return_generic_args = get_args(fn_return)
|
||||
|
||||
if not fn_return_generic_args:
|
||||
raise TypeError(f"Expected generic type of {fn_return} to be a type.")
|
||||
|
||||
arg_origin = get_origin(fn_first_arg_type) or fn_first_arg_type
|
||||
|
||||
if arg_origin is not Var:
|
||||
raise TypeError(
|
||||
f"Expected first argument of {fn.__name__} to be a Var, got {fn_first_arg_type}."
|
||||
)
|
||||
|
||||
arg_generic_args = get_args(fn_first_arg_type)
|
||||
|
||||
if not arg_generic_args:
|
||||
raise TypeError(
|
||||
f"Expected generic type of {fn_first_arg_type} to be a type."
|
||||
)
|
||||
|
||||
arg_type = arg_generic_args[0]
|
||||
fn_return_type = fn_return_generic_args[0]
|
||||
|
||||
var = (
|
||||
Var(
|
||||
field_name,
|
||||
_var_data=var_data,
|
||||
_var_type=resolve_arg_type_from_return_type(
|
||||
arg_type, fn_return_type, result_var_type
|
||||
),
|
||||
).guess_type()
|
||||
if existing_var is None
|
||||
else existing_var._replace(
|
||||
_var_type=resolve_arg_type_from_return_type(
|
||||
arg_type, fn_return_type, result_var_type
|
||||
),
|
||||
_var_data=var_data,
|
||||
_js_expr=field_name,
|
||||
).guess_type()
|
||||
)
|
||||
|
||||
return fn(var)
|
||||
|
||||
if existing_var is not None:
|
||||
return existing_var._replace(
|
||||
_js_expr=field_name,
|
||||
_var_data=var_data,
|
||||
_var_type=result_var_type,
|
||||
).guess_type()
|
||||
return Var(
|
||||
field_name,
|
||||
_var_data=var_data,
|
||||
_var_type=result_var_type,
|
||||
).guess_type()
|
||||
|
@ -1,10 +1,11 @@
|
||||
import pytest
|
||||
|
||||
from reflex.components.datadisplay.code import CodeBlock
|
||||
from reflex.components.datadisplay.code import CodeBlock, Theme
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"theme, expected", [("light", '"one-light"'), ("dark", '"one-dark"')]
|
||||
"theme, expected",
|
||||
[(Theme.one_light, "oneLight"), (Theme.one_dark, "oneDark")],
|
||||
)
|
||||
def test_code_light_dark_theme(theme, expected):
|
||||
code_block = CodeBlock.create(theme=theme)
|
||||
|
@ -2520,7 +2520,7 @@ def test_json_dumps_with_mutables():
|
||||
items: List[Foo] = [Foo()]
|
||||
|
||||
dict_val = MutableContainsBase().dict()
|
||||
assert isinstance(dict_val[MutableContainsBase.get_full_name()]["items"][0], dict)
|
||||
assert isinstance(dict_val[MutableContainsBase.get_full_name()]["items"][0], Foo)
|
||||
val = json_dumps(dict_val)
|
||||
f_items = '[{"tags": ["123", "456"]}]'
|
||||
f_formatted_router = str(formatted_router).replace("'", '"')
|
||||
|
@ -6,6 +6,7 @@ from typing import Dict, List, Optional, Set, Tuple, Union, cast
|
||||
import pytest
|
||||
from pandas import DataFrame
|
||||
|
||||
import reflex as rx
|
||||
from reflex.base import Base
|
||||
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
||||
from reflex.state import BaseState
|
||||
@ -1052,6 +1053,29 @@ def test_object_operations():
|
||||
)
|
||||
|
||||
|
||||
def test_var_component():
|
||||
class ComponentVarState(rx.State):
|
||||
field_var: rx.Component = rx.text("I am a field var")
|
||||
|
||||
@rx.var
|
||||
def computed_var(self) -> rx.Component:
|
||||
return rx.text("I am a computed var")
|
||||
|
||||
def has_eval_react_component(var: Var):
|
||||
var_data = var._get_all_var_data()
|
||||
assert var_data is not None
|
||||
assert any(
|
||||
any(
|
||||
imported_object.name == "evalReactComponent"
|
||||
for imported_object in imported_objects
|
||||
)
|
||||
for _, imported_objects in var_data.imports
|
||||
)
|
||||
|
||||
has_eval_react_component(ComponentVarState.field_var) # type: ignore
|
||||
has_eval_react_component(ComponentVarState.computed_var)
|
||||
|
||||
|
||||
def test_type_chains():
|
||||
object_var = LiteralObjectVar.create({"a": 1, "b": 2, "c": 3})
|
||||
assert (object_var._key_type(), object_var._value_type()) == (str, int)
|
||||
|
Loading…
Reference in New Issue
Block a user