streamlit 1.49.1__py3-none-any.whl → 1.51.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. streamlit/__init__.py +4 -1
  2. streamlit/column_config.py +2 -0
  3. streamlit/commands/navigation.py +7 -7
  4. streamlit/commands/page_config.py +4 -6
  5. streamlit/components/v1/custom_component.py +17 -42
  6. streamlit/components/v2/__init__.py +458 -0
  7. streamlit/components/v2/bidi_component/__init__.py +20 -0
  8. streamlit/components/v2/bidi_component/constants.py +29 -0
  9. streamlit/components/v2/bidi_component/main.py +386 -0
  10. streamlit/components/v2/bidi_component/serialization.py +265 -0
  11. streamlit/components/v2/bidi_component/state.py +92 -0
  12. streamlit/components/v2/component_definition_resolver.py +143 -0
  13. streamlit/components/v2/component_file_watcher.py +403 -0
  14. streamlit/components/v2/component_manager.py +431 -0
  15. streamlit/components/v2/component_manifest_handler.py +122 -0
  16. streamlit/components/v2/component_path_utils.py +245 -0
  17. streamlit/components/v2/component_registry.py +409 -0
  18. streamlit/components/v2/get_bidi_component_manager.py +51 -0
  19. streamlit/components/v2/manifest_scanner.py +615 -0
  20. streamlit/components/v2/presentation.py +198 -0
  21. streamlit/components/v2/types.py +324 -0
  22. streamlit/config.py +741 -32
  23. streamlit/config_option.py +4 -1
  24. streamlit/config_util.py +650 -1
  25. streamlit/connections/base_connection.py +4 -2
  26. streamlit/dataframe_util.py +18 -10
  27. streamlit/delta_generator.py +8 -7
  28. streamlit/delta_generator_singletons.py +3 -1
  29. streamlit/deprecation_util.py +17 -6
  30. streamlit/elements/arrow.py +90 -42
  31. streamlit/elements/deck_gl_json_chart.py +98 -39
  32. streamlit/elements/dialog_decorator.py +2 -1
  33. streamlit/elements/exception.py +3 -1
  34. streamlit/elements/form.py +6 -6
  35. streamlit/elements/graphviz_chart.py +24 -9
  36. streamlit/elements/heading.py +3 -5
  37. streamlit/elements/iframe.py +0 -2
  38. streamlit/elements/image.py +12 -13
  39. streamlit/elements/layouts.py +89 -22
  40. streamlit/elements/lib/built_in_chart_utils.py +95 -31
  41. streamlit/elements/lib/color_util.py +8 -18
  42. streamlit/elements/lib/column_config_utils.py +9 -8
  43. streamlit/elements/lib/column_types.py +595 -148
  44. streamlit/elements/lib/dialog.py +3 -2
  45. streamlit/elements/lib/image_utils.py +3 -5
  46. streamlit/elements/lib/layout_utils.py +50 -13
  47. streamlit/elements/lib/mutable_status_container.py +2 -2
  48. streamlit/elements/lib/options_selector_utils.py +2 -2
  49. streamlit/elements/lib/pandas_styler_utils.py +30 -14
  50. streamlit/elements/lib/utils.py +21 -9
  51. streamlit/elements/map.py +81 -40
  52. streamlit/elements/media.py +7 -7
  53. streamlit/elements/metric.py +11 -35
  54. streamlit/elements/pdf.py +2 -4
  55. streamlit/elements/plotly_chart.py +142 -26
  56. streamlit/elements/progress.py +2 -4
  57. streamlit/elements/pyplot.py +6 -6
  58. streamlit/elements/space.py +113 -0
  59. streamlit/elements/vega_charts.py +400 -143
  60. streamlit/elements/widgets/audio_input.py +52 -4
  61. streamlit/elements/widgets/button.py +29 -29
  62. streamlit/elements/widgets/button_group.py +33 -6
  63. streamlit/elements/widgets/camera_input.py +3 -4
  64. streamlit/elements/widgets/chat.py +7 -0
  65. streamlit/elements/widgets/checkbox.py +1 -0
  66. streamlit/elements/widgets/color_picker.py +1 -0
  67. streamlit/elements/widgets/data_editor.py +34 -29
  68. streamlit/elements/widgets/file_uploader.py +6 -10
  69. streamlit/elements/widgets/multiselect.py +14 -3
  70. streamlit/elements/widgets/number_input.py +5 -4
  71. streamlit/elements/widgets/radio.py +10 -2
  72. streamlit/elements/widgets/select_slider.py +8 -4
  73. streamlit/elements/widgets/selectbox.py +9 -2
  74. streamlit/elements/widgets/slider.py +38 -41
  75. streamlit/elements/widgets/text_widgets.py +6 -0
  76. streamlit/elements/widgets/time_widgets.py +15 -12
  77. streamlit/elements/write.py +28 -23
  78. streamlit/emojis.py +1 -1
  79. streamlit/errors.py +115 -0
  80. streamlit/git_util.py +65 -43
  81. streamlit/hello/hello.py +8 -0
  82. streamlit/hello/utils.py +2 -1
  83. streamlit/material_icon_names.py +1 -1
  84. streamlit/navigation/page.py +4 -1
  85. streamlit/proto/ArrowData_pb2.py +27 -0
  86. streamlit/proto/ArrowData_pb2.pyi +46 -0
  87. streamlit/proto/Arrow_pb2.py +10 -8
  88. streamlit/proto/Arrow_pb2.pyi +31 -2
  89. streamlit/proto/AudioInput_pb2.py +2 -2
  90. streamlit/proto/AudioInput_pb2.pyi +6 -2
  91. streamlit/proto/BidiComponent_pb2.py +34 -0
  92. streamlit/proto/BidiComponent_pb2.pyi +153 -0
  93. streamlit/proto/Block_pb2.py +11 -11
  94. streamlit/proto/Block_pb2.pyi +9 -1
  95. streamlit/proto/DeckGlJsonChart_pb2.py +10 -4
  96. streamlit/proto/DeckGlJsonChart_pb2.pyi +9 -3
  97. streamlit/proto/Element_pb2.py +5 -3
  98. streamlit/proto/Element_pb2.pyi +14 -4
  99. streamlit/proto/HeightConfig_pb2.py +2 -2
  100. streamlit/proto/HeightConfig_pb2.pyi +6 -3
  101. streamlit/proto/NewSession_pb2.py +18 -16
  102. streamlit/proto/NewSession_pb2.pyi +158 -6
  103. streamlit/proto/PlotlyChart_pb2.py +8 -6
  104. streamlit/proto/PlotlyChart_pb2.pyi +3 -1
  105. streamlit/proto/Space_pb2.py +27 -0
  106. streamlit/proto/Space_pb2.pyi +42 -0
  107. streamlit/proto/WidgetStates_pb2.py +2 -2
  108. streamlit/proto/WidgetStates_pb2.pyi +13 -3
  109. streamlit/proto/WidthConfig_pb2.py +2 -2
  110. streamlit/proto/WidthConfig_pb2.pyi +6 -3
  111. streamlit/runtime/app_session.py +45 -6
  112. streamlit/runtime/caching/cache_data_api.py +4 -4
  113. streamlit/runtime/caching/cache_errors.py +4 -1
  114. streamlit/runtime/caching/cache_resource_api.py +3 -2
  115. streamlit/runtime/caching/cache_utils.py +2 -1
  116. streamlit/runtime/caching/cached_message_replay.py +3 -3
  117. streamlit/runtime/caching/hashing.py +3 -4
  118. streamlit/runtime/caching/legacy_cache_api.py +2 -1
  119. streamlit/runtime/connection_factory.py +1 -3
  120. streamlit/runtime/forward_msg_queue.py +4 -1
  121. streamlit/runtime/fragment.py +2 -1
  122. streamlit/runtime/memory_media_file_storage.py +1 -1
  123. streamlit/runtime/metrics_util.py +6 -2
  124. streamlit/runtime/runtime.py +14 -0
  125. streamlit/runtime/scriptrunner/exec_code.py +2 -1
  126. streamlit/runtime/scriptrunner/script_runner.py +2 -2
  127. streamlit/runtime/scriptrunner_utils/script_run_context.py +3 -6
  128. streamlit/runtime/secrets.py +2 -4
  129. streamlit/runtime/session_manager.py +3 -1
  130. streamlit/runtime/state/common.py +30 -5
  131. streamlit/runtime/state/presentation.py +85 -0
  132. streamlit/runtime/state/safe_session_state.py +2 -2
  133. streamlit/runtime/state/session_state.py +220 -16
  134. streamlit/runtime/state/widgets.py +19 -3
  135. streamlit/runtime/theme_util.py +148 -0
  136. streamlit/runtime/websocket_session_manager.py +3 -1
  137. streamlit/source_util.py +2 -2
  138. streamlit/static/index.html +2 -2
  139. streamlit/static/manifest.json +244 -227
  140. streamlit/static/static/css/{index.C8X8rNzw.css → index.BpABIXK9.css} +1 -1
  141. streamlit/static/static/css/index.DgR7E2CV.css +1 -0
  142. streamlit/static/static/js/{ErrorOutline.esm.DcGrhbBP.js → ErrorOutline.esm.YoJdlW1p.js} +1 -1
  143. streamlit/static/static/js/{FileDownload.esm.DgBvV6Pq.js → FileDownload.esm.Ddx8VEYy.js} +1 -1
  144. streamlit/static/static/js/{FileHelper.M6AAaeuA.js → FileHelper.90EtOmj9.js} +1 -1
  145. streamlit/static/static/js/{FormClearHelper.DHh1GFzm.js → FormClearHelper.BB1Km6eP.js} +1 -1
  146. streamlit/static/static/js/InputInstructions.jhH15PqV.js +1 -0
  147. streamlit/static/static/js/{Particles.DDVT-6Qc.js → Particles.DUsputn1.js} +1 -1
  148. streamlit/static/static/js/{ProgressBar.BEY0cXXV.js → ProgressBar.DLY8H6nE.js} +2 -2
  149. streamlit/static/static/js/Toolbar.D8nHCkuz.js +1 -0
  150. streamlit/static/static/js/{base-input.CK3UVGp1.js → base-input.CJGiNqed.js} +3 -3
  151. streamlit/static/static/js/{checkbox.D8W881TL.js → checkbox.Cpdd482O.js} +1 -1
  152. streamlit/static/static/js/{createSuper.B6W-Dh9S.js → createSuper.CuQIogbW.js} +1 -1
  153. streamlit/static/static/js/data-grid-overlay-editor.2Ufgxc6y.js +1 -0
  154. streamlit/static/static/js/{downloader.DiKpuU_S.js → downloader.CN0K7xlu.js} +1 -1
  155. streamlit/static/static/js/{es6.B8zRNPZ-.js → es6.BJcsVXQ0.js} +2 -2
  156. streamlit/static/static/js/{iframeResizer.contentWindow.DIewJmmh.js → iframeResizer.contentWindow.XzUvQqcZ.js} +1 -1
  157. streamlit/static/static/js/index.B1ZQh4P1.js +1 -0
  158. streamlit/static/static/js/index.BKstZk0M.js +27 -0
  159. streamlit/static/static/js/{index.Bte_9Lyq.js → index.BMcFsUee.js} +1 -1
  160. streamlit/static/static/js/{index.qhs54UAB.js → index.BR-IdcTb.js} +1 -1
  161. streamlit/static/static/js/{index.CejBxbg1.js → index.B_dWA3vd.js} +1 -1
  162. streamlit/static/static/js/{index.D5naqx-J.js → index.BgnZEMVh.js} +1 -1
  163. streamlit/static/static/js/{index.C7fRKRs4.js → index.BohqXifI.js} +1 -1
  164. streamlit/static/static/js/{index.cnnXF7xQ.js → index.Br5nxKNj.js} +1 -1
  165. streamlit/static/static/js/index.BrIKVbNc.js +3 -0
  166. streamlit/static/static/js/index.BtWUPzle.js +1 -0
  167. streamlit/static/static/js/index.C0RLraek.js +1 -0
  168. streamlit/static/static/js/{index.CP5TD2z1.js → index.CAIjskgG.js} +1 -1
  169. streamlit/static/static/js/{index.CD8HuT3N.js → index.CAj-7vWz.js} +135 -162
  170. streamlit/static/static/js/{index.DtYN2x4k.js → index.CMtEit2O.js} +1 -1
  171. streamlit/static/static/js/index.CkRlykEE.js +12 -0
  172. streamlit/static/static/js/{index.Ts_0SdB9.js → index.CmN3FXfI.js} +2 -2
  173. streamlit/static/static/js/{index.BnEpvLEz.js → index.CwbFI1_-.js} +1 -1
  174. streamlit/static/static/js/{index.CcJf6BCU.js → index.CxIUUfab.js} +27 -27
  175. streamlit/static/static/js/index.D2KPNy7e.js +1 -0
  176. streamlit/static/static/js/{index.Ch7MBCx0.js → index.D3GPA5k4.js} +47 -47
  177. streamlit/static/static/js/{index.ho6NIXGl.js → index.DGAh7DMq.js} +1 -1
  178. streamlit/static/static/js/index.DKb_NvmG.js +197 -0
  179. streamlit/static/static/js/{index.CvYYtxD_.js → index.DMqgUYKq.js} +1 -1
  180. streamlit/static/static/js/{index.zecpGxtj.js → index.DOFlg3dS.js} +1 -1
  181. streamlit/static/static/js/{index.B9mjBcgE.js → index.DPUXkcQL.js} +1 -1
  182. streamlit/static/static/js/index.DX1xY89g.js +1 -0
  183. streamlit/static/static/js/index.DYATBCsq.js +2 -0
  184. streamlit/static/static/js/{index.D2-atlaQ.js → index.DaSmGJ76.js} +3 -3
  185. streamlit/static/static/js/index.Dd7bMeLP.js +1 -0
  186. streamlit/static/static/js/{index.4eF4NxG2.js → index.DjmmgI5U.js} +1 -1
  187. streamlit/static/static/js/index.Dq56CyM2.js +1 -0
  188. streamlit/static/static/js/index.DuiXaS5_.js +7 -0
  189. streamlit/static/static/js/index.DvFidMLe.js +2 -0
  190. streamlit/static/static/js/{index.452cqrrL.js → index.DwkhC5Pc.js} +1 -1
  191. streamlit/static/static/js/{index.Dk4C7X3i.js → index.Q-3sFn1v.js} +1 -1
  192. streamlit/static/static/js/{index.CjXWwH-y.js → index.QJ5QO9sJ.js} +1 -1
  193. streamlit/static/static/js/{index.B6U8LQo3.js → index.VwTaeety.js} +1 -1
  194. streamlit/static/static/js/index.YOqQbeX8.js +1 -0
  195. streamlit/static/static/js/{input.nzVJphXi.js → input.D4MN_FzN.js} +1 -1
  196. streamlit/static/static/js/{memory.CjCgTQz3.js → memory.DrZjtdGT.js} +1 -1
  197. streamlit/static/static/js/{number-overlay-editor.DaRFzZEO.js → number-overlay-editor.DRwAw1In.js} +1 -1
  198. streamlit/static/static/js/{possibleConstructorReturn.DgiPnZ9N.js → possibleConstructorReturn.exeeJQEP.js} +1 -1
  199. streamlit/static/static/js/record.B-tDciZb.js +1 -0
  200. streamlit/static/static/js/{sandbox.mithfq7Z.js → sandbox.ClO3IuUr.js} +1 -1
  201. streamlit/static/static/js/{timepicker.Dbl5KFh6.js → timepicker.DAhu-vcF.js} +4 -4
  202. streamlit/static/static/js/{toConsumableArray.D-Dx88BQ.js → toConsumableArray.DNbljYEC.js} +1 -1
  203. streamlit/static/static/js/{uniqueId.Bh26R_3S.js → uniqueId.oG4Gvj1v.js} +1 -1
  204. streamlit/static/static/js/{useBasicWidgetState.DeK-QJpD.js → useBasicWidgetState.D6sOH6oI.js} +1 -1
  205. streamlit/static/static/js/{useTextInputAutoExpand.4iAdLWD-.js → useTextInputAutoExpand.4u3_GcuN.js} +2 -2
  206. streamlit/static/static/js/{useUpdateUiValue.CmT7_nJN.js → useUpdateUiValue.F2R3eTeR.js} +1 -1
  207. streamlit/static/static/js/wavesurfer.esm.vI8Eid4k.js +73 -0
  208. streamlit/static/static/js/withFullScreenWrapper.zothJIsI.js +1 -0
  209. streamlit/static/static/media/MaterialSymbols-Rounded.C7IFxh57.woff2 +0 -0
  210. streamlit/string_util.py +56 -1
  211. streamlit/testing/v1/app_test.py +2 -2
  212. streamlit/testing/v1/element_tree.py +23 -9
  213. streamlit/testing/v1/util.py +2 -2
  214. streamlit/type_util.py +3 -4
  215. streamlit/url_util.py +1 -3
  216. streamlit/user_info.py +1 -2
  217. streamlit/util.py +3 -1
  218. streamlit/watcher/event_based_path_watcher.py +23 -12
  219. streamlit/watcher/local_sources_watcher.py +11 -1
  220. streamlit/watcher/path_watcher.py +9 -6
  221. streamlit/watcher/polling_path_watcher.py +4 -1
  222. streamlit/watcher/util.py +2 -2
  223. streamlit/web/bootstrap.py +0 -31
  224. streamlit/web/cli.py +51 -22
  225. streamlit/web/server/bidi_component_request_handler.py +193 -0
  226. streamlit/web/server/component_file_utils.py +97 -0
  227. streamlit/web/server/component_request_handler.py +8 -21
  228. streamlit/web/server/oidc_mixin.py +3 -1
  229. streamlit/web/server/routes.py +18 -5
  230. streamlit/web/server/server.py +10 -0
  231. streamlit/web/server/server_util.py +3 -1
  232. streamlit/web/server/upload_file_request_handler.py +3 -1
  233. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/METADATA +4 -5
  234. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/RECORD +238 -209
  235. streamlit/static/static/css/index.COe1010n.css +0 -1
  236. streamlit/static/static/js/Hooks.DGu1od_L.js +0 -1
  237. streamlit/static/static/js/InputInstructions.z6sVgyYt.js +0 -1
  238. streamlit/static/static/js/Toolbar.DSnK1fUh.js +0 -1
  239. streamlit/static/static/js/data-grid-overlay-editor.DRTHOydk.js +0 -1
  240. streamlit/static/static/js/index.BXYmrqnf.js +0 -1
  241. streamlit/static/static/js/index.B_8AnktO.js +0 -1
  242. streamlit/static/static/js/index.Bl7zGQSh.js +0 -7
  243. streamlit/static/static/js/index.BnJIOYn9.js +0 -73
  244. streamlit/static/static/js/index.C1HcTl5K.js +0 -1
  245. streamlit/static/static/js/index.C7lSmSOP.js +0 -1
  246. streamlit/static/static/js/index.C_tmcx4B.js +0 -1
  247. streamlit/static/static/js/index.D3K5nOu9.js +0 -197
  248. streamlit/static/static/js/index.DkKT3LUI.js +0 -1
  249. streamlit/static/static/js/index.MTPPBDHk.js +0 -2
  250. streamlit/static/static/js/index.pqW9AMJD.js +0 -3
  251. streamlit/static/static/js/index.urHgTgMQ.js +0 -12
  252. streamlit/static/static/js/index.wzkv_11M.js +0 -1
  253. streamlit/static/static/js/index.yF5AncHY.js +0 -1
  254. streamlit/static/static/js/withFullScreenWrapper.DLp1ENGm.js +0 -1
  255. streamlit/static/static/media/MaterialSymbols-Rounded.CBxVaFdk.woff2 +0 -0
  256. {streamlit-1.49.1.data → streamlit-1.51.0.data}/scripts/streamlit.cmd +0 -0
  257. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/WHEEL +0 -0
  258. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/entry_points.txt +0 -0
  259. {streamlit-1.49.1.dist-info → streamlit-1.51.0.dist-info}/top_level.txt +0 -0
@@ -19,16 +19,7 @@ from __future__ import annotations
19
19
  from dataclasses import dataclass
20
20
  from datetime import date
21
21
  from enum import Enum
22
- from typing import (
23
- TYPE_CHECKING,
24
- Any,
25
- Final,
26
- Literal,
27
- TypedDict,
28
- cast,
29
- )
30
-
31
- from typing_extensions import TypeAlias
22
+ from typing import TYPE_CHECKING, Any, Final, Literal, TypeAlias, TypedDict, cast
32
23
 
33
24
  from streamlit import dataframe_util, type_util
34
25
  from streamlit.elements.lib.color_util import (
@@ -47,6 +38,10 @@ if TYPE_CHECKING:
47
38
  import pandas as pd
48
39
 
49
40
  from streamlit.dataframe_util import Data
41
+ from streamlit.elements.lib.layout_utils import (
42
+ Height,
43
+ Width,
44
+ )
50
45
 
51
46
  VegaLiteType: TypeAlias = Literal["quantitative", "ordinal", "temporal", "nominal"]
52
47
  ChartStackType: TypeAlias = Literal["normalize", "center", "layered"]
@@ -59,6 +54,7 @@ class PrepDataColumns(TypedDict):
59
54
  y_column_list: list[str]
60
55
  color_column: str | None
61
56
  size_column: str | None
57
+ sort_column: str | None
62
58
 
63
59
 
64
60
  @dataclass
@@ -73,13 +69,14 @@ class AddRowsMetadata:
73
69
  columns: PrepDataColumns
74
70
  # Chart styling properties
75
71
  color: str | Color | list[Color] | None = None
76
- width: int | None = None
77
- height: int | None = None
78
- use_container_width: bool = True
72
+ width: Width | None = None
73
+ height: Height | None = None
74
+ use_container_width: bool | None = None
79
75
  # Only applicable for bar & area charts
80
76
  stack: bool | ChartStackType | None = None
81
77
  # Only applicable for bar charts
82
78
  horizontal: bool = False
79
+ sort: bool | str = False
83
80
 
84
81
 
85
82
  class ChartType(Enum):
@@ -151,13 +148,14 @@ def generate_chart(
151
148
  y_axis_label: str | None = None,
152
149
  color_from_user: str | Color | list[Color] | None = None,
153
150
  size_from_user: str | float | None = None,
154
- width: int | None = None,
155
- height: int | None = None,
156
- use_container_width: bool = True,
151
+ width: Width | None = None,
152
+ height: Height | None = None,
153
+ use_container_width: bool | None = None,
157
154
  # Bar & Area charts only:
158
155
  stack: bool | ChartStackType | None = None,
159
156
  # Bar charts only:
160
157
  horizontal: bool = False,
158
+ sort_from_user: bool | str = False,
161
159
  ) -> tuple[alt.Chart | alt.LayerChart, AddRowsMetadata]:
162
160
  """Function to use the chart's type, data columns and indices to figure out the
163
161
  chart's spec.
@@ -181,6 +179,8 @@ def generate_chart(
181
179
  # Get name of column to use for size, or constant value to use. Any/both could
182
180
  # be None.
183
181
  size_column, size_value = _parse_generic_column(df, size_from_user)
182
+ # Get name of column to use for sort.
183
+ sort_column = _parse_sort_column(df, sort_from_user)
184
184
 
185
185
  # Store some info so we can use it in add_rows.
186
186
  add_rows_metadata = AddRowsMetadata(
@@ -194,6 +194,7 @@ def generate_chart(
194
194
  "y_column_list": y_column_list,
195
195
  "color_column": color_column,
196
196
  "size_column": size_column,
197
+ "sort_column": sort_column,
197
198
  },
198
199
  # Chart styling properties
199
200
  color=color_from_user,
@@ -202,13 +203,13 @@ def generate_chart(
202
203
  use_container_width=use_container_width,
203
204
  stack=stack,
204
205
  horizontal=horizontal,
206
+ sort=sort_from_user,
205
207
  )
206
208
 
207
209
  # At this point, all foo_column variables are either None/empty or contain actual
208
210
  # columns that are guaranteed to exist.
209
-
210
- df, x_column, y_column, color_column, size_column = _prep_data(
211
- df, x_column, y_column_list, color_column, size_column
211
+ df, x_column, y_column, color_column, size_column, sort_column = _prep_data(
212
+ df, x_column, y_column_list, color_column, size_column, sort_column
212
213
  )
213
214
 
214
215
  # At this point, x_column is only None if user did not provide one AND df is empty.
@@ -224,14 +225,18 @@ def generate_chart(
224
225
  x_axis_label,
225
226
  y_axis_label,
226
227
  stack,
228
+ sort_from_user,
227
229
  )
228
230
 
231
+ chart_width = width if isinstance(width, int) else None
232
+ chart_height = height if isinstance(height, int) else None
233
+
229
234
  # Create a Chart with x and y encodings.
230
235
  chart = alt.Chart(
231
236
  data=df,
232
237
  mark=chart_type.value["mark_type"],
233
- width=width or 0,
234
- height=height or 0,
238
+ width=chart_width or 0,
239
+ height=chart_height or 0,
235
240
  ).encode(
236
241
  x=x_encoding,
237
242
  y=y_encoding,
@@ -281,7 +286,7 @@ def generate_chart(
281
286
  and is_altair_version_5_or_greater
282
287
  ):
283
288
  return _add_improved_hover_tooltips(
284
- chart, x_column, width, height
289
+ chart, x_column, chart_width, chart_height
285
290
  ).interactive(), add_rows_metadata
286
291
 
287
292
  return chart.interactive(), add_rows_metadata
@@ -359,6 +364,7 @@ def prep_chart_data_for_add_rows(
359
364
  y_column_list=add_rows_metadata.columns["y_column_list"],
360
365
  color_column=add_rows_metadata.columns["color_column"],
361
366
  size_column=add_rows_metadata.columns["size_column"],
367
+ sort_column=add_rows_metadata.columns["sort_column"],
362
368
  )
363
369
 
364
370
  return out_data, add_rows_metadata
@@ -437,7 +443,8 @@ def _prep_data(
437
443
  y_column_list: list[str],
438
444
  color_column: str | None,
439
445
  size_column: str | None,
440
- ) -> tuple[pd.DataFrame, str | None, str | None, str | None, str | None]:
446
+ sort_column: str | None = None,
447
+ ) -> tuple[pd.DataFrame, str | None, str | None, str | None, str | None, str | None]:
441
448
  """Prepares the data for charting. This is also used in add_rows.
442
449
 
443
450
  Returns the prepared dataframe and the new names of the x column (taking the index
@@ -450,7 +457,7 @@ def _prep_data(
450
457
 
451
458
  # Drop columns we're not using.
452
459
  selected_data = _drop_unused_columns(
453
- df, x_column, color_column, size_column, *y_column_list
460
+ df, x_column, color_column, size_column, sort_column, *y_column_list
454
461
  )
455
462
 
456
463
  # Maybe convert color to Vega colors.
@@ -462,17 +469,18 @@ def _prep_data(
462
469
  y_column_list,
463
470
  color_column,
464
471
  size_column,
472
+ sort_column,
465
473
  ) = _convert_col_names_to_str_in_place(
466
- selected_data, x_column, y_column_list, color_column, size_column
474
+ selected_data, x_column, y_column_list, color_column, size_column, sort_column
467
475
  )
468
476
 
469
477
  # Maybe melt data from wide format into long format.
470
478
  melted_data, y_column, color_column = _maybe_melt(
471
- selected_data, x_column, y_column_list, color_column, size_column
479
+ selected_data, x_column, y_column_list, color_column, size_column, sort_column
472
480
  )
473
481
 
474
482
  # Return the data, but also the new names to use for x, y, and color.
475
- return melted_data, x_column, y_column, color_column, size_column
483
+ return melted_data, x_column, y_column, color_column, size_column, sort_column
476
484
 
477
485
 
478
486
  def _last_index_for_melted_dataframes(
@@ -505,7 +513,7 @@ def _is_date_column(df: pd.DataFrame, name: str | None) -> bool:
505
513
  if column.size == 0:
506
514
  return False
507
515
 
508
- return isinstance(column.iloc[0], date)
516
+ return isinstance(column.iat[0], date)
509
517
 
510
518
 
511
519
  def _melt_data(
@@ -636,7 +644,7 @@ def _maybe_convert_color_column_in_place(
636
644
  if color_column is None or len(df[color_column]) == 0:
637
645
  return
638
646
 
639
- first_color_datum = df[color_column].iloc[0]
647
+ first_color_datum = df[color_column].iat[0]
640
648
 
641
649
  if is_hex_color_like(first_color_datum):
642
650
  # Hex is already CSS-valid.
@@ -657,7 +665,8 @@ def _convert_col_names_to_str_in_place(
657
665
  y_column_list: list[str],
658
666
  color_column: str | None,
659
667
  size_column: str | None,
660
- ) -> tuple[str | None, list[str], str | None, str | None]:
668
+ sort_column: str | None,
669
+ ) -> tuple[str | None, list[str], str | None, str | None, str | None]:
661
670
  """Converts column names to strings, since Vega-Lite does not accept ints, etc."""
662
671
  import pandas as pd
663
672
 
@@ -670,6 +679,7 @@ def _convert_col_names_to_str_in_place(
670
679
  [str(c) for c in y_column_list],
671
680
  None if color_column is None else str(color_column),
672
681
  None if size_column is None else str(size_column),
682
+ None if sort_column is None else str(sort_column),
673
683
  )
674
684
 
675
685
 
@@ -703,6 +713,17 @@ def _parse_x_column(df: pd.DataFrame, x_from_user: str | None) -> str | None:
703
713
  )
704
714
 
705
715
 
716
+ def _parse_sort_column(df: pd.DataFrame, sort_from_user: bool | str) -> str | None:
717
+ if sort_from_user is False or sort_from_user is True:
718
+ return None
719
+
720
+ sort_column = sort_from_user.removeprefix("-")
721
+ if sort_column not in df.columns:
722
+ raise StreamlitColumnNotFoundError(df, sort_column)
723
+
724
+ return sort_column
725
+
726
+
706
727
  def _parse_y_columns(
707
728
  df: pd.DataFrame,
708
729
  y_from_user: str | Sequence[str] | None,
@@ -790,6 +811,7 @@ def _maybe_melt(
790
811
  y_column_list: list[str],
791
812
  color_column: str | None,
792
813
  size_column: str | None,
814
+ sort_column: str | None,
793
815
  ) -> tuple[pd.DataFrame, str | None, str | None]:
794
816
  """If multiple columns are set for y, melt the dataframe into long format."""
795
817
  y_column: str | None
@@ -806,6 +828,8 @@ def _maybe_melt(
806
828
  columns_to_leave_alone = [x_column]
807
829
  if size_column:
808
830
  columns_to_leave_alone.append(size_column)
831
+ if sort_column:
832
+ columns_to_leave_alone.append(sort_column)
809
833
 
810
834
  df = _melt_data(
811
835
  df=df,
@@ -828,8 +852,10 @@ def _get_axis_encodings(
828
852
  x_axis_label: str | None,
829
853
  y_axis_label: str | None,
830
854
  stack: bool | ChartStackType | None,
855
+ sort_from_user: bool | str,
831
856
  ) -> tuple[alt.X, alt.Y]:
832
857
  stack_encoding: alt.X | alt.Y
858
+ sort_encoding: alt.X | alt.Y
833
859
  if chart_type == ChartType.HORIZONTAL_BAR:
834
860
  # Handle horizontal bar chart - switches x and y data:
835
861
  x_encoding = _get_x_encoding(
@@ -839,6 +865,7 @@ def _get_axis_encodings(
839
865
  df, x_column, x_from_user, y_axis_label, chart_type
840
866
  )
841
867
  stack_encoding = x_encoding
868
+ sort_encoding = y_encoding
842
869
  else:
843
870
  x_encoding = _get_x_encoding(
844
871
  df, x_column, x_from_user, x_axis_label, chart_type
@@ -847,10 +874,15 @@ def _get_axis_encodings(
847
874
  df, y_column, y_from_user, y_axis_label, chart_type
848
875
  )
849
876
  stack_encoding = y_encoding
877
+ sort_encoding = x_encoding
850
878
 
851
879
  # Handle stacking - only relevant for bar & area charts
852
880
  _update_encoding_with_stack(stack, stack_encoding)
853
881
 
882
+ # Handle sorting - only relevant for bar charts
883
+ if chart_type in (ChartType.VERTICAL_BAR, ChartType.HORIZONTAL_BAR):
884
+ _update_encoding_with_sort(sort_from_user, sort_encoding)
885
+
854
886
  return x_encoding, y_encoding
855
887
 
856
888
 
@@ -957,6 +989,38 @@ def _update_encoding_with_stack(
957
989
  encoding["stack"] = stack
958
990
 
959
991
 
992
+ def _update_encoding_with_sort(
993
+ sort_from_user: bool | str,
994
+ encoding: alt.X | alt.Y,
995
+ ) -> None:
996
+ """Apply sort to the given encoding in-place.
997
+
998
+ - If sort is False: disable Altair's default sorting on the bar's categorical axis
999
+ (i.e., set to None).
1000
+ - If sort is True: use Altair's default sorting.
1001
+ - If sort is a column name (optionally starting with '-') set a SortField with the correct order.
1002
+
1003
+ Note: Column validation should be done before calling this function.
1004
+ """
1005
+ import altair as alt
1006
+
1007
+ if sort_from_user is False:
1008
+ # Disable Altair's default sorting
1009
+ encoding["sort"] = None
1010
+ elif sort_from_user is True:
1011
+ # Use Altair's default sorting
1012
+ pass
1013
+ else:
1014
+ # String: sort by column name (optional '-' prefix for descending)
1015
+ sort_order: Literal["ascending", "descending"]
1016
+ if sort_from_user.startswith("-"):
1017
+ sort_order = "descending"
1018
+ else:
1019
+ sort_order = "ascending"
1020
+ sort_field = sort_from_user.removeprefix("-")
1021
+ encoding["sort"] = alt.SortField(field=sort_field, order=sort_order)
1022
+
1023
+
960
1024
  def _get_color_encoding(
961
1025
  df: pd.DataFrame,
962
1026
  color_value: Color | None,
@@ -1022,7 +1086,7 @@ def _get_color_encoding(
1022
1086
 
1023
1087
  # If the 0th element in the color column looks like a color, we'll use the color
1024
1088
  # column's values as the colors in our chart.
1025
- elif len(df[color_column]) and is_color_like(df[color_column].iloc[0]):
1089
+ elif len(df[color_column]) and is_color_like(df[color_column].iat[0]):
1026
1090
  color_range = [to_css_color(c) for c in df[color_column].unique()]
1027
1091
  color_enc["scale"] = alt.Scale(range=color_range)
1028
1092
  # Don't show the color legend, because it will just show text with the
@@ -14,10 +14,8 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from collections.abc import Collection
18
- from typing import Any, Callable, Union, cast
19
-
20
- from typing_extensions import TypeAlias
17
+ from collections.abc import Callable, Collection
18
+ from typing import Any, TypeAlias, cast
21
19
 
22
20
  from streamlit.errors import StreamlitInvalidColorError
23
21
 
@@ -35,26 +33,18 @@ IntRGBAColorTuple: TypeAlias = tuple[int, int, int, int]
35
33
  # CSS uses these.
36
34
  MixedRGBAColorTuple: TypeAlias = tuple[int, int, int, float]
37
35
 
38
- Color4Tuple: TypeAlias = Union[
39
- FloatRGBAColorTuple,
40
- IntRGBAColorTuple,
41
- MixedRGBAColorTuple,
42
- ]
36
+ Color4Tuple: TypeAlias = FloatRGBAColorTuple | IntRGBAColorTuple | MixedRGBAColorTuple
43
37
 
44
- Color3Tuple: TypeAlias = Union[
45
- FloatRGBColorTuple,
46
- IntRGBColorTuple,
47
- ]
38
+ Color3Tuple: TypeAlias = FloatRGBColorTuple | IntRGBColorTuple
48
39
 
49
- ColorTuple: TypeAlias = Union[Color4Tuple, Color3Tuple]
40
+ ColorTuple: TypeAlias = Color4Tuple | Color3Tuple
50
41
 
51
- IntColorTuple = Union[IntRGBColorTuple, IntRGBAColorTuple]
52
- CSSColorStr = Union[IntRGBAColorTuple, MixedRGBAColorTuple]
42
+ IntColorTuple: TypeAlias = IntRGBColorTuple | IntRGBAColorTuple
53
43
 
54
44
  ColorStr: TypeAlias = str
55
45
 
56
- Color: TypeAlias = Union[ColorTuple, ColorStr]
57
- MaybeColor: TypeAlias = Union[str, Collection[Any]]
46
+ Color: TypeAlias = ColorTuple | ColorStr
47
+ MaybeColor: TypeAlias = str | Collection[Any]
58
48
 
59
49
 
60
50
  def to_int_color_tuple(color: MaybeColor) -> IntColorTuple:
@@ -18,9 +18,7 @@ import copy
18
18
  import json
19
19
  from collections.abc import Mapping
20
20
  from enum import Enum
21
- from typing import TYPE_CHECKING, Final, Literal, Union
22
-
23
- from typing_extensions import TypeAlias
21
+ from typing import TYPE_CHECKING, Final, Literal, TypeAlias
24
22
 
25
23
  from streamlit.dataframe_util import DataFormat
26
24
  from streamlit.elements.lib.column_types import ColumnConfig, ColumnType
@@ -109,6 +107,11 @@ _EDITING_COMPATIBILITY_MAPPING: Final[dict[ColumnType, list[ColumnDataKind]]] =
109
107
  ColumnDataKind.STRING,
110
108
  ColumnDataKind.EMPTY,
111
109
  ],
110
+ "multiselect": [
111
+ ColumnDataKind.LIST,
112
+ ColumnDataKind.STRING,
113
+ ColumnDataKind.EMPTY,
114
+ ],
112
115
  }
113
116
 
114
117
 
@@ -400,16 +403,14 @@ def determine_dataframe_schema(
400
403
 
401
404
 
402
405
  # A mapping of column names/IDs to column configs.
403
- ColumnConfigMapping: TypeAlias = dict[
404
- Union[IndexIdentifierType, str, int], ColumnConfig
405
- ]
406
+ ColumnConfigMapping: TypeAlias = dict[IndexIdentifierType | str | int, ColumnConfig]
406
407
  ColumnConfigMappingInput: TypeAlias = Mapping[
407
408
  # TODO(lukasmasuch): This should also use int here to
408
409
  # correctly type the support for positional index. However,
409
410
  # allowing int here leads mypy to complain about simple dict[str, ...]
410
411
  # as input -> which seems like a mypy bug.
411
- Union[IndexIdentifierType, str],
412
- Union[ColumnConfig, None, str],
412
+ IndexIdentifierType | str,
413
+ ColumnConfig | None | str,
413
414
  ]
414
415
 
415
416