├── .gitignore ├── .streamlit └── config.toml ├── README.md ├── requirements.txt ├── streamlit_app.py └── streamlit_gallery ├── __init__.py ├── apps ├── __init__.py └── gallery.py ├── components ├── __init__.py ├── ace_editor │ ├── __init__.py │ └── streamlit_app.py ├── discourse │ ├── __init__.py │ └── streamlit_app.py ├── disqus │ ├── __init__.py │ └── streamlit_app.py ├── elements │ ├── README.md │ ├── __init__.py │ ├── dashboard │ │ ├── __init__.py │ │ ├── card.py │ │ ├── dashboard.py │ │ ├── datagrid.py │ │ ├── editor.py │ │ ├── pie.py │ │ ├── player.py │ │ └── radar.py │ └── streamlit_app.py ├── pandas_profiling │ ├── __init__.py │ └── streamlit_app.py ├── quill_editor │ ├── __init__.py │ └── streamlit_app.py └── react_player │ ├── __init__.py │ └── streamlit_app.py └── utils ├── __init__.py ├── page.py └── readme.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Streamlit 2 | .streamlit/* 3 | !.streamlit/config.toml 4 | !*/frontend/.env 5 | 6 | # Visual Studio Code 7 | .vscode/* 8 | #!.vscode/settings.json 9 | !.vscode/tasks.json 10 | !.vscode/launch.json 11 | !.vscode/extensions.json 12 | *.code-workspace 13 | 14 | # Local History for Visual Studio Code 15 | .history/ 16 | 17 | # Byte-compiled / optimized / DLL files 18 | __pycache__/ 19 | *.py[cod] 20 | *$py.class 21 | 22 | # C extensions 23 | *.so 24 | 25 | # Distribution / packaging 26 | .Python 27 | build/ 28 | develop-eggs/ 29 | dist/ 30 | downloads/ 31 | eggs/ 32 | .eggs/ 33 | lib/ 34 | lib64/ 35 | parts/ 36 | sdist/ 37 | var/ 38 | wheels/ 39 | share/python-wheels/ 40 | *.egg-info/ 41 | .installed.cfg 42 | *.egg 43 | MANIFEST 44 | 45 | # PyInstaller 46 | # Usually these files are written by a python script from a template 47 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 48 | *.manifest 49 | *.spec 50 | 51 | # Installer logs 52 | pip-log.txt 53 | pip-delete-this-directory.txt 54 | 55 | # Unit test / coverage reports 56 | htmlcov/ 57 | .tox/ 58 | .nox/ 59 | .coverage 60 | .coverage.* 61 | .cache 62 | nosetests.xml 63 | coverage.xml 64 | *.cover 65 | *.py,cover 66 | .hypothesis/ 67 | .pytest_cache/ 68 | cover/ 69 | 70 | # Translations 71 | *.mo 72 | *.pot 73 | 74 | # Django stuff: 75 | *.log 76 | local_settings.py 77 | db.sqlite3 78 | db.sqlite3-journal 79 | 80 | # Flask stuff: 81 | instance/ 82 | .webassets-cache 83 | 84 | # Scrapy stuff: 85 | .scrapy 86 | 87 | # Sphinx documentation 88 | docs/_build/ 89 | 90 | # PyBuilder 91 | .pybuilder/ 92 | target/ 93 | 94 | # Jupyter Notebook 95 | .ipynb_checkpoints 96 | 97 | # IPython 98 | profile_default/ 99 | ipython_config.py 100 | 101 | # pyenv 102 | # For a library or package, you might want to ignore these files since the code is 103 | # intended to run in multiple environments; otherwise, check them in: 104 | .python-version 105 | 106 | # pipenv 107 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 108 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 109 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 110 | # install all needed dependencies. 111 | #Pipfile.lock 112 | 113 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 114 | __pypackages__/ 115 | 116 | # Celery stuff 117 | celerybeat-schedule 118 | celerybeat.pid 119 | 120 | # SageMath parsed files 121 | *.sage.py 122 | 123 | # Environments 124 | .env 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # Logs 157 | logs 158 | *.log 159 | npm-debug.log* 160 | yarn-debug.log* 161 | yarn-error.log* 162 | lerna-debug.log* 163 | 164 | # Diagnostic reports (https://nodejs.org/api/report.html) 165 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 166 | 167 | # Runtime data 168 | pids 169 | *.pid 170 | *.seed 171 | *.pid.lock 172 | 173 | # Directory for instrumented libs generated by jscoverage/JSCover 174 | lib-cov 175 | 176 | # Coverage directory used by tools like istanbul 177 | coverage 178 | *.lcov 179 | 180 | # nyc test coverage 181 | .nyc_output 182 | 183 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 184 | .grunt 185 | 186 | # Bower dependency directory (https://bower.io/) 187 | bower_components 188 | 189 | # node-waf configuration 190 | .lock-wscript 191 | 192 | # Compiled binary addons (https://nodejs.org/api/addons.html) 193 | build/Release 194 | 195 | # Dependency directories 196 | node_modules/ 197 | jspm_packages/ 198 | 199 | # Snowpack dependency directory (https://snowpack.dev/) 200 | web_modules/ 201 | 202 | # TypeScript cache 203 | *.tsbuildinfo 204 | 205 | # Optional npm cache directory 206 | .npm 207 | 208 | # Optional eslint cache 209 | .eslintcache 210 | 211 | # Microbundle cache 212 | .rpt2_cache/ 213 | .rts2_cache_cjs/ 214 | .rts2_cache_es/ 215 | .rts2_cache_umd/ 216 | 217 | # Optional REPL history 218 | .node_repl_history 219 | 220 | # Output of 'npm pack' 221 | *.tgz 222 | 223 | # Yarn Integrity file 224 | .yarn-integrity 225 | 226 | # dotenv environment variables file 227 | .env 228 | .env.test 229 | 230 | # parcel-bundler cache (https://parceljs.org/) 231 | .cache 232 | .parcel-cache 233 | 234 | # Next.js build output 235 | .next 236 | out 237 | 238 | # Nuxt.js build / generate output 239 | .nuxt 240 | dist 241 | 242 | # Gatsby files 243 | .cache/ 244 | # Comment in the public line in if your project uses Gatsby and not Next.js 245 | # https://nextjs.org/blog/next-9-1#public-directory-support 246 | # public 247 | 248 | # vuepress build output 249 | .vuepress/dist 250 | 251 | # Serverless directories 252 | .serverless/ 253 | 254 | # FuseBox cache 255 | .fusebox/ 256 | 257 | # DynamoDB Local files 258 | .dynamodb/ 259 | 260 | # TernJS port file 261 | .tern-port 262 | 263 | # Stores VSCode versions used for testing VSCode extensions 264 | .vscode-test 265 | 266 | # yarn v2 267 | .yarn/cache 268 | .yarn/unplugged 269 | .yarn/build-state.yml 270 | .yarn/install-state.gz 271 | .pnp.* 272 | -------------------------------------------------------------------------------- /.streamlit/config.toml: -------------------------------------------------------------------------------- 1 | [theme] 2 | base="dark" 3 | backgroundColor="#1e1e1e" 4 | secondaryBackgroundColor="#252526" 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🎉 Streamlit Gallery 2 | 3 | [![Open in Streamlit][share_badge]][share_link] [![GitHub][github_badge]][github_link] 4 | 5 | [share_badge]: https://static.streamlit.io/badges/streamlit_badge_black_white.svg 6 | [share_link]: https://share.streamlit.io/okld/streamlit-gallery/main 7 | 8 | [github_badge]: https://badgen.net/badge/icon/GitHub?icon=github&color=black&label 9 | [github_link]: https://github.com/okld/streamlit-gallery 10 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Core 2 | streamlit 3 | 4 | # Streamlit components 5 | streamlit-ace 6 | streamlit-discourse 7 | streamlit-disqus 8 | streamlit-elements 9 | streamlit-pandas-profiling 10 | streamlit-player 11 | streamlit-quill 12 | 13 | # Dependencies 14 | pandas-profiling 15 | requests 16 | -------------------------------------------------------------------------------- /streamlit_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from streamlit_gallery import apps, components 4 | from streamlit_gallery.utils.page import page_group 5 | 6 | def main(): 7 | page = page_group("p") 8 | 9 | with st.sidebar: 10 | st.title("🎈 Okld's Gallery") 11 | 12 | with st.expander("✨ APPS", True): 13 | page.item("Streamlit gallery", apps.gallery, default=True) 14 | 15 | with st.expander("🧩 COMPONENTS", True): 16 | page.item("Ace editor", components.ace_editor) 17 | page.item("Disqus", components.disqus) 18 | page.item("Elements⭐", components.elements) 19 | page.item("Pandas profiling", components.pandas_profiling) 20 | page.item("Quill editor", components.quill_editor) 21 | page.item("React player", components.react_player) 22 | 23 | page.show() 24 | 25 | if __name__ == "__main__": 26 | st.set_page_config(page_title="Streamlit Gallery by Okld", page_icon="🎈", layout="wide") 27 | main() 28 | -------------------------------------------------------------------------------- /streamlit_gallery/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/apps/__init__.py: -------------------------------------------------------------------------------- 1 | from .gallery import main as gallery 2 | -------------------------------------------------------------------------------- /streamlit_gallery/apps/gallery.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from pathlib import Path 4 | 5 | 6 | def main(): 7 | st.markdown((Path(__file__).parents[2]/"README.md").read_text()) 8 | 9 | 10 | if __name__ == "__main__": 11 | main() 12 | -------------------------------------------------------------------------------- /streamlit_gallery/components/__init__.py: -------------------------------------------------------------------------------- 1 | from .ace_editor.streamlit_app import main as ace_editor 2 | from .discourse.streamlit_app import main as discourse 3 | from .disqus.streamlit_app import main as disqus 4 | from .elements.streamlit_app import main as elements 5 | from .pandas_profiling.streamlit_app import main as pandas_profiling 6 | from .quill_editor.streamlit_app import main as quill_editor 7 | from .react_player.streamlit_app import main as react_player 8 | -------------------------------------------------------------------------------- /streamlit_gallery/components/ace_editor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/ace_editor/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/ace_editor/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from streamlit_ace import st_ace, KEYBINDINGS, LANGUAGES, THEMES 4 | from streamlit_gallery.utils.readme import readme 5 | 6 | 7 | def main(): 8 | with readme("streamlit-ace", st_ace, __file__): 9 | c1, c2 = st.columns([3, 1]) 10 | 11 | c2.subheader("Parameters") 12 | 13 | with c1: 14 | content = st_ace( 15 | placeholder=c2.text_input("Editor placeholder", value="Write your code here"), 16 | language=c2.selectbox("Language mode", options=LANGUAGES, index=121), 17 | theme=c2.selectbox("Theme", options=THEMES, index=35), 18 | keybinding=c2.selectbox("Keybinding mode", options=KEYBINDINGS, index=3), 19 | font_size=c2.slider("Font size", 5, 24, 14), 20 | tab_size=c2.slider("Tab size", 1, 8, 4), 21 | show_gutter=c2.checkbox("Show gutter", value=True), 22 | show_print_margin=c2.checkbox("Show print margin", value=False), 23 | wrap=c2.checkbox("Wrap enabled", value=False), 24 | auto_update=c2.checkbox("Auto update", value=False), 25 | readonly=c2.checkbox("Read-only", value=False), 26 | min_lines=45, 27 | key="ace", 28 | ) 29 | 30 | if content: 31 | st.subheader("Content") 32 | st.text(content) 33 | 34 | 35 | if __name__ == "__main__": 36 | main() 37 | -------------------------------------------------------------------------------- /streamlit_gallery/components/discourse/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/discourse/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/discourse/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from streamlit_discourse import st_discourse 4 | from streamlit_gallery.utils.readme import readme 5 | 6 | 7 | def main(): 8 | with readme("streamlit-discourse", st_discourse, __file__): 9 | st_discourse("discuss.streamlit.io", 8061, key="discourse") 10 | 11 | 12 | if __name__ == "__main__": 13 | main() 14 | -------------------------------------------------------------------------------- /streamlit_gallery/components/disqus/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/disqus/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/disqus/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from streamlit_disqus import st_disqus 4 | from streamlit_gallery.utils.readme import readme 5 | 6 | 7 | def main(): 8 | with readme("streamlit-disqus", st_disqus, __file__): 9 | st_disqus("streamlit-disqus-demo") 10 | 11 | 12 | if __name__ == "__main__": 13 | main() 14 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/README.md: -------------------------------------------------------------------------------- 1 | ### 1. Introduction 2 | 3 | Streamlit Elements is a component that gives you the tools to compose beautiful applications with Material UI widgets, Monaco, Nivo charts, and more. 4 | It also includes a feature to create draggable and resizable dashboards. 5 | 6 | #### Installation 7 | 8 | ```sh 9 | pip install streamlit-elements==0.1.* 10 | ``` 11 | 12 | **Caution**: It is recommended to pin the version to `0.1.*`. Future versions might introduce breaking API changes. 13 | 14 | #### Available elements and objects 15 | 16 | Here is a list of elements and objects you can import in your app: 17 | 18 | Element | Description 19 | :--------:|:----------- 20 | elements | Create a frame where elements will be displayed. 21 | dashboard | Build a draggable and resizable dashboard. 22 | mui | Material UI (MUI) widgets and icons. 23 | html | HTML objects. 24 | editor | Monaco code and diff editor that powers Visual Studio Code. 25 | nivo | Nivo chart library. 26 | media | Media player. 27 | sync | Callback to synchronize Streamlit's session state with elements events data. 28 | lazy | Defer a callback call until another non-lazy callback is called. 29 | 30 | #### Caution 31 | 32 | - A few Material UI widgets may not work as expected (ie. modal dialogs and snackbars). 33 | - Using many element frames can significantly impact your app's performance. Try to gather elements in few frames at most. 34 | 35 | --- 36 | 37 | ### 2. Display elements 38 | 39 | #### 2.1. Create an element with a child 40 | 41 | ```python 42 | # First, import the elements you need 43 | 44 | from streamlit_elements import elements, mui, html 45 | 46 | # Create a frame where Elements widgets will be displayed. 47 | # 48 | # Elements widgets will not render outside of this frame. 49 | # Native Streamlit widgets will not render inside this frame. 50 | # 51 | # elements() takes a key as parameter. 52 | # This key can't be reused by another frame or Streamlit widget. 53 | 54 | with elements("new_element"): 55 | 56 | # Let's create a Typography element with "Hello world" as children. 57 | # The first step is to check Typography's documentation on MUI: 58 | # https://mui.com/components/typography/ 59 | # 60 | # Here is how you would write it in React JSX: 61 | # 62 | # 63 | # Hello world 64 | # 65 | 66 | mui.Typography("Hello world") 67 | ``` 68 | - MUI Typography: https://mui.com/components/typography/ 69 | 70 | --- 71 | 72 | #### 2.2. Create an element with multiple children 73 | 74 | ```python 75 | with elements("multiple_children"): 76 | 77 | # You have access to Material UI icons using: mui.icon.IconNameHere 78 | # 79 | # Multiple children can be added in a single element. 80 | # 81 | # 86 | 87 | mui.Button( 88 | mui.icon.EmojiPeople, 89 | mui.icon.DoubleArrow, 90 | "Button with multiple children" 91 | ) 92 | 93 | # You can also add children to an element using a 'with' statement. 94 | # 95 | # 102 | 103 | with mui.Button: 104 | mui.icon.EmojiPeople() 105 | mui.icon.DoubleArrow() 106 | mui.Typography("Button with multiple children") 107 | ``` 108 | - MUI button: https://mui.com/components/buttons/ 109 | - MUI icons: https://mui.com/components/material-icons/ 110 | 111 | --- 112 | 113 | #### 2.3. Create an element with nested children 114 | 115 | ```python 116 | with elements("nested_children"): 117 | 118 | # You can nest children using multiple 'with' statements. 119 | # 120 | # 121 | # 122 | #

Hello world

123 | #

Goodbye world

124 | #
125 | #
126 | 127 | with mui.Paper: 128 | with mui.Typography: 129 | html.p("Hello world") 130 | html.p("Goodbye world") 131 | ``` 132 | - MUI paper: https://mui.com/components/paper/ 133 | 134 | --- 135 | 136 | #### 2.4. Add properties to an element 137 | 138 | ```python 139 | with elements("properties"): 140 | 141 | # You can add properties to elements with named parameters. 142 | # 143 | # To find all available parameters for a given element, you can 144 | # refer to its related documentation on mui.com for MUI widgets, 145 | # on https://microsoft.github.io/monaco-editor/ for Monaco editor, 146 | # and so on. 147 | # 148 | # 149 | # 150 | # 151 | 152 | with mui.Paper(elevation=3, variant="outlined", square=True): 153 | mui.TextField( 154 | label="My text input", 155 | defaultValue="Type here", 156 | variant="outlined", 157 | ) 158 | 159 | # If you must pass a parameter which is also a Python keyword, you can append an 160 | # underscore to avoid a syntax error. 161 | # 162 | # 163 | 164 | mui.Collapse(in_=True) 165 | 166 | # mui.collapse(in=True) 167 | # > Syntax error: 'in' is a Python keyword: 168 | ``` 169 | - MUI text field: https://mui.com/components/text-fields/ 170 | 171 | --- 172 | 173 | #### 2.5. Apply custom CSS styles 174 | 175 | ##### 2.5.1. Material UI elements 176 | 177 | ```python 178 | with elements("style_mui_sx"): 179 | 180 | # For Material UI elements, use the 'sx' property. 181 | # 182 | # 191 | # Some text in a styled box 192 | # 193 | 194 | mui.Box( 195 | "Some text in a styled box", 196 | sx={ 197 | "bgcolor": "background.paper", 198 | "boxShadow": 1, 199 | "borderRadius": 2, 200 | "p": 2, 201 | "minWidth": 300, 202 | } 203 | ) 204 | ``` 205 | - MUI's **sx** property: https://mui.com/system/the-sx-prop/ 206 | 207 | ##### 2.5.2. Other elements 208 | 209 | ```python 210 | with elements("style_elements_css"): 211 | 212 | # For any other element, use the 'css' property. 213 | # 214 | #
222 | # This has a hotpink background 223 | #
224 | 225 | html.div( 226 | "This has a hotpink background", 227 | css={ 228 | "backgroundColor": "hotpink", 229 | "&:hover": { 230 | "color": "lightgreen" 231 | } 232 | } 233 | ) 234 | ``` 235 | - Emotion's **css** property: https://emotion.sh/docs/css-prop#object-styles 236 | 237 | --- 238 | 239 | ### 3. Callbacks 240 | 241 | #### 3.1. Retrieve an element's data 242 | 243 | ```python 244 | import streamlit as st 245 | 246 | with elements("callbacks_retrieve_data"): 247 | 248 | # Some element allows executing a callback on specific event. 249 | # 250 | # const [name, setName] = React.useState("") 251 | # const handleChange = (event) => { 252 | # // You can see here that a text field value 253 | # // is stored in event.target.value 254 | # setName(event.target.value) 255 | # } 256 | # 257 | # 261 | 262 | # Initialize a new item in session state called "my_text" 263 | if "my_text" not in st.session_state: 264 | st.session_state.my_text = "" 265 | 266 | # When text field changes, this function will be called. 267 | # To know which parameters are passed to the callback, 268 | # you can refer to the element's documentation. 269 | def handle_change(event): 270 | st.session_state.my_text = event.target.value 271 | 272 | # Here we display what we have typed in our text field 273 | mui.Typography(st.session_state.my_text) 274 | 275 | # And here we give our 'handle_change' callback to the 'onChange' 276 | # property of the text field. 277 | mui.TextField(label="Input some text here", onChange=handle_change) 278 | ``` 279 | - MUI text field event: https://mui.com/components/text-fields/#uncontrolled-vs-controlled 280 | - MUI text field API: https://mui.com/api/text-field/ 281 | 282 | --- 283 | 284 | #### 3.2. Synchronize a session state item with an element event using sync() 285 | 286 | ```python 287 | with elements("callbacks_sync"): 288 | 289 | # If you just want to store callback parameters into Streamlit's session state 290 | # like above, you can also use the special function sync(). 291 | # 292 | # When an onChange event occurs, the callback is called with an event data object 293 | # as argument. In the example below, we are synchronizing that event data object with 294 | # the session state item 'my_event'. 295 | # 296 | # If an event passes more than one parameter, you can synchronize as many session state item 297 | # as needed like so: 298 | # >>> sync("my_first_param", "my_second_param") 299 | # 300 | # If you want to ignore the first parameter of an event but keep synchronizing the second, 301 | # pass None to sync: 302 | # >>> sync(None, "second_parameter_to_keep") 303 | 304 | from streamlit_elements import sync 305 | 306 | if "my_event" not in st.session_state: 307 | st.session_state.my_event = None 308 | 309 | if st.session_state.my_event is not None: 310 | text = st.session_state.my_event.target.value 311 | else: 312 | text = "" 313 | 314 | mui.Typography(text) 315 | mui.TextField(label="Input some text here", onChange=sync("my_event")) 316 | ``` 317 | 318 | --- 319 | 320 | #### 3.3. Avoid too many reloads with lazy() 321 | 322 | ```python 323 | with elements("callbacks_lazy"): 324 | 325 | # With the two first examples, each time you input a letter into the text field, 326 | # the callback is invoked but the whole app is reloaded as well. 327 | # 328 | # To avoid reloading the whole app on every input, you can wrap your callback with 329 | # lazy(). This will defer the callback invocation until another non-lazy callback 330 | # is invoked. This can be useful to implement forms. 331 | 332 | from streamlit_elements import lazy 333 | 334 | if "first_name" not in st.session_state: 335 | st.session_state.first_name = None 336 | st.session_state.last_name = None 337 | 338 | if st.session_state.first_name is not None: 339 | first_name = st.session_state.first_name.target.value 340 | else: 341 | first_name = "John" 342 | 343 | if st.session_state.last_name is not None: 344 | last_name = st.session_state.last_name.target.value 345 | else: 346 | last_name = "Doe" 347 | 348 | def set_last_name(event): 349 | st.session_state.last_name = event 350 | 351 | # Display first name and last name 352 | mui.Typography("Your first name: ", first_name) 353 | mui.Typography("Your last name: ", last_name) 354 | 355 | # Lazily synchronize onChange with first_name and last_name state. 356 | # Inputting some text won't synchronize the value yet. 357 | mui.TextField(label="First name", onChange=lazy(sync("first_name"))) 358 | 359 | # You can also pass regular python functions to lazy(). 360 | mui.TextField(label="Last name", onChange=lazy(set_last_name)) 361 | 362 | # Here we give a non-lazy callback to onClick using sync(). 363 | # We are not interested in getting onClick event data object, 364 | # so we call sync() with no argument. 365 | # 366 | # You can use either sync() or a regular python function. 367 | # As long as the callback is not wrapped with lazy(), its invocation will 368 | # also trigger every other defered callbacks. 369 | mui.Button("Update first namd and last name", onClick=sync()) 370 | ``` 371 | 372 | --- 373 | 374 | #### 3.4. Invoke a callback when a sequence is pressed using event.Hotkey() 375 | 376 | ```python 377 | with elements("callbacks_hotkey"): 378 | 379 | # Invoke a callback when a specific hotkey sequence is pressed. 380 | # 381 | # For more information regarding sequences syntax and supported keys, 382 | # go to Mousetrap's project page linked below. 383 | # 384 | # /!\ Hotkeys work if you don't have focus on Streamlit Elements's frame /!\ 385 | # /!\ As with other callbacks, this reruns the whole app /!\ 386 | 387 | from streamlit_elements import event 388 | 389 | def hotkey_pressed(): 390 | print("Hotkey pressed") 391 | 392 | event.Hotkey("g", hotkey_pressed) 393 | 394 | # If you want your hotkey to work even in text fields, set bind_inputs to True. 395 | event.Hotkey("h", hotkey_pressed, bindInputs=True) 396 | mui.TextField(label="Try pressing 'h' while typing some text here.") 397 | 398 | # If you want to override default hotkeys (ie. ctrl+f to search in page), 399 | # set overrideDefault to True. 400 | event.Hotkey("ctrl+f", hotkey_pressed, overrideDefault=True) 401 | ``` 402 | - Mousetrap: https://craig.is/killing/mice 403 | - Github page: https://github.com/ccampbell/mousetrap 404 | 405 | --- 406 | 407 | #### 3.5. Invoke a callback periodically using event.Interval() 408 | 409 | ```python 410 | with elements("callbacks_interval"): 411 | 412 | # Invoke a callback every n seconds. 413 | # 414 | # /!\ As with other callbacks, this reruns the whole app /!\ 415 | 416 | def call_every_second(): 417 | print("Hello world") 418 | 419 | event.Interval(1, call_every_second) 420 | ``` 421 | 422 | --- 423 | 424 | ### 4. Draggable and resizable dashboard 425 | 426 | ```python 427 | with elements("dashboard"): 428 | 429 | # You can create a draggable and resizable dashboard using 430 | # any element available in Streamlit Elements. 431 | 432 | from streamlit_elements import dashboard 433 | 434 | # First, build a default layout for every element you want to include in your dashboard 435 | 436 | layout = [ 437 | # Parameters: element_identifier, x_pos, y_pos, width, height, [item properties...] 438 | dashboard.Item("first_item", 0, 0, 2, 2), 439 | dashboard.Item("second_item", 2, 0, 2, 2, isDraggable=False, moved=False), 440 | dashboard.Item("third_item", 0, 2, 1, 1, isResizable=False), 441 | ] 442 | 443 | # Next, create a dashboard layout using the 'with' syntax. It takes the layout 444 | # as first parameter, plus additional properties you can find in the GitHub links below. 445 | 446 | with dashboard.Grid(layout): 447 | mui.Paper("First item", key="first_item") 448 | mui.Paper("Second item (cannot drag)", key="second_item") 449 | mui.Paper("Third item (cannot resize)", key="third_item") 450 | 451 | # If you want to retrieve updated layout values as the user move or resize dashboard items, 452 | # you can pass a callback to the onLayoutChange event parameter. 453 | 454 | def handle_layout_change(updated_layout): 455 | # You can save the layout in a file, or do anything you want with it. 456 | # You can pass it back to dashboard.Grid() if you want to restore a saved layout. 457 | print(updated_layout) 458 | 459 | with dashboard.Grid(layout, onLayoutChange=handle_layout_change): 460 | mui.Paper("First item", key="first_item") 461 | mui.Paper("Second item (cannot drag)", key="second_item") 462 | mui.Paper("Third item (cannot resize)", key="third_item") 463 | ``` 464 | - Dashboard item properties: https://github.com/react-grid-layout/react-grid-layout#grid-item-props 465 | - Dashboard grid properties (Streamlit Elements uses the Responsive grid layout): 466 | - https://github.com/react-grid-layout/react-grid-layout#grid-layout-props 467 | - https://github.com/react-grid-layout/react-grid-layout#responsive-grid-layout-props 468 | 469 | --- 470 | 471 | ### 5. Other third-party elements 472 | 473 | #### 5.1. Monaco code and diff editor 474 | 475 | ```python 476 | with elements("monaco_editors"): 477 | 478 | # Streamlit Elements embeds Monaco code and diff editor that powers Visual Studio Code. 479 | # You can configure editor's behavior and features with the 'options' parameter. 480 | # 481 | # Streamlit Elements uses an unofficial React implementation (GitHub links below for 482 | # documentation). 483 | 484 | from streamlit_elements import editor 485 | 486 | if "content" not in st.session_state: 487 | st.session_state.content = "Default value" 488 | 489 | mui.Typography("Content: ", st.session_state.content) 490 | 491 | def update_content(value): 492 | st.session_state.content = value 493 | 494 | editor.Monaco( 495 | height=300, 496 | defaultValue=st.session_state.content, 497 | onChange=lazy(update_content) 498 | ) 499 | 500 | mui.Button("Update content", onClick=sync()) 501 | 502 | editor.MonacoDiff( 503 | original="Happy Streamlit-ing!", 504 | modified="Happy Streamlit-in' with Elements!", 505 | height=300, 506 | ) 507 | ``` 508 | - Monaco examples and properties: https://github.com/suren-atoyan/monaco-react 509 | - Code editor options: https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html 510 | - Diff editor options: https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneDiffEditorConstructionOptions.html 511 | - Monaco project page: https://microsoft.github.io/monaco-editor/ 512 | 513 | --- 514 | 515 | #### 5.2. Nivo charts 516 | 517 | ```python 518 | with elements("nivo_charts"): 519 | 520 | # Streamlit Elements includes 45 dataviz components powered by Nivo. 521 | 522 | from streamlit_elements import nivo 523 | 524 | DATA = [ 525 | { "taste": "fruity", "chardonay": 93, "carmenere": 61, "syrah": 114 }, 526 | { "taste": "bitter", "chardonay": 91, "carmenere": 37, "syrah": 72 }, 527 | { "taste": "heavy", "chardonay": 56, "carmenere": 95, "syrah": 99 }, 528 | { "taste": "strong", "chardonay": 64, "carmenere": 90, "syrah": 30 }, 529 | { "taste": "sunny", "chardonay": 119, "carmenere": 94, "syrah": 103 }, 530 | ] 531 | 532 | with mui.Box(sx={"height": 500}): 533 | nivo.Radar( 534 | data=DATA, 535 | keys=[ "chardonay", "carmenere", "syrah" ], 536 | indexBy="taste", 537 | valueFormat=">-.2f", 538 | margin={ "top": 70, "right": 80, "bottom": 40, "left": 80 }, 539 | borderColor={ "from": "color" }, 540 | gridLabelOffset=36, 541 | dotSize=10, 542 | dotColor={ "theme": "background" }, 543 | dotBorderWidth=2, 544 | motionConfig="wobbly", 545 | legends=[ 546 | { 547 | "anchor": "top-left", 548 | "direction": "column", 549 | "translateX": -50, 550 | "translateY": -40, 551 | "itemWidth": 80, 552 | "itemHeight": 20, 553 | "itemTextColor": "#999", 554 | "symbolSize": 12, 555 | "symbolShape": "circle", 556 | "effects": [ 557 | { 558 | "on": "hover", 559 | "style": { 560 | "itemTextColor": "#000" 561 | } 562 | } 563 | ] 564 | } 565 | ], 566 | theme={ 567 | "background": "#FFFFFF", 568 | "textColor": "#31333F", 569 | "tooltip": { 570 | "container": { 571 | "background": "#FFFFFF", 572 | "color": "#31333F", 573 | } 574 | } 575 | } 576 | ) 577 | ``` 578 | - Nivo charts: https://nivo.rocks/ 579 | - Github page: https://github.com/plouc/nivo 580 | 581 | --- 582 | 583 | #### 5.3. Media player 584 | 585 | ```python 586 | with elements("media_player"): 587 | 588 | # Play video from many third-party sources: YouTube, Facebook, Twitch, 589 | # SoundCloud, Streamable, Vimeo, Wistia, Mixcloud, DailyMotion and Kaltura. 590 | # 591 | # This element is powered by ReactPlayer (GitHub link below). 592 | 593 | from streamlit_elements import media 594 | 595 | media.Player(url="https://www.youtube.com/watch?v=iik25wqIuFo", controls=True) 596 | ``` 597 | - ReactPlayer properties: https://github.com/cookpete/react-player#props 598 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/elements/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/__init__.py: -------------------------------------------------------------------------------- 1 | from .card import Card 2 | from .dashboard import Dashboard 3 | from .datagrid import DataGrid 4 | from .editor import Editor 5 | from .pie import Pie 6 | from .player import Player 7 | from .radar import Radar 8 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/card.py: -------------------------------------------------------------------------------- 1 | from streamlit_elements import mui 2 | from .dashboard import Dashboard 3 | 4 | 5 | class Card(Dashboard.Item): 6 | 7 | DEFAULT_CONTENT = ( 8 | "This impressive paella is a perfect party dish and a fun meal to cook " 9 | "together with your guests. Add 1 cup of frozen peas along with the mussels, " 10 | "if you like." 11 | ) 12 | 13 | def __call__(self, content): 14 | with mui.Card(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): 15 | mui.CardHeader( 16 | title="Shrimp and Chorizo Paella", 17 | subheader="September 14, 2016", 18 | avatar=mui.Avatar("R", sx={"bgcolor": "red"}), 19 | action=mui.IconButton(mui.icon.MoreVert), 20 | className=self._draggable_class, 21 | ) 22 | mui.CardMedia( 23 | component="img", 24 | height=194, 25 | image="https://mui.com/static/images/cards/paella.jpg", 26 | alt="Paella dish", 27 | ) 28 | 29 | with mui.CardContent(sx={"flex": 1}): 30 | mui.Typography(content) 31 | 32 | with mui.CardActions(disableSpacing=True): 33 | mui.IconButton(mui.icon.Favorite) 34 | mui.IconButton(mui.icon.Share) 35 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/dashboard.py: -------------------------------------------------------------------------------- 1 | from uuid import uuid4 2 | from abc import ABC, abstractmethod 3 | from streamlit_elements import dashboard, mui 4 | from contextlib import contextmanager 5 | 6 | 7 | class Dashboard: 8 | 9 | DRAGGABLE_CLASS = "draggable" 10 | 11 | def __init__(self): 12 | self._layout = [] 13 | 14 | def _register(self, item): 15 | self._layout.append(item) 16 | 17 | @contextmanager 18 | def __call__(self, **props): 19 | # Draggable classname query selector. 20 | props["draggableHandle"] = f".{Dashboard.DRAGGABLE_CLASS}" 21 | 22 | with dashboard.Grid(self._layout, **props): 23 | yield 24 | 25 | class Item(ABC): 26 | 27 | def __init__(self, board, x, y, w, h, **item_props): 28 | self._key = str(uuid4()) 29 | self._draggable_class = Dashboard.DRAGGABLE_CLASS 30 | self._dark_mode = True 31 | board._register(dashboard.Item(self._key, x, y, w, h, **item_props)) 32 | 33 | def _switch_theme(self): 34 | self._dark_mode = not self._dark_mode 35 | 36 | @contextmanager 37 | def title_bar(self, padding="5px 15px 5px 15px", dark_switcher=True): 38 | with mui.Stack( 39 | className=self._draggable_class, 40 | alignItems="center", 41 | direction="row", 42 | spacing=1, 43 | sx={ 44 | "padding": padding, 45 | "borderBottom": 1, 46 | "borderColor": "divider", 47 | }, 48 | ): 49 | yield 50 | 51 | if dark_switcher: 52 | if self._dark_mode: 53 | mui.IconButton(mui.icon.DarkMode, onClick=self._switch_theme) 54 | else: 55 | mui.IconButton(mui.icon.LightMode, sx={"color": "#ffc107"}, onClick=self._switch_theme) 56 | 57 | @abstractmethod 58 | def __call__(self): 59 | """Show elements.""" 60 | raise NotImplementedError 61 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/datagrid.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from streamlit_elements import mui 4 | from .dashboard import Dashboard 5 | 6 | 7 | class DataGrid(Dashboard.Item): 8 | 9 | DEFAULT_COLUMNS = [ 10 | { "field": 'id', "headerName": 'ID', "width": 90 }, 11 | { "field": 'firstName', "headerName": 'First name', "width": 150, "editable": True, }, 12 | { "field": 'lastName', "headerName": 'Last name', "width": 150, "editable": True, }, 13 | { "field": 'age', "headerName": 'Age', "type": 'number', "width": 110, "editable": True, }, 14 | ] 15 | DEFAULT_ROWS = [ 16 | { "id": 1, "lastName": 'Snow', "firstName": 'Jon', "age": 35 }, 17 | { "id": 2, "lastName": 'Lannister', "firstName": 'Cersei', "age": 42 }, 18 | { "id": 3, "lastName": 'Lannister', "firstName": 'Jaime', "age": 45 }, 19 | { "id": 4, "lastName": 'Stark', "firstName": 'Arya', "age": 16 }, 20 | { "id": 5, "lastName": 'Targaryen', "firstName": 'Daenerys', "age": None }, 21 | { "id": 6, "lastName": 'Melisandre', "firstName": None, "age": 150 }, 22 | { "id": 7, "lastName": 'Clifford', "firstName": 'Ferrara', "age": 44 }, 23 | { "id": 8, "lastName": 'Frances', "firstName": 'Rossini', "age": 36 }, 24 | { "id": 9, "lastName": 'Roxie', "firstName": 'Harvey', "age": 65 }, 25 | ] 26 | 27 | def _handle_edit(self, params): 28 | print(params) 29 | 30 | def __call__(self, json_data): 31 | try: 32 | data = json.loads(json_data) 33 | except json.JSONDecodeError: 34 | data = self.DEFAULT_ROWS 35 | 36 | with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): 37 | with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): 38 | mui.icon.ViewCompact() 39 | mui.Typography("Data grid") 40 | 41 | with mui.Box(sx={"flex": 1, "minHeight": 0}): 42 | mui.DataGrid( 43 | columns=self.DEFAULT_COLUMNS, 44 | rows=data, 45 | pageSize=5, 46 | rowsPerPageOptions=[5], 47 | checkboxSelection=True, 48 | disableSelectionOnClick=True, 49 | onCellEditCommit=self._handle_edit, 50 | ) 51 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/editor.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | from streamlit_elements import mui, editor, sync, lazy 3 | from .dashboard import Dashboard 4 | 5 | 6 | class Editor(Dashboard.Item): 7 | 8 | def __init__(self, *args, **kwargs): 9 | super().__init__(*args, **kwargs) 10 | 11 | self._dark_theme = False 12 | self._index = 0 13 | self._tabs = {} 14 | self._editor_box_style = { 15 | "flex": 1, 16 | "minHeight": 0, 17 | "borderBottom": 1, 18 | "borderTop": 1, 19 | "borderColor": "divider" 20 | } 21 | 22 | def _change_tab(self, _, index): 23 | self._index = index 24 | 25 | def update_content(self, label, content): 26 | self._tabs[label]["content"] = content 27 | 28 | def add_tab(self, label, default_content, language): 29 | self._tabs[label] = { 30 | "content": default_content, 31 | "language": language 32 | } 33 | 34 | def get_content(self, label): 35 | return self._tabs[label]["content"] 36 | 37 | def __call__(self): 38 | with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): 39 | 40 | with self.title_bar("0px 15px 0px 15px"): 41 | mui.icon.Terminal() 42 | mui.Typography("Editor") 43 | 44 | with mui.Tabs(value=self._index, onChange=self._change_tab, scrollButtons=True, variant="scrollable", sx={"flex": 1}): 45 | for label in self._tabs.keys(): 46 | mui.Tab(label=label) 47 | 48 | for index, (label, tab) in enumerate(self._tabs.items()): 49 | with mui.Box(sx=self._editor_box_style, hidden=(index != self._index)): 50 | editor.Monaco( 51 | css={"padding": "0 2px 0 2px"}, 52 | defaultValue=tab["content"], 53 | language=tab["language"], 54 | onChange=lazy(partial(self.update_content, label)), 55 | theme="vs-dark" if self._dark_mode else "light", 56 | path=label, 57 | options={ 58 | "wordWrap": True 59 | } 60 | ) 61 | 62 | with mui.Stack(direction="row", spacing=2, alignItems="center", sx={"padding": "10px"}): 63 | mui.Button("Apply", variant="contained", onClick=sync()) 64 | mui.Typography("Or press ctrl+s", sx={"flex": 1}) 65 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/pie.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from streamlit_elements import nivo, mui 4 | from .dashboard import Dashboard 5 | 6 | 7 | class Pie(Dashboard.Item): 8 | 9 | DEFAULT_DATA = [ 10 | { "id": "java", "label": "java", "value": 465, "color": "hsl(128, 70%, 50%)" }, 11 | { "id": "rust", "label": "rust", "value": 140, "color": "hsl(178, 70%, 50%)" }, 12 | { "id": "scala", "label": "scala", "value": 40, "color": "hsl(322, 70%, 50%)" }, 13 | { "id": "ruby", "label": "ruby", "value": 439, "color": "hsl(117, 70%, 50%)" }, 14 | { "id": "elixir", "label": "elixir", "value": 366, "color": "hsl(286, 70%, 50%)" } 15 | ] 16 | 17 | def __init__(self, *args, **kwargs): 18 | super().__init__(*args, **kwargs) 19 | self._theme = { 20 | "dark": { 21 | "background": "#252526", 22 | "textColor": "#FAFAFA", 23 | "tooltip": { 24 | "container": { 25 | "background": "#3F3F3F", 26 | "color": "FAFAFA", 27 | } 28 | } 29 | }, 30 | "light": { 31 | "background": "#FFFFFF", 32 | "textColor": "#31333F", 33 | "tooltip": { 34 | "container": { 35 | "background": "#FFFFFF", 36 | "color": "#31333F", 37 | } 38 | } 39 | } 40 | } 41 | 42 | def __call__(self, json_data): 43 | try: 44 | data = json.loads(json_data) 45 | except json.JSONDecodeError: 46 | data = self.DEFAULT_DATA 47 | 48 | with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): 49 | with self.title_bar(): 50 | mui.icon.PieChart() 51 | mui.Typography("Pie chart", sx={"flex": 1}) 52 | 53 | with mui.Box(sx={"flex": 1, "minHeight": 0}): 54 | nivo.Pie( 55 | data=data, 56 | theme=self._theme["dark" if self._dark_mode else "light"], 57 | margin={ "top": 40, "right": 80, "bottom": 80, "left": 80 }, 58 | innerRadius=0.5, 59 | padAngle=0.7, 60 | cornerRadius=3, 61 | activeOuterRadiusOffset=8, 62 | borderWidth=1, 63 | borderColor={ 64 | "from": "color", 65 | "modifiers": [ 66 | [ 67 | "darker", 68 | 0.2, 69 | ] 70 | ] 71 | }, 72 | arcLinkLabelsSkipAngle=10, 73 | arcLinkLabelsTextColor="grey", 74 | arcLinkLabelsThickness=2, 75 | arcLinkLabelsColor={ "from": "color" }, 76 | arcLabelsSkipAngle=10, 77 | arcLabelsTextColor={ 78 | "from": "color", 79 | "modifiers": [ 80 | [ 81 | "darker", 82 | 2 83 | ] 84 | ] 85 | }, 86 | defs=[ 87 | { 88 | "id": "dots", 89 | "type": "patternDots", 90 | "background": "inherit", 91 | "color": "rgba(255, 255, 255, 0.3)", 92 | "size": 4, 93 | "padding": 1, 94 | "stagger": True 95 | }, 96 | { 97 | "id": "lines", 98 | "type": "patternLines", 99 | "background": "inherit", 100 | "color": "rgba(255, 255, 255, 0.3)", 101 | "rotation": -45, 102 | "lineWidth": 6, 103 | "spacing": 10 104 | } 105 | ], 106 | fill=[ 107 | { "match": { "id": "ruby" }, "id": "dots" }, 108 | { "match": { "id": "c" }, "id": "dots" }, 109 | { "match": { "id": "go" }, "id": "dots" }, 110 | { "match": { "id": "python" }, "id": "dots" }, 111 | { "match": { "id": "scala" }, "id": "lines" }, 112 | { "match": { "id": "lisp" }, "id": "lines" }, 113 | { "match": { "id": "elixir" }, "id": "lines" }, 114 | { "match": { "id": "javascript" }, "id": "lines" } 115 | ], 116 | legends=[ 117 | { 118 | "anchor": "bottom", 119 | "direction": "row", 120 | "justify": False, 121 | "translateX": 0, 122 | "translateY": 56, 123 | "itemsSpacing": 0, 124 | "itemWidth": 100, 125 | "itemHeight": 18, 126 | "itemTextColor": "#999", 127 | "itemDirection": "left-to-right", 128 | "itemOpacity": 1, 129 | "symbolSize": 18, 130 | "symbolShape": "circle", 131 | "effects": [ 132 | { 133 | "on": "hover", 134 | "style": { 135 | "itemTextColor": "#000" 136 | } 137 | } 138 | ] 139 | } 140 | ] 141 | ) 142 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/player.py: -------------------------------------------------------------------------------- 1 | from streamlit_elements import media, mui, sync, lazy 2 | from .dashboard import Dashboard 3 | 4 | class Player(Dashboard.Item): 5 | 6 | def __init__(self, *args, **kwargs): 7 | super().__init__(*args, **kwargs) 8 | self._url = "https://www.youtube.com/watch?v=CmSKVW1v0xM" 9 | 10 | def _set_url(self, event): 11 | self._url = event.target.value 12 | 13 | def __call__(self): 14 | with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): 15 | with self.title_bar(padding="10px 15px 10px 15px", dark_switcher=False): 16 | mui.icon.OndemandVideo() 17 | mui.Typography("Media player") 18 | 19 | with mui.Stack(direction="row", spacing=2, justifyContent="space-evenly", alignItems="center", sx={"padding": "10px"}): 20 | mui.TextField(defaultValue=self._url, label="URL", variant="standard", sx={"flex": 0.97}, onChange=lazy(self._set_url)) 21 | mui.IconButton(mui.icon.PlayCircleFilled, onClick=sync(), sx={"color": "primary.main"}) 22 | 23 | media.Player(self._url, controls=True, width="100%", height="100%") 24 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/dashboard/radar.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from streamlit_elements import mui, nivo 4 | from .dashboard import Dashboard 5 | 6 | 7 | class Radar(Dashboard.Item): 8 | 9 | DEFAULT_DATA = [ 10 | { "taste": "fruity", "chardonay": 93, "carmenere": 61, "syrah": 114 }, 11 | { "taste": "bitter", "chardonay": 91, "carmenere": 37, "syrah": 72 }, 12 | { "taste": "heavy", "chardonay": 56, "carmenere": 95, "syrah": 99 }, 13 | { "taste": "strong", "chardonay": 64, "carmenere": 90, "syrah": 30 }, 14 | { "taste": "sunny", "chardonay": 119, "carmenere": 94, "syrah": 103 }, 15 | ] 16 | 17 | def __init__(self, *args, **kwargs): 18 | super().__init__(*args, **kwargs) 19 | self._theme = { 20 | "dark": { 21 | "background": "#252526", 22 | "textColor": "#FAFAFA", 23 | "tooltip": { 24 | "container": { 25 | "background": "#3F3F3F", 26 | "color": "FAFAFA", 27 | } 28 | } 29 | }, 30 | "light": { 31 | "background": "#FFFFFF", 32 | "textColor": "#31333F", 33 | "tooltip": { 34 | "container": { 35 | "background": "#FFFFFF", 36 | "color": "#31333F", 37 | } 38 | } 39 | } 40 | } 41 | 42 | def __call__(self, json_data): 43 | try: 44 | data = json.loads(json_data) 45 | except json.JSONDecodeError: 46 | data = self.DEFAULT_DATA 47 | 48 | with mui.Paper(key=self._key, sx={"display": "flex", "flexDirection": "column", "borderRadius": 3, "overflow": "hidden"}, elevation=1): 49 | with self.title_bar(): 50 | mui.icon.Radar() 51 | mui.Typography("Radar chart", sx={"flex": 1}) 52 | 53 | with mui.Box(sx={"flex": 1, "minHeight": 0}): 54 | nivo.Radar( 55 | data=data, 56 | theme=self._theme["dark" if self._dark_mode else "light"], 57 | keys=[ "chardonay", "carmenere", "syrah" ], 58 | indexBy="taste", 59 | valueFormat=">-.2f", 60 | margin={ "top": 70, "right": 80, "bottom": 40, "left": 80 }, 61 | borderColor={ "from": "color" }, 62 | gridLabelOffset=36, 63 | dotSize=10, 64 | dotColor={ "theme": "background" }, 65 | dotBorderWidth=2, 66 | motionConfig="wobbly", 67 | legends=[ 68 | { 69 | "anchor": "top-left", 70 | "direction": "column", 71 | "translateX": -50, 72 | "translateY": -40, 73 | "itemWidth": 80, 74 | "itemHeight": 20, 75 | "itemTextColor": "#999", 76 | "symbolSize": 12, 77 | "symbolShape": "circle", 78 | "effects": [ 79 | { 80 | "on": "hover", 81 | "style": { 82 | "itemTextColor": "#000" 83 | } 84 | } 85 | ] 86 | } 87 | ] 88 | ) 89 | -------------------------------------------------------------------------------- /streamlit_gallery/components/elements/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import json 2 | import streamlit as st 3 | 4 | from pathlib import Path 5 | from streamlit import session_state as state 6 | from streamlit_elements import elements, sync, event 7 | from types import SimpleNamespace 8 | 9 | from .dashboard import Dashboard, Editor, Card, DataGrid, Radar, Pie, Player 10 | 11 | 12 | def main(): 13 | st.write( 14 | """ 15 | ✨ Streamlit Elements   [![GitHub][github_badge]][github_link] [![PyPI][pypi_badge]][pypi_link] 16 | ===================== 17 | 18 | Create a draggable and resizable dashboard in Streamlit, featuring Material UI widgets, 19 | Monaco editor (Visual Studio Code), Nivo charts, and more! 20 | 21 | [github_badge]: https://badgen.net/badge/icon/GitHub?icon=github&color=black&label 22 | [github_link]: https://github.com/okld/streamlit-elements 23 | 24 | [pypi_badge]: https://badgen.net/pypi/v/streamlit-elements?icon=pypi&color=black&label 25 | [pypi_link]: https://pypi.org/project/streamlit-elements 26 | """ 27 | ) 28 | 29 | with st.expander("GETTING STARTED"): 30 | st.write((Path(__file__).parent/"README.md").read_text()) 31 | 32 | st.title("") 33 | 34 | if "w" not in state: 35 | board = Dashboard() 36 | w = SimpleNamespace( 37 | dashboard=board, 38 | editor=Editor(board, 0, 0, 6, 11, minW=3, minH=3), 39 | player=Player(board, 0, 12, 6, 10, minH=5), 40 | pie=Pie(board, 6, 0, 6, 7, minW=3, minH=4), 41 | radar=Radar(board, 12, 7, 3, 7, minW=2, minH=4), 42 | card=Card(board, 6, 7, 3, 7, minW=2, minH=4), 43 | data_grid=DataGrid(board, 6, 13, 6, 7, minH=4), 44 | ) 45 | state.w = w 46 | 47 | w.editor.add_tab("Card content", Card.DEFAULT_CONTENT, "plaintext") 48 | w.editor.add_tab("Data grid", json.dumps(DataGrid.DEFAULT_ROWS, indent=2), "json") 49 | w.editor.add_tab("Radar chart", json.dumps(Radar.DEFAULT_DATA, indent=2), "json") 50 | w.editor.add_tab("Pie chart", json.dumps(Pie.DEFAULT_DATA, indent=2), "json") 51 | else: 52 | w = state.w 53 | 54 | with elements("demo"): 55 | event.Hotkey("ctrl+s", sync(), bindInputs=True, overrideDefault=True) 56 | 57 | with w.dashboard(rowHeight=57): 58 | w.editor() 59 | w.player() 60 | w.pie(w.editor.get_content("Pie chart")) 61 | w.radar(w.editor.get_content("Radar chart")) 62 | w.card(w.editor.get_content("Card content")) 63 | w.data_grid(w.editor.get_content("Data grid")) 64 | 65 | 66 | if __name__ == "__main__": 67 | st.set_page_config(layout="wide") 68 | main() 69 | -------------------------------------------------------------------------------- /streamlit_gallery/components/pandas_profiling/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/pandas_profiling/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/pandas_profiling/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import pandas_profiling 3 | import streamlit as st 4 | 5 | from streamlit_gallery.utils.readme import readme 6 | from streamlit_pandas_profiling import st_profile_report 7 | 8 | 9 | def main(): 10 | with readme("streamlit-pandas-profiling", st_profile_report, __file__): 11 | dataset = "https://storage.googleapis.com/tf-datasets/titanic/train.csv" 12 | 13 | df = pd.read_csv(dataset) 14 | pr = gen_profile_report(df, explorative=True) 15 | 16 | st.write(f"🔗 [Titanic dataset]({dataset})") 17 | st.write(df) 18 | 19 | with st.expander("REPORT", expanded=True): 20 | st_profile_report(pr) 21 | 22 | 23 | @st.cache(allow_output_mutation=True) 24 | def gen_profile_report(df, *report_args, **report_kwargs): 25 | return df.profile_report(*report_args, **report_kwargs) 26 | 27 | 28 | if __name__ == "__main__": 29 | main() 30 | -------------------------------------------------------------------------------- /streamlit_gallery/components/quill_editor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/quill_editor/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/quill_editor/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from streamlit_gallery.utils.readme import readme 4 | from streamlit_quill import st_quill 5 | 6 | 7 | def main(): 8 | with readme("streamlit-quill", st_quill, __file__): 9 | c1, c2 = st.columns([3, 1]) 10 | 11 | c2.subheader("Parameters") 12 | 13 | with c1: 14 | content = st_quill( 15 | placeholder="Write your text here", 16 | html=c2.checkbox("Return HTML", False), 17 | readonly=c2.checkbox("Read only", False), 18 | key="quill", 19 | ) 20 | 21 | if content: 22 | st.subheader("Content") 23 | st.text(content) 24 | 25 | 26 | if __name__ == "__main__": 27 | main() 28 | -------------------------------------------------------------------------------- /streamlit_gallery/components/react_player/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/components/react_player/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/components/react_player/streamlit_app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | 3 | from streamlit_player import st_player, _SUPPORTED_EVENTS 4 | from streamlit_gallery.utils.readme import readme 5 | 6 | 7 | def main(): 8 | with readme("streamlit-player", st_player, __file__): 9 | c1, c2, c3 = st.columns([3, 3, 2]) 10 | 11 | with c3: 12 | st.subheader("Parameters") 13 | 14 | options = { 15 | "events": st.multiselect("Events to listen", _SUPPORTED_EVENTS, ["onProgress"]), 16 | "progress_interval": 1000, 17 | "volume": st.slider("Volume", 0.0, 1.0, 1.0, .01), 18 | "playing": st.checkbox("Playing", False), 19 | "loop": st.checkbox("Loop", False), 20 | "controls": st.checkbox("Controls", True), 21 | "muted": st.checkbox("Muted", False), 22 | } 23 | 24 | with st.expander("SUPPORTED PLAYERS", expanded=True): 25 | st.write(""" 26 | - Dailymotion 27 | - Facebook 28 | - Mixcloud 29 | - SoundCloud 30 | - Streamable 31 | - Twitch 32 | - Vimeo 33 | - Wistia 34 | - YouTube 35 |

36 | """, unsafe_allow_html=True) 37 | 38 | 39 | with c1: 40 | url = st.text_input("First URL", "https://youtu.be/c9k8K1eII4g") 41 | event = st_player(url, **options, key="youtube_player") 42 | 43 | st.write(event) 44 | 45 | with c2: 46 | url = st.text_input("Second URL", "https://soundcloud.com/imaginedragons/demons") 47 | event = st_player(url, **options, key="soundcloud_player") 48 | 49 | st.write(event) 50 | 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /streamlit_gallery/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okld/streamlit-gallery/ad224abfd5c2bc43a2937fa17c6f392672330688/streamlit_gallery/utils/__init__.py -------------------------------------------------------------------------------- /streamlit_gallery/utils/page.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from typing import Callable, Optional 3 | 4 | 5 | def page_group(param): 6 | key = f"{__name__}_page_group_{param}" 7 | 8 | if key not in st.session_state: 9 | st.session_state[key] = PageGroup(param) 10 | 11 | return st.session_state[key] 12 | 13 | 14 | class PageGroup: 15 | 16 | def __init__(self, param): 17 | self._param: str = param 18 | self._default: str = None 19 | self._selected: str = None 20 | 21 | # Fix some rollback issues when multiple pages are selected in the same run. 22 | self._backup: Optional[str] = None 23 | 24 | @property 25 | def selected(self) -> bool: 26 | params = st.experimental_get_query_params() 27 | return params[self._param][0] if self._param in params else self._default 28 | 29 | def item(self, label: str, callback: Callable, default=False) -> None: 30 | self._backup = None 31 | 32 | key = f"{__name__}_{self._param}_{label}" 33 | page = self._normalize_label(label) 34 | 35 | if default: 36 | self._default = page 37 | 38 | selected = (page == self.selected) 39 | 40 | if selected: 41 | self._selected = callback 42 | 43 | st.session_state[key] = selected 44 | st.checkbox(label, key=key, disabled=selected, on_change=self._on_change, args=(page,)) 45 | 46 | def show(self) -> None: 47 | if self._selected is not None: 48 | self._selected() 49 | else: 50 | st.title("🤷 404 Not Found") 51 | 52 | def _on_change(self, page: str) -> None: 53 | params = st.experimental_get_query_params() 54 | 55 | if self._backup is None: 56 | if self._param in params: 57 | self._backup = params[self._param][0] 58 | params[self._param] = [page] 59 | else: 60 | params[self._param] = [self._backup] 61 | 62 | st.experimental_set_query_params(**params) 63 | 64 | def _normalize_label(self, label: str) -> str: 65 | return "".join(char.lower() for char in label if char.isascii()).strip().replace(" ", "-") 66 | -------------------------------------------------------------------------------- /streamlit_gallery/utils/readme.py: -------------------------------------------------------------------------------- 1 | import re 2 | import requests 3 | import streamlit as st 4 | 5 | from contextlib import contextmanager 6 | from pathlib import Path 7 | 8 | FILTER_SHARE = re.compile(r"^.*\[share_\w+\].*$", re.MULTILINE) 9 | 10 | 11 | @contextmanager 12 | def readme(project, usage=None, source=None): 13 | content = requests.get(f"https://raw.githubusercontent.com/okld/{project}/main/README.md").text 14 | st.markdown(FILTER_SHARE.sub("", content)) 15 | 16 | demo = st.container() 17 | 18 | if usage or source: 19 | st.title("") 20 | 21 | if usage: 22 | with st.expander("USAGE"): 23 | st.help(usage) 24 | 25 | if source: 26 | with st.expander("SOURCE"): 27 | st.code(Path(source).read_text()) 28 | 29 | with demo: 30 | yield 31 | --------------------------------------------------------------------------------