diff --git a/app/components/CodeEditor.tsx b/app/components/CodeEditor.tsx index 18704aa..22c282a 100644 --- a/app/components/CodeEditor.tsx +++ b/app/components/CodeEditor.tsx @@ -73,7 +73,7 @@ export default function CodeEditor({ }; return ( -
+
); case "nav-tab-content": @@ -526,10 +527,12 @@ export function RenderedChildren({ children, onChange, state, + className, }: { children: Array; onChange: OnChange; state: Record; + className?: string; }) { let elements = children.map((node, idx) => { let key; @@ -538,6 +541,10 @@ export function RenderedChildren({ } else { key = `idx:${idx}`; } + let prevClassName = node.props.className || ""; + if (className && !prevClassName.includes(className)) { + node.props.className = `${className} ${prevClassName}`; + } return ( str: if value is None: return BLANK_OPTION @@ -54,17 +66,17 @@ def nav_tab_content(): return _node("nav-tab-content") -def div(**props) -> core.NestingCtx: - return tag("div", **props) +def div(*, style: StyleProp = None, **props) -> core.NestingCtx: + return tag("div", style=style, **props) def link(*, to: str, **props) -> core.NestingCtx: return _node("Link", to=to, **props) -def tag(tag_name: str, **props) -> core.NestingCtx: +def tag(tag_name: str, *, style: StyleProp = None, **props) -> core.NestingCtx: props["__reactjsxelement"] = tag_name - return _node("tag", **props) + return _node("tag", style=style, **props) def html(body: str, **props): @@ -115,11 +127,30 @@ def markdown( def _node(nodename: str, **props): + if style := props.get("style"): + selector = style.pop("selector", None) + if selector: + identifier = "." + core.md5_values(selector) + for s, rules in selector.items(): + _node("css-in-js", selector=s.replace("&", identifier), rules=rules) + props["className"] = " ".join( + filter(None, (props.get("className"), identifier)) + ) + # media = style.pop("@media", None) node = core.RenderTreeNode(name=nodename, props=props) node.mount() return core.NestingCtx(node) +def styled(css: str) -> core.RenderTreeNode: + css = dedent(css).strip() + className = "gui-" + core.md5_values(css) + selector = "." + className + css = css.replace("&", selector) + core.add_styles(className, css) + return _node("", className=className) + + def text(body: str, **props): core.RenderTreeNode( name="pre", diff --git a/py/gooey_gui/core/__init__.py b/py/gooey_gui/core/__init__.py index 602aa15..4191848 100644 --- a/py/gooey_gui/core/__init__.py +++ b/py/gooey_gui/core/__init__.py @@ -16,7 +16,14 @@ realtime_clear_subs, md5_values, ) -from .renderer import RenderTreeNode, NestingCtx, renderer, route, current_root_ctx +from .renderer import ( + RenderTreeNode, + NestingCtx, + renderer, + route, + current_root_ctx, + add_styles, +) from .state import ( get_session_state, set_session_state, diff --git a/py/gooey_gui/core/renderer.py b/py/gooey_gui/core/renderer.py index 4157f20..50a16b4 100644 --- a/py/gooey_gui/core/renderer.py +++ b/py/gooey_gui/core/renderer.py @@ -23,6 +23,10 @@ def current_root_ctx() -> "NestingCtx": return threadlocal.root_ctx +def add_styles(className: str, css: str): + threadlocal.styles[className] = css + + class RenderTreeNode(BaseModel): name: str props: ReactHTMLProps = {} @@ -108,17 +112,27 @@ def renderer( set_query_params(query_params or {}) realtime_clear_subs() threadlocal.use_state_count = 0 + threadlocal.styles = {} while True: try: root = RenderTreeNode(name="root") threadlocal.root_ctx = NestingCtx(root) with threadlocal.root_ctx: + styles_node = RenderTreeNode( + name="tag", + props=dict(__reactjsxelement="style"), + ).mount() try: ret = render() except StopException: ret = None except RedirectException as e: return RedirectResponse(e.url, status_code=e.status_code) + if threadlocal.styles: + styles_node.props["dangerouslySetInnerHTML"] = { + "__html": "\n".join(threadlocal.styles.values()) + } + print(get_subscriptions()) if isinstance(ret, Response): return ret return JSONResponse(