Spaces:
Runtime error
Runtime error
| /** | |
| * @license | |
| * Copyright 2018-2020 Streamlit Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // Safari doesn't support the EventTarget class, so we use a shim. | |
| import { EventTarget } from "event-target-shim" | |
| import { ArrowDataframeProto, ArrowTable } from "./ArrowTable" | |
| /** Data sent in the custom Streamlit render event. */ | |
| export interface RenderData { | |
| args: any | |
| disabled: boolean | |
| } | |
| /** Messages from Component -> Streamlit */ | |
| enum ComponentMessageType { | |
| // A component sends this message when it's ready to receive messages | |
| // from Streamlit. Streamlit won't send any messages until it gets this. | |
| // Data: { apiVersion: number } | |
| COMPONENT_READY = "streamlit:componentReady", | |
| // The component has a new widget value. Send it back to Streamlit, which | |
| // will then re-run the app. | |
| // Data: { value: any } | |
| SET_COMPONENT_VALUE = "streamlit:setComponentValue", | |
| // The component has a new height for its iframe. | |
| // Data: { height: number } | |
| SET_FRAME_HEIGHT = "streamlit:setFrameHeight", | |
| } | |
| /** | |
| * Streamlit communication API. | |
| * | |
| * Components can send data to Streamlit via the functions defined here, | |
| * and receive data from Streamlit via the `events` property. | |
| */ | |
| export class Streamlit { | |
| /** | |
| * The Streamlit component API version we're targetting. | |
| * There's currently only 1! | |
| */ | |
| public static readonly API_VERSION = 1 | |
| public static readonly RENDER_EVENT = "streamlit:render" | |
| /** Dispatches events received from Streamlit. */ | |
| public static readonly events = new EventTarget() | |
| private static registeredMessageListener = false | |
| private static lastFrameHeight?: number | |
| /** | |
| * Tell Streamlit that the component is ready to start receiving data. | |
| * Streamlit will defer emitting RENDER events until it receives the | |
| * COMPONENT_READY message. | |
| */ | |
| public static setComponentReady = (): void => { | |
| if (!Streamlit.registeredMessageListener) { | |
| // Register for message events if we haven't already | |
| window.addEventListener("message", Streamlit.onMessageEvent) | |
| Streamlit.registeredMessageListener = true | |
| } | |
| Streamlit.sendBackMsg(ComponentMessageType.COMPONENT_READY, { | |
| apiVersion: Streamlit.API_VERSION, | |
| }) | |
| } | |
| /** | |
| * Report the component's height to Streamlit. | |
| * This should be called every time the component changes its DOM - that is, | |
| * when it's first loaded, and any time it updates. | |
| */ | |
| public static setFrameHeight = (height?: number): void => { | |
| if (height === undefined) { | |
| // `height` is optional. If undefined, it defaults to scrollHeight, | |
| // which is the entire height of the element minus its border, | |
| // scrollbar, and margin. | |
| height = document.body.scrollHeight + 10; | |
| } | |
| if (height === Streamlit.lastFrameHeight) { | |
| // Don't bother updating if our height hasn't changed. | |
| return | |
| } | |
| Streamlit.lastFrameHeight = height | |
| Streamlit.sendBackMsg(ComponentMessageType.SET_FRAME_HEIGHT, { height }) | |
| } | |
| /** | |
| * Set the component's value. This value will be returned to the Python | |
| * script, and the script will be re-run. | |
| * | |
| * For example: | |
| * | |
| * JavaScript: | |
| * Streamlit.setComponentValue("ahoy!") | |
| * | |
| * Python: | |
| * value = st.my_component(...) | |
| * st.write(value) # -> "ahoy!" | |
| * | |
| * The value must be serializable into JSON. | |
| */ | |
| public static setComponentValue = (value: any): void => { | |
| Streamlit.sendBackMsg(ComponentMessageType.SET_COMPONENT_VALUE, { value }) | |
| } | |
| /** Receive a ForwardMsg from the Streamlit app */ | |
| private static onMessageEvent = (event: MessageEvent): void => { | |
| const type = event.data["type"] | |
| switch (type) { | |
| case Streamlit.RENDER_EVENT: | |
| Streamlit.onRenderMessage(event.data) | |
| break | |
| } | |
| } | |
| /** | |
| * Handle an untyped Streamlit render event and redispatch it as a | |
| * StreamlitRenderEvent. | |
| */ | |
| private static onRenderMessage = (data: any): void => { | |
| let args = data["args"] | |
| if (args == null) { | |
| console.error( | |
| `Got null args in onRenderMessage. This should never happen` | |
| ) | |
| args = {} | |
| } | |
| // Parse our dataframe arguments with arrow, and merge them into our args dict | |
| const dataframeArgs = | |
| data["dfs"] && data["dfs"].length > 0 | |
| ? Streamlit.argsDataframeToObject(data["dfs"]) | |
| : {} | |
| args = { | |
| ...args, | |
| ...dataframeArgs, | |
| } | |
| const disabled = Boolean(data["disabled"]) | |
| // Dispatch a render event! | |
| const eventData = { disabled, args } | |
| const event = new CustomEvent<RenderData>(Streamlit.RENDER_EVENT, { | |
| detail: eventData, | |
| }) | |
| Streamlit.events.dispatchEvent(event) | |
| } | |
| private static argsDataframeToObject = ( | |
| argsDataframe: ArgsDataframe[] | |
| ): object => { | |
| const argsDataframeArrow = argsDataframe.map( | |
| ({ key, value }: ArgsDataframe) => [key, Streamlit.toArrowTable(value)] | |
| ) | |
| return Object.fromEntries(argsDataframeArrow) | |
| } | |
| private static toArrowTable = (df: ArrowDataframeProto): ArrowTable => { | |
| const { data, index, columns } = df.data | |
| return new ArrowTable(data, index, columns) | |
| } | |
| /** Post a message to the Streamlit app. */ | |
| private static sendBackMsg = (type: string, data?: any): void => { | |
| window.parent.postMessage( | |
| { | |
| isStreamlitMessage: true, | |
| type: type, | |
| ...data, | |
| }, | |
| "*" | |
| ) | |
| } | |
| } | |
| interface ArgsDataframe { | |
| key: string | |
| value: ArrowDataframeProto | |
| } | |