solara 1.29.1__py2.py3-none-any.whl → 1.30.1__py2.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.
- solara/__init__.py +5 -5
- solara/__main__.py +6 -2
- solara/autorouting.py +88 -56
- solara/cache.py +2 -2
- solara/checks.html +1 -1
- solara/components/__init__.py +1 -1
- solara/components/applayout.py +5 -5
- solara/components/button.py +4 -4
- solara/components/card.py +1 -2
- solara/components/component_vue.py +9 -8
- solara/components/cross_filter.py +6 -7
- solara/components/datatable.py +2 -3
- solara/components/figure_altair.py +1 -1
- solara/components/file_download.py +2 -2
- solara/components/file_drop.py +76 -33
- solara/components/file_drop.vue +24 -10
- solara/components/head.py +1 -2
- solara/components/head_tag.py +2 -3
- solara/components/image.py +1 -1
- solara/components/link.py +3 -4
- solara/components/meta.py +1 -1
- solara/components/misc.py +5 -9
- solara/datatypes.py +2 -2
- solara/hooks/use_reactive.py +2 -2
- solara/lab/components/confirmation_dialog.py +1 -1
- solara/lab/components/tabs.py +6 -6
- solara/reactive.py +1 -1
- solara/routing.py +9 -8
- solara/scope/__init__.py +3 -2
- solara/server/app.py +44 -1
- solara/server/assets/style.css +6 -0
- solara/server/esm.py +28 -4
- solara/server/kernel_context.py +78 -7
- solara/server/patch.py +3 -0
- solara/server/reload.py +2 -2
- solara/server/server.py +3 -3
- solara/server/settings.py +1 -0
- solara/server/starlette.py +66 -33
- solara/server/static/solara_bootstrap.py +1 -1
- solara/server/templates/solara.html.j2 +62 -42
- solara/tasks.py +1 -6
- solara/util.py +23 -1
- solara/website/assets/custom.css +56 -0
- solara/website/components/__init__.py +1 -0
- solara/website/components/algolia_api.vue +157 -0
- solara/website/components/docs.py +118 -0
- solara/website/components/header.py +20 -10
- solara/website/components/hero.py +1 -1
- solara/website/components/markdown.py +30 -0
- solara/website/pages/__init__.py +234 -20
- solara/website/pages/apps/jupyter-dashboard-1.py +1 -1
- solara/website/pages/apps/multipage/__init__.py +1 -1
- solara/website/pages/apps/multipage/page2.py +1 -1
- solara/website/pages/apps/scatter.py +21 -7
- solara/website/pages/changelog/__init__.py +8 -0
- solara/website/pages/{docs/content/95-changelog.md → changelog/changelog.md} +43 -2
- solara/website/pages/contact/__init__.py +8 -0
- solara/website/pages/documentation/__init__.py +184 -0
- solara/website/pages/{docs → documentation/advanced}/__init__.py +2 -2
- solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
- solara/website/pages/{docs → documentation/advanced}/content/10-howto/00-overview.md +5 -0
- solara/website/pages/{docs/content/10-howto/20-multipage.md → documentation/advanced/content/10-howto/10-multipage.md} +9 -5
- solara/website/pages/{docs/content/10-howto/30-layout.md → documentation/advanced/content/10-howto/20-layout.md} +26 -21
- solara/website/pages/{docs/content/10-howto/50-testing.md → documentation/advanced/content/10-howto/30-testing.md} +5 -0
- solara/website/pages/{docs/content/10-howto/51-debugging.md → documentation/advanced/content/10-howto/31-debugging.md} +4 -1
- solara/website/pages/{docs/content/10-howto/80-embed.md → documentation/advanced/content/10-howto/40-embed.md} +7 -1
- solara/website/pages/{docs/content/10-howto/ipywidget_libraries.md → documentation/advanced/content/10-howto/50-ipywidget_libraries.md} +4 -0
- solara/website/pages/documentation/advanced/content/20-understanding/00-introduction.md +10 -0
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/05-ipywidgets.md +5 -0
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/06-ipyvuetify.md +5 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/10-reacton.md +4 -0
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/12-reacton-basics.md +5 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/15-anatomy.md +6 -2
- solara/website/pages/documentation/advanced/content/20-understanding/17-rules-of-hooks.md +7 -0
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/18-containers.md +9 -3
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/20-solara.md +5 -0
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/40-routing.md +13 -9
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/50-solara-server.md +6 -1
- solara/website/pages/{docs → documentation/advanced}/content/20-understanding/60-voila.md +5 -0
- solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/10-oauth.md +7 -3
- solara/website/pages/{docs/content/10-howto → documentation/advanced/content/40-development}/01-contribute.md +9 -5
- solara/website/pages/{docs/content/90-development → documentation/advanced/content/40-development}/10-setup.md +6 -2
- 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/hooks/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/hooks}/use_effect.md +3 -3
- solara/website/pages/{api → documentation/api/hooks}/use_effect.py +2 -1
- solara/website/pages/{api → documentation/api/hooks}/use_memo.md +2 -2
- solara/website/pages/{api → documentation/api/hooks}/use_memo.py +2 -1
- solara/website/pages/{api → documentation/api/hooks}/use_reactive.py +1 -2
- solara/website/pages/{api → documentation/api/hooks}/use_state.py +1 -2
- solara/website/pages/documentation/api/routing/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/routing}/generate_routes.py +1 -2
- solara/website/pages/{api → documentation/api/routing}/generate_routes_directory.py +1 -2
- solara/website/pages/{api → documentation/api/routing}/use_route.py +2 -2
- solara/website/pages/{api → documentation/api/routing}/use_router.py +1 -2
- solara/website/pages/documentation/api/utilities/__init__.py +9 -0
- solara/website/pages/{api → documentation/api/utilities}/component_vue.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/computed.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/display.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/get_kernel_id.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/get_session_id.py +2 -2
- solara/website/pages/{api → documentation/api/utilities}/on_kernel_start.py +9 -3
- solara/website/pages/{api → documentation/api/utilities}/reactive.py +1 -2
- solara/website/pages/{api → documentation/api/utilities}/widget.py +2 -2
- solara/website/pages/documentation/components/__init__.py +12 -0
- solara/website/pages/documentation/components/advanced/__init__.py +9 -0
- solara/website/pages/documentation/components/data/__init__.py +9 -0
- solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/enterprise}/avatar.py +1 -2
- solara/website/pages/{api → documentation/components/enterprise}/avatar_menu.py +1 -2
- solara/website/pages/documentation/components/input/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/input}/checkbox.py +1 -2
- solara/website/pages/documentation/components/input/file_drop.py +75 -0
- solara/website/pages/{api → documentation/components/input}/input.py +1 -2
- solara/website/pages/{api → documentation/components/input}/select.py +1 -2
- solara/website/pages/{api → documentation/components/input}/slider.py +1 -2
- solara/website/pages/{api → documentation/components/input}/switch.py +1 -2
- solara/website/pages/{api → documentation/components/input}/togglebuttons.py +1 -2
- solara/website/pages/documentation/components/lab/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/lab}/chat.py +2 -3
- solara/website/pages/{api → documentation/components/lab}/cookies_headers.py +1 -1
- solara/website/pages/{api → documentation/components/lab}/input_date.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/menu.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/task.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/theming.py +1 -2
- solara/website/pages/{api → documentation/components/lab}/use_task.py +1 -2
- solara/website/pages/documentation/components/layout/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/layout}/app_bar.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/app_bar_title.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/card.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/card_actions.py +1 -2
- solara/website/pages/{api → documentation/components/layout}/griddraggable.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/gridfixed.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/hbox.py +1 -1
- solara/website/pages/{api → documentation/components/layout}/vbox.py +1 -1
- solara/website/pages/documentation/components/output/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/output}/file_download.py +1 -2
- solara/website/pages/{api → documentation/components/output}/image.py +1 -2
- solara/website/pages/{api → documentation/components/output}/tooltip.py +1 -2
- solara/website/pages/documentation/components/page/__init__.py +9 -0
- solara/website/pages/documentation/components/status/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/status}/error.py +3 -3
- solara/website/pages/{api → documentation/components/status}/info.py +3 -3
- solara/website/pages/{api → documentation/components/status}/progress.py +1 -2
- solara/website/pages/{api → documentation/components/status}/spinner.py +1 -2
- solara/website/pages/{api → documentation/components/status}/success.py +3 -3
- solara/website/pages/{api → documentation/components/status}/warning.py +3 -3
- solara/website/pages/documentation/components/viz/__init__.py +9 -0
- solara/website/pages/{api → documentation/components/viz}/plotly.py +1 -0
- solara/website/pages/{examples → documentation/examples}/__init__.py +3 -43
- solara/website/pages/{examples → documentation/examples}/general/custom_storage.py +1 -1
- solara/website/pages/{examples → documentation/examples}/general/deploy_model.py +3 -3
- solara/website/pages/{examples → documentation/examples}/general/login_oauth.py +1 -1
- solara/website/pages/{examples → documentation/examples}/general/vue_component.py +1 -2
- solara/website/pages/{examples → documentation/examples}/libraries/altair.py +2 -3
- solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet_advanced.py +1 -1
- solara/website/pages/{examples → documentation/examples}/utilities/countdown_timer.py +1 -1
- solara/website/pages/{examples → documentation/examples}/visualization/plotly.py +1 -2
- solara/website/pages/documentation/faq/__init__.py +12 -0
- solara/website/pages/{docs → documentation/faq}/content/99-faq.md +4 -1
- solara/website/pages/documentation/getting_started/__init__.py +9 -0
- solara/website/pages/{docs/content/03-quickstart.md → documentation/getting_started/content/00-quickstart.md} +7 -2
- solara/website/pages/{docs/content/00-introduction.md → documentation/getting_started/content/01-introduction.md} +20 -16
- solara/website/pages/{docs → documentation/getting_started}/content/02-installing.md +5 -1
- solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +14 -0
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/20-web-app.md +9 -4
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/30-ipywidgets.md +13 -9
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/40-streamlit.md +17 -12
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/50-dash.md +7 -3
- solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_data_science.ipynb +5 -5
- solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +11 -0
- solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/10-components.md +7 -2
- solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/50-state-management.md +8 -3
- solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +3 -0
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/40-static_files.md +4 -0
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/41-asset-files.md +5 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/60-static-site-generation.md +5 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/70-search.md +5 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/80-reloading.md +7 -3
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/90-notebook-support.md +4 -1
- solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/95-caching.md +6 -1
- solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +7 -0
- solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/10-self-hosted.md +7 -3
- solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/20-cloud-hosted.md +5 -1
- solara/website/pages/documentation/getting_started/content/80-what-is-lab.md +7 -0
- solara/website/pages/{docs → documentation/getting_started}/content/90-troubleshoot.md +4 -0
- solara/website/pages/showcase/__init__.py +1 -1
- solara/website/pages/showcase/domino_code_assist.py +1 -1
- solara/website/pages/showcase/solara_dev.py +1 -1
- 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-1.29.1.dist-info → solara-1.30.1.dist-info}/METADATA +8 -7
- solara-1.30.1.dist-info/RECORD +437 -0
- {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/WHEEL +1 -1
- solara/website/pages/api/__init__.py +0 -292
- solara/website/pages/api/default_layout.py +0 -16
- solara/website/pages/api/file_drop.py +0 -36
- solara/website/pages/docs/content/04-tutorial/00-overview.md +0 -9
- solara/website/pages/docs/content/07-fundamentals/00-overview.md +0 -7
- solara/website/pages/docs/content/15-reference/00-overview.md +0 -6
- solara/website/pages/docs/content/20-understanding/00-introduction.md +0 -4
- solara/website/pages/docs/content/20-understanding/17-rules-of-hooks.md +0 -3
- solara/website/pages/docs/content/30-deploying/00-overview.md +0 -3
- solara/website/pages/docs/content/lab/00-what-is-lab.md +0 -3
- solara-1.29.1.dist-info/RECORD +0 -411
- /solara/website/pages/{docs/content/99-contact.md → contact/contact.md} +0 -0
- /solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/00-overview.md +0 -0
- /solara/website/pages/{docs/content/__init__.py → documentation/advanced/content/40-development/00-overview.md} +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_dataframe.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_report.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_select.py +0 -0
- /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_slider.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_cross_filter.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_dark_effective.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_exception.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_previous.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_state_or_update.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_thread.md +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_thread.py +0 -0
- /solara/website/pages/{api → documentation/api/hooks}/use_trait_observe.py +0 -0
- /solara/website/pages/{api → documentation/api/routing}/resolve_path.py +0 -0
- /solara/website/pages/{api → documentation/api/routing}/route.py +0 -0
- /solara/website/pages/{api → documentation/api/utilities}/memoize.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/link.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/meta.py +0 -0
- /solara/website/pages/{api → documentation/components/advanced}/style.py +0 -0
- /solara/website/pages/{api → documentation/components}/common.py +0 -0
- /solara/website/pages/{api → documentation/components/data}/dataframe.py +0 -0
- /solara/website/pages/{api → documentation/components/data}/pivot_table.py +0 -0
- /solara/website/pages/{api → documentation/components/input}/button.py +0 -0
- /solara/website/pages/{api → documentation/components/input}/file_browser.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/confirmation_dialog.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/tab.py +0 -0
- /solara/website/pages/{api → documentation/components/lab}/tabs.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/app_layout.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/column.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/columns.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/columns_responsive.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/row.py +0 -0
- /solara/website/pages/{api → documentation/components/layout}/sidebar.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/html.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/markdown.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/markdown_editor.py +0 -0
- /solara/website/pages/{api → documentation/components/output}/sql_code.py +0 -0
- /solara/website/pages/{api → documentation/components/page}/head.py +0 -0
- /solara/website/pages/{api → documentation/components/page}/title.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/altair.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/echarts.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/matplotlib.py +0 -0
- /solara/website/pages/{api → documentation/components/viz}/plotly_express.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/chatbot.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ai/tokenizer.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/basics/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/basics/sine.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/authorization.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/layout_demo.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/multipage.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/scatter.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/scrolling.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/fullscreen/tutorial_streamlit.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/live_update.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/mycard.vue +0 -0
- /solara/website/pages/{examples → documentation/examples}/general/pokemon_search.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/ipycanvas.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/bqplot.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/calculator.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/utilities/todo.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/__init__.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/annotator.py +0 -0
- /solara/website/pages/{examples → documentation/examples}/visualization/linked_views.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/10_data_science.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/60-jupyter-dashboard-part1.py +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/SF_crime_sample.csv.gz +0 -0
- /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_jupyter_dashboard_1.ipynb +0 -0
- {solara-1.29.1.data → solara-1.30.1.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara-1.29.1.data → solara-1.30.1.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/entry_points.txt +0 -0
- {solara-1.29.1.dist-info → solara-1.30.1.dist-info}/licenses/LICENSE +0 -0
solara/components/file_drop.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
import typing
|
|
3
|
-
from typing import Callable, Optional, cast
|
|
3
|
+
from typing import Callable, List, Optional, Union, cast
|
|
4
4
|
|
|
5
5
|
import traitlets
|
|
6
6
|
from ipyvue import Template
|
|
@@ -25,45 +25,26 @@ class FileDropZone(FileInput):
|
|
|
25
25
|
template_file = (__file__, "file_drop.vue")
|
|
26
26
|
items = traitlets.List(default_value=[]).tag(sync=True)
|
|
27
27
|
label = traitlets.Unicode().tag(sync=True)
|
|
28
|
+
multiple = traitlets.Bool(True).tag(sync=True)
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@solara.component
|
|
31
|
-
def
|
|
32
|
-
label="Drop file here",
|
|
32
|
+
def _FileDrop(
|
|
33
|
+
label="Drop file(s) here",
|
|
33
34
|
on_total_progress: Optional[Callable[[float], None]] = None,
|
|
34
|
-
on_file: Optional[Callable[[FileInfo], None]] = None,
|
|
35
|
+
on_file: Optional[Callable[[Union[FileInfo, List[FileInfo]]], None]] = None,
|
|
35
36
|
lazy: bool = True,
|
|
37
|
+
multiple: bool = False,
|
|
36
38
|
):
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
If lazy=True, no file content will be loaded into memory,
|
|
40
|
-
nor will any data be transferred by default.
|
|
41
|
-
A file object is passed to the `on_file` callback, and data will be transferred
|
|
42
|
-
when needed.
|
|
43
|
-
|
|
44
|
-
If lazy=False, the file content will be loaded into memory and passed to the `on_file` callback via the `.data` attribute.
|
|
45
|
-
|
|
46
|
-
The on_file callback takes the following argument type:
|
|
47
|
-
```python
|
|
48
|
-
class FileInfo(typing.TypedDict):
|
|
49
|
-
name: str # file name
|
|
50
|
-
size: int # file size in bytes
|
|
51
|
-
file_obj: typing.BinaryIO
|
|
52
|
-
data: Optional[bytes]: bytes # only present if lazy=False
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
## Arguments
|
|
57
|
-
* `on_total_progress`: Will be called with the progress in % of the file upload.
|
|
58
|
-
* `on_file`: Will be called with a `FileInfo` object, which contains the file `.name`, `.length` and a `.file_obj` object.
|
|
59
|
-
* `lazy`: Whether to load the file content into memory or not. If `False`,
|
|
60
|
-
the file content will be loaded into memory and passed to the `on_file` callback via the `.data` attribute.
|
|
39
|
+
"""Generic implementation used by FileDrop and FileDropMultiple.
|
|
61
40
|
|
|
41
|
+
If multiple=True, multiple files can be uploaded.
|
|
62
42
|
"""
|
|
43
|
+
|
|
63
44
|
file_info, set_file_info = solara.use_state(None)
|
|
64
45
|
wired_files, set_wired_files = solara.use_state(cast(Optional[typing.List[FileInfo]], None))
|
|
65
46
|
|
|
66
|
-
file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info) # type: ignore
|
|
47
|
+
file_drop = FileDropZone.element(label=label, on_total_progress=on_total_progress, on_file_info=set_file_info, multiple=multiple) # type: ignore
|
|
67
48
|
|
|
68
49
|
def wire_files():
|
|
69
50
|
if not file_info:
|
|
@@ -83,14 +64,76 @@ def FileDrop(
|
|
|
83
64
|
if not wired_files:
|
|
84
65
|
return
|
|
85
66
|
if on_file:
|
|
86
|
-
|
|
87
|
-
|
|
67
|
+
for i in range(len(wired_files)):
|
|
68
|
+
if not lazy:
|
|
69
|
+
wired_files[i]["data"] = wired_files[i]["file_obj"].read()
|
|
70
|
+
else:
|
|
71
|
+
wired_files[i]["data"] = None
|
|
72
|
+
if multiple:
|
|
73
|
+
on_file(wired_files)
|
|
88
74
|
else:
|
|
89
|
-
wired_files[0]
|
|
90
|
-
on_file(wired_files[0])
|
|
75
|
+
on_file(wired_files[0])
|
|
91
76
|
|
|
92
77
|
result: solara.Result = hooks.use_thread(handle_file, [wired_files])
|
|
93
78
|
if result.error:
|
|
94
79
|
raise result.error
|
|
95
80
|
|
|
96
81
|
return file_drop
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@solara.component
|
|
85
|
+
def FileDrop(
|
|
86
|
+
label="Drop file here",
|
|
87
|
+
on_total_progress: Optional[Callable[[float], None]] = None,
|
|
88
|
+
on_file: Optional[Callable[[FileInfo], None]] = None,
|
|
89
|
+
lazy: bool = True,
|
|
90
|
+
):
|
|
91
|
+
"""Region a user can drop a file into for file uploading.
|
|
92
|
+
|
|
93
|
+
If lazy=True, no file content will be loaded into memory,
|
|
94
|
+
nor will any data be transferred by default.
|
|
95
|
+
If lazy=False, file content will be loaded into memory and passed to the `on_file` callback via the `FileInfo.data` attribute.
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
A file object is of the following argument type:
|
|
99
|
+
```python
|
|
100
|
+
class FileInfo(typing.TypedDict):
|
|
101
|
+
name: str # file name
|
|
102
|
+
size: int # file size in bytes
|
|
103
|
+
file_obj: typing.BinaryIO
|
|
104
|
+
data: Optional[bytes]: bytes # only present if lazy=False
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
## Arguments
|
|
109
|
+
* `on_total_progress`: Will be called with the progress in % of the file upload.
|
|
110
|
+
* `on_file`: Will be called with a `FileInfo` object, which contains the file `.name`, `.length` and a `.file_obj` object.
|
|
111
|
+
* `lazy`: Whether to load the file contents into memory or not. If `False`,
|
|
112
|
+
the file contents will be loaded into memory via the `.data` attribute of file object(s).
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
return _FileDrop(label=label, on_total_progress=on_total_progress, on_file=on_file, lazy=lazy, multiple=False)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@solara.component
|
|
120
|
+
def FileDropMultiple(
|
|
121
|
+
label="Drop files here",
|
|
122
|
+
on_total_progress: Optional[Callable[[float], None]] = None,
|
|
123
|
+
on_file: Optional[Callable[[List[FileInfo]], None]] = None,
|
|
124
|
+
lazy: bool = True,
|
|
125
|
+
):
|
|
126
|
+
"""Region a user can drop multiple files into for file uploading.
|
|
127
|
+
|
|
128
|
+
Almost identical to `FileDrop` except that multiple files can be dropped and `on_file` is called
|
|
129
|
+
with a list of `FileInfo` objects.
|
|
130
|
+
|
|
131
|
+
## Arguments
|
|
132
|
+
* `on_total_progress`: Will be called with the progress in % of the file(s) upload.
|
|
133
|
+
* `on_file`: Will be called with a `List[FileInfo]`.
|
|
134
|
+
Each `FileInfo` contains the file `.name`, `.length`, `.file_obj` object, and `.data` attributes.
|
|
135
|
+
* `lazy`: Whether to load the file contents into memory or not.
|
|
136
|
+
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
return _FileDrop(label=label, on_total_progress=on_total_progress, on_file=on_file, lazy=lazy, multiple=True)
|
solara/components/file_drop.vue
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="dropzone" class="solara-file-drop" effectAllowed="move">
|
|
3
|
-
|
|
3
|
+
<template v-if="file_info && file_info.length > 0">
|
|
4
|
+
<template v-if="multiple">
|
|
5
|
+
<div v-for="file in file_info">
|
|
6
|
+
{{ file.name }}
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
<template v-else>
|
|
10
|
+
{{ file_info[0].name }}
|
|
11
|
+
</template>
|
|
12
|
+
</template>
|
|
13
|
+
<template v-else>
|
|
14
|
+
{{ label }}
|
|
15
|
+
</template>
|
|
4
16
|
</div>
|
|
5
17
|
</template>
|
|
6
18
|
|
|
@@ -16,16 +28,17 @@ module.exports = {
|
|
|
16
28
|
event.preventDefault();
|
|
17
29
|
const items = await Promise.all([...event.dataTransfer.items]);
|
|
18
30
|
const files = items.map(i => i.webkitGetAsEntry())
|
|
19
|
-
const
|
|
20
|
-
const
|
|
31
|
+
const fileHolders = files.filter(f => f.isFile)
|
|
32
|
+
const nativeFilesPromises = fileHolders.map(fileHolder => new Promise((rs, rj) => fileHolder.file(rs, rj)))
|
|
33
|
+
const nativeFiles = await Promise.all(nativeFilesPromises)
|
|
21
34
|
|
|
22
|
-
this.native_file_info =
|
|
35
|
+
this.native_file_info = nativeFiles
|
|
23
36
|
this.file_info = this.native_file_info.map(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
37
|
+
({name, size}) => ({
|
|
38
|
+
name,
|
|
39
|
+
isFile: true,
|
|
40
|
+
size,
|
|
41
|
+
}));
|
|
29
42
|
});
|
|
30
43
|
},
|
|
31
44
|
methods: {
|
|
@@ -64,6 +77,7 @@ module.exports = {
|
|
|
64
77
|
height: 100px;
|
|
65
78
|
border: 1px dashed gray;
|
|
66
79
|
margin: 8px 0;
|
|
67
|
-
padding: 8px
|
|
80
|
+
padding: 8px;
|
|
81
|
+
overflow: auto;
|
|
68
82
|
}
|
|
69
83
|
</style>
|
solara/components/head.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
|
|
3
3
|
import reacton
|
|
4
|
-
|
|
5
4
|
import solara
|
|
6
5
|
|
|
7
6
|
|
|
@@ -9,7 +8,7 @@ import solara
|
|
|
9
8
|
def Head(children: List[reacton.core.Element] = []):
|
|
10
9
|
"""A component that manager the "head" tag of the page to avoid duplicate tags, such as titles.
|
|
11
10
|
|
|
12
|
-
Currently only supports the [title](/
|
|
11
|
+
Currently only supports the [title](/documentation/components/page/title) tag as child, e.g.:
|
|
13
12
|
|
|
14
13
|
```python
|
|
15
14
|
import solara
|
solara/components/head_tag.py
CHANGED
|
@@ -2,9 +2,8 @@ from typing import Optional
|
|
|
2
2
|
|
|
3
3
|
import ipyvuetify as vy
|
|
4
4
|
import reacton.core
|
|
5
|
-
import traitlets
|
|
6
|
-
|
|
7
5
|
import solara
|
|
6
|
+
import traitlets
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class HeadTagWidget(vy.VuetifyTemplate):
|
|
@@ -19,7 +18,7 @@ class HeadTagWidget(vy.VuetifyTemplate):
|
|
|
19
18
|
def HeadTag(tagname: str, key=None, attributes: Optional[dict] = None):
|
|
20
19
|
"""Add a child element to head element, or replace a meta tag with the same tagname and key.
|
|
21
20
|
|
|
22
|
-
This component should be used inside a [Head](/
|
|
21
|
+
This component should be used inside a [Head](/documentation/components/page/head) component, e.g.:
|
|
23
22
|
|
|
24
23
|
```python
|
|
25
24
|
import solara
|
solara/components/image.py
CHANGED
|
@@ -163,7 +163,7 @@ def Image(
|
|
|
163
163
|
layout=layout,
|
|
164
164
|
)
|
|
165
165
|
elif solara.util.isinstanceof(image, "numpy:ndarray"):
|
|
166
|
-
value = solara.util.numpy_to_image(image, format="png")
|
|
166
|
+
value = solara.util.numpy_to_image(image, format="png") # type: ignore
|
|
167
167
|
return rw.Image(
|
|
168
168
|
value=value,
|
|
169
169
|
format="png",
|
solara/components/link.py
CHANGED
|
@@ -2,7 +2,6 @@ from typing import Dict, List, Union
|
|
|
2
2
|
|
|
3
3
|
import ipyvue as vue
|
|
4
4
|
import reacton.ipyvue as vuer
|
|
5
|
-
|
|
6
5
|
import solara
|
|
7
6
|
|
|
8
7
|
|
|
@@ -18,8 +17,8 @@ def Link(
|
|
|
18
17
|
|
|
19
18
|
See also:
|
|
20
19
|
|
|
21
|
-
* [Multipage](/
|
|
22
|
-
* [Understanding Routing](/
|
|
20
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
21
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
23
22
|
|
|
24
23
|
Most common usage is in combination with a button, e.g.:
|
|
25
24
|
|
|
@@ -32,7 +31,7 @@ def Link(
|
|
|
32
31
|
## Arguments
|
|
33
32
|
|
|
34
33
|
* path_or_route: the path or route to navigate to. Paths should be absolute, e.g. '/fruit/banana'.
|
|
35
|
-
If a route is given, [`resolve_path`](/api/resolve_path)] will be used to resolve to the absolute path.
|
|
34
|
+
If a route is given, [`resolve_path`](/documentation/api/routing/resolve_path)] will be used to resolve to the absolute path.
|
|
36
35
|
* children: the children of the link. If a child is clicked, the link will be followed.
|
|
37
36
|
* nofollow: If True, the link will not be followed by web crawlers (such as google).
|
|
38
37
|
* style: CSS styles to apply to the HTML link element. Either a string or a dictionary.
|
solara/components/meta.py
CHANGED
|
@@ -9,7 +9,7 @@ from .head_tag import HeadTag
|
|
|
9
9
|
def Meta(name: Optional[str] = None, property: Optional[str] = None, content: Optional[str] = None):
|
|
10
10
|
"""Add a meta tag to the head element, or replace a meta tag with the same name and or property.
|
|
11
11
|
|
|
12
|
-
This component should be used inside a [Head](/
|
|
12
|
+
This component should be used inside a [Head](/documentation/components/page/head) component, e.g.:
|
|
13
13
|
|
|
14
14
|
```python
|
|
15
15
|
import solara
|
solara/components/misc.py
CHANGED
|
@@ -3,7 +3,6 @@ from typing import Any, Callable, Dict, List, Union
|
|
|
3
3
|
|
|
4
4
|
import reacton
|
|
5
5
|
import reacton.ipyvuetify as v
|
|
6
|
-
|
|
7
6
|
import solara
|
|
8
7
|
import solara.widgets
|
|
9
8
|
from solara.util import _combine_classes
|
|
@@ -159,7 +158,7 @@ def HBox(children=[], grow=True, align_items="stretch", classes: List[str] = [])
|
|
|
159
158
|
def Row(children=[], gap="12px", justify="start", margin: int = 0, classes: List[str] = [], style: Union[str, Dict[str, str], None] = None):
|
|
160
159
|
"""Lays out children in a row, side by side, with the given gap between them.
|
|
161
160
|
|
|
162
|
-
See also [Column](/
|
|
161
|
+
See also [Column](/documentation/components/layout/column).
|
|
163
162
|
|
|
164
163
|
Example with three children side by side:
|
|
165
164
|
|
|
@@ -200,7 +199,7 @@ def Row(children=[], gap="12px", justify="start", margin: int = 0, classes: List
|
|
|
200
199
|
def Column(children=[], gap="12px", align="stretch", margin: int = 0, classes: List[str] = [], style: Union[str, Dict[str, str], None] = None):
|
|
201
200
|
"""Lays out children in a column on top of each other, with the given gap between them.
|
|
202
201
|
|
|
203
|
-
See also [Row](/
|
|
202
|
+
See also [Row](/documentation/components/layout/row).
|
|
204
203
|
|
|
205
204
|
Example with three children on top of each other:
|
|
206
205
|
|
|
@@ -287,17 +286,14 @@ def FigurePlotly(
|
|
|
287
286
|
"plotly_hover": on_hover,
|
|
288
287
|
"plotly_unhover": on_unhover,
|
|
289
288
|
"plotly_selected": on_selection,
|
|
290
|
-
"plotly_deselect": on_deselect
|
|
289
|
+
"plotly_deselect": on_deselect,
|
|
291
290
|
}
|
|
292
|
-
|
|
291
|
+
|
|
293
292
|
callback = event_mapping.get(event_type)
|
|
294
293
|
if callback:
|
|
295
294
|
callback(data)
|
|
296
295
|
|
|
297
|
-
fig_element = FigureWidget.element(
|
|
298
|
-
on__js2py_pointsCallback=on_points_callback,
|
|
299
|
-
on__js2py_relayout=on_relayout
|
|
300
|
-
)
|
|
296
|
+
fig_element = FigureWidget.element(on__js2py_pointsCallback=on_points_callback, on__js2py_relayout=on_relayout)
|
|
301
297
|
|
|
302
298
|
def update_data():
|
|
303
299
|
fig_widget: FigureWidget = solara.get_widget(fig_element)
|
solara/datatypes.py
CHANGED
|
@@ -114,8 +114,8 @@ class Route:
|
|
|
114
114
|
|
|
115
115
|
## See also
|
|
116
116
|
|
|
117
|
-
* [Multipage](/
|
|
118
|
-
* [Understanding Routing](/
|
|
117
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
118
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
119
119
|
|
|
120
120
|
"""
|
|
121
121
|
|
solara/hooks/use_reactive.py
CHANGED
|
@@ -13,7 +13,7 @@ def use_reactive(
|
|
|
13
13
|
|
|
14
14
|
It is a useful alternative to `use_state` when you want to use a
|
|
15
15
|
reactive variable for the component state.
|
|
16
|
-
See also [our documentation on state management](/
|
|
16
|
+
See also [our documentation on state management](/documentation/getting_started/fundamentals/state-management).
|
|
17
17
|
|
|
18
18
|
If the variable passed is a reactive variable, it will be returned instead and no
|
|
19
19
|
new reactive variable will be created. This is useful for implementing component
|
|
@@ -92,7 +92,7 @@ def use_reactive(
|
|
|
92
92
|
except RuntimeError as e:
|
|
93
93
|
raise RuntimeError(
|
|
94
94
|
"use_reactive must be called from a component function, inside the render function.\n"
|
|
95
|
-
"Do not call it top level, use [solara.reactive()](https://solara.dev/api/reactive) instead."
|
|
95
|
+
"Do not call it top level, use [solara.reactive()](https://solara.dev/documentation/api/utilities/reactive) instead."
|
|
96
96
|
) from e
|
|
97
97
|
on_change_ref.current = on_change
|
|
98
98
|
|
|
@@ -60,7 +60,7 @@ def ConfirmationDialog(
|
|
|
60
60
|
):
|
|
61
61
|
"""A dialog used to confirm a user action.
|
|
62
62
|
|
|
63
|
-
(*Note: [This component is experimental and its API may change in the future](/
|
|
63
|
+
(*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
|
|
64
64
|
|
|
65
65
|
By default, has a title, a text explaining the
|
|
66
66
|
decision to be made, and two buttons "OK" and "Cancel".
|
solara/lab/components/tabs.py
CHANGED
|
@@ -17,9 +17,9 @@ def Tab(
|
|
|
17
17
|
):
|
|
18
18
|
"""An item in a Tabs component.
|
|
19
19
|
|
|
20
|
-
(*Note: [This component is experimental and its API may change in the future](/
|
|
20
|
+
(*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
|
|
21
21
|
|
|
22
|
-
Should be a direct child of a [Tabs](/
|
|
22
|
+
Should be a direct child of a [Tabs](/documentation/components/lab/tabs).
|
|
23
23
|
|
|
24
24
|
## Arguments
|
|
25
25
|
* `label`: The label of the tab.
|
|
@@ -62,12 +62,12 @@ def Tabs(
|
|
|
62
62
|
):
|
|
63
63
|
"""A tabbed container showing one tab at a time.
|
|
64
64
|
|
|
65
|
-
(*Note: [This component is experimental and its API may change in the future](/
|
|
65
|
+
(*Note: [This component is experimental and its API may change in the future](/documentation/getting_started/lab).*)
|
|
66
66
|
|
|
67
|
-
Note that if Tabs are used as a child of the [AppBar](/
|
|
67
|
+
Note that if Tabs are used as a child of the [AppBar](/documentation/components/layout/app_bar) component, the tabs
|
|
68
68
|
will be placed under the app bar. See our [authorization app](/apps/authorization) for an example.
|
|
69
69
|
|
|
70
|
-
If the children [Tab](/
|
|
70
|
+
If the children [Tab](/documentation/components/lab/tab) elements are passed a `path_or_route` argument, the active tab
|
|
71
71
|
will be based on the path of the current page.
|
|
72
72
|
|
|
73
73
|
|
|
@@ -89,7 +89,7 @@ def Tabs(
|
|
|
89
89
|
|
|
90
90
|
### Tabs with content
|
|
91
91
|
|
|
92
|
-
This is usually only used when the tabs are placed in the [AppBar](/
|
|
92
|
+
This is usually only used when the tabs are placed in the [AppBar](/documentation/components/layout/app_bar) component.
|
|
93
93
|
|
|
94
94
|
```solara
|
|
95
95
|
import solara
|
solara/reactive.py
CHANGED
|
@@ -14,7 +14,7 @@ def reactive(value: T) -> Reactive[T]:
|
|
|
14
14
|
Solara web applications. They provide an easy-to-use mechanism for keeping
|
|
15
15
|
track of the changing state of data and for propagating those changes to
|
|
16
16
|
the appropriate UI components. For managing local or component-specific
|
|
17
|
-
state, consider using the [`solara.use_state()`](/api/use_state) function.
|
|
17
|
+
state, consider using the [`solara.use_state()`](/documentation/api/hooks/use_state) function.
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
Reactive variables can be accessed using the `.value` attribute. To modify
|
solara/routing.py
CHANGED
|
@@ -101,7 +101,7 @@ def use_route_level():
|
|
|
101
101
|
def use_router() -> Router:
|
|
102
102
|
"""Returns the current router object.
|
|
103
103
|
|
|
104
|
-
See also [Understanding Routing](/
|
|
104
|
+
See also [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
105
105
|
|
|
106
106
|
`use_router` returns the current router object. This is useful to build custom routing.
|
|
107
107
|
|
|
@@ -123,7 +123,7 @@ def use_router() -> Router:
|
|
|
123
123
|
router = solara.use_router()
|
|
124
124
|
|
|
125
125
|
def redirect():
|
|
126
|
-
router.push(f"/api/use_route")
|
|
126
|
+
router.push(f"/documentation/api/routing/use_route")
|
|
127
127
|
|
|
128
128
|
solara.Button("Navigate using an event", on_click=redirect)
|
|
129
129
|
```
|
|
@@ -138,7 +138,7 @@ def use_route(
|
|
|
138
138
|
) -> Tuple[Optional[solara.Route], List[solara.Route]]:
|
|
139
139
|
"""Returns (if found) the current route that matches the pathname, or None
|
|
140
140
|
|
|
141
|
-
See also [Understanding Routing](/
|
|
141
|
+
See also [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
142
142
|
|
|
143
143
|
`use_route` returns (if found) the current route that matches the pathname, or None. It also returns all resolved routes of that level
|
|
144
144
|
(i.e. all siblings and itself). This return tuple is useful to build custom navigation (e.g. using tabs or buttons).
|
|
@@ -166,9 +166,10 @@ def use_route(
|
|
|
166
166
|
|
|
167
167
|
Note that all routes are relative, since a component does not know if it is embedded into a larger application, which may also do routing.
|
|
168
168
|
Therefore you should never use the `route.path` for navigation since the route object has no knowledge of the full url
|
|
169
|
-
(e.g. `/api/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
|
|
169
|
+
(e.g. `/documentation/api/routing/use_route/fruit/banana`) but only knows its small piece of the pathname (e.g. `banana`)
|
|
170
170
|
|
|
171
|
-
Use [`resolve_path`](/api/resolve_path) to request the full url for navigation,
|
|
171
|
+
Use [`resolve_path`](/documentation/api/routing/resolve_path) to request the full url for navigation,
|
|
172
|
+
or simply use the `Link` component that can do this for us.
|
|
172
173
|
|
|
173
174
|
If the current route has children, any child component that calls `use_route` will return the matched route and its siblings of our children.
|
|
174
175
|
|
|
@@ -229,12 +230,12 @@ def resolve_path(path_or_route: Union[str, solara.Route], level=0) -> str:
|
|
|
229
230
|
|
|
230
231
|
## Arguments
|
|
231
232
|
|
|
232
|
-
* path_or_route: a path string or a [`solara.Route`](/api/route) object to resolve.
|
|
233
|
+
* path_or_route: a path string or a [`solara.Route`](/documentation/api/routing/route) object to resolve.
|
|
233
234
|
|
|
234
235
|
## See also
|
|
235
236
|
|
|
236
|
-
* [Multipage](/
|
|
237
|
-
* [Understanding Routing](/
|
|
237
|
+
* [Multipage](/documentation/advanced/howto/multipage).
|
|
238
|
+
* [Understanding Routing](/documentation/advanced/understanding/routing).
|
|
238
239
|
|
|
239
240
|
|
|
240
241
|
"""
|
solara/scope/__init__.py
CHANGED
|
@@ -50,7 +50,7 @@ def get_kernel_id(ipython_fallback=True) -> str:
|
|
|
50
50
|
See [Understanding solara server](/docs/understanding/solara-server) for understanding the concept of virtual kernels
|
|
51
51
|
and their lifetime.
|
|
52
52
|
|
|
53
|
-
This unique ID can be useful to to implement storing state, scoped to a kernel. See [the scope example](/examples/general/
|
|
53
|
+
This unique ID can be useful to to implement storing state, scoped to a kernel. See [the scope example](/examples/general/custom_storage) for an example.
|
|
54
54
|
|
|
55
55
|
If `ipython_fallback` is `True` (default), this function will also work in IPython notebooks, where it will return the IPython kernel id.
|
|
56
56
|
|
|
@@ -79,7 +79,8 @@ def get_session_id() -> str:
|
|
|
79
79
|
|
|
80
80
|
See [Understanding solara server](/docs/understanding/solara-server#session) for more information about the Solara sessions.
|
|
81
81
|
|
|
82
|
-
This unique ID can be useful to to implement storing state, scoped to a browser session. See [the scope example](/examples/general/
|
|
82
|
+
This unique ID can be useful to to implement storing state, scoped to a browser session. See [the scope example](/examples/general/custom_storage)
|
|
83
|
+
for an example.
|
|
83
84
|
"""
|
|
84
85
|
import solara.server.kernel_context
|
|
85
86
|
|
solara/server/app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
import importlib.util
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
@@ -5,6 +6,7 @@ import pickle
|
|
|
5
6
|
import sys
|
|
6
7
|
import threading
|
|
7
8
|
import traceback
|
|
9
|
+
import warnings
|
|
8
10
|
from enum import Enum
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
from typing import Any, Dict, List, Optional, cast
|
|
@@ -14,6 +16,7 @@ import reacton
|
|
|
14
16
|
from reacton.core import Element, render
|
|
15
17
|
|
|
16
18
|
import solara
|
|
19
|
+
from solara.util import nested_get
|
|
17
20
|
|
|
18
21
|
from . import kernel_context, patch, reload, settings
|
|
19
22
|
from .kernel import Kernel
|
|
@@ -149,9 +152,24 @@ class AppScript:
|
|
|
149
152
|
routes = solara.generate_routes(mod)
|
|
150
153
|
|
|
151
154
|
app = solara.autorouting.RenderPage(self.app_name)
|
|
152
|
-
|
|
155
|
+
|
|
156
|
+
# when the root moduled defined routes, skip the enclosing route object
|
|
157
|
+
if len(routes) == 1 and routes[0].module and hasattr(routes[0].module, "routes"):
|
|
158
|
+
if routes[0].component:
|
|
159
|
+
warnings.warn(
|
|
160
|
+
f"{self.name} has a component defined, but you are also defining routes."
|
|
161
|
+
" To avoid confusing, consider renaming the {self.app_name} component."
|
|
162
|
+
)
|
|
153
163
|
routes = routes[0].children
|
|
154
164
|
|
|
165
|
+
if self.app_name != "Page":
|
|
166
|
+
# if we specified the app name, we replace the component
|
|
167
|
+
if len(routes) > 1:
|
|
168
|
+
raise ValueError(f"App {self.name} has multiple routes, but a default app name was given: {self.app_name}")
|
|
169
|
+
assert len(routes) == 1
|
|
170
|
+
component = nested_get(routes[0].module.__dict__, self.app_name, None)
|
|
171
|
+
routes = [dataclasses.replace(routes[0], component=component)]
|
|
172
|
+
|
|
155
173
|
if settings.ssg.build_path is None:
|
|
156
174
|
settings.ssg.build_path = self.directory.parent.resolve() / "build"
|
|
157
175
|
|
|
@@ -231,6 +249,31 @@ class AppScript:
|
|
|
231
249
|
|
|
232
250
|
solara.lab.toestand.ConnectionStore._type_counter.clear()
|
|
233
251
|
|
|
252
|
+
# we need to remove callbacks that are added in the app code
|
|
253
|
+
# which will be re-executed after the reload and we do not
|
|
254
|
+
# want to keep executing the old ones.
|
|
255
|
+
for kc in kernel_context._on_kernel_start_callbacks.copy():
|
|
256
|
+
callback, path, module, cleanup = kc
|
|
257
|
+
will_reload = False
|
|
258
|
+
if module is not None:
|
|
259
|
+
module_name = module.__name__
|
|
260
|
+
if module_name in reload.reloader.get_reload_module_names():
|
|
261
|
+
will_reload = True
|
|
262
|
+
elif path is not None:
|
|
263
|
+
if str(path.resolve()).startswith(str(self.directory)):
|
|
264
|
+
will_reload = True
|
|
265
|
+
else:
|
|
266
|
+
logger.warning(
|
|
267
|
+
"script %s is not in the same directory as the app %s but is using on_kernel_start, "
|
|
268
|
+
"this might lead to multiple entries, and might indicate a bug.",
|
|
269
|
+
path,
|
|
270
|
+
self.directory,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if will_reload:
|
|
274
|
+
logger.info("reload: Removing on_kernel_start callback: %s (since it will be added when reloaded)", callback)
|
|
275
|
+
cleanup()
|
|
276
|
+
|
|
234
277
|
context_values = list(kernel_context.contexts.values())
|
|
235
278
|
# save states into the context so the hot reload will
|
|
236
279
|
# keep the same state
|
solara/server/assets/style.css
CHANGED
|
@@ -23,6 +23,12 @@ code::before {
|
|
|
23
23
|
box-shadow: unset !important;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
.v-application--wrap .v-application--wrap {
|
|
27
|
+
/* disable min-height: 100vh set by vuetify for nested apps */
|
|
28
|
+
min-height: unset;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
26
32
|
div.highlight pre code {
|
|
27
33
|
/* box-shadow: 0 2px 1px -1px rgb(0 0 0 / 20%), 0 1px 1px 0 rgb(0 0 0 / 14%), 0 1px 3px 0 rgb(0 0 0 / 12%); */
|
|
28
34
|
/* border: 1px #333 solid; */
|
solara/server/esm.py
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
# in the future, we may want to move the esm features of ipyreact
|
|
3
3
|
# into a separate package, and then we can import it unconditionally
|
|
4
4
|
import logging
|
|
5
|
+
import threading
|
|
6
|
+
from collections import defaultdict
|
|
5
7
|
from pathlib import Path
|
|
6
8
|
from typing import Dict, List, Tuple, Union
|
|
7
9
|
|
|
8
10
|
import ipyreact.importmap
|
|
9
11
|
import ipyreact.module
|
|
10
12
|
|
|
13
|
+
from solara.server import kernel_context
|
|
14
|
+
|
|
11
15
|
logger = logging.getLogger("solara.server.esm")
|
|
16
|
+
lock = threading.Lock()
|
|
12
17
|
|
|
13
18
|
_modules: Dict[str, Tuple[Union[str, Path], List[str]]] = {}
|
|
19
|
+
_modules_added_per_kernel: Dict[str, Dict[str, ipyreact.module.Module]] = defaultdict(dict)
|
|
20
|
+
_import_map_per_kernel: Dict[str, ipyreact.importmap.ImportMap] = {}
|
|
14
21
|
|
|
15
22
|
|
|
16
23
|
# in solara server, we'll monkey patch ipyreact.module with this
|
|
@@ -29,10 +36,20 @@ def get_module_names():
|
|
|
29
36
|
|
|
30
37
|
|
|
31
38
|
def create_modules():
|
|
39
|
+
kernel_id = kernel_context.get_current_context().id
|
|
40
|
+
_modules_added = _modules_added_per_kernel[kernel_id]
|
|
32
41
|
logger.info("define modules %s", _modules)
|
|
33
42
|
widgets = {}
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
with lock:
|
|
44
|
+
for name, (module, dependencies) in _modules.items():
|
|
45
|
+
if name not in _modules_added:
|
|
46
|
+
_modules_added[name] = create_module(name, module, dependencies=dependencies)
|
|
47
|
+
logger.info("create module %s %s %s", name, module, dependencies)
|
|
48
|
+
else:
|
|
49
|
+
_modules_added[name].code = module if not isinstance(module, Path) else module.read_text(encoding="utf8")
|
|
50
|
+
_modules_added[name].dependencies = dependencies
|
|
51
|
+
logger.info("update module %s %s %s", name, module, dependencies)
|
|
52
|
+
widgets[name] = _modules_added[name]
|
|
36
53
|
return widgets
|
|
37
54
|
|
|
38
55
|
|
|
@@ -41,5 +58,12 @@ def create_module(name, module: Union[str, Path], dependencies: List[str]):
|
|
|
41
58
|
|
|
42
59
|
|
|
43
60
|
def create_import_map():
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
kernel_id = kernel_context.get_current_context().id
|
|
62
|
+
with lock:
|
|
63
|
+
if kernel_id not in _import_map_per_kernel:
|
|
64
|
+
_import_map_per_kernel[kernel_id] = ipyreact.importmap.ImportMap(import_map=ipyreact.importmap._effective_import_map)
|
|
65
|
+
logger.info("create import map %s", ipyreact.importmap._effective_import_map)
|
|
66
|
+
else:
|
|
67
|
+
_import_map_per_kernel[kernel_id].import_map = ipyreact.importmap._effective_import_map
|
|
68
|
+
logger.info("update import map %s", ipyreact.importmap._effective_import_map)
|
|
69
|
+
return
|