Spaces:
Runtime error
Runtime error
| import hoistNonReactStatics from "hoist-non-react-statics" | |
| import React, { ReactNode } from "react" | |
| import { RenderData, Streamlit } from "./streamlit" | |
| /** | |
| * Props passed to custom Streamlit components. | |
| */ | |
| export interface ComponentProps { | |
| /** Named dictionary of arguments passed from Python. */ | |
| args: any | |
| /** The component's width. */ | |
| width: number | |
| /** | |
| * True if the component should be disabled. | |
| * All components get disabled while the app is being re-run, | |
| * and become re-enabled when the re-run has finished. | |
| */ | |
| disabled: boolean | |
| } | |
| /** | |
| * Optional Streamlit React-based component base class. | |
| * | |
| * You are not required to extend this base class to create a Streamlit | |
| * component. If you decide not to extend it, you should implement the | |
| * `componentDidMount` and `componentDidUpdate` functions in your own class, | |
| * so that your plugin properly resizes. | |
| */ | |
| export class StreamlitComponentBase<S = {}> extends React.PureComponent< | |
| ComponentProps, | |
| S | |
| > { | |
| public componentDidMount(): void { | |
| // After we're rendered for the first time, tell Streamlit that our height | |
| // has changed. | |
| Streamlit.setFrameHeight() | |
| } | |
| public componentDidUpdate(): void { | |
| // After we're updated, tell Streamlit that our height may have changed. | |
| Streamlit.setFrameHeight() | |
| } | |
| } | |
| /** | |
| * Wrapper for React-based Streamlit components. | |
| * | |
| * Bootstraps the communication interface between Streamlit and the component. | |
| */ | |
| export function withStreamlitConnection( | |
| WrappedComponent: React.ComponentType<ComponentProps> | |
| ): React.ComponentType { | |
| interface WrapperProps { } | |
| interface WrapperState { | |
| renderData?: RenderData | |
| componentError?: Error | |
| } | |
| class ComponentWrapper extends React.PureComponent< | |
| WrapperProps, | |
| WrapperState | |
| > { | |
| public constructor(props: WrapperProps) { | |
| super(props) | |
| this.state = { | |
| renderData: undefined, | |
| componentError: undefined, | |
| } | |
| } | |
| /** | |
| * Error boundary function. This will be called if our wrapped | |
| * component throws an error. We store the caught error in our state, | |
| * and display it in the next render(). | |
| */ | |
| public static getDerivedStateFromError = ( | |
| error: Error | |
| ): Partial<WrapperState> => { | |
| return { componentError: error } | |
| } | |
| public componentDidMount = (): void => { | |
| // Set up event listeners, and signal to Streamlit that we're ready. | |
| // We won't render the component until we receive the first RENDER_EVENT. | |
| Streamlit.events.addEventListener( | |
| Streamlit.RENDER_EVENT, | |
| this.onRenderEvent | |
| ) | |
| Streamlit.setComponentReady() | |
| } | |
| public componentDidUpdate = (prevProps: any): void => { | |
| // If our child threw an error, we display it in render(). In this | |
| // case, the child won't be mounted and therefore won't call | |
| // `setFrameHeight` on its own. We do it here so that the rendered | |
| // error will be visible. | |
| if (this.state.componentError != null) { | |
| Streamlit.setFrameHeight() | |
| } | |
| } | |
| public componentWillUnmount = (): void => { | |
| Streamlit.events.removeEventListener( | |
| Streamlit.RENDER_EVENT, | |
| this.onRenderEvent | |
| ) | |
| } | |
| /** | |
| * Streamlit is telling this component to redraw. | |
| * We save the render data in State, so that it can be passed to the | |
| * component in our own render() function. | |
| */ | |
| private onRenderEvent = (event: Event): void => { | |
| // Update our state with the newest render data | |
| const renderEvent = event as CustomEvent<RenderData> | |
| this.setState({ renderData: renderEvent.detail }) | |
| } | |
| public render = (): ReactNode => { | |
| // If our wrapped component threw an error, display it. | |
| if (this.state.componentError != null) { | |
| return ( | |
| <div> | |
| <h1>Component Error</h1> | |
| <span>{this.state.componentError.message}</span> | |
| </div> | |
| ) | |
| } | |
| // Don't render until we've gotten our first RENDER_EVENT from Streamlit. | |
| if (this.state.renderData == null) { | |
| return null | |
| } | |
| return ( | |
| <WrappedComponent | |
| width={window.innerWidth} | |
| disabled={this.state.renderData.disabled} | |
| args={this.state.renderData.args} | |
| /> | |
| ) | |
| } | |
| } | |
| return hoistNonReactStatics(ComponentWrapper, WrappedComponent) | |
| } | |