From 56b876fc8a5fd6017be69a3b79141f6c9eea04ea Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Wed, 13 Dec 2023 12:02:10 -0400 Subject: [PATCH 1/2] Use layout_columns --- .../templates/app-templates/dashboard/app.py | 6 +- .../app-templates/multi-page/modules.py | 56 +++++++++---------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/shiny/templates/app-templates/dashboard/app.py b/shiny/templates/app-templates/dashboard/app.py index 41983bbba..3e83227e0 100644 --- a/shiny/templates/app-templates/dashboard/app.py +++ b/shiny/templates/app-templates/dashboard/app.py @@ -30,13 +30,12 @@ def make_value_box(penguin): ), ), ui.row( - ui.layout_column_wrap( + ui.layout_columns( *[make_value_box(penguin) for penguin in species], - width=1 / 3, ) ), ui.row( - ui.layout_column_wrap( + ui.layout_columns( ui.card( ui.card_header("Summary statistics"), ui.output_data_frame("summary_statistics"), @@ -45,7 +44,6 @@ def make_value_box(penguin): ui.card_header("Penguin bills"), ui.output_plot("length_depth"), ), - width=1 / 2, ), ), ) diff --git a/shiny/templates/app-templates/multi-page/modules.py b/shiny/templates/app-templates/multi-page/modules.py index c8df0bfd2..0a3532862 100644 --- a/shiny/templates/app-templates/multi-page/modules.py +++ b/shiny/templates/app-templates/multi-page/modules.py @@ -10,22 +10,19 @@ def training_ui(): return ui.nav( "Training Dashboard", - ui.row( - ui.layout_column_wrap( - ui.card( - ui.card_header("Model Metrics"), - ui.output_plot("metric"), - ui.input_select( - "metric", - "Metric", - choices=["ROC Curve", "Precision-Recall"], - ), + ui.layout_columns( + ui.card( + ui.card_header("Model Metrics"), + ui.output_plot("metric"), + ui.input_select( + "metric", + "Metric", + choices=["ROC Curve", "Precision-Recall"], ), - ui.card( - ui.card_header("Training Scores"), - ui.output_plot("score_dist"), - ), - width=1 / 2, + ), + ui.card( + ui.card_header("Training Scores"), + ui.output_plot("score_dist"), ), ), ) @@ -54,22 +51,23 @@ def metric(): def data_view_ui(): return ui.nav( "View Data", - ui.row( - ui.layout_column_wrap( - ui.value_box( - title="Row count", - value=ui.output_text("row_count"), - theme="primary", - ), - ui.value_box( - title="Mean score", - value=ui.output_text("mean_score"), - theme="bg-green", - ), - width=1 / 2, + ui.layout_columns( + ui.value_box( + title="Row count", + value=ui.output_text("row_count"), + theme="primary", + ), + ui.value_box( + title="Mean score", + value=ui.output_text("mean_score"), + theme="bg-green", ), + gap="20px", + ), + ui.layout_columns( + ui.card(ui.output_data_frame("data")), + style="margin-top: 20px;", ), - ui.card(ui.output_data_frame("data")), ) From fcecf2e048dcf58ea194381486f06f3b04ef26dc Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Wed, 13 Dec 2023 14:07:37 -0400 Subject: [PATCH 2/2] Add output to React Component --- .../js-react/custom_component/__init__.py | 10 ++++- .../custom_component/custom_component.py | 42 ++++++++++++++++++- .../js-react/example-app/app.py | 16 ++++--- .../js-react/srcts/index.tsx | 18 +++++++- 4 files changed, 74 insertions(+), 12 deletions(-) diff --git a/shiny/templates/package-templates/js-react/custom_component/__init__.py b/shiny/templates/package-templates/js-react/custom_component/__init__.py index ec4c3a3fe..ff6916ded 100644 --- a/shiny/templates/package-templates/js-react/custom_component/__init__.py +++ b/shiny/templates/package-templates/js-react/custom_component/__init__.py @@ -1,5 +1,11 @@ -from .custom_component import custom_component +from .custom_component import ( + input_custom_component, + output_custom_component, + render_custom_component, +) __all__ = [ - "custom_component", + "input_custom_component", + "output_custom_component", + "render_custom_component", ] diff --git a/shiny/templates/package-templates/js-react/custom_component/custom_component.py b/shiny/templates/package-templates/js-react/custom_component/custom_component.py index 114f037f4..91529e1f6 100644 --- a/shiny/templates/package-templates/js-react/custom_component/custom_component.py +++ b/shiny/templates/package-templates/js-react/custom_component/custom_component.py @@ -3,6 +3,12 @@ from htmltools import HTMLDependency, Tag from shiny.module import resolve_id +from shiny.render.transformer import ( + TransformerMetadata, + ValueFn, + output_transformer, + resolve_value_fn, +) # This object is used to let Shiny know where the dependencies needed to run # our component all live. In this case, we're just using a single javascript @@ -18,14 +24,46 @@ ) -def custom_component(id: str): +def input_custom_component(id: str): """ A shiny input. """ return Tag( # This is the name of the custom tag we created with our webcomponent - "custom-component", + "custom-component-input", custom_component_deps, # Use resolve_id so that our component will work in a module id=resolve_id(id), ) + + +# Output component + + +@output_transformer() +async def render_custom_component( + _meta: TransformerMetadata, + _fn: ValueFn[str | None], +): + res = await resolve_value_fn(_fn) + if res is None: + return None + + if not isinstance(res, str): + # Throw an error if the value is not a string + raise TypeError(f"Expected a string, got {type(res)}. ") + + # Send the results to the client. Make sure that this is a serializable + # object and matches what is expected in the javascript code. + return {"value": res} + + +def output_custom_component(id: str): + """ + Show a color + """ + return Tag( + "custom-component-output", + custom_component_deps, + id=resolve_id(id), + ) diff --git a/shiny/templates/package-templates/js-react/example-app/app.py b/shiny/templates/package-templates/js-react/example-app/app.py index 795cca6a1..a87660408 100644 --- a/shiny/templates/package-templates/js-react/example-app/app.py +++ b/shiny/templates/package-templates/js-react/example-app/app.py @@ -1,19 +1,23 @@ # pyright: basic -from custom_component import custom_component +from custom_component import ( + input_custom_component, + output_custom_component, + render_custom_component, +) -from shiny import App, render, ui +from shiny import App, ui app_ui = ui.page_fluid( - custom_component("myComponent"), - ui.output_text("valueOut"), + input_custom_component("color"), + output_custom_component("valueOut"), ) def server(input, output, session): - @render.text + @render_custom_component def valueOut(): - return f"Value from input is {input.myComponent()}" + return input.color() app = App(app_ui, server) diff --git a/shiny/templates/package-templates/js-react/srcts/index.tsx b/shiny/templates/package-templates/js-react/srcts/index.tsx index 606038896..46565c2db 100644 --- a/shiny/templates/package-templates/js-react/srcts/index.tsx +++ b/shiny/templates/package-templates/js-react/srcts/index.tsx @@ -2,12 +2,12 @@ import { SketchPicker } from "react-color"; import type { ColorResult } from "react-color"; import React from "react"; -import { makeReactInput } from "@shiny-helpers/react"; +import { makeReactInput, makeReactOutput } from "@shiny-helpers/react"; // Generates a new input binding that renders the supplied react component // into the root of the webcomponent. makeReactInput({ - tagName: "custom-component", + tagName: "custom-component-input", initialValue: "#fff", renderComp: ({ onNewValue }) => ( ); } + +makeReactOutput<{ value: string }>({ + tagName: "custom-component-output", + renderComp: ({ value }) => ( +
+ ), +});