Spaces:
Runtime error
Runtime error
File size: 4,387 Bytes
3fc2d14 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
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)
}
|