solara-ui 1.41.0__py2.py3-none-any.whl → 1.43.0__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 +1 -1
- solara/__main__.py +17 -6
- solara/_stores.py +189 -0
- solara/components/__init__.py +18 -1
- solara/components/component_vue.py +23 -0
- solara/components/datatable.py +4 -4
- solara/components/echarts.py +5 -2
- solara/components/echarts.vue +22 -5
- solara/components/file_drop.py +20 -0
- solara/components/input.py +21 -1
- solara/components/markdown.py +62 -17
- solara/components/misc.py +2 -2
- solara/components/spinner-solara.vue +2 -2
- solara/components/spinner.py +17 -2
- solara/hooks/use_reactive.py +8 -1
- solara/lab/components/__init__.py +1 -0
- solara/lab/components/chat.py +3 -3
- solara/lab/components/input_time.py +133 -0
- solara/lab/hooks/dataframe.py +1 -0
- solara/lab/utils/dataframe.py +11 -1
- solara/reactive.py +9 -3
- solara/server/app.py +63 -30
- solara/server/flask.py +12 -2
- solara/server/jupyter/server_extension.py +1 -0
- solara/server/kernel.py +52 -4
- solara/server/kernel_context.py +66 -7
- solara/server/patch.py +25 -29
- solara/server/qt.py +1 -1
- solara/server/server.py +15 -5
- solara/server/settings.py +11 -0
- solara/server/shell.py +19 -1
- solara/server/starlette.py +39 -11
- solara/server/static/solara_bootstrap.py +1 -1
- solara/settings.py +17 -0
- solara/tasks.py +18 -8
- solara/template/portal/pyproject.toml +1 -1
- solara/test/pytest_plugin.py +4 -0
- solara/toestand.py +170 -16
- solara/util.py +40 -0
- solara/website/components/docs.py +4 -0
- solara/website/components/markdown.py +60 -2
- solara/website/pages/changelog/changelog.md +17 -0
- solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +10 -0
- solara/website/pages/documentation/api/cross_filter/cross_filter_dataframe.py +4 -5
- solara/website/pages/documentation/api/cross_filter/cross_filter_report.py +3 -5
- solara/website/pages/documentation/api/cross_filter/cross_filter_select.py +3 -5
- solara/website/pages/documentation/api/cross_filter/cross_filter_slider.py +3 -5
- solara/website/pages/documentation/api/hooks/use_cross_filter.py +3 -5
- solara/website/pages/documentation/api/hooks/use_exception.py +9 -11
- solara/website/pages/documentation/api/hooks/use_previous.py +6 -9
- solara/website/pages/documentation/api/hooks/use_state_or_update.py +23 -26
- solara/website/pages/documentation/api/hooks/use_thread.py +11 -13
- solara/website/pages/documentation/api/routing/route.py +10 -12
- solara/website/pages/documentation/api/routing/use_route.py +26 -30
- solara/website/pages/documentation/api/utilities/on_kernel_start.py +17 -0
- solara/website/pages/documentation/components/advanced/link.py +6 -8
- solara/website/pages/documentation/components/advanced/meta.py +6 -9
- solara/website/pages/documentation/components/advanced/style.py +7 -9
- solara/website/pages/documentation/components/input/file_browser.py +12 -14
- solara/website/pages/documentation/components/input/input.py +22 -0
- solara/website/pages/documentation/components/lab/input_time.py +15 -0
- solara/website/pages/documentation/components/layout/columns_responsive.py +37 -39
- solara/website/pages/documentation/components/layout/gridfixed.py +4 -6
- solara/website/pages/documentation/components/output/html.py +1 -3
- solara/website/pages/documentation/components/page/head.py +4 -7
- solara/website/pages/documentation/components/viz/echarts.py +3 -1
- solara/website/pages/documentation/examples/__init__.py +9 -0
- solara/website/pages/documentation/examples/ai/chatbot.py +60 -44
- solara/website/pages/documentation/examples/general/live_update.py +1 -0
- solara/website/pages/documentation/examples/general/pokemon_search.py +3 -3
- solara/website/pages/documentation/examples/visualization/linked_views.py +0 -3
- solara/website/pages/documentation/faq/content/99-faq.md +9 -0
- solara/website/pages/documentation/getting_started/content/00-quickstart.md +2 -2
- solara/website/pages/documentation/getting_started/content/01-introduction.md +1 -1
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +23 -19
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +2 -2
- solara/website/pages/roadmap/roadmap.md +6 -0
- {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/METADATA +9 -6
- {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/RECORD +83 -80
- {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/WHEEL +1 -1
- {solara_ui-1.41.0.data → solara_ui-1.43.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara_ui-1.41.0.data → solara_ui-1.43.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara_ui-1.41.0.dist-info → solara_ui-1.43.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,6 +5,8 @@ from .breadcrumbs import BreadCrumbs
|
|
|
5
5
|
|
|
6
6
|
@solara.component
|
|
7
7
|
def Gallery(route_external=None):
|
|
8
|
+
from ..pages.documentation.examples import pycafe_projects
|
|
9
|
+
|
|
8
10
|
if route_external is not None:
|
|
9
11
|
route_current = route_external
|
|
10
12
|
else:
|
|
@@ -49,6 +51,8 @@ def Gallery(route_external=None):
|
|
|
49
51
|
image_url = "https://dxhl76zpt6fap.cloudfront.net/public/api/" + child.path + ".gif"
|
|
50
52
|
elif child.path in ["card", "dataframe", "pivot_table", "slider"]:
|
|
51
53
|
image_url = "https://dxhl76zpt6fap.cloudfront.net/public/api/" + child.path + ".png"
|
|
54
|
+
elif child.path in pycafe_projects:
|
|
55
|
+
image_url = f"https://py.cafe/preview/solara/{child.path}"
|
|
52
56
|
else:
|
|
53
57
|
image_url = "https://dxhl76zpt6fap.cloudfront.net/public/logo.svg"
|
|
54
58
|
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
from typing import Dict, List, Union
|
|
1
|
+
from typing import Callable, Dict, List, Union, cast
|
|
2
2
|
|
|
3
3
|
import yaml
|
|
4
|
+
import markdown
|
|
5
|
+
import mkdocs_pycafe
|
|
6
|
+
import pymdownx.superfences
|
|
4
7
|
|
|
5
8
|
import solara
|
|
9
|
+
from solara.components.markdown import formatter, _no_deep_copy_emojione
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
# We want to separate metadata from the markdown files before rendering them, which solara.Markdown doesn't support
|
|
9
13
|
@solara.component
|
|
10
14
|
def MarkdownWithMetadata(content: str, unsafe_solara_execute=True):
|
|
15
|
+
cleanups = solara.use_ref(cast(List[Callable[[], None]], []))
|
|
11
16
|
if "---" in content:
|
|
12
17
|
pre_content, raw_metadata, post_content = content.split("---")
|
|
13
18
|
metadata: Dict[str, Union[str, List[str]]] = yaml.safe_load(raw_metadata)
|
|
@@ -27,12 +32,65 @@ def MarkdownWithMetadata(content: str, unsafe_solara_execute=True):
|
|
|
27
32
|
solara.Meta(property=key, content=value)
|
|
28
33
|
else:
|
|
29
34
|
solara.Meta(name=key, content=value)
|
|
35
|
+
|
|
36
|
+
def make_markdown_object():
|
|
37
|
+
return markdown.Markdown( # type: ignore
|
|
38
|
+
extensions=[
|
|
39
|
+
"pymdownx.highlight",
|
|
40
|
+
"pymdownx.superfences",
|
|
41
|
+
"pymdownx.emoji",
|
|
42
|
+
"toc", # so we get anchors for h1 h2 etc
|
|
43
|
+
"tables",
|
|
44
|
+
],
|
|
45
|
+
extension_configs={
|
|
46
|
+
"pymdownx.emoji": {
|
|
47
|
+
"emoji_index": _no_deep_copy_emojione,
|
|
48
|
+
},
|
|
49
|
+
"pymdownx.superfences": {
|
|
50
|
+
"custom_fences": [
|
|
51
|
+
{
|
|
52
|
+
"name": "mermaid",
|
|
53
|
+
"class": "mermaid",
|
|
54
|
+
"format": pymdownx.superfences.fence_div_format,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "solara",
|
|
58
|
+
"class": "",
|
|
59
|
+
"validator": mkdocs_pycafe.validator,
|
|
60
|
+
"format": mkdocs_pycafe.formatter(
|
|
61
|
+
type="solara", next_formatter=formatter(unsafe_solara_execute, cleanups.current), inside_last_div=False
|
|
62
|
+
),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "python",
|
|
66
|
+
"class": "highlight",
|
|
67
|
+
"validator": mkdocs_pycafe.validator,
|
|
68
|
+
"format": mkdocs_pycafe.formatter(
|
|
69
|
+
type="solara", next_formatter=formatter(unsafe_solara_execute, cleanups.current), inside_last_div=False
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
md_parser = solara.use_memo(make_markdown_object, dependencies=[unsafe_solara_execute])
|
|
78
|
+
|
|
79
|
+
def cleanup_wrapper():
|
|
80
|
+
def cleanup():
|
|
81
|
+
for cleanup in cleanups.current:
|
|
82
|
+
cleanup()
|
|
83
|
+
|
|
84
|
+
return cleanup
|
|
85
|
+
|
|
86
|
+
solara.use_effect(cleanup_wrapper, [])
|
|
87
|
+
|
|
30
88
|
with solara.v.Html(
|
|
31
89
|
tag="div",
|
|
32
90
|
style_="display: flex; flex-direction: row; justify-content: center; gap: 15px; max-width: 90%; margin: 0 auto;",
|
|
33
91
|
attributes={"id": "markdown-to-navigate"},
|
|
34
92
|
):
|
|
35
|
-
solara.Markdown(content, unsafe_solara_execute=unsafe_solara_execute, style="flex-grow: 1; max-width: min(100%, 1024px);")
|
|
93
|
+
solara.Markdown(content, unsafe_solara_execute=unsafe_solara_execute, style="flex-grow: 1; max-width: min(100%, 1024px);", md_parser=md_parser)
|
|
36
94
|
MarkdownNavigation(id="markdown-to-navigate").key("markdown-nav" + str(hash(content)))
|
|
37
95
|
|
|
38
96
|
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Solara Changelog
|
|
2
2
|
|
|
3
|
+
## Version 1.42.0
|
|
4
|
+
* Feature: Mutation detection is now available under the `SOLARA_STORAGE_MUTATION_DETECTION` environmental variable. [#595](https://github.com/widgetti/solara/pull/595).
|
|
5
|
+
* Feature: Autofocusing text inputs is now supported. [#788](https://github.com/widgetti/solara/pull/788).
|
|
6
|
+
* Feature: Custom colours are now supported for the Solara loading spinner. [#858](https://github.com/widgetti/solara/pull/858)
|
|
7
|
+
* Bug Fix: Echarts responsive size is now properly supported. [#273](https://github.com/widgetti/solara/pull/273).
|
|
8
|
+
* Bug Fix: Some version checks would prevent Solara from starting. [#904](https://github.com/widgetti/solara/pull/904).
|
|
9
|
+
* Bug Fix: Solara apps running in qt mode (`--qt`) should now always work correctly. [#856](https://github.com/widgetti/solara/pull/856).
|
|
10
|
+
* Bug Fix: Hot reloading of files outside working directory would crash app. [069a205](https://github.com/widgetti/solara/commit/069a205c88a8cbcb0b0ca23f4d56889c8ad6134a) and [#869](https://github.com/widgetti/solara/pull/869).
|
|
11
|
+
|
|
12
|
+
## Version 1.41.0
|
|
13
|
+
* Feature: Support automatic resizing of Altair (Vega-Lite) figures. [#833](https://github.com/widgetti/solara/pull/833).
|
|
14
|
+
* Feature (Experimental): Support running Solara applications as standalone QT apps. [#835](https://github.com/widgetti/solara/pull/835).
|
|
15
|
+
* Feature: Add option to hide "This website runs on Solara"-banner. [#836](https://github.com/widgetti/solara/pull/836).
|
|
16
|
+
* Feature: Support navigating to hashes. [#814](https://github.com/widgetti/solara/pull/814).
|
|
17
|
+
* Bug Fix: Chunks and assets in nbextensions would fail to load. [9efe26c](https://github.com/widgetti/solara/commit/9efe26cbe00210163a6e8ef251ebfe50ca87fce2).
|
|
18
|
+
* Bug Fix: Vue widget decorator now always uses absolute paths. [#826](https://github.com/widgetti/solara/pull/826).
|
|
19
|
+
|
|
3
20
|
## Version 1.40.0
|
|
4
21
|
* Feature: In Jupyter Notebook and Lab, Solara (server) now renders the [ipypopout](https://github.com/widgetti/ipypopout) window instead of Voila [#805](render ipypopout content in jupyter notebook and lab)
|
|
5
22
|
* Feature: Support styling input field of [ChatInput component](https://solara.dev/documentation/components/lab/chat). [#800](https://github.com/widgetti/solara/pull/800).
|
|
@@ -51,6 +51,7 @@ browser pages. This can be used to store state that outlives a page refresh.
|
|
|
51
51
|
We recommend storing the state in an external database, especially in the case of multiple workers/nodes. If you want to store state associated to a session in-memory, make sure to set up sticky sessions.
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
The `solara-session-id` cookie is accessible in the browser using JavaScript. If you deem this a security risk, you can disable the cookie by setting the `SOLARA_SESSION_HTTP_ONLY` environment variable to `True`.
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
## Readiness check
|
|
@@ -78,7 +79,16 @@ $ curl http://localhost:8765/resourcez\?verbose
|
|
|
78
79
|
|
|
79
80
|
The JSON format may be subject to change.
|
|
80
81
|
|
|
82
|
+
## Ignoring notebook extensions
|
|
81
83
|
|
|
84
|
+
Not all (classic) jupyter notebook extensions are compatible with Solara, and there is not way to distinguish between notebook extensions that are needed for widgets and those that are not.
|
|
85
|
+
To ignore notebook extensions, you can set the `SOLARA_SERVER_IGNORE_NBEXTENSIONS` environment variable. This is a comma separated list of notebook extensions to ignore. For example, to ignore the `dash/main` and `foo/bar` extensions, you can run:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
$ SOLARA_SERVER_IGNORE_NBEXTENSIONS="dash/main,foo/bar" solara run nogit/sol.py -a
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Note that these error are not fatal, and the Solara app will still run.
|
|
82
92
|
|
|
83
93
|
## Production mode
|
|
84
94
|
|
|
@@ -13,11 +13,10 @@ df = plotly.data.gapminder()
|
|
|
13
13
|
@solara.component
|
|
14
14
|
def Page():
|
|
15
15
|
solara.provide_cross_filter()
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return main
|
|
16
|
+
|
|
17
|
+
solara.CrossFilterReport(df, classes=["py-2"])
|
|
18
|
+
solara.CrossFilterSelect(df, "country")
|
|
19
|
+
solara.CrossFilterDataFrame(df)
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
__doc__ += apidoc(solara.CrossFilterDataFrame.f) # type: ignore
|
|
@@ -12,11 +12,9 @@ df = plotly.data.gapminder()
|
|
|
12
12
|
@solara.component
|
|
13
13
|
def Page():
|
|
14
14
|
solara.provide_cross_filter()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
solara.CrossFilterSlider(df, "gdpPercap", mode=">")
|
|
19
|
-
return main
|
|
15
|
+
solara.CrossFilterReport(df, classes=["py-2"])
|
|
16
|
+
solara.CrossFilterSelect(df, "country")
|
|
17
|
+
solara.CrossFilterSlider(df, "gdpPercap", mode=">")
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
__doc__ += apidoc(solara.CrossFilterReport.f) # type: ignore
|
|
@@ -12,11 +12,9 @@ df = plotly.data.tips()
|
|
|
12
12
|
@solara.component
|
|
13
13
|
def Page():
|
|
14
14
|
solara.provide_cross_filter()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
solara.CrossFilterSelect(df, "time")
|
|
19
|
-
return main
|
|
15
|
+
solara.CrossFilterReport(df, classes=["py-2"])
|
|
16
|
+
solara.CrossFilterSelect(df, "sex")
|
|
17
|
+
solara.CrossFilterSelect(df, "time")
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
__doc__ += apidoc(solara.CrossFilterSelect.f) # type: ignore
|
|
@@ -12,11 +12,9 @@ df = plotly.data.gapminder()
|
|
|
12
12
|
@solara.component
|
|
13
13
|
def Page():
|
|
14
14
|
solara.provide_cross_filter()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
solara.CrossFilterSlider(df, "gdpPercap", mode="<")
|
|
19
|
-
return main
|
|
15
|
+
solara.CrossFilterReport(df, classes=["py-2"])
|
|
16
|
+
solara.CrossFilterSlider(df, "pop", mode=">")
|
|
17
|
+
solara.CrossFilterSlider(df, "gdpPercap", mode="<")
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
__doc__ += apidoc(solara.CrossFilterSlider.f) # type: ignore
|
|
@@ -15,11 +15,9 @@ df = plotly.data.gapminder()
|
|
|
15
15
|
@solara.component
|
|
16
16
|
def Page():
|
|
17
17
|
solara.provide_cross_filter()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
solara.CrossFilterSlider(df, "gdpPercap", mode=">")
|
|
22
|
-
return main
|
|
18
|
+
solara.CrossFilterReport(df, classes=["py-2"])
|
|
19
|
+
solara.CrossFilterSelect(df, "continent")
|
|
20
|
+
solara.CrossFilterSlider(df, "gdpPercap", mode=">")
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
__doc__ += apidoc(solara.use_cross_filter) # type: ignore
|
|
@@ -18,16 +18,14 @@ def Page():
|
|
|
18
18
|
value_previous = solara.use_previous(value)
|
|
19
19
|
exception, clear_exception = solara.use_exception()
|
|
20
20
|
# print(exception)
|
|
21
|
-
|
|
22
|
-
if exception:
|
|
21
|
+
if exception:
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def reset():
|
|
24
|
+
set_value(value_previous)
|
|
25
|
+
clear_exception()
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return main
|
|
27
|
+
solara.Text("Exception: " + str(exception))
|
|
28
|
+
solara.Button(label="Go to previous state", on_click=reset)
|
|
29
|
+
else:
|
|
30
|
+
solara.IntSlider(value=value, min=0, max=10, on_value=set_value, label="Pick a number, except 3")
|
|
31
|
+
UnstableComponent(value)
|
|
@@ -20,14 +20,11 @@ title = "use_previous"
|
|
|
20
20
|
def Page():
|
|
21
21
|
value, set_value = solara.use_state(4)
|
|
22
22
|
value_previous = solara.use_previous(value)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
**Current**: `{value}`
|
|
23
|
+
solara.IntSlider("value", value=value, on_value=set_value)
|
|
24
|
+
solara.Markdown(
|
|
25
|
+
f"""
|
|
26
|
+
**Current**: `{value}`
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
**Previous**: `{value_previous}`
|
|
30
29
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return main
|
|
30
|
+
)
|
|
@@ -41,29 +41,26 @@ def Page():
|
|
|
41
41
|
parent_value, set_parent_value = solara.use_state(4)
|
|
42
42
|
# used to force rerenders
|
|
43
43
|
rerender_counter, set_rerender_counter = solara.use_state(4)
|
|
44
|
-
with solara.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
SliderWithState(parent_value).key(f"slider-{parent_value}")
|
|
68
|
-
|
|
69
|
-
return main
|
|
44
|
+
with solara.Card("Parent value selection"):
|
|
45
|
+
solara.Info("This slider value gets passed down to the child components")
|
|
46
|
+
solara.IntSlider("parent value", value=parent_value, on_value=set_parent_value)
|
|
47
|
+
solara.Button("Force redraw", on_click=lambda: set_rerender_counter(rerender_counter + 1))
|
|
48
|
+
|
|
49
|
+
with solara.Card("Child without state"):
|
|
50
|
+
solara.Info("This child will simply render the value passed into the argument, a redraw will reset it to its parent value.")
|
|
51
|
+
SliderWithoutState(parent_value)
|
|
52
|
+
|
|
53
|
+
with solara.Card("Child with state"):
|
|
54
|
+
solara.Info("This child will not care about the value passed into the prop, it manages its own state.")
|
|
55
|
+
SliderWithState(parent_value)
|
|
56
|
+
|
|
57
|
+
with solara.Card("Child with state (or update)"):
|
|
58
|
+
solara.Info("This child will update when the passes in a new value, but a redraw will not reset it.")
|
|
59
|
+
SliderWithStateOrUpdate(parent_value)
|
|
60
|
+
|
|
61
|
+
with solara.Card("Child with state + key"):
|
|
62
|
+
solara.Info(
|
|
63
|
+
"We can also use the `.key(...)` method to force the component to forget its state, this will however cause the widget to be re-created"
|
|
64
|
+
"(a performance penalty)."
|
|
65
|
+
)
|
|
66
|
+
SliderWithState(parent_value).key(f"slider-{parent_value}")
|
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from typing import Optional, cast
|
|
4
4
|
|
|
5
5
|
import solara
|
|
6
|
-
from solara.alias import
|
|
6
|
+
from solara.alias import rw
|
|
7
7
|
|
|
8
8
|
HERE = Path(__file__).parent
|
|
9
9
|
title = "use_thread"
|
|
@@ -29,16 +29,14 @@ def Page():
|
|
|
29
29
|
# work will be cancelled/restarted every time the dependency changes
|
|
30
30
|
result: solara.Result[bool] = solara.use_thread(work, dependencies=[number])
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if result.
|
|
35
|
-
|
|
36
|
-
solara.Success(f"{number} is a prime!")
|
|
37
|
-
else:
|
|
38
|
-
solara.Error(f"{number} is not a prime, it can be divided by {proof} ")
|
|
39
|
-
elif result.state == solara.ResultState.ERROR:
|
|
40
|
-
solara.Error(f"Error occurred: {result.error}")
|
|
32
|
+
rw.IntText(value=number, on_value=set_number)
|
|
33
|
+
if result.state == solara.ResultState.FINISHED:
|
|
34
|
+
if result.value:
|
|
35
|
+
solara.Success(f"{number} is a prime!")
|
|
41
36
|
else:
|
|
42
|
-
solara.
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
solara.Error(f"{number} is not a prime, it can be divided by {proof} ")
|
|
38
|
+
elif result.state == solara.ResultState.ERROR:
|
|
39
|
+
solara.Error(f"Error occurred: {result.error}")
|
|
40
|
+
else:
|
|
41
|
+
solara.Info(f"Running... (status = {result.state})")
|
|
42
|
+
solara.v.ProgressLinear(indeterminate=True)
|
|
@@ -14,18 +14,16 @@ routes = [
|
|
|
14
14
|
@solara.component
|
|
15
15
|
def Page():
|
|
16
16
|
route_current, routes = solara.use_route()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
solara.Info(f"Go to solara.Route(path={route.path!r})")
|
|
28
|
-
return main
|
|
17
|
+
|
|
18
|
+
solara.Markdown("*Click on one of the links below to change the route and see the url in your browser change, and match the route.*")
|
|
19
|
+
with solara.VBox():
|
|
20
|
+
for route in routes:
|
|
21
|
+
with solara.Link(route):
|
|
22
|
+
current = route_current is route
|
|
23
|
+
if current:
|
|
24
|
+
solara.Success(f"You are at solara.Route(path={route.path!r})")
|
|
25
|
+
else:
|
|
26
|
+
solara.Info(f"Go to solara.Route(path={route.path!r})")
|
|
29
27
|
|
|
30
28
|
|
|
31
29
|
__doc__ += apidoc(solara.Route, full=True) # type: ignore
|
|
@@ -23,43 +23,39 @@ def Fruit():
|
|
|
23
23
|
solara.Button("Choose a fruit, I recomment banana")
|
|
24
24
|
return main
|
|
25
25
|
|
|
26
|
-
with solara.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
solara.Button(route_fruit.path)
|
|
26
|
+
with solara.Row():
|
|
27
|
+
for route_fruit in routes[1:]:
|
|
28
|
+
with solara.Link(solara.resolve_path(route_fruit)):
|
|
29
|
+
solara.Button(route_fruit.path)
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return main
|
|
31
|
+
with solara.Link("/documentation/api/routing/use_route/fruit/nofruit", nofollow=True):
|
|
32
|
+
solara.Button("Wrong fruit")
|
|
33
|
+
with solara.Link("/documentation/api/routing/use_route/not-routed", nofollow=True):
|
|
34
|
+
solara.Button("Wrong url")
|
|
35
|
+
solara.Success(f"You chose {route.path}")
|
|
38
36
|
|
|
39
37
|
|
|
40
38
|
@solara.component
|
|
41
39
|
def Page():
|
|
42
40
|
# this gets the top level routes, '/' and 'fruit'
|
|
43
41
|
route_current, routes_all = solara.use_route()
|
|
44
|
-
with solara.
|
|
45
|
-
with solara.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
solara.Error(f"Unknown route: {route_current.path}")
|
|
62
|
-
return main
|
|
42
|
+
with solara.Card("Navigation using buttons"):
|
|
43
|
+
with solara.Row():
|
|
44
|
+
for route in routes_all:
|
|
45
|
+
with solara.Link(route):
|
|
46
|
+
solara.Button(route.path, color="red" if route_current == route else None)
|
|
47
|
+
with solara.Card("Content decided by route:"):
|
|
48
|
+
if route_current is None:
|
|
49
|
+
solara.Error("Page does not exist")
|
|
50
|
+
with solara.Link("fruit/kiwi"):
|
|
51
|
+
solara.Button("Go to fruit/kiwi")
|
|
52
|
+
elif route_current.path == "/":
|
|
53
|
+
with solara.Link("fruit/banana"):
|
|
54
|
+
solara.Button("Go to fruit/banana")
|
|
55
|
+
elif route_current.path == "fruit":
|
|
56
|
+
Fruit()
|
|
57
|
+
else:
|
|
58
|
+
solara.Error(f"Unknown route: {route_current.path}")
|
|
63
59
|
|
|
64
60
|
|
|
65
61
|
routes = [
|
|
@@ -19,6 +19,23 @@ The return value of on_kernel_start is a cleanup function that will remove the c
|
|
|
19
19
|
|
|
20
20
|
During hot reload, the callbacks that are added from scripts or modules that will be reloaded will be removed before the app is loaded
|
|
21
21
|
again. This can cause the order of the callbacks to be different than at first run.
|
|
22
|
+
|
|
23
|
+
## Example
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
import solara
|
|
27
|
+
import solara.lab
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@solara.lab.on_kernel_start
|
|
31
|
+
def on_kernel_start():
|
|
32
|
+
id = solara.get_kernel_id()
|
|
33
|
+
print("Kernel started", id)
|
|
34
|
+
def cleanup():
|
|
35
|
+
print("Kernel stopped", id)
|
|
36
|
+
return cleanup # this function will be called on kernel shutdown
|
|
37
|
+
```
|
|
38
|
+
|
|
22
39
|
"""
|
|
23
40
|
|
|
24
41
|
from solara.website.components import NoPage
|
|
@@ -14,14 +14,12 @@ routes = [
|
|
|
14
14
|
@solara.component
|
|
15
15
|
def Page():
|
|
16
16
|
route_current, routes = solara.use_route()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
solara.Button(f"Go to {route.path}", color="red" if current else None)
|
|
24
|
-
return main
|
|
17
|
+
solara.Info("Note the address bar in the browser. It should change to the path of the link.")
|
|
18
|
+
with solara.Row():
|
|
19
|
+
for route in routes:
|
|
20
|
+
with solara.Link(route):
|
|
21
|
+
current = route_current is route
|
|
22
|
+
solara.Button(f"Go to {route.path}", color="red" if current else None)
|
|
25
23
|
|
|
26
24
|
|
|
27
25
|
__doc__ += apidoc(solara.Link.f) # type: ignore
|
|
@@ -6,15 +6,12 @@ from solara.website.utils import apidoc
|
|
|
6
6
|
|
|
7
7
|
@solara.component
|
|
8
8
|
def Page():
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
return main
|
|
9
|
+
solara.Info("Nothing to see here, only in this page's source code, or by looking at the google search results for this page.")
|
|
10
|
+
with solara.Head():
|
|
11
|
+
solara.Meta(
|
|
12
|
+
name="description",
|
|
13
|
+
content="The Meta component can be used to set the description of a page. This is useful for SEO, or crawlers that index your page.",
|
|
14
|
+
)
|
|
18
15
|
|
|
19
16
|
|
|
20
17
|
__doc__ += apidoc(solara.Meta.f) # type: ignore
|
|
@@ -26,20 +26,18 @@ def Page():
|
|
|
26
26
|
}
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
f"""
|
|
29
|
+
solara.Checkbox(label="Use CSS", value=insert_css, on_value=set_insert_css)
|
|
30
|
+
solara.Markdown(
|
|
31
|
+
f"""
|
|
33
32
|
## CSS Example that styles the button below
|
|
34
33
|
```css
|
|
35
34
|
{css}
|
|
36
35
|
```
|
|
37
36
|
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return main
|
|
37
|
+
)
|
|
38
|
+
if insert_css:
|
|
39
|
+
solara.Style(css)
|
|
40
|
+
solara.Button(label="Advanced users might want to style this", icon_name="mdi-thumb-up", classes=["mybutton"])
|
|
43
41
|
|
|
44
42
|
|
|
45
43
|
__doc__ += apidoc(solara.Style.f) # type: ignore
|
|
@@ -13,20 +13,18 @@ def Page():
|
|
|
13
13
|
path, set_path = solara.use_state(cast(Optional[Path], None))
|
|
14
14
|
directory, set_directory = solara.use_state(Path("~").expanduser())
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
solara.Info(f"You opened file: {file}")
|
|
29
|
-
return main
|
|
16
|
+
can_select = solara.ui_checkbox("Enable select")
|
|
17
|
+
|
|
18
|
+
def reset_path():
|
|
19
|
+
set_path(None)
|
|
20
|
+
set_file(None)
|
|
21
|
+
|
|
22
|
+
# reset path and file when can_select changes
|
|
23
|
+
solara.use_memo(reset_path, [can_select])
|
|
24
|
+
solara.FileBrowser(directory, on_directory_change=set_directory, on_path_select=set_path, on_file_open=set_file, can_select=can_select)
|
|
25
|
+
solara.Info(f"You are in directory: {directory}")
|
|
26
|
+
solara.Info(f"You selected path: {path}")
|
|
27
|
+
solara.Info(f"You opened file: {file}")
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
__doc__ += apidoc(solara.FileBrowser.f) # type: ignore
|