solara-ui 1.45.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
- prefix/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
- solara/__init__.py +124 -0
- solara/__main__.py +765 -0
- solara/_stores.py +297 -0
- solara/alias.py +6 -0
- solara/autorouting.py +555 -0
- solara/cache.py +305 -0
- solara/checks.html +71 -0
- solara/checks.py +227 -0
- solara/comm.py +28 -0
- solara/components/__init__.py +77 -0
- solara/components/alert.py +155 -0
- solara/components/applayout.py +397 -0
- solara/components/button.py +85 -0
- solara/components/card.py +87 -0
- solara/components/checkbox.py +50 -0
- solara/components/code_highlight_css.py +11 -0
- solara/components/code_highlight_css.vue +63 -0
- solara/components/columns.py +159 -0
- solara/components/component_vue.py +134 -0
- solara/components/cross_filter.py +335 -0
- solara/components/dataframe.py +546 -0
- solara/components/datatable.py +214 -0
- solara/components/datatable.vue +175 -0
- solara/components/details.py +56 -0
- solara/components/download.vue +35 -0
- solara/components/echarts.py +86 -0
- solara/components/echarts.vue +139 -0
- solara/components/figure_altair.py +39 -0
- solara/components/file_browser.py +181 -0
- solara/components/file_download.py +199 -0
- solara/components/file_drop.py +159 -0
- solara/components/file_drop.vue +83 -0
- solara/components/file_list_widget.vue +78 -0
- solara/components/head.py +27 -0
- solara/components/head_tag.py +49 -0
- solara/components/head_tag.vue +60 -0
- solara/components/image.py +173 -0
- solara/components/input.py +456 -0
- solara/components/input_text_area.py +86 -0
- solara/components/link.py +55 -0
- solara/components/markdown.py +441 -0
- solara/components/markdown_editor.py +33 -0
- solara/components/markdown_editor.vue +359 -0
- solara/components/matplotlib.py +74 -0
- solara/components/meta.py +47 -0
- solara/components/misc.py +333 -0
- solara/components/pivot_table.py +258 -0
- solara/components/pivot_table.vue +158 -0
- solara/components/progress.py +47 -0
- solara/components/select.py +182 -0
- solara/components/select.vue +27 -0
- solara/components/slider.py +442 -0
- solara/components/slider_date.vue +56 -0
- solara/components/spinner-solara.vue +105 -0
- solara/components/spinner.py +45 -0
- solara/components/sql_code.py +41 -0
- solara/components/sql_code.vue +125 -0
- solara/components/style.py +105 -0
- solara/components/switch.py +71 -0
- solara/components/tab_navigation.py +37 -0
- solara/components/title.py +90 -0
- solara/components/title.vue +38 -0
- solara/components/togglebuttons.py +200 -0
- solara/components/tooltip.py +61 -0
- solara/core.py +42 -0
- solara/datatypes.py +143 -0
- solara/express.py +241 -0
- solara/hooks/__init__.py +4 -0
- solara/hooks/dataframe.py +99 -0
- solara/hooks/misc.py +263 -0
- solara/hooks/use_reactive.py +151 -0
- solara/hooks/use_thread.py +129 -0
- solara/kitchensink.py +8 -0
- solara/lab/__init__.py +34 -0
- solara/lab/components/__init__.py +7 -0
- solara/lab/components/chat.py +215 -0
- solara/lab/components/confirmation_dialog.py +163 -0
- solara/lab/components/cross_filter.py +7 -0
- solara/lab/components/input_date.py +339 -0
- solara/lab/components/input_time.py +133 -0
- solara/lab/components/menu.py +181 -0
- solara/lab/components/menu.vue +38 -0
- solara/lab/components/tabs.py +274 -0
- solara/lab/components/theming.py +98 -0
- solara/lab/components/theming.vue +72 -0
- solara/lab/hooks/__init__.py +0 -0
- solara/lab/hooks/dataframe.py +2 -0
- solara/lab/toestand.py +3 -0
- solara/lab/utils/__init__.py +2 -0
- solara/lab/utils/cookies.py +5 -0
- solara/lab/utils/dataframe.py +165 -0
- solara/lab/utils/headers.py +5 -0
- solara/layout.py +44 -0
- solara/lifecycle.py +46 -0
- solara/minisettings.py +141 -0
- solara/py.typed +0 -0
- solara/reactive.py +99 -0
- solara/routing.py +268 -0
- solara/scope/__init__.py +88 -0
- solara/scope/types.py +55 -0
- solara/server/__init__.py +0 -0
- solara/server/app.py +527 -0
- solara/server/assets/custom.css +1 -0
- solara/server/assets/custom.js +1 -0
- solara/server/assets/favicon.png +0 -0
- solara/server/assets/favicon.svg +5 -0
- solara/server/assets/style.css +1681 -0
- solara/server/assets/theme-dark.css +437 -0
- solara/server/assets/theme-light.css +420 -0
- solara/server/assets/theme.js +3 -0
- solara/server/cdn_helper.py +91 -0
- solara/server/esm.py +71 -0
- solara/server/fastapi.py +5 -0
- solara/server/flask.py +297 -0
- solara/server/jupyter/__init__.py +2 -0
- solara/server/jupyter/cdn_handler.py +28 -0
- solara/server/jupyter/server_extension.py +40 -0
- solara/server/jupyter/solara.py +91 -0
- solara/server/jupytertools.py +46 -0
- solara/server/kernel.py +388 -0
- solara/server/kernel_context.py +467 -0
- solara/server/patch.py +564 -0
- solara/server/pyinstaller/__init__.py +9 -0
- solara/server/pyinstaller/hook-ipyreact.py +5 -0
- solara/server/pyinstaller/hook-ipyvuetify.py +5 -0
- solara/server/pyinstaller/hook-solara.py +9 -0
- solara/server/qt.py +113 -0
- solara/server/reload.py +251 -0
- solara/server/server.py +484 -0
- solara/server/settings.py +249 -0
- solara/server/shell.py +269 -0
- solara/server/starlette.py +770 -0
- solara/server/static/ansi.js +270 -0
- solara/server/static/highlight-dark.css +82 -0
- solara/server/static/highlight.css +43 -0
- solara/server/static/main-vuetify.js +272 -0
- solara/server/static/main.js +163 -0
- solara/server/static/solara_bootstrap.py +129 -0
- solara/server/static/sun.svg +23 -0
- solara/server/static/webworker.js +42 -0
- solara/server/telemetry.py +212 -0
- solara/server/templates/index.html.j2 +1 -0
- solara/server/templates/loader-plain.css +11 -0
- solara/server/templates/loader-plain.html +20 -0
- solara/server/templates/loader-solara.css +111 -0
- solara/server/templates/loader-solara.html +40 -0
- solara/server/templates/plain.html +82 -0
- solara/server/templates/solara.html.j2 +486 -0
- solara/server/threaded.py +84 -0
- solara/server/utils.py +44 -0
- solara/server/websocket.py +45 -0
- solara/settings.py +86 -0
- solara/tasks.py +893 -0
- solara/template/button.py +16 -0
- solara/template/markdown.py +42 -0
- solara/template/portal/.flake8 +6 -0
- solara/template/portal/.pre-commit-config.yaml +28 -0
- solara/template/portal/LICENSE +21 -0
- solara/template/portal/Procfile +7 -0
- solara/template/portal/mypy.ini +3 -0
- solara/template/portal/pyproject.toml +26 -0
- solara/template/portal/solara_portal/__init__.py +4 -0
- solara/template/portal/solara_portal/components/__init__.py +2 -0
- solara/template/portal/solara_portal/components/article.py +28 -0
- solara/template/portal/solara_portal/components/data.py +28 -0
- solara/template/portal/solara_portal/components/header.py +6 -0
- solara/template/portal/solara_portal/components/layout.py +6 -0
- solara/template/portal/solara_portal/content/articles/equis-in-vidi.md +85 -0
- solara/template/portal/solara_portal/content/articles/substiterat-vati.md +70 -0
- solara/template/portal/solara_portal/data.py +60 -0
- solara/template/portal/solara_portal/pages/__init__.py +67 -0
- solara/template/portal/solara_portal/pages/article/__init__.py +26 -0
- solara/template/portal/solara_portal/pages/tabular.py +29 -0
- solara/template/portal/solara_portal/pages/viz/__init__.py +70 -0
- solara/template/portal/solara_portal/pages/viz/overview.py +14 -0
- solara/test/__init__.py +0 -0
- solara/test/pytest_plugin.py +783 -0
- solara/toestand.py +998 -0
- solara/util.py +348 -0
- solara/validate_hooks.py +258 -0
- solara/website/__init__.py +0 -0
- solara/website/assets/custom.css +444 -0
- solara/website/assets/images/logo-small.png +0 -0
- solara/website/assets/images/logo.svg +17 -0
- solara/website/assets/images/logo_white.svg +50 -0
- solara/website/assets/theme.js +8 -0
- solara/website/components/__init__.py +5 -0
- solara/website/components/algolia.py +6 -0
- solara/website/components/algolia.vue +24 -0
- solara/website/components/algolia_api.vue +202 -0
- solara/website/components/breadcrumbs.py +28 -0
- solara/website/components/contact.py +144 -0
- solara/website/components/docs.py +143 -0
- solara/website/components/header.py +75 -0
- solara/website/components/mailchimp.py +12 -0
- solara/website/components/mailchimp.vue +47 -0
- solara/website/components/markdown.py +99 -0
- solara/website/components/markdown_nav.vue +34 -0
- solara/website/components/notebook.py +171 -0
- solara/website/components/sidebar.py +105 -0
- solara/website/pages/__init__.py +370 -0
- solara/website/pages/about/__init__.py +9 -0
- solara/website/pages/about/about.md +3 -0
- solara/website/pages/apps/__init__.py +16 -0
- solara/website/pages/apps/authorization/__init__.py +119 -0
- solara/website/pages/apps/authorization/admin.py +12 -0
- solara/website/pages/apps/authorization/users.py +12 -0
- solara/website/pages/apps/jupyter-dashboard-1.py +116 -0
- solara/website/pages/apps/layout-demo.py +40 -0
- solara/website/pages/apps/multipage/__init__.py +38 -0
- solara/website/pages/apps/multipage/page1.py +26 -0
- solara/website/pages/apps/multipage/page2.py +34 -0
- solara/website/pages/apps/scatter.py +136 -0
- solara/website/pages/apps/scrolling.py +63 -0
- solara/website/pages/apps/tutorial-streamlit.py +18 -0
- solara/website/pages/careers/__init__.py +27 -0
- solara/website/pages/changelog/__init__.py +10 -0
- solara/website/pages/changelog/changelog.md +372 -0
- solara/website/pages/contact/__init__.py +34 -0
- solara/website/pages/doc_use_download.py +85 -0
- solara/website/pages/documentation/__init__.py +90 -0
- solara/website/pages/documentation/advanced/__init__.py +9 -0
- solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
- solara/website/pages/documentation/advanced/content/10-howto/00-overview.md +6 -0
- solara/website/pages/documentation/advanced/content/10-howto/10-multipage.md +196 -0
- solara/website/pages/documentation/advanced/content/10-howto/20-layout.md +125 -0
- solara/website/pages/documentation/advanced/content/10-howto/30-testing.md +417 -0
- solara/website/pages/documentation/advanced/content/10-howto/31-debugging.md +69 -0
- solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +50 -0
- solara/website/pages/documentation/advanced/content/10-howto/50-ipywidget_libraries.md +124 -0
- solara/website/pages/documentation/advanced/content/15-reference/00-overview.md +3 -0
- solara/website/pages/documentation/advanced/content/15-reference/40-static_files.md +31 -0
- solara/website/pages/documentation/advanced/content/15-reference/41-asset-files.md +72 -0
- solara/website/pages/documentation/advanced/content/15-reference/60-static-site-generation.md +59 -0
- solara/website/pages/documentation/advanced/content/15-reference/70-search.md +34 -0
- solara/website/pages/documentation/advanced/content/15-reference/80-reloading.md +34 -0
- solara/website/pages/documentation/advanced/content/15-reference/90-notebook-support.md +7 -0
- solara/website/pages/documentation/advanced/content/15-reference/95-caching.md +148 -0
- solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +10 -0
- solara/website/pages/documentation/advanced/content/20-understanding/05-ipywidgets.md +35 -0
- solara/website/pages/documentation/advanced/content/20-understanding/06-ipyvuetify.md +42 -0
- solara/website/pages/documentation/advanced/content/20-understanding/10-reacton.md +28 -0
- solara/website/pages/documentation/advanced/content/20-understanding/12-reacton-basics.md +108 -0
- solara/website/pages/documentation/advanced/content/20-understanding/15-anatomy.md +23 -0
- solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +192 -0
- solara/website/pages/documentation/advanced/content/20-understanding/18-containers.md +166 -0
- solara/website/pages/documentation/advanced/content/20-understanding/20-solara.md +18 -0
- solara/website/pages/documentation/advanced/content/20-understanding/40-routing.md +256 -0
- solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +108 -0
- solara/website/pages/documentation/advanced/content/20-understanding/60-voila.md +12 -0
- solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +7 -0
- solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +187 -0
- solara/website/pages/documentation/advanced/content/40-development/00-overview.md +0 -0
- solara/website/pages/documentation/advanced/content/40-development/01-contribute.md +45 -0
- solara/website/pages/documentation/advanced/content/40-development/10-setup.md +76 -0
- solara/website/pages/documentation/api/__init__.py +19 -0
- solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +22 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +20 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +20 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +20 -0
- solara/website/pages/documentation/api/hooks/__init__.py +9 -0
- solara/website/pages/documentation/api/hooks/use_cross_filter.py +23 -0
- solara/website/pages/documentation/api/hooks/use_dark_effective.py +12 -0
- solara/website/pages/documentation/api/hooks/use_effect.md +43 -0
- solara/website/pages/documentation/api/hooks/use_effect.py +9 -0
- solara/website/pages/documentation/api/hooks/use_exception.py +31 -0
- solara/website/pages/documentation/api/hooks/use_memo.md +16 -0
- solara/website/pages/documentation/api/hooks/use_memo.py +9 -0
- solara/website/pages/documentation/api/hooks/use_previous.py +30 -0
- solara/website/pages/documentation/api/hooks/use_reactive.py +16 -0
- solara/website/pages/documentation/api/hooks/use_state.py +10 -0
- solara/website/pages/documentation/api/hooks/use_state_or_update.py +66 -0
- solara/website/pages/documentation/api/hooks/use_thread.md +64 -0
- solara/website/pages/documentation/api/hooks/use_thread.py +42 -0
- solara/website/pages/documentation/api/hooks/use_trait_observe.py +12 -0
- solara/website/pages/documentation/api/routing/__init__.py +9 -0
- solara/website/pages/documentation/api/routing/generate_routes.py +10 -0
- solara/website/pages/documentation/api/routing/generate_routes_directory.py +10 -0
- solara/website/pages/documentation/api/routing/resolve_path.py +35 -0
- solara/website/pages/documentation/api/routing/route.py +29 -0
- solara/website/pages/documentation/api/routing/use_route.py +76 -0
- solara/website/pages/documentation/api/routing/use_router.py +16 -0
- solara/website/pages/documentation/api/utilities/__init__.py +9 -0
- solara/website/pages/documentation/api/utilities/component_vue.py +10 -0
- solara/website/pages/documentation/api/utilities/computed.py +16 -0
- solara/website/pages/documentation/api/utilities/display.py +16 -0
- solara/website/pages/documentation/api/utilities/get_kernel_id.py +16 -0
- solara/website/pages/documentation/api/utilities/get_session_id.py +16 -0
- solara/website/pages/documentation/api/utilities/memoize.py +35 -0
- solara/website/pages/documentation/api/utilities/on_kernel_start.py +44 -0
- solara/website/pages/documentation/api/utilities/reactive.py +16 -0
- solara/website/pages/documentation/api/utilities/widget.py +104 -0
- solara/website/pages/documentation/components/__init__.py +12 -0
- solara/website/pages/documentation/components/advanced/__init__.py +9 -0
- solara/website/pages/documentation/components/advanced/link.py +25 -0
- solara/website/pages/documentation/components/advanced/meta.py +17 -0
- solara/website/pages/documentation/components/advanced/style.py +43 -0
- solara/website/pages/documentation/components/common.py +9 -0
- solara/website/pages/documentation/components/data/__init__.py +9 -0
- solara/website/pages/documentation/components/data/dataframe.py +44 -0
- solara/website/pages/documentation/components/data/pivot_table.py +81 -0
- solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
- solara/website/pages/documentation/components/enterprise/avatar.py +24 -0
- solara/website/pages/documentation/components/enterprise/avatar_menu.py +25 -0
- solara/website/pages/documentation/components/input/__init__.py +9 -0
- solara/website/pages/documentation/components/input/button.py +23 -0
- solara/website/pages/documentation/components/input/checkbox.py +10 -0
- solara/website/pages/documentation/components/input/file_browser.py +30 -0
- solara/website/pages/documentation/components/input/file_drop.py +76 -0
- solara/website/pages/documentation/components/input/input.py +43 -0
- solara/website/pages/documentation/components/input/select.py +22 -0
- solara/website/pages/documentation/components/input/slider.py +29 -0
- solara/website/pages/documentation/components/input/switch.py +10 -0
- solara/website/pages/documentation/components/input/togglebuttons.py +21 -0
- solara/website/pages/documentation/components/lab/__init__.py +9 -0
- solara/website/pages/documentation/components/lab/chat.py +109 -0
- solara/website/pages/documentation/components/lab/confirmation_dialog.py +55 -0
- solara/website/pages/documentation/components/lab/cookies_headers.py +48 -0
- solara/website/pages/documentation/components/lab/input_date.py +20 -0
- solara/website/pages/documentation/components/lab/input_time.py +15 -0
- solara/website/pages/documentation/components/lab/menu.py +22 -0
- solara/website/pages/documentation/components/lab/tab.py +25 -0
- solara/website/pages/documentation/components/lab/tabs.py +45 -0
- solara/website/pages/documentation/components/lab/task.py +11 -0
- solara/website/pages/documentation/components/lab/theming.py +74 -0
- solara/website/pages/documentation/components/lab/use_task.py +11 -0
- solara/website/pages/documentation/components/layout/__init__.py +9 -0
- solara/website/pages/documentation/components/layout/app_bar.py +16 -0
- solara/website/pages/documentation/components/layout/app_bar_title.py +16 -0
- solara/website/pages/documentation/components/layout/app_layout.py +24 -0
- solara/website/pages/documentation/components/layout/card.py +15 -0
- solara/website/pages/documentation/components/layout/card_actions.py +16 -0
- solara/website/pages/documentation/components/layout/column.py +30 -0
- solara/website/pages/documentation/components/layout/columns.py +27 -0
- solara/website/pages/documentation/components/layout/columns_responsive.py +66 -0
- solara/website/pages/documentation/components/layout/details.py +13 -0
- solara/website/pages/documentation/components/layout/griddraggable.py +62 -0
- solara/website/pages/documentation/components/layout/gridfixed.py +19 -0
- solara/website/pages/documentation/components/layout/hbox.py +18 -0
- solara/website/pages/documentation/components/layout/row.py +30 -0
- solara/website/pages/documentation/components/layout/sidebar.py +24 -0
- solara/website/pages/documentation/components/layout/vbox.py +19 -0
- solara/website/pages/documentation/components/output/__init__.py +9 -0
- solara/website/pages/documentation/components/output/file_download.py +11 -0
- solara/website/pages/documentation/components/output/html.py +19 -0
- solara/website/pages/documentation/components/output/image.py +11 -0
- solara/website/pages/documentation/components/output/markdown.py +57 -0
- solara/website/pages/documentation/components/output/markdown_editor.py +51 -0
- solara/website/pages/documentation/components/output/sql_code.py +83 -0
- solara/website/pages/documentation/components/output/tooltip.py +11 -0
- solara/website/pages/documentation/components/page/__init__.py +9 -0
- solara/website/pages/documentation/components/page/head.py +15 -0
- solara/website/pages/documentation/components/page/title.py +25 -0
- solara/website/pages/documentation/components/status/__init__.py +9 -0
- solara/website/pages/documentation/components/status/error.py +39 -0
- solara/website/pages/documentation/components/status/info.py +39 -0
- solara/website/pages/documentation/components/status/progress.py +10 -0
- solara/website/pages/documentation/components/status/spinner.py +11 -0
- solara/website/pages/documentation/components/status/success.py +40 -0
- solara/website/pages/documentation/components/status/warning.py +47 -0
- solara/website/pages/documentation/components/viz/__init__.py +9 -0
- solara/website/pages/documentation/components/viz/altair.py +42 -0
- solara/website/pages/documentation/components/viz/echarts.py +77 -0
- solara/website/pages/documentation/components/viz/matplotlib.py +30 -0
- solara/website/pages/documentation/components/viz/plotly.py +63 -0
- solara/website/pages/documentation/components/viz/plotly_express.py +41 -0
- solara/website/pages/documentation/examples/__init__.py +54 -0
- solara/website/pages/documentation/examples/ai/__init__.py +11 -0
- solara/website/pages/documentation/examples/ai/chatbot.py +113 -0
- solara/website/pages/documentation/examples/ai/tokenizer.py +107 -0
- solara/website/pages/documentation/examples/basics/__init__.py +10 -0
- solara/website/pages/documentation/examples/basics/sine.py +28 -0
- solara/website/pages/documentation/examples/fullscreen/__init__.py +10 -0
- solara/website/pages/documentation/examples/fullscreen/authorization.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/layout_demo.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/multipage.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/scatter.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/scrolling.py +3 -0
- solara/website/pages/documentation/examples/fullscreen/tutorial_streamlit.py +3 -0
- solara/website/pages/documentation/examples/general/__init__.py +10 -0
- solara/website/pages/documentation/examples/general/custom_storage.py +70 -0
- solara/website/pages/documentation/examples/general/deploy_model.py +115 -0
- solara/website/pages/documentation/examples/general/live_update.py +32 -0
- solara/website/pages/documentation/examples/general/login_oauth.py +81 -0
- solara/website/pages/documentation/examples/general/mycard.vue +58 -0
- solara/website/pages/documentation/examples/general/pokemon_search.py +51 -0
- solara/website/pages/documentation/examples/general/vue_component.py +50 -0
- solara/website/pages/documentation/examples/ipycanvas.py +49 -0
- solara/website/pages/documentation/examples/libraries/__init__.py +10 -0
- solara/website/pages/documentation/examples/libraries/altair.py +65 -0
- solara/website/pages/documentation/examples/libraries/bqplot.py +39 -0
- solara/website/pages/documentation/examples/libraries/ipyleaflet.py +33 -0
- solara/website/pages/documentation/examples/libraries/ipyleaflet_advanced.py +66 -0
- solara/website/pages/documentation/examples/utilities/__init__.py +10 -0
- solara/website/pages/documentation/examples/utilities/calculator.py +157 -0
- solara/website/pages/documentation/examples/utilities/countdown_timer.py +62 -0
- solara/website/pages/documentation/examples/utilities/todo.py +196 -0
- solara/website/pages/documentation/examples/visualization/__init__.py +6 -0
- solara/website/pages/documentation/examples/visualization/annotator.py +67 -0
- solara/website/pages/documentation/examples/visualization/linked_views.py +81 -0
- solara/website/pages/documentation/examples/visualization/plotly.py +44 -0
- solara/website/pages/documentation/faq/__init__.py +12 -0
- solara/website/pages/documentation/faq/content/99-faq.md +112 -0
- solara/website/pages/documentation/getting_started/__init__.py +9 -0
- solara/website/pages/documentation/getting_started/content/00-quickstart.md +107 -0
- solara/website/pages/documentation/getting_started/content/01-introduction.md +125 -0
- solara/website/pages/documentation/getting_started/content/02-installing.md +134 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +14 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/10_data_science.py +13 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/20-web-app.md +89 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/30-ipywidgets.md +124 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/40-streamlit.md +146 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/50-dash.md +144 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/60-jupyter-dashboard-part1.py +65 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/SF_crime_sample.csv.gz +0 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/_data_science.ipynb +445 -0
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +1021 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +11 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +228 -0
- solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +278 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +7 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +305 -0
- solara/website/pages/documentation/getting_started/content/07-deploying/20-cloud-hosted.md +80 -0
- solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +7 -0
- solara/website/pages/documentation/getting_started/content/90-troubleshoot.md +26 -0
- solara/website/pages/docutils.py +38 -0
- solara/website/pages/home.vue +1199 -0
- solara/website/pages/our_team/__init__.py +83 -0
- solara/website/pages/pricing/__init__.py +31 -0
- solara/website/pages/roadmap/__init__.py +11 -0
- solara/website/pages/roadmap/roadmap.md +47 -0
- solara/website/pages/scale_ipywidgets.py +45 -0
- solara/website/pages/showcase/__init__.py +105 -0
- solara/website/pages/showcase/domino_code_assist.py +60 -0
- solara/website/pages/showcase/planeto_tessa.py +19 -0
- solara/website/pages/showcase/solara_dev.py +54 -0
- solara/website/pages/showcase/solarathon_2023_team_2.py +22 -0
- solara/website/pages/showcase/solarathon_2023_team_4.py +22 -0
- solara/website/pages/showcase/solarathon_2023_team_5.py +23 -0
- solara/website/pages/showcase/solarathon_2023_team_6.py +34 -0
- solara/website/pages/showcase/wanderlust.py +27 -0
- solara/website/public/beach.jpeg +0 -0
- solara/website/public/logo.svg +6 -0
- solara/website/public/social/discord.svg +1 -0
- solara/website/public/social/github.svg +1 -0
- solara/website/public/social/twitter.svg +3 -0
- solara/website/public/success.html +25 -0
- solara/website/templates/index.html.j2 +117 -0
- solara/website/utils.py +51 -0
- solara/widgets/__init__.py +1 -0
- solara/widgets/vue/gridlayout.vue +107 -0
- solara/widgets/vue/html.vue +4 -0
- solara/widgets/vue/navigator.vue +134 -0
- solara/widgets/vue/vegalite.vue +130 -0
- solara/widgets/widgets.py +74 -0
- solara_ui-1.45.0.data/data/etc/jupyter/jupyter_notebook_config.d/solara.json +7 -0
- solara_ui-1.45.0.data/data/etc/jupyter/jupyter_server_config.d/solara.json +7 -0
- solara_ui-1.45.0.dist-info/METADATA +162 -0
- solara_ui-1.45.0.dist-info/RECORD +464 -0
- solara_ui-1.45.0.dist-info/WHEEL +4 -0
- solara_ui-1.45.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import Optional, Union
|
|
2
|
+
|
|
3
|
+
import reacton.ipyvuetify as v
|
|
4
|
+
|
|
5
|
+
import solara
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@solara.component
|
|
9
|
+
def Tooltip(
|
|
10
|
+
tooltip: Union[str, solara.Element],
|
|
11
|
+
children=[],
|
|
12
|
+
color: Optional[str] = None,
|
|
13
|
+
):
|
|
14
|
+
"""A tooltip that is shown when you hover above an element.
|
|
15
|
+
|
|
16
|
+
Not all components support tooltips, in case it does not work,
|
|
17
|
+
try wrapping the element in a `Column` or `Row`.
|
|
18
|
+
|
|
19
|
+
```solara
|
|
20
|
+
import solara
|
|
21
|
+
|
|
22
|
+
@solara.component
|
|
23
|
+
def Page():
|
|
24
|
+
with solara.Tooltip("This is a tooltip over a button"):
|
|
25
|
+
solara.Button("Hover me")
|
|
26
|
+
with solara.Tooltip("This is a tooltip over a text"):
|
|
27
|
+
solara.Text("Hover me")
|
|
28
|
+
info = solara.Info("Any component is supported as tooltip.")
|
|
29
|
+
with solara.Tooltip(info, color="white"):
|
|
30
|
+
with solara.Column():
|
|
31
|
+
solara.Markdown("# Lorem ipsum\\n\\nDolor sit amet")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Arguments
|
|
35
|
+
|
|
36
|
+
* `tooltip`: the text, or element to display on hover.
|
|
37
|
+
* `children`: the element to display the tooltip over.
|
|
38
|
+
* `color`: the color of the tooltip (if None, the default color).
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def set_v_on():
|
|
43
|
+
for child in children:
|
|
44
|
+
widget = solara.get_widget(child)
|
|
45
|
+
# this only works on vue/vuetify components
|
|
46
|
+
widget.v_on = "tooltip.on" # type: ignore
|
|
47
|
+
|
|
48
|
+
solara.use_effect(set_v_on, children)
|
|
49
|
+
|
|
50
|
+
return v.Tooltip(
|
|
51
|
+
bottom=True,
|
|
52
|
+
v_slots=[
|
|
53
|
+
{
|
|
54
|
+
"name": "activator",
|
|
55
|
+
"variable": "tooltip",
|
|
56
|
+
"children": children,
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
color=color,
|
|
60
|
+
children=[tooltip],
|
|
61
|
+
)
|
solara/core.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from typing import Any, Callable, Dict, Union, overload, TypeVar
|
|
3
|
+
import typing_extensions
|
|
4
|
+
import reacton
|
|
5
|
+
from . import validate_hooks
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
P = typing_extensions.ParamSpec("P")
|
|
9
|
+
FuncT = TypeVar("FuncT", bound=Callable[..., reacton.core.Element])
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@overload
|
|
13
|
+
def component(obj: None = None, mime_bundle: Dict[str, Any] = ...) -> Callable[[FuncT], FuncT]: ...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@overload
|
|
17
|
+
def component(obj: FuncT, mime_bundle: Dict[str, Any] = ...) -> FuncT: ...
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@overload
|
|
21
|
+
def component(obj: Callable[P, None], mime_bundle: Dict[str, Any] = ...) -> Callable[P, reacton.core.Element]: ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def component(
|
|
25
|
+
obj: Union[Callable[P, None], FuncT, None] = None, mime_bundle: Dict[str, Any] = reacton.core.mime_bundle_default
|
|
26
|
+
) -> Union[Callable[[FuncT], FuncT], FuncT, Callable[P, reacton.core.Element]]:
|
|
27
|
+
def wrapper(obj: Union[Callable[P, None], FuncT]) -> FuncT:
|
|
28
|
+
try:
|
|
29
|
+
validate_hooks.HookValidator(obj).run()
|
|
30
|
+
except Exception as e:
|
|
31
|
+
if not isinstance(e, validate_hooks.HookValidationError):
|
|
32
|
+
# we probably failed because of an unknown reason, but we do not want to break the user's code
|
|
33
|
+
warnings.warn(f"Failed to validate hooks for component {obj.__qualname__}: {e}")
|
|
34
|
+
else:
|
|
35
|
+
raise
|
|
36
|
+
|
|
37
|
+
return reacton.component(obj, mime_bundle) # type: ignore
|
|
38
|
+
|
|
39
|
+
if obj is not None:
|
|
40
|
+
return wrapper(obj)
|
|
41
|
+
else:
|
|
42
|
+
return wrapper
|
solara/datatypes.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from types import ModuleType
|
|
5
|
+
from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, Union
|
|
6
|
+
|
|
7
|
+
import reacton
|
|
8
|
+
from typing_extensions import Literal, TypedDict
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
U = TypeVar("U")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclasses.dataclass(frozen=True)
|
|
15
|
+
class Action:
|
|
16
|
+
name: str
|
|
17
|
+
icon: Optional[str] = None
|
|
18
|
+
on_click: Optional[Callable] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclasses.dataclass(frozen=True)
|
|
22
|
+
class ColumnAction(Action):
|
|
23
|
+
on_click: Optional[Callable[[str], None]] = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclasses.dataclass(frozen=True)
|
|
27
|
+
class CellAction(Action):
|
|
28
|
+
on_click: Optional[Callable[[str, int], None]] = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fallback_retry():
|
|
32
|
+
raise RuntimeError("Should not happen")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ResultState(Enum):
|
|
36
|
+
INITIAL = 1
|
|
37
|
+
STARTING = 2
|
|
38
|
+
WAITING = 3
|
|
39
|
+
RUNNING = 4
|
|
40
|
+
ERROR = 5
|
|
41
|
+
FINISHED = 6
|
|
42
|
+
CANCELLED = 7
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclasses.dataclass(frozen=True)
|
|
46
|
+
class Result(Generic[T]):
|
|
47
|
+
value: Optional[T] = None
|
|
48
|
+
error: Optional[Exception] = None
|
|
49
|
+
state: ResultState = ResultState.INITIAL
|
|
50
|
+
progress: Optional[float] = None
|
|
51
|
+
|
|
52
|
+
def retry(self):
|
|
53
|
+
# mypy does not like members that are callable
|
|
54
|
+
# gets confused about self argument.
|
|
55
|
+
# we wrap it to avoid hitting this error in user
|
|
56
|
+
# code
|
|
57
|
+
self._retry() # type: ignore
|
|
58
|
+
|
|
59
|
+
# can we avoid storing these into the dataclass?
|
|
60
|
+
_retry: Callable[[], Any] = dataclasses.field(compare=False, default=lambda: None)
|
|
61
|
+
cancel: Callable[[], Any] = dataclasses.field(compare=False, default=lambda: None)
|
|
62
|
+
|
|
63
|
+
def __or__(self, next: Callable[["Result[T]"], "Result[U]"]):
|
|
64
|
+
return next(self)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclasses.dataclass(frozen=True)
|
|
68
|
+
class FileContentResult(Result[T]):
|
|
69
|
+
@property
|
|
70
|
+
def exists(self):
|
|
71
|
+
return not isinstance(self.error, FileNotFoundError)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AggregationCount(TypedDict):
|
|
75
|
+
type: Literal["count"]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class AggregationSum(TypedDict):
|
|
79
|
+
type: Literal["sum"]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
JsonType = Union[None, int, str, bool, List[Any], Dict[str, Any]]
|
|
83
|
+
|
|
84
|
+
Aggregation = Union[AggregationCount, AggregationSum]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class PivotTableData(TypedDict):
|
|
88
|
+
x: List[str]
|
|
89
|
+
y: List[str]
|
|
90
|
+
agg: str
|
|
91
|
+
values: List[List[JsonType]]
|
|
92
|
+
values_x: List[str]
|
|
93
|
+
values_y: List[str]
|
|
94
|
+
headers_x: List[List[str]]
|
|
95
|
+
headers_y: List[List[str]]
|
|
96
|
+
counts_x: int
|
|
97
|
+
counts_y: int
|
|
98
|
+
total: str
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclasses.dataclass(frozen=True)
|
|
102
|
+
class Route:
|
|
103
|
+
"""A route tells Solara which component to render for a given URL. (Not a Solara component!)
|
|
104
|
+
|
|
105
|
+
## Arguments
|
|
106
|
+
|
|
107
|
+
* `path` - The path of the route. This is the part of the URL that you see in the browser.
|
|
108
|
+
* `children` - (Optional) A list of child routes. These are routes that are nested under this route.
|
|
109
|
+
* `module` - (Optional) The module that contains the component to render for this route, used for autorouting.
|
|
110
|
+
* `component` - (Optional) The component to render for this route.
|
|
111
|
+
* `layout` - (Optional) The layout to use for this route. If not specified, the default layout will be used.
|
|
112
|
+
* `data` - (Optional) The data to pass to the component for this route, usage is up to the user.
|
|
113
|
+
* `label` - (Optional) The label to use for this route, can be used for labeling tabs or links.
|
|
114
|
+
|
|
115
|
+
## See also
|
|
116
|
+
|
|
117
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
118
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
119
|
+
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
path: str
|
|
123
|
+
children: List["Route"] = dataclasses.field(default_factory=list)
|
|
124
|
+
|
|
125
|
+
# these are free to use, depending on the implementation
|
|
126
|
+
# see autorouting.py for how Solara uses them
|
|
127
|
+
module: Optional[ModuleType] = None
|
|
128
|
+
|
|
129
|
+
# in the autorouting implementation, this is the
|
|
130
|
+
# the same as module.Page (unless we are rendering a markdown)
|
|
131
|
+
component: Union[None, Callable, reacton.core.Component] = None
|
|
132
|
+
layout: Union[None, Callable, reacton.core.Component] = None
|
|
133
|
+
|
|
134
|
+
# in the autorouting implementation, this is the
|
|
135
|
+
# path of the markdown file
|
|
136
|
+
data: Any = None
|
|
137
|
+
|
|
138
|
+
# Can be used for a title and/or a tab label
|
|
139
|
+
label: Optional[str] = None
|
|
140
|
+
|
|
141
|
+
# file corresponding to this route, can be used for
|
|
142
|
+
# checking of content has changed (using mtime)
|
|
143
|
+
file: Optional[Path] = None
|
solara/express.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import typing
|
|
3
|
+
from typing import Callable
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import plotly.express as px
|
|
7
|
+
import plotly.graph_objs as go
|
|
8
|
+
import typing_extensions
|
|
9
|
+
|
|
10
|
+
# behave as the px module
|
|
11
|
+
from plotly.express import * # noqa: F401, F403
|
|
12
|
+
from plotly.express._core import make_figure
|
|
13
|
+
|
|
14
|
+
import solara
|
|
15
|
+
|
|
16
|
+
P = typing_extensions.ParamSpec("P")
|
|
17
|
+
T = typing.TypeVar("T")
|
|
18
|
+
T2 = typing.TypeVar("T2")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# patch histogram until issue 3859 in plotly is resolved
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def histogram(
|
|
25
|
+
data_frame=None,
|
|
26
|
+
x=None,
|
|
27
|
+
y=None,
|
|
28
|
+
custom_data=None,
|
|
29
|
+
color=None,
|
|
30
|
+
pattern_shape=None,
|
|
31
|
+
facet_row=None,
|
|
32
|
+
facet_col=None,
|
|
33
|
+
facet_col_wrap=0,
|
|
34
|
+
facet_row_spacing=None,
|
|
35
|
+
facet_col_spacing=None,
|
|
36
|
+
hover_name=None,
|
|
37
|
+
hover_data=None,
|
|
38
|
+
animation_frame=None,
|
|
39
|
+
animation_group=None,
|
|
40
|
+
category_orders=None,
|
|
41
|
+
labels=None,
|
|
42
|
+
color_discrete_sequence=None,
|
|
43
|
+
color_discrete_map=None,
|
|
44
|
+
pattern_shape_sequence=None,
|
|
45
|
+
pattern_shape_map=None,
|
|
46
|
+
marginal=None,
|
|
47
|
+
opacity=None,
|
|
48
|
+
orientation=None,
|
|
49
|
+
barmode="relative",
|
|
50
|
+
barnorm=None,
|
|
51
|
+
histnorm=None,
|
|
52
|
+
log_x=False,
|
|
53
|
+
log_y=False,
|
|
54
|
+
range_x=None,
|
|
55
|
+
range_y=None,
|
|
56
|
+
histfunc=None,
|
|
57
|
+
cumulative=None,
|
|
58
|
+
nbins=None,
|
|
59
|
+
text_auto=False,
|
|
60
|
+
title=None,
|
|
61
|
+
template=None,
|
|
62
|
+
width=None,
|
|
63
|
+
height=None,
|
|
64
|
+
) -> go.Figure:
|
|
65
|
+
"""
|
|
66
|
+
In a histogram, rows of `data_frame` are grouped together into a
|
|
67
|
+
rectangular mark to visualize the 1D distribution of an aggregate
|
|
68
|
+
function `histfunc` (e.g. the count or sum) of the value `y` (or `x` if
|
|
69
|
+
`orientation` is `'h'`).
|
|
70
|
+
"""
|
|
71
|
+
return make_figure(
|
|
72
|
+
args=locals(),
|
|
73
|
+
constructor=go.Histogram,
|
|
74
|
+
trace_patch=dict(
|
|
75
|
+
histnorm=histnorm,
|
|
76
|
+
histfunc=histfunc,
|
|
77
|
+
cumulative=dict(enabled=cumulative),
|
|
78
|
+
),
|
|
79
|
+
layout_patch=dict(barmode=barmode, barnorm=barnorm),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
px.histogram = histogram
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _make_self_describing(f: Callable[P, T]):
|
|
87
|
+
# put the arguments on the function and the function into the return value
|
|
88
|
+
if hasattr(f, "__wrapped__"):
|
|
89
|
+
# on hot reload, we need to unwrap the function to not wrap it twice
|
|
90
|
+
f = f.__wrapped__
|
|
91
|
+
|
|
92
|
+
@functools.wraps(f)
|
|
93
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs):
|
|
94
|
+
fig = f(*args, **kwargs)
|
|
95
|
+
fig._func = f # type: ignore
|
|
96
|
+
fig._args = args # type: ignore
|
|
97
|
+
fig._kwargs = kwargs # type: ignore
|
|
98
|
+
return fig
|
|
99
|
+
|
|
100
|
+
return wrapper
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _patch():
|
|
104
|
+
for name in dir(px):
|
|
105
|
+
f = getattr(px, name)
|
|
106
|
+
if callable(f):
|
|
107
|
+
f = _make_self_describing(f)
|
|
108
|
+
setattr(px, name, f)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
_patch()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@solara.component
|
|
115
|
+
def CrossFilteredFigurePlotly(fig):
|
|
116
|
+
first_arg_name = fig._func.__code__.co_varnames[0]
|
|
117
|
+
kwargs = fig._kwargs.copy()
|
|
118
|
+
kwargs.update(zip(fig._func.__code__.co_varnames, fig._args))
|
|
119
|
+
|
|
120
|
+
df = kwargs[first_arg_name]
|
|
121
|
+
name = fig._func.__name__
|
|
122
|
+
filter, set_filter = solara.use_cross_filter(id(df), name)
|
|
123
|
+
dff = df
|
|
124
|
+
if filter is not None:
|
|
125
|
+
dff = df[filter]
|
|
126
|
+
for key, value in kwargs.items():
|
|
127
|
+
if not isinstance(value, str):
|
|
128
|
+
try:
|
|
129
|
+
n = len(value)
|
|
130
|
+
except TypeError:
|
|
131
|
+
pass # int or bool, or anything without a length
|
|
132
|
+
except Exception:
|
|
133
|
+
raise
|
|
134
|
+
else:
|
|
135
|
+
if n == len(df):
|
|
136
|
+
kwargs[key] = np.array(value)[filter]
|
|
137
|
+
original_indices = np.arange(len(df), dtype="int64")
|
|
138
|
+
else:
|
|
139
|
+
original_indices = None
|
|
140
|
+
|
|
141
|
+
kwargs[first_arg_name] = dff
|
|
142
|
+
|
|
143
|
+
index = np.arange(len(dff))
|
|
144
|
+
custom_data = [index]
|
|
145
|
+
has_custom_data = "custom_data" in kwargs
|
|
146
|
+
|
|
147
|
+
if has_custom_data:
|
|
148
|
+
kwargs["custom_data"] = custom_data + list(kwargs["custom_data"])
|
|
149
|
+
else:
|
|
150
|
+
kwargs["custom_data"] = custom_data
|
|
151
|
+
new_fig = fig._func(**kwargs)
|
|
152
|
+
new_fig.layout = fig.layout
|
|
153
|
+
|
|
154
|
+
# the df is split into multiple traces, generate the data such that we can transform back from trace+point indices to df.index
|
|
155
|
+
indices = []
|
|
156
|
+
offset = 0
|
|
157
|
+
index_offsets_list = []
|
|
158
|
+
for data in new_fig.data:
|
|
159
|
+
indices.append(data.customdata.T[0])
|
|
160
|
+
index_offsets_list.append(offset)
|
|
161
|
+
offset += len(data.customdata.T[0])
|
|
162
|
+
index_offsets = np.array(index_offsets_list)
|
|
163
|
+
# strip of the custom data we added
|
|
164
|
+
for data in new_fig.data:
|
|
165
|
+
if has_custom_data:
|
|
166
|
+
data.customdata = data.customdata.T[len(custom_data) :].T
|
|
167
|
+
else:
|
|
168
|
+
data.customdata = None
|
|
169
|
+
|
|
170
|
+
def on_selection(data):
|
|
171
|
+
if data is not None:
|
|
172
|
+
trace_indexes = np.array(data["points"]["trace_indexes"])
|
|
173
|
+
point_indexes = np.array(data["points"]["point_indexes"])
|
|
174
|
+
if len(trace_indexes):
|
|
175
|
+
indices_selected = index_offsets[trace_indexes] + point_indexes
|
|
176
|
+
|
|
177
|
+
if filter is not None:
|
|
178
|
+
assert original_indices is not None
|
|
179
|
+
# these are references to the filtered dataframe
|
|
180
|
+
indices_selected = original_indices[filter][indices_selected]
|
|
181
|
+
mask = np.zeros(len(df), dtype=bool)
|
|
182
|
+
mask[indices_selected] = True
|
|
183
|
+
set_filter(mask)
|
|
184
|
+
else:
|
|
185
|
+
set_filter(None)
|
|
186
|
+
|
|
187
|
+
def on_deselect(data):
|
|
188
|
+
set_filter(None)
|
|
189
|
+
|
|
190
|
+
return solara.FigurePlotly(new_fig, on_selection=on_selection, on_deselect=on_deselect)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# for backwards compatibility
|
|
194
|
+
FigurePlotlyCrossFiltered = CrossFilteredFigurePlotly
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _wraps(f: Callable[P, T]):
|
|
198
|
+
@functools.wraps(f)
|
|
199
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs):
|
|
200
|
+
fig = f(*args, **kwargs)
|
|
201
|
+
return CrossFilteredFigurePlotly(fig)
|
|
202
|
+
|
|
203
|
+
return wrapper
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
scatter = _wraps(px.scatter)
|
|
207
|
+
scatter_3d = _wraps(px.scatter_3d)
|
|
208
|
+
scatter_polar = _wraps(px.scatter_polar)
|
|
209
|
+
scatter_ternary = _wraps(px.scatter_ternary)
|
|
210
|
+
scatter_mapbox = _wraps(px.scatter_mapbox)
|
|
211
|
+
scatter_geo = _wraps(px.scatter_geo)
|
|
212
|
+
scatter_matrix = _wraps(px.scatter_matrix)
|
|
213
|
+
density_contour = _wraps(px.density_contour)
|
|
214
|
+
density_heatmap = _wraps(px.density_heatmap)
|
|
215
|
+
density_mapbox = _wraps(px.density_mapbox)
|
|
216
|
+
line = _wraps(px.line)
|
|
217
|
+
line_3d = _wraps(px.line_3d)
|
|
218
|
+
line_polar = _wraps(px.line_polar)
|
|
219
|
+
line_ternary = _wraps(px.line_ternary)
|
|
220
|
+
line_mapbox = _wraps(px.line_mapbox)
|
|
221
|
+
line_geo = _wraps(px.line_geo)
|
|
222
|
+
parallel_coordinates = _wraps(px.parallel_coordinates)
|
|
223
|
+
parallel_categories = _wraps(px.parallel_categories)
|
|
224
|
+
area = _wraps(px.area)
|
|
225
|
+
bar = _wraps(px.bar)
|
|
226
|
+
timeline = _wraps(px.timeline)
|
|
227
|
+
bar_polar = _wraps(px.bar_polar)
|
|
228
|
+
violin = _wraps(px.violin)
|
|
229
|
+
box = _wraps(px.box)
|
|
230
|
+
strip = _wraps(px.strip)
|
|
231
|
+
histogram = _wraps(px.histogram)
|
|
232
|
+
ecdf = _wraps(px.ecdf)
|
|
233
|
+
choropleth = _wraps(px.choropleth)
|
|
234
|
+
choropleth_mapbox = _wraps(px.choropleth_mapbox)
|
|
235
|
+
pie = _wraps(px.pie)
|
|
236
|
+
sunburst = _wraps(px.sunburst)
|
|
237
|
+
treemap = _wraps(px.treemap)
|
|
238
|
+
icicle = _wraps(px.icicle)
|
|
239
|
+
funnel = _wraps(px.funnel)
|
|
240
|
+
funnel_area = _wraps(px.funnel_area)
|
|
241
|
+
imshow = _wraps(px.imshow)
|
solara/hooks/__init__.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
from functools import reduce
|
|
3
|
+
from typing import Any, Callable, Dict, List, TypeVar
|
|
4
|
+
|
|
5
|
+
import solara.util
|
|
6
|
+
from solara.hooks.misc import use_force_update, use_unique_key
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"provide_cross_filter",
|
|
12
|
+
"use_cross_filter",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CrossFilterStore:
|
|
17
|
+
def __init__(self) -> None:
|
|
18
|
+
self.listeners: List[Callable] = []
|
|
19
|
+
self.filters: Dict[Any, Dict[str, Any]] = {}
|
|
20
|
+
|
|
21
|
+
def add(self, data_key, key, filter):
|
|
22
|
+
data_filters = self.filters.setdefault(data_key, {})
|
|
23
|
+
data_filters[key] = filter
|
|
24
|
+
|
|
25
|
+
def use(self, data_key, key, eq=None):
|
|
26
|
+
# we use this state to trigger update, we could do without
|
|
27
|
+
updater = use_force_update()
|
|
28
|
+
|
|
29
|
+
data_filters = self.filters.setdefault(data_key, {})
|
|
30
|
+
filter, set_filter = solara.use_state(data_filters.get(key), eq=eq)
|
|
31
|
+
|
|
32
|
+
def on_change():
|
|
33
|
+
set_filter(data_filters.get(key))
|
|
34
|
+
# even if we don't change our own filter, the other may change
|
|
35
|
+
updater()
|
|
36
|
+
|
|
37
|
+
def connect():
|
|
38
|
+
self.listeners.append(on_change)
|
|
39
|
+
# we need to force an extra render after the first render
|
|
40
|
+
# to make sure we have the correct filter, since others components
|
|
41
|
+
# may set a filter after we have rendered, *or* mounted
|
|
42
|
+
on_change()
|
|
43
|
+
|
|
44
|
+
def cleanup():
|
|
45
|
+
self.listeners.remove(on_change)
|
|
46
|
+
# also remove our filter, and notify the rest
|
|
47
|
+
data_filters.pop(key, None) # remove, ignoring key error
|
|
48
|
+
for listener in self.listeners:
|
|
49
|
+
listener()
|
|
50
|
+
|
|
51
|
+
return cleanup
|
|
52
|
+
|
|
53
|
+
solara.use_effect(connect, [key])
|
|
54
|
+
|
|
55
|
+
def setter(filter):
|
|
56
|
+
data_filters[key] = filter
|
|
57
|
+
for listener in self.listeners:
|
|
58
|
+
listener()
|
|
59
|
+
|
|
60
|
+
otherfilters = [filter for key_other, filter in data_filters.items() if key != key_other and filter is not None]
|
|
61
|
+
return filter, otherfilters, setter
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
cross_filter_context = solara.create_context(CrossFilterStore())
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def provide_cross_filter():
|
|
68
|
+
# create it once
|
|
69
|
+
cross_filter_object = solara.use_memo(CrossFilterStore, [])
|
|
70
|
+
cross_filter_context.provide(cross_filter_object)
|
|
71
|
+
return cross_filter_object
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def use_cross_filter(data_key, name: str = "no-name", reducer: Callable[[T, T], T] = operator.and_, eq=solara.util.numpy_equals):
|
|
75
|
+
"""Provides cross filtering, all other filters are combined using the reducer.
|
|
76
|
+
|
|
77
|
+
Cross filtering will collect a set of filters (from other components), and combine
|
|
78
|
+
them into a single filter, that excludes the filter we set for the current component.
|
|
79
|
+
This is often used in dashboards where a filter is defined in a visualization component,
|
|
80
|
+
but only applied to all other components.
|
|
81
|
+
|
|
82
|
+
The graph below shows what happens when component A and B set a filter, and C does not.
|
|
83
|
+
|
|
84
|
+
```mermaid
|
|
85
|
+
graph TD;
|
|
86
|
+
A--"filter A"-->B;
|
|
87
|
+
B--"filter B"-->C;
|
|
88
|
+
A--"filter A"-->C;
|
|
89
|
+
B--"filter B"-->A;
|
|
90
|
+
```
|
|
91
|
+
"""
|
|
92
|
+
key = use_unique_key(prefix=f"cross-filter-{name}-")
|
|
93
|
+
cross_filter_store = solara.use_context(cross_filter_context)
|
|
94
|
+
_own_filter, otherfilters, set_filter = cross_filter_store.use(data_key, key, eq=eq)
|
|
95
|
+
if otherfilters:
|
|
96
|
+
cross_filter = reduce(reducer, otherfilters[1:], otherfilters[0])
|
|
97
|
+
else:
|
|
98
|
+
cross_filter = None
|
|
99
|
+
return cross_filter, set_filter
|