streamlit-nightly 1.38.1.dev20240917__py2.py3-none-any.whl → 1.38.1.dev20240919__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 (57) hide show
  1. streamlit/commands/navigation.py +9 -0
  2. streamlit/components/v1/custom_component.py +2 -6
  3. streamlit/elements/arrow.py +1 -3
  4. streamlit/elements/deck_gl_json_chart.py +26 -2
  5. streamlit/elements/form.py +10 -1
  6. streamlit/elements/layouts.py +6 -1
  7. streamlit/elements/lib/utils.py +52 -6
  8. streamlit/elements/map.py +27 -1
  9. streamlit/elements/media.py +16 -5
  10. streamlit/elements/plotly_chart.py +1 -3
  11. streamlit/elements/vega_charts.py +1 -3
  12. streamlit/elements/widgets/button.py +7 -5
  13. streamlit/elements/widgets/button_group.py +1 -3
  14. streamlit/elements/widgets/camera_input.py +1 -3
  15. streamlit/elements/widgets/chat.py +2 -2
  16. streamlit/elements/widgets/checkbox.py +1 -3
  17. streamlit/elements/widgets/color_picker.py +1 -3
  18. streamlit/elements/widgets/data_editor.py +1 -3
  19. streamlit/elements/widgets/file_uploader.py +1 -3
  20. streamlit/elements/widgets/multiselect.py +1 -3
  21. streamlit/elements/widgets/number_input.py +1 -3
  22. streamlit/elements/widgets/radio.py +1 -3
  23. streamlit/elements/widgets/select_slider.py +1 -3
  24. streamlit/elements/widgets/selectbox.py +1 -3
  25. streamlit/elements/widgets/slider.py +1 -3
  26. streamlit/elements/widgets/text_widgets.py +2 -6
  27. streamlit/elements/widgets/time_widgets.py +2 -6
  28. streamlit/navigation/page.py +10 -6
  29. streamlit/proto/Block_pb2.py +13 -13
  30. streamlit/proto/Block_pb2.pyi +4 -1
  31. streamlit/proto/DeckGlJsonChart_pb2.py +3 -3
  32. streamlit/proto/DeckGlJsonChart_pb2.pyi +9 -1
  33. streamlit/proto/Navigation_pb2.py +4 -4
  34. streamlit/proto/Navigation_pb2.pyi +4 -1
  35. streamlit/static/asset-manifest.json +12 -11
  36. streamlit/static/index.html +1 -1
  37. streamlit/static/static/js/{1086.b7ec1344.chunk.js → 1086.464de8f9.chunk.js} +1 -1
  38. streamlit/static/static/js/{1260.eaaa4e75.chunk.js → 1260.e6289cc2.chunk.js} +1 -1
  39. streamlit/static/static/js/2055.bca43613.chunk.js +1 -0
  40. streamlit/static/static/js/{5618.f7838309.chunk.js → 5618.0a42d599.chunk.js} +1 -1
  41. streamlit/static/static/js/{7077.e21833ae.chunk.js → 6789.f8dde736.chunk.js} +2 -2
  42. streamlit/static/static/js/8485.81bdf474.chunk.js +1 -0
  43. streamlit/static/static/js/{954.88cae675.chunk.js → 954.1da91b19.chunk.js} +2 -2
  44. streamlit/static/static/js/9943.d18fdff1.chunk.js +1 -0
  45. streamlit/static/static/js/{main.50e02474.js → main.61ca8755.js} +5 -5
  46. {streamlit_nightly-1.38.1.dev20240917.dist-info → streamlit_nightly-1.38.1.dev20240919.dist-info}/METADATA +1 -1
  47. {streamlit_nightly-1.38.1.dev20240917.dist-info → streamlit_nightly-1.38.1.dev20240919.dist-info}/RECORD +55 -54
  48. streamlit/static/static/js/3156.002c6ee0.chunk.js +0 -1
  49. streamlit/static/static/js/7493.95e79b96.chunk.js +0 -1
  50. /streamlit/static/static/css/{7077.81b3d18f.chunk.css → 6789.81b3d18f.chunk.css} +0 -0
  51. /streamlit/static/static/css/{3156.93909c7e.chunk.css → 9943.93909c7e.chunk.css} +0 -0
  52. /streamlit/static/static/js/{7077.e21833ae.chunk.js.LICENSE.txt → 6789.f8dde736.chunk.js.LICENSE.txt} +0 -0
  53. /streamlit/static/static/js/{main.50e02474.js.LICENSE.txt → main.61ca8755.js.LICENSE.txt} +0 -0
  54. {streamlit_nightly-1.38.1.dev20240917.data → streamlit_nightly-1.38.1.dev20240919.data}/scripts/streamlit.cmd +0 -0
  55. {streamlit_nightly-1.38.1.dev20240917.dist-info → streamlit_nightly-1.38.1.dev20240919.dist-info}/WHEEL +0 -0
  56. {streamlit_nightly-1.38.1.dev20240917.dist-info → streamlit_nightly-1.38.1.dev20240919.dist-info}/entry_points.txt +0 -0
  57. {streamlit_nightly-1.38.1.dev20240917.dist-info → streamlit_nightly-1.38.1.dev20240919.dist-info}/top_level.txt +0 -0
@@ -57,6 +57,7 @@ def navigation(
57
57
  pages: list[StreamlitPage] | dict[SectionHeader, list[StreamlitPage]],
58
58
  *,
59
59
  position: Literal["sidebar", "hidden"] = "sidebar",
60
+ expanded: bool = False,
60
61
  ) -> StreamlitPage:
61
62
  """
62
63
  Configure the available pages in a multipage app.
@@ -102,6 +103,12 @@ def navigation(
102
103
  If there is only one page in ``pages``, the navigation will be hidden
103
104
  for any value of ``position``.
104
105
 
106
+ expanded: bool
107
+ Whether the navigation menu should be expanded. If ``True``,
108
+ the navigation menu will always be expanded. If ``False``, the
109
+ navigation menu will be collapsed and will include a button
110
+ to view more options.
111
+
105
112
  Returns
106
113
  -------
107
114
  StreamlitPage
@@ -215,6 +222,8 @@ def navigation(
215
222
  msg.navigation.position = NavigationProto.Position.HIDDEN
216
223
  else:
217
224
  msg.navigation.position = NavigationProto.Position.SIDEBAR
225
+
226
+ msg.navigation.expanded = expanded
218
227
  msg.navigation.sections[:] = nav_sections.keys()
219
228
  for section_header in nav_sections:
220
229
  for page in nav_sections[section_header]:
@@ -173,23 +173,19 @@ And if you're using Streamlit Cloud, add "pyarrow" to your requirements.txt."""
173
173
  computed_id = compute_and_register_element_id(
174
174
  "component_instance",
175
175
  user_key=key,
176
- name=self.name,
177
176
  form_id=current_form_id(dg),
177
+ name=self.name,
178
178
  url=self.url,
179
- key=key,
180
179
  json_args=serialized_json_args,
181
180
  special_args=special_args,
182
- page=ctx.active_script_hash if ctx else None,
183
181
  )
184
182
  else:
185
183
  computed_id = compute_and_register_element_id(
186
184
  "component_instance",
187
185
  user_key=key,
188
- name=self.name,
189
186
  form_id=current_form_id(dg),
187
+ name=self.name,
190
188
  url=self.url,
191
- key=key,
192
- page=ctx.active_script_hash if ctx else None,
193
189
  )
194
190
  element.component_instance.id = computed_id
195
191
 
@@ -568,17 +568,15 @@ class ArrowMixin:
568
568
  proto.id = compute_and_register_element_id(
569
569
  "dataframe",
570
570
  user_key=key,
571
+ form_id=proto.form_id,
571
572
  data=proto.data,
572
573
  width=width,
573
574
  height=height,
574
575
  use_container_width=use_container_width,
575
576
  column_order=proto.column_order,
576
577
  column_config=proto.columns,
577
- key=key,
578
578
  selection_mode=selection_mode,
579
579
  is_selection_activated=is_selection_activated,
580
- form_id=proto.form_id,
581
- page=ctx.active_script_hash if ctx else None,
582
580
  )
583
581
 
584
582
  serde = DataframeSelectionSerde()
@@ -39,6 +39,8 @@ class PydeckMixin:
39
39
  self,
40
40
  pydeck_obj: Deck | None = None,
41
41
  use_container_width: bool = False,
42
+ width: int | None = None,
43
+ height: int | None = None,
42
44
  ) -> DeltaGenerator:
43
45
  """Draw a chart using the PyDeck library.
44
46
 
@@ -68,7 +70,7 @@ class PydeckMixin:
68
70
 
69
71
  Parameters
70
72
  ----------
71
- pydeck_obj: pydeck.Deck or None
73
+ pydeck_obj : pydeck.Deck or None
72
74
  Object specifying the PyDeck chart to draw.
73
75
  use_container_width : bool
74
76
  Whether to override the figure's native width with the width of
@@ -77,6 +79,19 @@ class PydeckMixin:
77
79
  according to the plotting library, up to the width of the parent
78
80
  container. If ``use_container_width`` is ``True``, Streamlit sets
79
81
  the width of the figure to match the width of the parent container.
82
+ width : int or None
83
+ Desired width of the chart expressed in pixels. If ``width`` is
84
+ ``None`` (default), Streamlit sets the width of the chart to fit
85
+ its contents according to the plotting library, up to the width of
86
+ the parent container. If ``width`` is greater than the width of the
87
+ parent container, Streamlit sets the chart width to match the width
88
+ of the parent container.
89
+
90
+ To use ``width``, you must set ``use_container_width=False``.
91
+ height : int or None
92
+ Desired height of the chart expressed in pixels. If ``height`` is
93
+ ``None`` (default), Streamlit sets the height of the chart to fit
94
+ its contents according to the plotting library.
80
95
 
81
96
  Example
82
97
  -------
@@ -134,7 +149,9 @@ class PydeckMixin:
134
149
 
135
150
  """
136
151
  pydeck_proto = PydeckProto()
137
- marshall(pydeck_proto, pydeck_obj, use_container_width)
152
+ marshall(
153
+ pydeck_proto, pydeck_obj, use_container_width, width=width, height=height
154
+ )
138
155
  return self.dg._enqueue("deck_gl_json_chart", pydeck_proto)
139
156
 
140
157
  @property
@@ -165,6 +182,8 @@ def marshall(
165
182
  pydeck_proto: PydeckProto,
166
183
  pydeck_obj: Deck | None,
167
184
  use_container_width: bool,
185
+ width: int | None = None,
186
+ height: int | None = None,
168
187
  ) -> None:
169
188
  if pydeck_obj is None:
170
189
  spec = json.dumps(EMPTY_MAP)
@@ -174,6 +193,11 @@ def marshall(
174
193
  pydeck_proto.json = spec
175
194
  pydeck_proto.use_container_width = use_container_width
176
195
 
196
+ if width:
197
+ pydeck_proto.width = width
198
+ if height:
199
+ pydeck_proto.height = height
200
+
177
201
  pydeck_proto.id = ""
178
202
 
179
203
  tooltip = _get_pydeck_tooltip(pydeck_obj)
@@ -61,7 +61,12 @@ def _build_duplicate_form_message(user_key: str | None = None) -> str:
61
61
  class FormMixin:
62
62
  @gather_metrics("form")
63
63
  def form(
64
- self, key: str, clear_on_submit: bool = False, *, border: bool = True
64
+ self,
65
+ key: str,
66
+ clear_on_submit: bool = False,
67
+ *,
68
+ enter_to_submit: bool = True,
69
+ border: bool = True,
65
70
  ) -> DeltaGenerator:
66
71
  """Create a form that batches elements together with a "Submit" button.
67
72
 
@@ -93,6 +98,9 @@ class FormMixin:
93
98
  values after the user presses the Submit button. Defaults to False.
94
99
  (Note that Custom Components are unaffected by this flag, and
95
100
  will not be reset to their defaults on form submission.)
101
+ enter_to_submit : bool
102
+ Whether to submit the form when a user presses Enter while
103
+ interacting with a widget inside the form. Defaults to True.
96
104
  border : bool
97
105
  Whether to show a border around the form. Defaults to True.
98
106
 
@@ -159,6 +167,7 @@ class FormMixin:
159
167
  block_proto = Block_pb2.Block()
160
168
  block_proto.form.form_id = form_id
161
169
  block_proto.form.clear_on_submit = clear_on_submit
170
+ block_proto.form.enter_to_submit = enter_to_submit
162
171
  block_proto.form.border = border
163
172
  block_dg = self.dg._block(block_proto)
164
173
 
@@ -162,8 +162,13 @@ class LayoutsMixin:
162
162
  block_proto.vertical.border = True
163
163
 
164
164
  if key:
165
+ # At the moment, the ID is only used for extracting the
166
+ # key on the frontend and setting it as CSS class.
167
+ # There are plans to use the ID for other container features
168
+ # in the future. This might require including more container
169
+ # parameters in the ID calculation.
165
170
  block_proto.id = compute_and_register_element_id(
166
- "container", user_key=key, height=height, border=border
171
+ "container", user_key=key, form_id=None
167
172
  )
168
173
 
169
174
  return self.dg._block(block_proto)
@@ -90,7 +90,9 @@ def to_key(key: Key | None) -> str | None:
90
90
  return None if key is None else str(key)
91
91
 
92
92
 
93
- def _register_element_id(element_type: str, element_id: str) -> None:
93
+ def _register_element_id(
94
+ ctx: ScriptRunContext, element_type: str, element_id: str
95
+ ) -> None:
94
96
  """Register the element ID and key for the given element.
95
97
 
96
98
  If the element ID or key is not unique, an error is raised.
@@ -114,8 +116,8 @@ def _register_element_id(element_type: str, element_id: str) -> None:
114
116
  If the element ID is not unique.
115
117
 
116
118
  """
117
- ctx = get_script_run_ctx()
118
- if ctx is None or not element_id:
119
+
120
+ if not element_id:
119
121
  return
120
122
 
121
123
  if user_key := user_key_from_element_id(element_id):
@@ -148,6 +150,12 @@ def _compute_element_id(
148
150
  """
149
151
  h = hashlib.new("md5", **HASHLIB_KWARGS)
150
152
  h.update(element_type.encode("utf-8"))
153
+ if user_key:
154
+ # Adding this to the hash isn't necessary for uniqueness since the
155
+ # key is also appended to the ID as raw text. But since the hash and
156
+ # the appending of the key are two slightly different aspects, it
157
+ # still gets put into the hash.
158
+ h.update(user_key.encode("utf-8"))
151
159
  # This will iterate in a consistent order when the provided arguments have
152
160
  # consistent order; dicts are always in insertion order.
153
161
  for k, v in kwargs.items():
@@ -158,7 +166,9 @@ def _compute_element_id(
158
166
 
159
167
  def compute_and_register_element_id(
160
168
  element_type: str,
161
- user_key: str | None = None,
169
+ *,
170
+ user_key: str | None,
171
+ form_id: str | None,
162
172
  **kwargs: SAFE_VALUES | Iterable[SAFE_VALUES],
163
173
  ) -> str:
164
174
  """Compute and register the ID for the given element.
@@ -175,9 +185,45 @@ def compute_and_register_element_id(
175
185
  The element ID gets registered to make sure that only one ID and user-specified
176
186
  key exists at the same time. If there are duplicated IDs or keys, an error
177
187
  is raised.
188
+
189
+ Parameters
190
+ ----------
191
+ element_type : str
192
+ The type (command name) of the element to register.
193
+
194
+ user_key : str | None
195
+ The user-specified key for the element. `None` if no key is provided
196
+ or if the element doesn't support a specifying a key.
197
+
198
+ form_id : str | None
199
+ The ID of the form that the element belongs to. `None` or empty string
200
+ if the element doesn't belong to a form or doesn't support forms.
201
+
202
+ kwargs : SAFE_VALUES | Iterable[SAFE_VALUES]
203
+ The arguments to use to compute the element ID.
204
+ The arguments must be stable, deterministic values.
205
+ Some common parameters like key, disabled,
206
+ format_func, label_visibility, args, kwargs, on_change, and
207
+ the active_script_hash are not supposed to be added here
178
208
  """
179
- element_id = _compute_element_id(element_type, user_key, **kwargs)
180
- _register_element_id(element_type, element_id)
209
+ ctx = get_script_run_ctx()
210
+
211
+ # If form_id is provided, add it to the kwargs.
212
+ kwargs_to_use = {"form_id": form_id, **kwargs} if form_id else kwargs
213
+
214
+ if ctx:
215
+ # Add the active script hash to give elements on different
216
+ # pages unique IDs.
217
+ kwargs_to_use["active_script_hash"] = ctx.active_script_hash
218
+
219
+ element_id = _compute_element_id(
220
+ element_type,
221
+ user_key,
222
+ **kwargs_to_use,
223
+ )
224
+
225
+ if ctx:
226
+ _register_element_id(ctx, element_type, element_id)
181
227
  return element_id
182
228
 
183
229
 
streamlit/elements/map.py CHANGED
@@ -84,6 +84,8 @@ class MapMixin:
84
84
  size: None | str | float = None,
85
85
  zoom: int | None = None,
86
86
  use_container_width: bool = True,
87
+ width: int | None = None,
88
+ height: int | None = None,
87
89
  ) -> DeltaGenerator:
88
90
  """Display a map with a scatterplot overlaid onto it.
89
91
 
@@ -162,6 +164,21 @@ class MapMixin:
162
164
  Streamlit sets the width of the chart to fit its contents according
163
165
  to the plotting library, up to the width of the parent container.
164
166
 
167
+ width : int or None
168
+ Desired width of the chart expressed in pixels. If ``width`` is
169
+ ``None`` (default), Streamlit sets the width of the chart to fit
170
+ its contents according to the plotting library, up to the width of
171
+ the parent container. If ``width`` is greater than the width of the
172
+ parent container, Streamlit sets the chart width to match the width
173
+ of the parent container.
174
+
175
+ To use ``width``, you must set ``use_container_width=False``.
176
+
177
+ height : int or None
178
+ Desired height of the chart expressed in pixels. If ``height`` is
179
+ ``None`` (default), Streamlit sets the height of the chart to fit
180
+ its contents according to the plotting library.
181
+
165
182
  Examples
166
183
  --------
167
184
  >>> import streamlit as st
@@ -223,7 +240,9 @@ class MapMixin:
223
240
  deck_gl_json = to_deckgl_json(
224
241
  data, latitude, longitude, size, color, map_style, zoom
225
242
  )
226
- marshall(map_proto, deck_gl_json, use_container_width)
243
+ marshall(
244
+ map_proto, deck_gl_json, use_container_width, width=width, height=height
245
+ )
227
246
  return self.dg._enqueue("deck_gl_json_chart", map_proto)
228
247
 
229
248
  @property
@@ -471,8 +490,15 @@ def marshall(
471
490
  pydeck_proto: DeckGlJsonChartProto,
472
491
  pydeck_json: str,
473
492
  use_container_width: bool,
493
+ height: int | None = None,
494
+ width: int | None = None,
474
495
  ) -> None:
475
496
  pydeck_proto.json = pydeck_json
476
497
  pydeck_proto.use_container_width = use_container_width
477
498
 
499
+ if width:
500
+ pydeck_proto.width = width
501
+ if height:
502
+ pydeck_proto.height = height
503
+
478
504
  pydeck_proto.id = ""
@@ -23,6 +23,7 @@ from typing import TYPE_CHECKING, Dict, Final, Union, cast
23
23
  from typing_extensions import TypeAlias
24
24
 
25
25
  from streamlit import runtime, type_util, url_util
26
+ from streamlit.elements.lib.form_utils import current_form_id
26
27
  from streamlit.elements.lib.subtitle_utils import process_subtitle_data
27
28
  from streamlit.elements.lib.utils import compute_and_register_element_id
28
29
  from streamlit.errors import StreamlitAPIException
@@ -30,7 +31,6 @@ from streamlit.proto.Audio_pb2 import Audio as AudioProto
30
31
  from streamlit.proto.Video_pb2 import Video as VideoProto
31
32
  from streamlit.runtime import caching
32
33
  from streamlit.runtime.metrics_util import gather_metrics
33
- from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
34
34
  from streamlit.time_util import time_to_seconds
35
35
  from streamlit.type_util import NumpyShape
36
36
 
@@ -191,6 +191,7 @@ class MediaMixin:
191
191
  end_time,
192
192
  loop,
193
193
  autoplay,
194
+ form_id=current_form_id(self.dg),
194
195
  )
195
196
  return self.dg._enqueue("audio", audio_proto)
196
197
 
@@ -349,6 +350,7 @@ class MediaMixin:
349
350
  loop,
350
351
  autoplay,
351
352
  muted,
353
+ form_id=current_form_id(self.dg),
352
354
  )
353
355
  return self.dg._enqueue("video", video_proto)
354
356
 
@@ -457,6 +459,7 @@ def marshall_video(
457
459
  loop: bool = False,
458
460
  autoplay: bool = False,
459
461
  muted: bool = False,
462
+ form_id: str | None = None,
460
463
  ) -> None:
461
464
  """Marshalls a video proto, using url processors as needed.
462
465
 
@@ -499,6 +502,9 @@ def marshall_video(
499
502
  muted: bool
500
503
  Whether the video should play with the audio silenced. This can be used to
501
504
  enable autoplay without user interaction. Defaults to False.
505
+ form_id: str | None
506
+ The ID of the form that this element is placed in. Provide None if
507
+ the element is not placed in a form.
502
508
  """
503
509
 
504
510
  if start_time < 0 or (end_time is not None and end_time <= start_time):
@@ -565,10 +571,12 @@ def marshall_video(
565
571
  ) from original_err
566
572
 
567
573
  if autoplay:
568
- ctx = get_script_run_ctx()
569
574
  proto.autoplay = autoplay
570
575
  proto.id = compute_and_register_element_id(
571
576
  "video",
577
+ # video does not yet allow setting a user-defined key
578
+ user_key=None,
579
+ form_id=form_id,
572
580
  url=proto.url,
573
581
  mimetype=mimetype,
574
582
  start_time=start_time,
@@ -576,7 +584,6 @@ def marshall_video(
576
584
  loop=loop,
577
585
  autoplay=autoplay,
578
586
  muted=muted,
579
- page=ctx.active_script_hash if ctx else None,
580
587
  )
581
588
 
582
589
 
@@ -696,6 +703,7 @@ def marshall_audio(
696
703
  end_time: int | None = None,
697
704
  loop: bool = False,
698
705
  autoplay: bool = False,
706
+ form_id: str | None = None,
699
707
  ) -> None:
700
708
  """Marshalls an audio proto, using data and url processors as needed.
701
709
 
@@ -722,6 +730,9 @@ def marshall_audio(
722
730
  autoplay : bool
723
731
  Whether the audio should start playing automatically.
724
732
  Browsers will not autoplay audio files if the user has not interacted with the page yet.
733
+ form_id: str | None
734
+ The ID of the form that this element is placed in. Provide None if
735
+ the element is not placed in a form.
725
736
  """
726
737
 
727
738
  proto.start_time = start_time
@@ -739,10 +750,11 @@ def marshall_audio(
739
750
  _marshall_av_media(coordinates, proto, data, mimetype)
740
751
 
741
752
  if autoplay:
742
- ctx = get_script_run_ctx()
743
753
  proto.autoplay = autoplay
744
754
  proto.id = compute_and_register_element_id(
745
755
  "audio",
756
+ user_key=None,
757
+ form_id=form_id,
746
758
  url=proto.url,
747
759
  mimetype=mimetype,
748
760
  start_time=start_time,
@@ -750,5 +762,4 @@ def marshall_audio(
750
762
  end_time=end_time,
751
763
  loop=loop,
752
764
  autoplay=autoplay,
753
- page=ctx.active_script_hash if ctx else None,
754
765
  )
@@ -506,15 +506,13 @@ class PlotlyMixin:
506
506
  plotly_chart_proto.id = compute_and_register_element_id(
507
507
  "plotly_chart",
508
508
  user_key=key,
509
- key=key,
509
+ form_id=plotly_chart_proto.form_id,
510
510
  plotly_spec=plotly_chart_proto.spec,
511
511
  plotly_config=plotly_chart_proto.config,
512
512
  selection_mode=selection_mode,
513
513
  is_selection_activated=is_selection_activated,
514
514
  theme=theme,
515
- form_id=plotly_chart_proto.form_id,
516
515
  use_container_width=use_container_width,
517
- page=ctx.active_script_hash if ctx else None,
518
516
  )
519
517
 
520
518
  if is_selection_activated:
@@ -1894,7 +1894,7 @@ class VegaChartsMixin:
1894
1894
  vega_lite_proto.id = compute_and_register_element_id(
1895
1895
  "arrow_vega_lite_chart",
1896
1896
  user_key=key,
1897
- key=key,
1897
+ form_id=vega_lite_proto.form_id,
1898
1898
  vega_lite_spec=vega_lite_proto.spec,
1899
1899
  # The data is either in vega_lite_proto.data.data
1900
1900
  # or in a named dataset in vega_lite_proto.datasets
@@ -1905,8 +1905,6 @@ class VegaChartsMixin:
1905
1905
  theme=theme,
1906
1906
  use_container_width=use_container_width,
1907
1907
  selection_mode=parsed_selection_modes,
1908
- form_id=vega_lite_proto.form_id,
1909
- page=ctx.active_script_hash if ctx else None,
1910
1908
  )
1911
1909
 
1912
1910
  serde = VegaLiteStateSerde(parsed_selection_modes)
@@ -673,15 +673,15 @@ class ButtonMixin:
673
673
  element_id = compute_and_register_element_id(
674
674
  "download_button",
675
675
  user_key=key,
676
+ # download_button is not allowed to be used in a form.
677
+ form_id=None,
676
678
  label=label,
677
679
  icon=icon,
678
680
  file_name=file_name,
679
681
  mime=mime,
680
- key=key,
681
682
  help=help,
682
683
  type=type,
683
684
  use_container_width=use_container_width,
684
- page=ctx.active_script_hash if ctx else None,
685
685
  )
686
686
 
687
687
  if is_in_form(self.dg):
@@ -853,17 +853,19 @@ class ButtonMixin:
853
853
  enable_check_callback_rules=not is_form_submitter,
854
854
  )
855
855
 
856
+ # Only the form submitter button needs a form ID at the moment.
857
+ form_id = current_form_id(self.dg) if is_form_submitter else ""
856
858
  element_id = compute_and_register_element_id(
857
859
  "button",
858
860
  user_key=key,
861
+ # Only the
862
+ form_id=form_id,
859
863
  label=label,
860
864
  icon=icon,
861
- key=key,
862
865
  help=help,
863
866
  is_form_submitter=is_form_submitter,
864
867
  type=type,
865
868
  use_container_width=use_container_width,
866
- page=ctx.active_script_hash if ctx else None,
867
869
  )
868
870
 
869
871
  # It doesn't make sense to create a button inside a form (except
@@ -886,7 +888,7 @@ class ButtonMixin:
886
888
  button_proto.label = label
887
889
  button_proto.default = False
888
890
  button_proto.is_form_submitter = is_form_submitter
889
- button_proto.form_id = current_form_id(self.dg)
891
+ button_proto.form_id = form_id
890
892
  button_proto.type = type
891
893
  button_proto.use_container_width = use_container_width
892
894
  button_proto.disabled = disabled
@@ -399,12 +399,10 @@ class ButtonGroupMixin:
399
399
  element_id = compute_and_register_element_id(
400
400
  widget_name,
401
401
  user_key=key,
402
- key=key,
402
+ form_id=form_id,
403
403
  options=formatted_options,
404
404
  default=default,
405
- form_id=form_id,
406
405
  click_mode=click_mode,
407
- page=ctx.active_script_hash if ctx else None,
408
406
  )
409
407
 
410
408
  proto = _build_proto(
@@ -206,11 +206,9 @@ class CameraInputMixin:
206
206
  element_id = compute_and_register_element_id(
207
207
  "camera_input",
208
208
  user_key=key,
209
+ form_id=current_form_id(self.dg),
209
210
  label=label,
210
- key=key,
211
211
  help=help,
212
- form_id=current_form_id(self.dg),
213
- page=ctx.active_script_hash if ctx else None,
214
212
  )
215
213
 
216
214
  camera_input_proto = CameraInputProto()
@@ -330,10 +330,10 @@ class ChatMixin:
330
330
  element_id = compute_and_register_element_id(
331
331
  "chat_input",
332
332
  user_key=key,
333
- key=key,
333
+ # chat_input is not allowed to be used in a form.
334
+ form_id=None,
334
335
  placeholder=placeholder,
335
336
  max_chars=max_chars,
336
- page=ctx.active_script_hash if ctx else None,
337
337
  )
338
338
 
339
339
  # It doesn't make sense to create a chat input inside a form.
@@ -294,12 +294,10 @@ class CheckboxMixin:
294
294
  element_id = compute_and_register_element_id(
295
295
  "toggle" if type == CheckboxProto.StyleType.TOGGLE else "checkbox",
296
296
  user_key=key,
297
+ form_id=current_form_id(self.dg),
297
298
  label=label,
298
299
  value=bool(value),
299
- key=key,
300
300
  help=help,
301
- form_id=current_form_id(self.dg),
302
- page=ctx.active_script_hash if ctx else None,
303
301
  )
304
302
 
305
303
  checkbox_proto = CheckboxProto()
@@ -189,12 +189,10 @@ class ColorPickerMixin:
189
189
  element_id = compute_and_register_element_id(
190
190
  "color_picker",
191
191
  user_key=key,
192
+ form_id=current_form_id(self.dg),
192
193
  label=label,
193
194
  value=str(value),
194
- key=key,
195
195
  help=help,
196
- form_id=current_form_id(self.dg),
197
- page=ctx.active_script_hash if ctx else None,
198
196
  )
199
197
 
200
198
  # set value default
@@ -887,6 +887,7 @@ class DataEditorMixin:
887
887
  element_id = compute_and_register_element_id(
888
888
  "data_editor",
889
889
  user_key=key,
890
+ form_id=current_form_id(self.dg),
890
891
  data=arrow_bytes,
891
892
  width=width,
892
893
  height=height,
@@ -894,9 +895,6 @@ class DataEditorMixin:
894
895
  column_order=column_order,
895
896
  column_config_mapping=str(column_config_mapping),
896
897
  num_rows=num_rows,
897
- key=key,
898
- form_id=current_form_id(self.dg),
899
- page=ctx.active_script_hash if ctx else None,
900
898
  )
901
899
 
902
900
  proto = ArrowProto()
@@ -408,13 +408,11 @@ class FileUploaderMixin:
408
408
  element_id = compute_and_register_element_id(
409
409
  "file_uploader",
410
410
  user_key=key,
411
+ form_id=current_form_id(self.dg),
411
412
  label=label,
412
413
  type=type,
413
414
  accept_multiple_files=accept_multiple_files,
414
- key=key,
415
415
  help=help,
416
- form_id=current_form_id(self.dg),
417
- page=ctx.active_script_hash if ctx else None,
418
416
  )
419
417
 
420
418
  if type:
@@ -274,15 +274,13 @@ class MultiSelectMixin:
274
274
  element_id = compute_and_register_element_id(
275
275
  widget_name,
276
276
  user_key=key,
277
+ form_id=form_id,
277
278
  label=label,
278
279
  options=formatted_options,
279
280
  default=default_values,
280
- key=key,
281
281
  help=help,
282
282
  max_selections=max_selections,
283
283
  placeholder=placeholder,
284
- form_id=form_id,
285
- page=ctx.active_script_hash if ctx else None,
286
284
  )
287
285
 
288
286
  proto = MultiSelectProto()