solara 1.29.0__py2.py3-none-any.whl → 1.30.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.
Files changed (284) hide show
  1. solara/__init__.py +5 -5
  2. solara/__main__.py +6 -2
  3. solara/autorouting.py +79 -38
  4. solara/cache.py +2 -2
  5. solara/checks.html +1 -1
  6. solara/components/__init__.py +1 -1
  7. solara/components/applayout.py +5 -5
  8. solara/components/button.py +4 -4
  9. solara/components/card.py +1 -2
  10. solara/components/component_vue.py +1 -1
  11. solara/components/cross_filter.py +6 -7
  12. solara/components/datatable.py +2 -3
  13. solara/components/figure_altair.py +1 -1
  14. solara/components/file_download.py +2 -2
  15. solara/components/file_drop.py +76 -33
  16. solara/components/file_drop.vue +24 -10
  17. solara/components/head.py +1 -2
  18. solara/components/head_tag.py +2 -3
  19. solara/components/link.py +3 -4
  20. solara/components/meta.py +1 -1
  21. solara/components/misc.py +5 -9
  22. solara/datatypes.py +2 -2
  23. solara/hooks/use_reactive.py +2 -2
  24. solara/lab/components/confirmation_dialog.py +1 -1
  25. solara/lab/components/tabs.py +6 -6
  26. solara/reactive.py +1 -1
  27. solara/routing.py +9 -8
  28. solara/server/app.py +44 -1
  29. solara/server/assets/style.css +6 -0
  30. solara/server/esm.py +28 -4
  31. solara/server/kernel_context.py +75 -7
  32. solara/server/patch.py +3 -0
  33. solara/server/reload.py +2 -2
  34. solara/server/server.py +3 -3
  35. solara/server/settings.py +1 -0
  36. solara/server/starlette.py +66 -33
  37. solara/server/static/solara_bootstrap.py +1 -1
  38. solara/server/templates/solara.html.j2 +62 -42
  39. solara/tasks.py +1 -6
  40. solara/util.py +23 -1
  41. solara/website/assets/custom.css +56 -0
  42. solara/website/components/__init__.py +1 -0
  43. solara/website/components/algolia_api.vue +157 -0
  44. solara/website/components/docs.py +118 -0
  45. solara/website/components/header.py +20 -10
  46. solara/website/components/hero.py +1 -1
  47. solara/website/pages/__init__.py +223 -20
  48. solara/website/pages/apps/jupyter-dashboard-1.py +1 -1
  49. solara/website/pages/apps/multipage/__init__.py +1 -1
  50. solara/website/pages/apps/multipage/page2.py +1 -1
  51. solara/website/pages/apps/scatter.py +21 -7
  52. solara/website/pages/changelog/__init__.py +8 -0
  53. solara/website/pages/{docs/content/95-changelog.md → changelog/changelog.md} +28 -2
  54. solara/website/pages/contact/__init__.py +8 -0
  55. solara/website/pages/documentation/__init__.py +184 -0
  56. solara/website/pages/documentation/advanced/__init__.py +36 -0
  57. solara/website/pages/documentation/advanced/content/00-overview.md +1 -0
  58. solara/website/pages/{docs/content/10-howto/20-multipage.md → documentation/advanced/content/10-howto/10-multipage.md} +4 -5
  59. solara/website/pages/{docs/content/10-howto/30-layout.md → documentation/advanced/content/10-howto/20-layout.md} +21 -21
  60. solara/website/pages/{docs/content/10-howto/80-embed.md → documentation/advanced/content/10-howto/40-embed.md} +1 -1
  61. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/06-ipyvuetify.md +1 -1
  62. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/12-reacton-basics.md +1 -1
  63. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/15-anatomy.md +2 -2
  64. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/18-containers.md +3 -3
  65. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/40-routing.md +9 -9
  66. solara/website/pages/{docs → documentation/advanced}/content/20-understanding/50-solara-server.md +1 -1
  67. solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/10-oauth.md +3 -3
  68. solara/website/pages/{docs/content/10-howto → documentation/advanced/content/40-development}/01-contribute.md +5 -5
  69. solara/website/pages/{docs/content/90-development → documentation/advanced/content/40-development}/10-setup.md +2 -2
  70. solara/website/pages/documentation/advanced/content/__init__.py +0 -0
  71. solara/website/pages/documentation/api/__init__.py +19 -0
  72. solara/website/pages/documentation/api/cross_filter/__init__.py +9 -0
  73. solara/website/pages/documentation/api/hooks/__init__.py +9 -0
  74. solara/website/pages/{api → documentation/api/hooks}/use_effect.md +3 -3
  75. solara/website/pages/{api → documentation/api/hooks}/use_effect.py +2 -1
  76. solara/website/pages/{api → documentation/api/hooks}/use_memo.md +2 -2
  77. solara/website/pages/{api → documentation/api/hooks}/use_memo.py +2 -1
  78. solara/website/pages/{api → documentation/api/hooks}/use_reactive.py +1 -2
  79. solara/website/pages/{api → documentation/api/hooks}/use_state.py +1 -2
  80. solara/website/pages/documentation/api/routing/__init__.py +9 -0
  81. solara/website/pages/{api → documentation/api/routing}/generate_routes.py +1 -2
  82. solara/website/pages/{api → documentation/api/routing}/generate_routes_directory.py +1 -2
  83. solara/website/pages/{api → documentation/api/routing}/use_route.py +2 -2
  84. solara/website/pages/{api → documentation/api/routing}/use_router.py +1 -2
  85. solara/website/pages/documentation/api/utilities/__init__.py +9 -0
  86. solara/website/pages/{api → documentation/api/utilities}/component_vue.py +1 -2
  87. solara/website/pages/{api → documentation/api/utilities}/computed.py +2 -2
  88. solara/website/pages/{api → documentation/api/utilities}/display.py +1 -2
  89. solara/website/pages/{api → documentation/api/utilities}/get_kernel_id.py +2 -2
  90. solara/website/pages/{api → documentation/api/utilities}/get_session_id.py +2 -2
  91. solara/website/pages/{api → documentation/api/utilities}/on_kernel_start.py +9 -3
  92. solara/website/pages/{api → documentation/api/utilities}/reactive.py +1 -2
  93. solara/website/pages/{api → documentation/api/utilities}/widget.py +2 -2
  94. solara/website/pages/documentation/components/__init__.py +12 -0
  95. solara/website/pages/documentation/components/advanced/__init__.py +9 -0
  96. solara/website/pages/documentation/components/data/__init__.py +9 -0
  97. solara/website/pages/documentation/components/enterprise/__init__.py +9 -0
  98. solara/website/pages/{api → documentation/components/enterprise}/avatar.py +1 -2
  99. solara/website/pages/{api → documentation/components/enterprise}/avatar_menu.py +1 -2
  100. solara/website/pages/documentation/components/input/__init__.py +9 -0
  101. solara/website/pages/{api → documentation/components/input}/checkbox.py +1 -2
  102. solara/website/pages/documentation/components/input/file_drop.py +75 -0
  103. solara/website/pages/{api → documentation/components/input}/input.py +1 -2
  104. solara/website/pages/{api → documentation/components/input}/select.py +1 -2
  105. solara/website/pages/{api → documentation/components/input}/slider.py +1 -2
  106. solara/website/pages/{api → documentation/components/input}/switch.py +1 -2
  107. solara/website/pages/{api → documentation/components/input}/togglebuttons.py +1 -2
  108. solara/website/pages/documentation/components/lab/__init__.py +9 -0
  109. solara/website/pages/{api → documentation/components/lab}/chat.py +2 -3
  110. solara/website/pages/{api → documentation/components/lab}/cookies_headers.py +1 -1
  111. solara/website/pages/{api → documentation/components/lab}/input_date.py +1 -2
  112. solara/website/pages/{api → documentation/components/lab}/menu.py +1 -2
  113. solara/website/pages/{api → documentation/components/lab}/task.py +1 -2
  114. solara/website/pages/{api → documentation/components/lab}/theming.py +1 -2
  115. solara/website/pages/{api → documentation/components/lab}/use_task.py +1 -2
  116. solara/website/pages/documentation/components/layout/__init__.py +9 -0
  117. solara/website/pages/{api → documentation/components/layout}/app_bar.py +1 -2
  118. solara/website/pages/{api → documentation/components/layout}/app_bar_title.py +1 -2
  119. solara/website/pages/{api → documentation/components/layout}/card.py +1 -2
  120. solara/website/pages/{api → documentation/components/layout}/card_actions.py +1 -2
  121. solara/website/pages/{api → documentation/components/layout}/griddraggable.py +1 -1
  122. solara/website/pages/{api → documentation/components/layout}/gridfixed.py +1 -1
  123. solara/website/pages/{api → documentation/components/layout}/hbox.py +1 -1
  124. solara/website/pages/{api → documentation/components/layout}/vbox.py +1 -1
  125. solara/website/pages/documentation/components/output/__init__.py +9 -0
  126. solara/website/pages/{api → documentation/components/output}/file_download.py +1 -2
  127. solara/website/pages/{api → documentation/components/output}/image.py +1 -2
  128. solara/website/pages/{api → documentation/components/output}/tooltip.py +1 -2
  129. solara/website/pages/documentation/components/page/__init__.py +9 -0
  130. solara/website/pages/documentation/components/status/__init__.py +9 -0
  131. solara/website/pages/{api → documentation/components/status}/error.py +3 -3
  132. solara/website/pages/{api → documentation/components/status}/info.py +3 -3
  133. solara/website/pages/{api → documentation/components/status}/progress.py +1 -2
  134. solara/website/pages/{api → documentation/components/status}/spinner.py +1 -2
  135. solara/website/pages/{api → documentation/components/status}/success.py +3 -3
  136. solara/website/pages/{api → documentation/components/status}/warning.py +3 -3
  137. solara/website/pages/documentation/components/viz/__init__.py +9 -0
  138. solara/website/pages/{api → documentation/components/viz}/plotly.py +1 -0
  139. solara/website/pages/{examples → documentation/examples}/__init__.py +3 -43
  140. solara/website/pages/{examples → documentation/examples}/general/deploy_model.py +3 -3
  141. solara/website/pages/{examples → documentation/examples}/general/login_oauth.py +1 -1
  142. solara/website/pages/{examples → documentation/examples}/general/vue_component.py +1 -2
  143. solara/website/pages/{examples → documentation/examples}/libraries/altair.py +2 -3
  144. solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet_advanced.py +1 -1
  145. solara/website/pages/{examples → documentation/examples}/utilities/countdown_timer.py +1 -1
  146. solara/website/pages/{examples → documentation/examples}/visualization/plotly.py +1 -2
  147. solara/website/pages/documentation/faq/__init__.py +11 -0
  148. solara/website/pages/{docs → documentation/getting_started}/__init__.py +7 -1
  149. solara/website/pages/{docs/content/03-quickstart.md → documentation/getting_started/content/00-quickstart.md} +2 -2
  150. solara/website/pages/{docs/content/00-introduction.md → documentation/getting_started/content/01-introduction.md} +16 -16
  151. solara/website/pages/{docs → documentation/getting_started}/content/02-installing.md +1 -1
  152. solara/website/pages/documentation/getting_started/content/04-tutorials/00-overview.md +9 -0
  153. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/20-web-app.md +4 -4
  154. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/30-ipywidgets.md +9 -9
  155. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/40-streamlit.md +13 -13
  156. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/50-dash.md +3 -3
  157. solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_data_science.ipynb +5 -5
  158. solara/website/pages/documentation/getting_started/content/05-fundamentals/00-overview.md +7 -0
  159. solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/10-components.md +2 -2
  160. solara/website/pages/{docs/content/07-fundamentals → documentation/getting_started/content/05-fundamentals}/50-state-management.md +3 -3
  161. solara/website/pages/documentation/getting_started/content/06-reference/00-overview.md +3 -0
  162. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/41-asset-files.md +1 -1
  163. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/60-static-site-generation.md +1 -1
  164. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/70-search.md +1 -1
  165. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/80-reloading.md +3 -3
  166. solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/95-caching.md +1 -1
  167. solara/website/pages/documentation/getting_started/content/07-deploying/00-overview.md +3 -0
  168. solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/10-self-hosted.md +3 -3
  169. solara/website/pages/{docs/content/30-deploying → documentation/getting_started/content/07-deploying}/20-cloud-hosted.md +1 -1
  170. solara/website/pages/documentation/getting_started/content/__init__.py +0 -0
  171. solara/website/pages/showcase/__init__.py +1 -1
  172. solara/website/pages/showcase/domino_code_assist.py +1 -1
  173. solara/website/pages/showcase/solara_dev.py +1 -1
  174. solara/website/public/social/discord.svg +1 -0
  175. solara/website/public/social/github.svg +1 -0
  176. solara/website/public/social/twitter.svg +3 -0
  177. {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/METADATA +8 -7
  178. solara-1.30.0.dist-info/RECORD +438 -0
  179. {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/WHEEL +1 -1
  180. solara/website/pages/api/__init__.py +0 -292
  181. solara/website/pages/api/default_layout.py +0 -16
  182. solara/website/pages/api/file_drop.py +0 -36
  183. solara/website/pages/docs/content/04-tutorial/00-overview.md +0 -9
  184. solara/website/pages/docs/content/07-fundamentals/00-overview.md +0 -7
  185. solara/website/pages/docs/content/15-reference/00-overview.md +0 -6
  186. solara/website/pages/docs/content/30-deploying/00-overview.md +0 -3
  187. solara-1.29.0.data/data/prefix/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -7
  188. solara-1.29.0.data/data/prefix/etc/jupyter/jupyter_server_config.d/solara.json +0 -7
  189. solara-1.29.0.dist-info/RECORD +0 -413
  190. /solara/website/pages/{docs/content/99-contact.md → contact/contact.md} +0 -0
  191. /solara/website/pages/{docs → documentation/advanced}/content/10-howto/00-overview.md +0 -0
  192. /solara/website/pages/{docs/content/10-howto/50-testing.md → documentation/advanced/content/10-howto/30-testing.md} +0 -0
  193. /solara/website/pages/{docs/content/10-howto/51-debugging.md → documentation/advanced/content/10-howto/31-debugging.md} +0 -0
  194. /solara/website/pages/{docs/content/10-howto/ipywidget_libraries.md → documentation/advanced/content/10-howto/50-ipywidget_libraries.md} +0 -0
  195. /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/00-introduction.md +0 -0
  196. /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/05-ipywidgets.md +0 -0
  197. /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/10-reacton.md +0 -0
  198. /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/17-rules-of-hooks.md +0 -0
  199. /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/20-solara.md +0 -0
  200. /solara/website/pages/{docs → documentation/advanced}/content/20-understanding/60-voila.md +0 -0
  201. /solara/website/pages/{docs/content/50-enterprise → documentation/advanced/content/30-enterprise}/00-overview.md +0 -0
  202. /solara/website/pages/{docs/content/__init__.py → documentation/advanced/content/40-development/00-overview.md} +0 -0
  203. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_dataframe.py +0 -0
  204. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_report.py +0 -0
  205. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_select.py +0 -0
  206. /solara/website/pages/{api → documentation/api/cross_filter}/cross_filter_slider.py +0 -0
  207. /solara/website/pages/{api → documentation/api/hooks}/use_cross_filter.py +0 -0
  208. /solara/website/pages/{api → documentation/api/hooks}/use_dark_effective.py +0 -0
  209. /solara/website/pages/{api → documentation/api/hooks}/use_exception.py +0 -0
  210. /solara/website/pages/{api → documentation/api/hooks}/use_previous.py +0 -0
  211. /solara/website/pages/{api → documentation/api/hooks}/use_state_or_update.py +0 -0
  212. /solara/website/pages/{api → documentation/api/hooks}/use_thread.md +0 -0
  213. /solara/website/pages/{api → documentation/api/hooks}/use_thread.py +0 -0
  214. /solara/website/pages/{api → documentation/api/hooks}/use_trait_observe.py +0 -0
  215. /solara/website/pages/{api → documentation/api/routing}/resolve_path.py +0 -0
  216. /solara/website/pages/{api → documentation/api/routing}/route.py +0 -0
  217. /solara/website/pages/{api → documentation/api/utilities}/memoize.py +0 -0
  218. /solara/website/pages/{api → documentation/components/advanced}/link.py +0 -0
  219. /solara/website/pages/{api → documentation/components/advanced}/meta.py +0 -0
  220. /solara/website/pages/{api → documentation/components/advanced}/style.py +0 -0
  221. /solara/website/pages/{api → documentation/components}/common.py +0 -0
  222. /solara/website/pages/{api → documentation/components/data}/dataframe.py +0 -0
  223. /solara/website/pages/{api → documentation/components/data}/pivot_table.py +0 -0
  224. /solara/website/pages/{api → documentation/components/input}/button.py +0 -0
  225. /solara/website/pages/{api → documentation/components/input}/file_browser.py +0 -0
  226. /solara/website/pages/{api → documentation/components/lab}/confirmation_dialog.py +0 -0
  227. /solara/website/pages/{api → documentation/components/lab}/tab.py +0 -0
  228. /solara/website/pages/{api → documentation/components/lab}/tabs.py +0 -0
  229. /solara/website/pages/{api → documentation/components/layout}/app_layout.py +0 -0
  230. /solara/website/pages/{api → documentation/components/layout}/column.py +0 -0
  231. /solara/website/pages/{api → documentation/components/layout}/columns.py +0 -0
  232. /solara/website/pages/{api → documentation/components/layout}/columns_responsive.py +0 -0
  233. /solara/website/pages/{api → documentation/components/layout}/row.py +0 -0
  234. /solara/website/pages/{api → documentation/components/layout}/sidebar.py +0 -0
  235. /solara/website/pages/{api → documentation/components/output}/html.py +0 -0
  236. /solara/website/pages/{api → documentation/components/output}/markdown.py +0 -0
  237. /solara/website/pages/{api → documentation/components/output}/markdown_editor.py +0 -0
  238. /solara/website/pages/{api → documentation/components/output}/sql_code.py +0 -0
  239. /solara/website/pages/{api → documentation/components/page}/head.py +0 -0
  240. /solara/website/pages/{api → documentation/components/page}/title.py +0 -0
  241. /solara/website/pages/{api → documentation/components/viz}/altair.py +0 -0
  242. /solara/website/pages/{api → documentation/components/viz}/echarts.py +0 -0
  243. /solara/website/pages/{api → documentation/components/viz}/matplotlib.py +0 -0
  244. /solara/website/pages/{api → documentation/components/viz}/plotly_express.py +0 -0
  245. /solara/website/pages/{examples → documentation/examples}/ai/__init__.py +0 -0
  246. /solara/website/pages/{examples → documentation/examples}/ai/chatbot.py +0 -0
  247. /solara/website/pages/{examples → documentation/examples}/ai/tokenizer.py +0 -0
  248. /solara/website/pages/{examples → documentation/examples}/basics/__init__.py +0 -0
  249. /solara/website/pages/{examples → documentation/examples}/basics/sine.py +0 -0
  250. /solara/website/pages/{examples → documentation/examples}/fullscreen/__init__.py +0 -0
  251. /solara/website/pages/{examples → documentation/examples}/fullscreen/authorization.py +0 -0
  252. /solara/website/pages/{examples → documentation/examples}/fullscreen/layout_demo.py +0 -0
  253. /solara/website/pages/{examples → documentation/examples}/fullscreen/multipage.py +0 -0
  254. /solara/website/pages/{examples → documentation/examples}/fullscreen/scatter.py +0 -0
  255. /solara/website/pages/{examples → documentation/examples}/fullscreen/scrolling.py +0 -0
  256. /solara/website/pages/{examples → documentation/examples}/fullscreen/tutorial_streamlit.py +0 -0
  257. /solara/website/pages/{examples → documentation/examples}/general/__init__.py +0 -0
  258. /solara/website/pages/{examples → documentation/examples}/general/custom_storage.py +0 -0
  259. /solara/website/pages/{examples → documentation/examples}/general/live_update.py +0 -0
  260. /solara/website/pages/{examples → documentation/examples}/general/mycard.vue +0 -0
  261. /solara/website/pages/{examples → documentation/examples}/general/pokemon_search.py +0 -0
  262. /solara/website/pages/{examples → documentation/examples}/ipycanvas.py +0 -0
  263. /solara/website/pages/{examples → documentation/examples}/libraries/__init__.py +0 -0
  264. /solara/website/pages/{examples → documentation/examples}/libraries/bqplot.py +0 -0
  265. /solara/website/pages/{examples → documentation/examples}/libraries/ipyleaflet.py +0 -0
  266. /solara/website/pages/{examples → documentation/examples}/utilities/__init__.py +0 -0
  267. /solara/website/pages/{examples → documentation/examples}/utilities/calculator.py +0 -0
  268. /solara/website/pages/{examples → documentation/examples}/utilities/todo.py +0 -0
  269. /solara/website/pages/{examples → documentation/examples}/visualization/__init__.py +0 -0
  270. /solara/website/pages/{examples → documentation/examples}/visualization/annotator.py +0 -0
  271. /solara/website/pages/{examples → documentation/examples}/visualization/linked_views.py +0 -0
  272. /solara/website/pages/{docs → documentation/faq}/content/99-faq.md +0 -0
  273. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/10_data_science.py +0 -0
  274. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/60-jupyter-dashboard-part1.py +0 -0
  275. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/SF_crime_sample.csv.gz +0 -0
  276. /solara/website/pages/{docs/content/04-tutorial → documentation/getting_started/content/04-tutorials}/_jupyter_dashboard_1.ipynb +0 -0
  277. /solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/40-static_files.md +0 -0
  278. /solara/website/pages/{docs/content/15-reference → documentation/getting_started/content/06-reference}/90-notebook-support.md +0 -0
  279. /solara/website/pages/{docs/content/lab → documentation/getting_started/content/08-lab}/00-what-is-lab.md +0 -0
  280. /solara/website/pages/{docs → documentation/getting_started}/content/90-troubleshoot.md +0 -0
  281. {solara-1.29.0.data → solara-1.30.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
  282. {solara-1.29.0.data → solara-1.30.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
  283. {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/entry_points.txt +0 -0
  284. {solara-1.29.0.dist-info → solara-1.30.0.dist-info}/licenses/LICENSE +0 -0
solara/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """Build webapps using IPywidgets"""
2
- __version__ = "1.29.0"
2
+ __version__ = "1.30.0"
3
3
  github_url = "https://github.com/widgetti/solara"
4
4
  git_branch = "master"
5
5
 
@@ -77,10 +77,10 @@ def display(*objs, **kwargs):
77
77
 
78
78
  However, if you require callback functions, use the specific Solara components, e.g.:
79
79
 
80
- * [Plotly](/api/plotly)
81
- * [Altair](/api/altair)
82
- * [Matplotlib](/api/matplotlib)
83
- * [Dataframe](/api/dataframe)
80
+ * [Plotly](/documentation/components/viz/plotly)
81
+ * [Altair](/documentation/components/viz/altair)
82
+ * [Matplotlib](/documentation/components/viz/matplotlib)
83
+ * [Dataframe](/documentation/components/data/dataframe)
84
84
 
85
85
  ```solara
86
86
  import solara
solara/__main__.py CHANGED
@@ -489,7 +489,7 @@ def deploy():
489
489
 
490
490
  time.sleep(1)
491
491
  rprint("Want your app to run instantly on awesomeapp-mystartup-gh.solara.run?")
492
- rprint("\tCheck out https://solara.dev/docs/deploying/cloud-hosted")
492
+ rprint("\tCheck out https://solara.dev/documentation/getting_started/deploying/cloud-hosted")
493
493
 
494
494
 
495
495
  @cli.command()
@@ -723,7 +723,11 @@ cli.add_command(ssg)
723
723
 
724
724
 
725
725
  def main():
726
- cli()
726
+ args = sys.argv
727
+ # skip everything after -- if it exists
728
+ if "--" in args:
729
+ args = args[: args.index("--")]
730
+ cli(args[1:])
727
731
 
728
732
 
729
733
  if __name__ == "__main__":
solara/autorouting.py CHANGED
@@ -123,15 +123,14 @@ def RoutingProvider(children: List[reacton.core.Element] = [], routes: List[sola
123
123
  @solara.component
124
124
  def RenderPage(main_name: str = "Page"):
125
125
  """Renders the page that matches the route."""
126
- level_start = solara.use_route_level()
127
126
  router = solara.use_context(solara.routing.router_context)
128
127
  # we use these to cache script runs that use regular ipywidgets
129
128
  modules = cast(Dict[str, ModuleType], solara.use_memo(dict, dependencies=[]))
130
- modules_modified_times = solara.use_memo(dict, dependencies=[])
129
+ modules_modified_times = cast(Dict[str, float], solara.use_memo(dict, dependencies=[]))
131
130
 
132
- if len(router.path_routes) <= level_start:
131
+ if not router.path_routes:
133
132
  with solara.VBox() as main:
134
- solara.Error(f"Page not found: {router.path}, len(router.path_routes)={len(router.path_routes)} <= level_start={level_start}")
133
+ solara.Error(f"Page not found: {router.path}")
135
134
  parent = "/" + "/".join(router.parts[:-1])
136
135
  with solara.Link(parent):
137
136
  solara.Button(f"Go to parent: {parent}", text=True)
@@ -144,10 +143,9 @@ def RenderPage(main_name: str = "Page"):
144
143
  pp(router.routes)
145
144
  return main
146
145
 
147
- level_max = level_start
148
146
  layouts = [] # nested layouts
149
- # find the 'RenderPage' sentinel value to find the deepest level we should render
150
- for level in range(level_start, len(router.path_routes)):
147
+ level_max = len(router.path_routes) - 1
148
+ for level in range(level_max + 1):
151
149
  # we always level_start the 'package'/'root' layout
152
150
  roots = [k for k in router.path_routes_siblings[level] if k.path == "/"]
153
151
  if len(roots) == 1:
@@ -158,8 +156,12 @@ def RenderPage(main_name: str = "Page"):
158
156
  # and, if not the root layout, include the layout for the current route
159
157
  if route.path != "/" and route.layout:
160
158
  layouts.append(route.layout)
161
- if route.component is RenderPage:
162
- level_max = level
159
+ # used in for example the docs for use_route, only the route path are specified,
160
+ # nothing else, which means we will not follow that route path, but limit it to rendering
161
+ # router.path_routes[level_max] instead. It's assumed that component (Page) will continue
162
+ # handling the rest of the routing
163
+ if route.data is None and route.module is None and route.component is None:
164
+ level_max = level - 1
163
165
  route_current = router.path_routes[level_max]
164
166
  routes_siblings = router.path_routes_siblings[level_max]
165
167
  routes_siblings_index = routes_siblings.index(route_current)
@@ -224,9 +226,6 @@ def RenderPage(main_name: str = "Page"):
224
226
  title_element = solara.Title(title)
225
227
  module = None
226
228
  Page = route_current.component
227
- # translate the default RenderPage as no value given (None)
228
- if Page is RenderPage:
229
- Page = None
230
229
  if route_current.module is not None and (Page is None):
231
230
  # if not a custom component is given, we try to find a Page component
232
231
  # in the module
@@ -239,9 +238,6 @@ def RenderPage(main_name: str = "Page"):
239
238
  Page = namespace.get("page", namespace.get("app", Page))
240
239
  Page = nested_get(namespace, main_name, Page)
241
240
 
242
- if Page is None and route_current.children:
243
- # we we did not get a component, but we recursively render
244
- Page = RenderPage
245
241
  if isinstance(Page, ipywidgets.Widget):
246
242
  # If we have a widget, we need to execute this again for each
247
243
  # connection, since we cannot share widgets between connections/users.
@@ -291,12 +287,12 @@ def RenderPage(main_name: str = "Page"):
291
287
  main = wrap_in_layouts(main, layouts)
292
288
  else:
293
289
  if route_current.module:
294
- path = route_current.module.__file__
290
+ path_str = route_current.module.__file__
295
291
  local_scope = route_current.module.__dict__
296
292
  ignore = ["display"]
297
293
  options = [k for k in list(local_scope) if k not in ignore and not k.startswith("_")]
298
294
  matches = difflib.get_close_matches(main_name, options)
299
- msg = f"No object with name {main_name} found for {path}"
295
+ msg = f"No object with name {main_name} found for {path_str}"
300
296
  if matches:
301
297
  msg += " Did you mean: " + " or ".join(map(repr, matches))
302
298
  else:
@@ -347,17 +343,22 @@ def get_title(module: ModuleType, required=True):
347
343
  return title
348
344
 
349
345
 
350
- def fix_route(route: solara.Route, new_file: Path) -> solara.Route:
346
+ def fix_route(route: solara.Route, new_file: Path, new_layout=None) -> solara.Route:
351
347
  file = route.file or new_file
348
+ layout = route.layout or new_layout
352
349
  children = fix_routes(route.children, new_file) if route.children else []
353
350
 
354
- return dataclasses.replace(route, file=file, children=children)
351
+ return dataclasses.replace(route, file=file, children=children, layout=layout)
355
352
 
356
353
 
357
- def fix_routes(routes: List[solara.Route], new_file: Path):
354
+ def fix_routes(routes: List[solara.Route], new_file: Path, new_layout=None):
358
355
  new_routes = []
359
356
  for route in routes:
360
- new_routes.append(fix_route(route, new_file))
357
+ if route.path == "/":
358
+ route = fix_route(route, new_file, new_layout)
359
+ else:
360
+ route = fix_route(route, new_file)
361
+ new_routes.append(route)
361
362
  return new_routes
362
363
 
363
364
 
@@ -370,7 +371,7 @@ def generate_routes(module: ModuleType) -> List[solara.Route]:
370
371
  files are not Python modules.
371
372
 
372
373
 
373
- See [our multipage guide](/docs/howto/multipage#as-a-package) for more details.
374
+ See [our multipage guide](/documentation/advanced/howto/multipage#as-a-package) for more details.
374
375
 
375
376
 
376
377
  """
@@ -378,6 +379,7 @@ def generate_routes(module: ModuleType) -> List[solara.Route]:
378
379
 
379
380
  assert module.__file__ is not None
380
381
  routes = []
382
+ children: List[solara.Route]
381
383
  file = Path(module.__file__)
382
384
 
383
385
  if module.__file__.endswith("__init__.py"):
@@ -391,7 +393,9 @@ def generate_routes(module: ModuleType) -> List[solara.Route]:
391
393
  title = get_title(module)
392
394
  children = getattr(module, "routes", [])
393
395
  if hasattr(module, "Page"):
394
- routes.append(solara.Route(path="/", component=RenderPage, data=module, module=module, layout=layout, children=children, label=title, file=file))
396
+ routes.append(
397
+ solara.Route(path="/", component=get_page(module), data=module, module=module, layout=layout, children=children, label=title, file=file)
398
+ )
395
399
 
396
400
  assert module.__file__ is not None
397
401
  reload.reloader.watcher.add_file(module.__file__)
@@ -405,7 +409,9 @@ def generate_routes(module: ModuleType) -> List[solara.Route]:
405
409
  # however, this may break things.
406
410
  # name = name.replace("_", "-")
407
411
  if info.ispkg:
408
- route = solara.Route(name, component=RenderPage, children=generate_routes(submod), module=submod, layout=None, label=title)
412
+ route = solara.Route(
413
+ name, component=get_page(submod, required=False), children=generate_routes(submod), module=submod, layout=None, label=title
414
+ )
409
415
  # skip empty subpackages
410
416
  if len(route.children) == 0:
411
417
  continue
@@ -417,7 +423,9 @@ def generate_routes(module: ModuleType) -> List[solara.Route]:
417
423
  if subfile:
418
424
  children = fix_routes(children, subfile)
419
425
  module_layout = getattr(submod, "Layout", None)
420
- route = solara.Route(name, component=RenderPage, module=submod, layout=module_layout, children=children, label=title, file=subfile)
426
+ route = solara.Route(
427
+ name, component=get_page(submod, required=False), module=submod, layout=module_layout, children=children, label=title, file=subfile
428
+ )
421
429
  routes.append(route)
422
430
  if route_order:
423
431
  lookup = {k.path: k for k in routes}
@@ -429,12 +437,27 @@ def generate_routes(module: ModuleType) -> List[solara.Route]:
429
437
  warnings.warn(f"Some routes are not in route_order: {set(lookup) - set(route_order)}")
430
438
 
431
439
  else:
432
- children = getattr(module, "routes", [])
433
- children = fix_routes(children, file)
434
440
  layout = getattr(module, "Layout", None)
435
- # children = []
436
- # single module, single route
437
- return [solara.Route(path="/", component=RenderPage, data=None, module=module, label=get_title(module), layout=layout, children=children, file=file)]
441
+ children = []
442
+ if hasattr(module, "routes"):
443
+ children = getattr(module, "routes", [])
444
+ root = get_root(children)
445
+ if layout is not None and root is not None and root.layout is None:
446
+ warnings.warn(f'You defined routes in {file}, in this case, layout should be set on the root route (with path="/"), not on the module level')
447
+ layout = None
448
+ children = fix_routes(children, file, layout)
449
+ return [
450
+ solara.Route(
451
+ path="/",
452
+ component=get_page(module, required=False),
453
+ data=None,
454
+ module=module,
455
+ label=get_title(module),
456
+ layout=layout,
457
+ children=children,
458
+ file=file,
459
+ )
460
+ ]
438
461
 
439
462
  return routes
440
463
 
@@ -451,7 +474,7 @@ def generate_routes_directory(path: Path) -> List[solara.Route]:
451
474
  Python files ending in .py, or Jupyter notebooks ending in .ipynb
452
475
  will be executed and its `Page` component will be rendered.
453
476
 
454
- Automatic titles will be [generated as explained in the multipage guide](/docs/howto/multipage).
477
+ Automatic titles will be [generated as explained in the multipage guide](/documentation/advanced/howto/multipage).
455
478
 
456
479
  """
457
480
 
@@ -491,9 +514,8 @@ def _generate_route_path(subpath: Path, layout=None, first=False, has_index=Fals
491
514
  route_path = "/"
492
515
  else:
493
516
  route_path = "-".join([k.lower() for k in title_parts])
494
- # used as a 'sentinel' to find the deepest level of the route tree we need to render in 'RenderPage'
495
- component = RenderPage
496
- children = []
517
+ component = None
518
+ children: List[solara.Route] = []
497
519
  module: Optional[ModuleType] = None
498
520
  data: Any = None
499
521
  module_layout = layout if first else None
@@ -506,8 +528,27 @@ def _generate_route_path(subpath: Path, layout=None, first=False, has_index=Fals
506
528
  reload.reloader.watcher.add_file(subpath)
507
529
  module = source_to_module(subpath, initial_namespace=initial_namespace)
508
530
  title = get_title(module)
509
- children = getattr(module, "routes", children)
510
- children = fix_routes(children, subpath)
511
- module_layout = getattr(module, "Layout", module_layout)
512
- route = solara.Route(route_path, component=component, module=module, label=title, children=children, data=data, layout=module_layout, file=subpath)
531
+ layout = getattr(module, "Layout", module_layout)
532
+ root = get_root(children)
533
+ if hasattr(module, "routes"):
534
+ children = getattr(module, "routes", [])
535
+ root = get_root(children)
536
+ if layout is not None and root is not None and root.layout is None:
537
+ warnings.warn(f'You defined routes in {subpath}, in this case, layout should be set on the root route (with path="/"), not on the module level')
538
+ layout = None
539
+ children = fix_routes(children, subpath, layout)
540
+ component = get_page(module, required=False)
541
+ if root and component and root.component and component is not root.component:
542
+ warnings.warn(
543
+ f"In {subpath}, you defined a Page component, but also a component on the root route (with path='/') "
544
+ "which is not equal to the Page component at the module level. This is not recommended."
545
+ )
546
+ route = solara.Route(route_path, component=component, module=module, label=title, children=children, data=data, layout=layout, file=subpath)
513
547
  return route
548
+
549
+
550
+ def get_root(routes: List[solara.Route]) -> Optional[solara.Route]:
551
+ for route in routes:
552
+ if route.path == "/":
553
+ return route
554
+ return None
solara/cache.py CHANGED
@@ -221,7 +221,7 @@ def memoize(
221
221
  memory content.
222
222
 
223
223
  The storage can be any object that implements the MutableMapping interface, for instance a dict or
224
- a cachetools.LRUCache. Or a new instance of `solara.cache.Memory`, see [caching](/docs/reference/caching)
224
+ a cachetools.LRUCache. Or a new instance of `solara.cache.Memory`, see [caching](/documentation/getting_started/reference/caching)
225
225
  for cache storage options.
226
226
 
227
227
  The return value of the decorator behaves like the original function, but also has a few attributes:
@@ -233,7 +233,7 @@ def memoize(
233
233
  If the value is already cached, the function will not be executed in a thread.
234
234
 
235
235
 
236
- See also the [reference on caching](/docs/reference/caching) for more caching details.
236
+ See also the [reference on caching](/documentation/getting_started/reference/caching) for more caching details.
237
237
 
238
238
  """
239
239
 
solara/checks.html CHANGED
@@ -60,7 +60,7 @@
60
60
  const div = document.createElement("div")
61
61
  const div2 = document.createElement("div")
62
62
  div.innerHTML = `Run <code>${jupyter_python_executable} -m pip install ${needsInstall.join(" ")}</code>. Refresh the page after installation.`
63
- div2.innerHTML = `Visit <a href="https://solara.dev/docs/troubleshoot" target="_blank">https://solara/dev/docs/troubleshoot</a> for more information.`
63
+ div2.innerHTML = `Visit <a href="https://solara.dev/documentation/getting_started/troubleshoot" target="_blank">https://solara/dev/documentation/getting_started/troubleshoot</a> for more information.`
64
64
  rootEl.appendChild(div)
65
65
  rootEl.appendChild(div2)
66
66
  }
@@ -46,7 +46,7 @@ from .echarts import FigureEcharts # noqa: #F401 F403
46
46
  from .figure_altair import FigureAltair, AltairChart # noqa: #F401 F403
47
47
  from .meta import Meta # noqa: #F401 F403
48
48
  from .columns import Columns, ColumnsResponsive # noqa: #F401 F403
49
- from .file_drop import FileDrop # noqa: #F401 F403
49
+ from .file_drop import FileDrop, FileDropMultiple # noqa: #F401 F403
50
50
  from .file_download import FileDownload # noqa: #F401 F403
51
51
  from .tooltip import Tooltip # noqa: #F401 F403
52
52
  from .card import Card, CardActions # noqa: #F401 F403
@@ -90,7 +90,7 @@ def AppBar(children=[]):
90
90
 
91
91
  This component does not need to be a direct child of the AppLayout, it can be at any level in your component tree.
92
92
 
93
- If a [Tabs](/api/tabs) component is used as direct child of the app bar, it will be shown under the app bar.
93
+ If a [Tabs](/documentation/components/lab/tabs) component is used as direct child of the app bar, it will be shown under the app bar.
94
94
 
95
95
  ## Example showing an app bar
96
96
  ```solara
@@ -204,11 +204,11 @@ def AppLayout(
204
204
  ):
205
205
  """The default layout for Solara apps. It consists of an toolbar bar, a sidebar and a main content area.
206
206
 
207
- * The title of the app is set using the [Title](/api/title) component.
208
- * The sidebar content is set using the [Sidebar](/api/sidebar) component.
207
+ * The title of the app is set using the [Title](/documentation/components/page/title) component.
208
+ * The sidebar content is set using the [Sidebar](/documentation/components/layout/sidebar) component.
209
209
  * The content is set by the `Page` component provided by the user.
210
210
 
211
- This component is usually not used directly, but rather through via the [Layout system](/docs/howto/layout).
211
+ This component is usually not used directly, but rather through via the [Layout system](/documentation/advanced/howto/layout).
212
212
 
213
213
  The sidebar is only added when the AppLayout has more than one child.
214
214
 
@@ -224,7 +224,7 @@ def AppLayout(
224
224
 
225
225
  * `children`: The children of the AppLayout. The first child is used as the sidebar content, the rest as the main content.
226
226
  * `sidebar_open`: Whether the sidebar is open or not.
227
- * `title`: The title of the app shown in the app bar, can also be set using the [Title](/api/title) component.
227
+ * `title`: The title of the app shown in the app bar, can also be set using the [Title](/documentation/components/page/title) component.
228
228
  * `toolbar_dark`: Whether the toolbar should be dark or not.
229
229
  * `navigation`: Whether the navigation tabs based on routing should be shown.
230
230
  * `color`: The color of the toolbar.
@@ -1,10 +1,9 @@
1
1
  from typing import Callable, Dict, List, Optional, Union
2
2
 
3
- from reacton import ipyvue
4
- from reacton import ipyvuetify as v
5
-
6
3
  import solara
7
4
  import solara.util
5
+ from reacton import ipyvue
6
+ from reacton import ipyvuetify as v
8
7
 
9
8
 
10
9
  @solara.component
@@ -51,7 +50,8 @@ def Button(
51
50
  - `disabled`: Whether the button is disabled.
52
51
  - `text`: Whether the button should be displayed as text, it has no shadow and no background.
53
52
  - `outlined`: Whether the button should be displayed as outlined, it has no background.
54
- - `value`: (Optional) When used as a child of a ToggleButtons component, the value of the selected button, see [ToggleButtons](/api/togglebuttons).
53
+ - `value`: (Optional) When used as a child of a ToggleButtons component, the value of the selected button, see
54
+ [ToggleButtons](/documentation/components/input/togglebuttons).
55
55
  - `classes`: Additional CSS classes to apply.
56
56
  - `style`: CSS style to apply.
57
57
 
solara/components/card.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from typing import Dict, List, Optional, Union
2
2
 
3
3
  import reacton.ipyvuetify as v
4
-
5
4
  import solara
6
5
  from solara.util import _combine_classes
7
6
 
@@ -79,7 +78,7 @@ def Card(
79
78
  def CardActions(children: List[solara.Element] = []):
80
79
  """Container for actions in a card.
81
80
 
82
- See [Card](/api/card) for an example.
81
+ See [Card](/documentation/components/layout/card) for an example.
83
82
 
84
83
  # Arguments
85
84
 
@@ -78,7 +78,7 @@ def component_vue(vue_path: str, vuetify=True) -> Callable[[Callable[P, None]],
78
78
  [See the vue v2 api](https://v2.vuejs.org/v2/api/) for more information on how to use Vue, like `watch`,
79
79
  `methods` and lifecycle hooks such as `mounted` and `destroyed`.
80
80
 
81
- See the [Vue component example](/examples/general/vue_component) for an example of how to use this decorator.
81
+ See the [Vue component example](/documentation/examples/general/vue_component) for an example of how to use this decorator.
82
82
 
83
83
  ## Arguments
84
84
 
@@ -3,9 +3,8 @@ from typing import Any, List, cast
3
3
 
4
4
  import ipyvuetify
5
5
  import reacton.ipyvuetify as v
6
- import traitlets
7
-
8
6
  import solara
7
+ import traitlets
9
8
  from solara import CellAction, ColumnAction
10
9
 
11
10
  from ..lab.hooks.dataframe import use_df_column_names
@@ -55,7 +54,7 @@ def CrossFilterSelect(
55
54
  ):
56
55
  """A Select widget that will cross filter a DataFrame.
57
56
 
58
- See [use_cross_filter](/api/use_cross_filter) for more information about how to use cross filtering.
57
+ See [use_cross_filter](/documentation/api/hooks/use_cross_filter) for more information about how to use cross filtering.
59
58
 
60
59
  ## Arguments
61
60
 
@@ -170,7 +169,7 @@ def CrossFilterReport(df, classes: List[str] = []):
170
169
 
171
170
  Shows number of rows filtered, and the total number of rows.
172
171
 
173
- See [use_cross_filter](/api/use_cross_filter) for more information about how to use cross filtering.
172
+ See [use_cross_filter](/documentation/api/hooks/use_cross_filter) for more information about how to use cross filtering.
174
173
 
175
174
  ## Arguments
176
175
 
@@ -214,7 +213,7 @@ def CrossFilterSlider(
214
213
  ):
215
214
  """A Slider widget that will cross filter a DataFrame.
216
215
 
217
- See [use_cross_filter](/api/use_cross_filter) for more information about how to use cross filtering.
216
+ See [use_cross_filter](/documentation/api/hooks/use_cross_filter) for more information about how to use cross filtering.
218
217
 
219
218
  ## Arguments
220
219
 
@@ -318,9 +317,9 @@ def CrossFilterSlider(
318
317
  def CrossFilterDataFrame(df, items_per_page=20, column_actions: List[ColumnAction] = [], cell_actions: List[CellAction] = [], scrollable=False):
319
318
  """Display a DataFrame with filters applied from the cross filter.
320
319
 
321
- This component wraps [DataFrame](/api/dataframe).
320
+ This component wraps [DataFrame](/documentation/components/data/dataframe).
322
321
 
323
- See [use_cross_filter](/api/use_cross_filter) for more information about how to use cross filtering.
322
+ See [use_cross_filter](/documentation/api/hooks/use_cross_filter) for more information about how to use cross filtering.
324
323
 
325
324
  # Arguments
326
325
 
@@ -6,11 +6,10 @@ from typing import Callable, List, Optional
6
6
 
7
7
  import ipyvuetify as v
8
8
  import ipywidgets
9
- import traitlets
10
-
11
9
  import solara
12
10
  import solara.hooks.dataframe
13
11
  import solara.lab
12
+ import traitlets
14
13
  from solara.lab.hooks.dataframe import use_df_column_names
15
14
  from solara.lab.utils.dataframe import df_type
16
15
 
@@ -208,7 +207,7 @@ def DataFrame(
208
207
  * `cell_actions` - Triggered via clicking on the triple dot icon in the cell (visible when hovering).
209
208
  * `on_column_header_hover` - Optional callback when the user hovers over the triple dot icon on a header.
210
209
  * `column_header_info` - Element to display in the column menu popup (visible when hovering), provide an
211
- empty container element (like [Column](/api/column)) to force showing the trigle dot icon (see example).
210
+ empty container element (like [Column](/documentation/components/layout/column)) to force showing the trigle dot icon (see example).
212
211
 
213
212
  """
214
213
  return DataTable(
@@ -12,7 +12,7 @@ def FigureAltair(
12
12
  ):
13
13
  """Renders an Altair chart using VegaLite.
14
14
 
15
- See also [our altair example](/examples/libraries/altair).
15
+ See also [our altair example](/documentation/examples/libraries/altair).
16
16
 
17
17
  ## Arguments
18
18
 
@@ -144,8 +144,8 @@ def FileDownload(
144
144
  ## Note on file size
145
145
 
146
146
  Note that the data will be kept in memory when downloading.
147
- If the file is large (>10 MB), and when using [Solara server](/docs/understanding), we recommend using the
148
- [static files directory](/docs/reference/static-files) instead.
147
+ If the file is large (>10 MB), and when using [Solara server](/documentation/advanced/understanding/solara-server), we recommend using the
148
+ [static files directory](/documentation/getting_started/reference/static-files) instead.
149
149
 
150
150
  """
151
151
  request_download, set_request_download = solara.use_state(False)
@@ -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 FileDrop(
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
- """Region a user can drop a file into for file uploading.
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
- if not lazy:
87
- wired_files[0]["data"] = wired_files[0]["file_obj"].read()
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]["data"] = None
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)