├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── Readme.md ├── demo.gif ├── setup.py └── streamlit_vega_lite ├── __init__.py └── frontend ├── .env ├── .prettierignore ├── .prettierrc ├── package.json ├── public └── index.html ├── src ├── VegaLiteComponent.tsx ├── arrow-loader.ts ├── index.tsx └── react-app-env.d.ts ├── tsconfig.json └── yarn.lock /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------------------------------------- 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 4 | #------------------------------------------------------------------------------------------------------------- 5 | 6 | # Update the VARIANT arg in devcontainer.json to pick a Python version: 3, 3.8, 3.7, 3.6 7 | # To fully customize the contents of this image, use the following Dockerfile instead: 8 | # https://github.com/microsoft/vscode-dev-containers/tree/v0.128.0/containers/python-3/.devcontainer/base.Dockerfile 9 | ARG VARIANT="3" 10 | FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} 11 | 12 | # [Optional] If your requirements rarely change, uncomment this section to add them to the image. 13 | # 14 | # COPY requirements.txt /tmp/pip-tmp/ 15 | # RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ 16 | # && rm -rf /tmp/pip-tmp 17 | 18 | 19 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - 20 | RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list 21 | 22 | RUN apt-get update \ 23 | && export DEBIAN_FRONTEND=noninteractive \ 24 | && apt-get -y install --no-install-recommends nodejs yarn 25 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.128.0/containers/python-3 3 | { 4 | "name": "Python 3", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "context": "..", 8 | // Update 'VARIANT' to pick a Python version. Rebuild the container 9 | // if it already exists to update. Available variants: 3, 3.6, 3.7, 3.8 10 | "args": { "VARIANT": "3.8" } 11 | }, 12 | 13 | // Set *default* container specific settings.json values on container create. 14 | "settings": { 15 | "terminal.integrated.shell.linux": "/bin/zsh", 16 | "python.pythonPath": "/usr/local/bin/python", 17 | "python.linting.enabled": true, 18 | "python.linting.pylintEnabled": true, 19 | "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", 20 | "python.formatting.blackPath": "/usr/local/py-utils/bin/black", 21 | "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", 22 | "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", 23 | "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", 24 | "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", 25 | "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", 26 | "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", 27 | "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint" 28 | }, 29 | 30 | // Add the IDs of extensions you want installed when the container is created. 31 | "extensions": [ 32 | "ms-python.python" 33 | ], 34 | 35 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 36 | "forwardPorts": [8501, 3000, 3001], 37 | 38 | // Use 'postCreateCommand' to run commands after the container is created. 39 | // "postCreateCommand": "pip3 install --user -r requirements.txt", 40 | 41 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root. 42 | // "remoteUser": "vscode" 43 | } 44 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | time: "23:00" 8 | timezone: PST8PDT 9 | open-pull-requests-limit: 10 10 | - package-ecosystem: npm 11 | directory: "/" 12 | schedule: 13 | interval: monthly 14 | time: "23:00" 15 | timezone: PST8PDT 16 | open-pull-requests-limit: 10 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | streamlit_vega_lite/frontend/node_modules 2 | streamlit_vega_lite/frontend/build 3 | streamlit_vega_lite/frontend/yarn-error.log 4 | streamlit_vega_lite.egg-info/ 5 | build 6 | dist 7 | streamlit_vega_lite/frontend/.eslintcache 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Python Packaging Authority 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include streamlit_vega_lite/frontend/build * 2 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Streamlit Vega-Lite 2 | 3 | [![code style black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 4 | [![PyPI - Downloads](https://img.shields.io/pypi/v/streamlit-vega-lite)](https://pypi.org/project/streamlit-vega-lite) 5 | 6 | **🐉 Here be dragons. This is a proof of concept.** 7 | 8 | Making Vega-Lite selection created by user interactions available in Python. Works with [Altair](https://altair-viz.github.io/). 9 | 10 | For examples, see https://github.com/domoritz/streamlit-vega-lite/blob/master/streamlit_vega_lite/__init__.py. You can also try the demo at https://github.com/domoritz/streamlit-vega-lite-demo. 11 | 12 | Demo screencast 13 | 14 | ## Documentation 15 | 16 | ### Installation 17 | 18 | `pip install streamlit-vega-lite` 19 | 20 | ### Usage 21 | 22 | There are two functions available. `vega_lite_component` expects a Vega-Lite specification as a dictionary and any named datasets as keyword arguments. The datasets will be transferred as efficient Arrow tables. `altair_component` supports Altair charts and automatically extracts all datasets and transfers them as Arrow dataframes. 23 | 24 | #### Example 25 | 26 | ```python 27 | import altair as alt 28 | import streamlit as st 29 | import pandas as pd 30 | import numpy as np 31 | 32 | from streamlit_vega_lite import vega_lite_component, altair_component 33 | 34 | hist_data = pd.DataFrame(np.random.normal(42, 10, (200, 1)), columns=["x"]) 35 | 36 | @st.cache 37 | def altair_histogram(): 38 | brushed = alt.selection_interval(encodings=["x"], name="brushed") 39 | 40 | return ( 41 | alt.Chart(hist_data) 42 | .mark_bar() 43 | .encode(alt.X("x:Q", bin=True), y="count()") 44 | .add_selection(brushed) 45 | ) 46 | 47 | event_dict = altair_component(altair_chart=altair_histogram()) 48 | 49 | r = event_dict.get("x") 50 | if r: 51 | filtered = hist_data[(hist_data.x >= r[0]) & (hist_data.x < r[1])] 52 | st.write(filtered) 53 | ``` 54 | 55 | ## Dev Setup 56 | 57 | Open two terminals in the dev container using VSCode's [Remote Containers Extension](https://code.visualstudio.com/docs/remote/containers). 58 | 59 | In the first terminal, run: 60 | 61 | ```bash 62 | # Install python module in editable mode 63 | pip install -e . 64 | 65 | # Launch streamlit app 66 | streamlit run streamlit_vega_lite/__init__.py 67 | ``` 68 | 69 | In the second terminal: 70 | 71 | ```bash 72 | # Switch to location of frontend code 73 | cd streamlit_vega_lite/frontend 74 | # Install dependencies 75 | yarn 76 | # Launch frontend assets 77 | yarn start 78 | ``` 79 | 80 | Then open http://localhost:8501/. 81 | 82 | ## Style 83 | 84 | Run Black for Python formatting. 85 | 86 | ``` 87 | black . -l 120 88 | ``` 89 | 90 | Run Prettier for other formatting in the frontend directory. 91 | 92 | ``` 93 | yarn format 94 | ``` 95 | 96 | ## Publish 97 | 98 | See https://docs.streamlit.io/en/stable/publish_streamlit_components.html. 99 | 100 | Make sure that `_RELEASE` is set to `True`. 101 | 102 | ```sh 103 | pushd streamlit_vega_lite/frontend 104 | yarn build 105 | popd 106 | python setup.py sdist bdist_wheel 107 | python3 -m twine upload --repository pypi dist/* 108 | ``` 109 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/domoritz/streamlit-vega-lite/a72f8ca3a766d5e3e5b62ed9d57d18e37c456496/demo.gif -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name="streamlit-vega-lite", 5 | version="0.1.0", 6 | author="Dominik Moritz, Cameron Yick", 7 | author_email="", 8 | description="A Vega-Lite Component for Streamlit that Supports Selections", 9 | long_description="", 10 | long_description_content_type="text/plain", 11 | url="https://github.com/domoritz/streamlit-vega-lite", 12 | packages=setuptools.find_packages(), 13 | include_package_data=True, 14 | classifiers=[], 15 | python_requires=">=3.6", 16 | install_requires=[ 17 | "streamlit >= 0.63", 18 | ], 19 | ) 20 | -------------------------------------------------------------------------------- /streamlit_vega_lite/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import streamlit.components.v1 as components 3 | import pandas as pd 4 | 5 | 6 | # Set this to False while we're developing the component, and True when we're 7 | # ready to package and distribute it. 8 | _RELEASE = False 9 | 10 | COMPONENT_NAME = "vega_lite_component" 11 | 12 | if not _RELEASE: 13 | _component_func = components.declare_component( 14 | COMPONENT_NAME, 15 | url="http://localhost:3001", 16 | ) 17 | else: 18 | parent_dir = os.path.dirname(os.path.abspath(__file__)) 19 | build_dir = os.path.join(parent_dir, "frontend/build") 20 | _component_func = components.declare_component(COMPONENT_NAME, path=build_dir) 21 | 22 | 23 | def vega_lite_component(spec={}, key=None, **kwargs): 24 | """Returns selections from the Vega-Lite chart. 25 | 26 | Parameters 27 | ---------- 28 | spec: dict 29 | The Vega-Lite spec for the chart. See https://vega.github.io/vega-lite/docs/ 30 | for more info. 31 | key: str or None 32 | An optional key that uniquely identifies this component. If this is 33 | None, and the component"s arguments are changed, the component will 34 | be re-mounted in the Streamlit frontend and lose its current state. 35 | **kwargs: pandas.DataFrame or list 36 | Datasets to be passed to the spec via named datasets. 37 | 38 | Returns 39 | ------- 40 | dict 41 | The selections from the chart. 42 | 43 | """ 44 | 45 | # basic argument validation 46 | if not spec.get("selection"): 47 | raise ValueError("Spec must contain selection") 48 | 49 | for selection in spec["selection"].values(): 50 | if ( 51 | (selection["type"] == "single" or selection["type"] == "multi") 52 | and not selection.get("encodings") 53 | and not selection.get("fields") 54 | ): 55 | raise ValueError("Every single and multi selection in spec must be projected onto encodings or fields.") 56 | 57 | return _component_func(spec=spec, **kwargs, key=key, default={}) 58 | 59 | 60 | def altair_component(altair_chart, key=None): 61 | """Returns selections from the Altair chart. 62 | 63 | Parameters 64 | ---------- 65 | altair_chart: altair.vegalite.v2.api.Chart 66 | The Altair chart object to display. 67 | key: str or None 68 | An optional key that uniquely identifies this component. If this is 69 | None, and the component"s arguments are changed, the component will 70 | be re-mounted in the Streamlit frontend and lose its current state. 71 | 72 | Returns 73 | ------- 74 | dict 75 | The selections from the chart. 76 | 77 | """ 78 | 79 | import altair as alt 80 | 81 | # Normally altair_chart.to_dict() would transform the dataframe used by the 82 | # chart into an array of dictionaries. To avoid that, we install a 83 | # transformer that replaces datasets with a reference by the object id of 84 | # the dataframe. We then fill in the dataset manually later on. 85 | 86 | datasets = {} 87 | 88 | # make a copy of the chart so that we don't have to rerender it even if nothing changed 89 | altair_chart = altair_chart.copy() 90 | 91 | def id_transform(data): 92 | """Altair data transformer that returns a fake named dataset with the 93 | object id.""" 94 | name = f"d{id(data)}" 95 | datasets[name] = data 96 | return {"name": name} 97 | 98 | alt.data_transformers.register("id", id_transform) 99 | 100 | with alt.data_transformers.enable("id"): 101 | chart_dict = altair_chart.to_dict() 102 | 103 | return _component_func(spec=chart_dict, **datasets, key=key, default={}) 104 | 105 | 106 | # Add some test code to play with the component while it"s in development. 107 | # During development, we can run this just as we would any other Streamlit 108 | # app: `$ streamlit run my_component/__init__.py` 109 | if not _RELEASE: 110 | import streamlit as st 111 | import numpy as np 112 | import altair as alt 113 | 114 | st.subheader("Vega-Lite + Streamlit Event Emitter") 115 | 116 | bar_spec = { 117 | "$schema": "https://vega.github.io/schema/vega-lite/v4.json", 118 | "data": {"name": "bar_data"}, 119 | "selection": {"clicked": {"type": "multi", "empty": "none", "encodings": ["x"]}}, 120 | "mark": "bar", 121 | "encoding": { 122 | "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, 123 | "y": {"field": "b", "type": "quantitative"}, 124 | "color": { 125 | "condition": {"selection": "clicked", "value": "firebrick"}, 126 | "value": "steelblue", 127 | }, 128 | }, 129 | } 130 | 131 | bar_data = pd.DataFrame( 132 | [ 133 | {"a": "A", "b": 10}, 134 | {"a": "B", "b": 34}, 135 | {"a": "C", "b": 55}, 136 | {"a": "D", "b": 19}, 137 | {"a": "E", "b": 40}, 138 | {"a": "F", "b": 34}, 139 | {"a": "G", "b": 91}, 140 | {"a": "H", "b": 78}, 141 | {"a": "I", "b": 25}, 142 | ] 143 | ) 144 | 145 | event_dict = vega_lite_component(spec=bar_spec, bar_data=bar_data) 146 | selection = event_dict.get("a") 147 | if selection: 148 | fields = " and ".join(selection) 149 | st.write(f"You selected on { fields }.") 150 | else: 151 | st.write("You haven't selected any bars yet.") 152 | 153 | st.subheader("Vega-Lite + Streamlit Event Emitter") 154 | 155 | hist_spec = { 156 | "$schema": "https://vega.github.io/schema/vega-lite/v4.json", 157 | "data": {"name": "hist_data"}, 158 | "mark": "bar", 159 | "selection": {"brushed": {"type": "interval"}}, 160 | "encoding": {"x": {"bin": True, "field": "x"}, "y": {"aggregate": "count"}}, 161 | } 162 | 163 | np.random.seed(0) 164 | hist_data = pd.DataFrame(np.random.normal(42, 10, (200, 1)), columns=["x"]) 165 | 166 | event_dict = vega_lite_component(spec=hist_spec, hist_data=hist_data) 167 | 168 | def print_range(r): 169 | st.write( 170 | f"You selected data in the range from {r[0]:.1f} to {r[1]:.1f}." 171 | if r 172 | else "You haven't selected anything yet." 173 | ) 174 | 175 | print_range(event_dict.get("x")) 176 | 177 | st.subheader("Altair + Streamlit Event Emitter") 178 | 179 | @st.cache 180 | def make_altair_histogram(): 181 | brushed = alt.selection_interval(encodings=["x"], name="brushed") 182 | return ( 183 | alt.Chart(hist_data) 184 | .mark_bar() 185 | .encode( 186 | alt.X("x:Q", bin=True), 187 | y="count()", 188 | ) 189 | .add_selection(brushed) 190 | ) 191 | 192 | chart = make_altair_histogram() 193 | event_dict = altair_component(altair_chart=chart) 194 | r = event_dict.get("x") 195 | print_range(r) 196 | 197 | if r: 198 | filtered = hist_data[(hist_data.x >= r[0]) & (hist_data.x < r[1])] 199 | st.write(filtered.describe()) 200 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/.env: -------------------------------------------------------------------------------- 1 | # Run the component's dev server on :3001 2 | # (The Streamlit dev server already runs on :3000) 3 | PORT=3001 4 | 5 | # Don't automatically open the web browser on `npm run start`. 6 | BROWSER=none 7 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": true, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamlit-vega-lite", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "@types/jest": "^26.0.20", 7 | "@types/node": "^14.14.20", 8 | "@types/react-dom": "^17.0.0", 9 | "@types/react": "^17.0.0", 10 | "prettier": "^2.2.1", 11 | "react-scripts": "^4.0.1", 12 | "typescript": "~4.1.3" 13 | }, 14 | "dependencies": { 15 | "bootstrap": "^4.5.3", 16 | "react": "^17.0.1", 17 | "react-dom": "^17.0.1", 18 | "react-vega": "^7.4.2", 19 | "streamlit-component-lib": "^1.1.3", 20 | "vega": "^5.18.0", 21 | "vega-lite": "^4.17.0" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject", 28 | "format": "prettier . --write" 29 | }, 30 | "eslintConfig": { 31 | "extends": "react-app" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "homepage": "." 46 | } 47 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vega-Lite Streamlit Component 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/src/VegaLiteComponent.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { SignalListener, VegaLite, View } from "react-vega"; 3 | import { 4 | ArrowTable, 5 | Streamlit, 6 | withStreamlitConnection, 7 | } from "streamlit-component-lib"; 8 | import { isEmpty, TopLevelSpec } from "vega-lite"; 9 | import { arrow } from "./arrow-loader"; 10 | 11 | interface Args { 12 | spec: TopLevelSpec; 13 | [name: string]: any; 14 | } 15 | 16 | interface VegaLiteComponentProps { 17 | args: Args; 18 | } 19 | 20 | function handleSignals(name: string, payload: any) { 21 | Streamlit.setComponentValue({ 22 | name, 23 | ...payload, 24 | }); 25 | } 26 | 27 | function handleNewView(view: View) { 28 | view.addResizeListener((_, height) => { 29 | Streamlit.setFrameHeight(height); 30 | }); 31 | } 32 | 33 | const VegaLiteComponent: React.FC = (props) => { 34 | const { spec, ...args } = props.args; 35 | 36 | const signalListeners = useMemo(() => { 37 | const listenerMap: Record = {}; 38 | 39 | if ("selection" in spec) { 40 | for (const selectionName of Object.keys(spec.selection!)) { 41 | listenerMap[selectionName] = handleSignals; 42 | } 43 | } 44 | 45 | return listenerMap; 46 | }, [spec]); 47 | 48 | const data = useMemo(() => { 49 | const data: Record = {}; 50 | for (const name of Object.keys(args ?? {})) { 51 | const table = args[name]; 52 | if (table && !isEmpty(table)) { 53 | data[name] = table instanceof ArrowTable ? arrow(table) : table; 54 | } 55 | } 56 | 57 | return data; 58 | }, [args]); 59 | 60 | return ( 61 | 67 | ); 68 | }; 69 | 70 | export default withStreamlitConnection(VegaLiteComponent); 71 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/src/arrow-loader.ts: -------------------------------------------------------------------------------- 1 | import { ArrowTable } from "streamlit-component-lib"; 2 | 3 | // Forked from vega-arrow-loader for debugging/type safety/simplified type signature and support for ArrowTable. 4 | // https://github.com/vega/vega-loader-arrow/blob/master/src/arrow.js 5 | 6 | const RowIndex = Symbol("rowIndex"); 7 | 8 | // Convert arrow table to an array of proxy objects 9 | export function arrow(table: ArrowTable) { 10 | const proxy = rowProxy(table); 11 | const rows = Array(table.dataRows); 12 | 13 | for (let i = 0, n = rows.length; i < n; ++i) { 14 | rows[i] = proxy(i + table.headerRows); 15 | } 16 | 17 | return rows; 18 | } 19 | 20 | function rowProxy(table: ArrowTable) { 21 | const fields: string[] = []; 22 | for (let i = 0; i < table.columns; i++) { 23 | fields.push(table.getCell(0, i).content); 24 | } 25 | 26 | const proto = {}; 27 | 28 | fields.forEach((name, index) => { 29 | // skip columns with duplicate names 30 | if (proto.hasOwnProperty(name) || !name) return; 31 | 32 | Object.defineProperty(proto, name, { 33 | get: function () { 34 | return table.getCell(this[RowIndex], index).content; 35 | }, 36 | set: function () { 37 | throw Error("Arrow field values can not be overwritten."); 38 | }, 39 | enumerable: true, 40 | }); 41 | }); 42 | 43 | return (i: number) => { 44 | const r = Object.create(proto); 45 | r[RowIndex] = i; 46 | return r; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import "bootstrap/dist/css/bootstrap.min.css"; 2 | import React from "react"; 3 | import ReactDOM from "react-dom"; 4 | import VegaLiteComponent from "./VegaLiteComponent"; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById("root") 11 | ); 12 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /streamlit_vega_lite/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "noFallthroughCasesInSwitch": true 18 | }, 19 | "include": ["src"] 20 | } 21 | --------------------------------------------------------------------------------