reflex 0.4.1a1__py3-none-any.whl → 0.4.2a1__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.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (44) hide show
  1. reflex/.templates/jinja/web/pages/_app.js.jinja2 +2 -2
  2. reflex/.templates/jinja/web/utils/context.js.jinja2 +9 -2
  3. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +5 -3
  4. reflex/.templates/web/utils/state.js +35 -8
  5. reflex/app.py +5 -4
  6. reflex/base.py +4 -0
  7. reflex/compiler/compiler.py +14 -4
  8. reflex/compiler/templates.py +1 -0
  9. reflex/components/base/bare.pyi +84 -0
  10. reflex/components/component.py +23 -4
  11. reflex/components/core/cond.py +2 -2
  12. reflex/components/core/debounce.py +1 -1
  13. reflex/components/core/upload.py +2 -2
  14. reflex/components/core/upload.pyi +2 -2
  15. reflex/components/radix/primitives/accordion.py +5 -2
  16. reflex/components/radix/primitives/accordion.pyi +1 -1
  17. reflex/components/radix/primitives/progress.py +40 -8
  18. reflex/components/radix/primitives/progress.pyi +71 -2
  19. reflex/components/radix/themes/base.py +9 -2
  20. reflex/components/radix/themes/base.pyi +3 -1
  21. reflex/components/radix/themes/color_mode.py +1 -1
  22. reflex/components/radix/themes/layout/stack.py +6 -5
  23. reflex/components/radix/themes/layout/stack.pyi +6 -5
  24. reflex/constants/base.pyi +94 -0
  25. reflex/constants/compiler.py +2 -0
  26. reflex/constants/event.pyi +59 -0
  27. reflex/constants/installer.py +2 -2
  28. reflex/constants/route.pyi +50 -0
  29. reflex/constants/style.pyi +20 -0
  30. reflex/event.py +10 -0
  31. reflex/middleware/hydrate_middleware.py +0 -8
  32. reflex/state.py +194 -13
  33. reflex/testing.py +1 -0
  34. reflex/utils/prerequisites.py +31 -4
  35. reflex/utils/processes.py +12 -1
  36. reflex/utils/serializers.py +18 -2
  37. reflex/utils/types.py +6 -1
  38. reflex/vars.py +20 -1
  39. reflex/vars.pyi +2 -0
  40. {reflex-0.4.1a1.dist-info → reflex-0.4.2a1.dist-info}/METADATA +2 -1
  41. {reflex-0.4.1a1.dist-info → reflex-0.4.2a1.dist-info}/RECORD +44 -39
  42. {reflex-0.4.1a1.dist-info → reflex-0.4.2a1.dist-info}/WHEEL +1 -1
  43. {reflex-0.4.1a1.dist-info → reflex-0.4.2a1.dist-info}/LICENSE +0 -0
  44. {reflex-0.4.1a1.dist-info → reflex-0.4.2a1.dist-info}/entry_points.txt +0 -0
@@ -5,7 +5,7 @@ import '/styles/styles.css'
5
5
  {% endblock %}
6
6
 
7
7
  {% block declaration %}
8
- import { EventLoopProvider, StateProvider } from "/utils/context.js";
8
+ import { EventLoopProvider, StateProvider, defaultColorMode } from "/utils/context.js";
9
9
  import { ThemeProvider } from 'next-themes'
10
10
 
11
11
 
@@ -28,7 +28,7 @@ function AppWrap({children}) {
28
28
 
29
29
  export default function MyApp({ Component, pageProps }) {
30
30
  return (
31
- <ThemeProvider defaultTheme="light" storageKey="chakra-ui-color-mode" attribute="class">
31
+ <ThemeProvider defaultTheme={ defaultColorMode } storageKey="chakra-ui-color-mode" attribute="class">
32
32
  <AppWrap>
33
33
  <StateProvider>
34
34
  <EventLoopProvider>
@@ -7,6 +7,7 @@ export const initialState = {{ initial_state|json_dumps }}
7
7
  export const initialState = {}
8
8
  {% endif %}
9
9
 
10
+ export const defaultColorMode = "{{ default_color_mode }}"
10
11
  export const ColorModeContext = createContext(null);
11
12
  export const UploadFilesContext = createContext(null);
12
13
  export const DispatchContext = createContext(null);
@@ -23,13 +24,19 @@ export const clientStorage = {}
23
24
  {% endif %}
24
25
 
25
26
  {% if state_name %}
26
- export const onLoadInternalEvent = () => [Event('{{state_name}}.{{const.on_load_internal}}')]
27
+ export const state_name = "{{state_name}}"
28
+ export const onLoadInternalEvent = () => [
29
+ Event('{{state_name}}.{{const.update_vars_internal}}', {vars: hydrateClientStorage(clientStorage)}),
30
+ Event('{{state_name}}.{{const.on_load_internal}}')
31
+ ]
27
32
 
28
33
  export const initialEvents = () => [
29
- Event('{{state_name}}.{{const.hydrate}}', hydrateClientStorage(clientStorage)),
34
+ Event('{{state_name}}.{{const.hydrate}}'),
30
35
  ...onLoadInternalEvent()
31
36
  ]
32
37
  {% else %}
38
+ export const state_name = undefined
39
+
33
40
  export const onLoadInternalEvent = () => []
34
41
 
35
42
  export const initialEvents = () => []
@@ -1,13 +1,15 @@
1
1
  import { useTheme } from "next-themes"
2
2
  import { useEffect, useState } from "react"
3
- import { ColorModeContext } from "/utils/context.js"
3
+ import { ColorModeContext, defaultColorMode } from "/utils/context.js"
4
4
 
5
5
 
6
6
  export default function RadixThemesColorModeProvider({ children }) {
7
7
  const {theme, setTheme} = useTheme()
8
- const [colorMode, setColorMode] = useState("light")
8
+ const [colorMode, setColorMode] = useState(defaultColorMode)
9
9
 
10
- useEffect(() => setColorMode(theme), [theme])
10
+ useEffect(() => {
11
+ setColorMode(theme)
12
+ }, [theme])
11
13
 
12
14
  const toggleColorMode = () => {
13
15
  setTheme(theme === "light" ? "dark" : "light")
@@ -6,7 +6,7 @@ import env from "/env.json";
6
6
  import Cookies from "universal-cookie";
7
7
  import { useEffect, useReducer, useRef, useState } from "react";
8
8
  import Router, { useRouter } from "next/router";
9
- import { initialEvents, initialState, onLoadInternalEvent } from "utils/context.js"
9
+ import { initialEvents, initialState, onLoadInternalEvent, state_name } from "utils/context.js"
10
10
 
11
11
  // Endpoint URLs.
12
12
  const EVENTURL = env.EVENT
@@ -40,7 +40,7 @@ const upload_controllers = {};
40
40
  * Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
41
41
  * @returns A UUID.
42
42
  */
43
- const generateUUID = () => {
43
+ export const generateUUID = () => {
44
44
  let d = new Date().getTime(),
45
45
  d2 = (performance && performance.now && performance.now() * 1000) || 0;
46
46
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
@@ -441,17 +441,14 @@ export const Event = (name, payload = {}, handler = null) => {
441
441
  * @returns payload dict of client storage values
442
442
  */
443
443
  export const hydrateClientStorage = (client_storage) => {
444
- const client_storage_values = {
445
- "cookies": {},
446
- "local_storage": {}
447
- }
444
+ const client_storage_values = {}
448
445
  if (client_storage.cookies) {
449
446
  for (const state_key in client_storage.cookies) {
450
447
  const cookie_options = client_storage.cookies[state_key]
451
448
  const cookie_name = cookie_options.name || state_key
452
449
  const cookie_value = cookies.get(cookie_name)
453
450
  if (cookie_value !== undefined) {
454
- client_storage_values.cookies[state_key] = cookies.get(cookie_name)
451
+ client_storage_values[state_key] = cookies.get(cookie_name)
455
452
  }
456
453
  }
457
454
  }
@@ -460,7 +457,7 @@ export const hydrateClientStorage = (client_storage) => {
460
457
  const options = client_storage.local_storage[state_key]
461
458
  const local_storage_value = localStorage.getItem(options.name || state_key)
462
459
  if (local_storage_value !== null) {
463
- client_storage_values.local_storage[state_key] = local_storage_value
460
+ client_storage_values[state_key] = local_storage_value
464
461
  }
465
462
  }
466
463
  }
@@ -568,6 +565,36 @@ export const useEventLoop = (
568
565
  }
569
566
  })
570
567
 
568
+
569
+ // localStorage event handling
570
+ useEffect(() => {
571
+ const storage_to_state_map = {};
572
+
573
+ if (client_storage.local_storage && typeof window !== "undefined") {
574
+ for (const state_key in client_storage.local_storage) {
575
+ const options = client_storage.local_storage[state_key];
576
+ if (options.sync) {
577
+ const local_storage_value_key = options.name || state_key;
578
+ storage_to_state_map[local_storage_value_key] = state_key;
579
+ }
580
+ }
581
+ }
582
+
583
+ // e is StorageEvent
584
+ const handleStorage = (e) => {
585
+ if (storage_to_state_map[e.key]) {
586
+ const vars = {}
587
+ vars[storage_to_state_map[e.key]] = e.newValue
588
+ const event = Event(`${state_name}.update_vars_internal`, {vars: vars})
589
+ addEvents([event], e);
590
+ }
591
+ };
592
+
593
+ window.addEventListener("storage", handleStorage);
594
+ return () => window.removeEventListener("storage", handleStorage);
595
+ });
596
+
597
+
571
598
  // Route after the initial page hydration.
572
599
  useEffect(() => {
573
600
  const change_complete = () => addEvents(onLoadInternalEvent())
reflex/app.py CHANGED
@@ -785,7 +785,7 @@ class App(Base):
785
785
  submit_work(compiler.compile_theme, style=self.style)
786
786
 
787
787
  # Compile the contexts.
788
- submit_work(compiler.compile_contexts, self.state)
788
+ submit_work(compiler.compile_contexts, self.state, self.theme)
789
789
 
790
790
  # Compile the Tailwind config.
791
791
  if config.tailwind is not None:
@@ -926,7 +926,7 @@ async def process(
926
926
  }
927
927
  )
928
928
  # Get the state for the session exclusively.
929
- async with app.state_manager.modify_state(event.token) as state:
929
+ async with app.state_manager.modify_state(event.substate_token) as state:
930
930
  # re-assign only when the value is different
931
931
  if state.router_data != router_data:
932
932
  # assignment will recurse into substates and force recalculation of
@@ -1002,7 +1002,8 @@ def upload(app: App):
1002
1002
  )
1003
1003
 
1004
1004
  # Get the state for the session.
1005
- state = await app.state_manager.get_state(token)
1005
+ substate_token = token + "_" + handler.rpartition(".")[0]
1006
+ state = await app.state_manager.get_state(substate_token)
1006
1007
 
1007
1008
  # get the current session ID
1008
1009
  # get the current state(parent state/substate)
@@ -1049,7 +1050,7 @@ def upload(app: App):
1049
1050
  Each state update as JSON followed by a new line.
1050
1051
  """
1051
1052
  # Process the event.
1052
- async with app.state_manager.modify_state(token) as state:
1053
+ async with app.state_manager.modify_state(event.substate_token) as state:
1053
1054
  async for update in state._process(event):
1054
1055
  # Postprocess the event.
1055
1056
  update = await app.postprocess(state, event, update)
reflex/base.py CHANGED
@@ -115,6 +115,10 @@ class Base(pydantic.BaseModel):
115
115
  Returns:
116
116
  The value of the field.
117
117
  """
118
+ if isinstance(key, str) and key in self.__fields__:
119
+ # Seems like this function signature was wrong all along?
120
+ # If the user wants a field that we know of, get it and pass it off to _get_value
121
+ key = getattr(self, key)
118
122
  return self._get_value(
119
123
  key,
120
124
  to_dict=True,
@@ -16,6 +16,7 @@ from reflex.components.component import (
16
16
  )
17
17
  from reflex.config import get_config
18
18
  from reflex.state import BaseState
19
+ from reflex.style import LIGHT_COLOR_MODE
19
20
  from reflex.utils.imports import ImportVar
20
21
 
21
22
 
@@ -67,11 +68,12 @@ def _is_dev_mode() -> bool:
67
68
  return os.environ.get("REFLEX_ENV_MODE", "dev") == "dev"
68
69
 
69
70
 
70
- def _compile_contexts(state: Optional[Type[BaseState]]) -> str:
71
+ def _compile_contexts(state: Optional[Type[BaseState]], theme: Component) -> str:
71
72
  """Compile the initial state and contexts.
72
73
 
73
74
  Args:
74
75
  state: The app state.
76
+ theme: The top-level app theme.
75
77
 
76
78
  Returns:
77
79
  The compiled context file.
@@ -82,9 +84,13 @@ def _compile_contexts(state: Optional[Type[BaseState]]) -> str:
82
84
  state_name=state.get_name(),
83
85
  client_storage=utils.compile_client_storage(state),
84
86
  is_dev_mode=_is_dev_mode(),
87
+ default_color_mode=getattr(theme, "appearance", LIGHT_COLOR_MODE),
85
88
  )
86
89
  if state
87
- else templates.CONTEXT.render(is_dev_mode=_is_dev_mode())
90
+ else templates.CONTEXT.render(
91
+ is_dev_mode=_is_dev_mode(),
92
+ default_color_mode=getattr(theme, "appearance", LIGHT_COLOR_MODE),
93
+ )
88
94
  )
89
95
 
90
96
 
@@ -345,11 +351,15 @@ def compile_theme(style: ComponentStyle) -> tuple[str, str]:
345
351
  return output_path, code
346
352
 
347
353
 
348
- def compile_contexts(state: Optional[Type[BaseState]]) -> tuple[str, str]:
354
+ def compile_contexts(
355
+ state: Optional[Type[BaseState]],
356
+ theme: Component,
357
+ ) -> tuple[str, str]:
349
358
  """Compile the initial state / context.
350
359
 
351
360
  Args:
352
361
  state: The app state.
362
+ theme: The top-level app theme.
353
363
 
354
364
  Returns:
355
365
  The path and code of the compiled context.
@@ -357,7 +367,7 @@ def compile_contexts(state: Optional[Type[BaseState]]) -> tuple[str, str]:
357
367
  # Get the path for the output file.
358
368
  output_path = utils.get_context_path()
359
369
 
360
- return output_path, _compile_contexts(state)
370
+ return output_path, _compile_contexts(state, theme)
361
371
 
362
372
 
363
373
  def compile_page(
@@ -41,6 +41,7 @@ class ReflexJinjaEnvironment(Environment):
41
41
  "use_color_mode": constants.ColorMode.USE,
42
42
  "hydrate": constants.CompileVars.HYDRATE,
43
43
  "on_load_internal": constants.CompileVars.ON_LOAD_INTERNAL,
44
+ "update_vars_internal": constants.CompileVars.UPDATE_VARS_INTERNAL,
44
45
  }
45
46
 
46
47
 
@@ -0,0 +1,84 @@
1
+ """Stub file for reflex/components/base/bare.py"""
2
+ # ------------------- DO NOT EDIT ----------------------
3
+ # This file was generated by `scripts/pyi_generator.py`!
4
+ # ------------------------------------------------------
5
+
6
+ from typing import Any, Dict, Literal, Optional, Union, overload
7
+ from reflex.vars import Var, BaseVar, ComputedVar
8
+ from reflex.event import EventChain, EventHandler, EventSpec
9
+ from reflex.style import Style
10
+ from typing import Any, Iterator
11
+ from reflex.components.component import Component
12
+ from reflex.components.tags import Tag
13
+ from reflex.components.tags.tagless import Tagless
14
+ from reflex.vars import Var
15
+
16
+ class Bare(Component):
17
+ @overload
18
+ @classmethod
19
+ def create( # type: ignore
20
+ cls,
21
+ *children,
22
+ contents: Optional[Union[Var[str], str]] = None,
23
+ style: Optional[Style] = None,
24
+ key: Optional[Any] = None,
25
+ id: Optional[Any] = None,
26
+ class_name: Optional[Any] = None,
27
+ autofocus: Optional[bool] = None,
28
+ custom_attrs: Optional[Dict[str, Union[Var, str]]] = None,
29
+ on_blur: Optional[
30
+ Union[EventHandler, EventSpec, list, function, BaseVar]
31
+ ] = None,
32
+ on_click: Optional[
33
+ Union[EventHandler, EventSpec, list, function, BaseVar]
34
+ ] = None,
35
+ on_context_menu: Optional[
36
+ Union[EventHandler, EventSpec, list, function, BaseVar]
37
+ ] = None,
38
+ on_double_click: Optional[
39
+ Union[EventHandler, EventSpec, list, function, BaseVar]
40
+ ] = None,
41
+ on_focus: Optional[
42
+ Union[EventHandler, EventSpec, list, function, BaseVar]
43
+ ] = None,
44
+ on_mount: Optional[
45
+ Union[EventHandler, EventSpec, list, function, BaseVar]
46
+ ] = None,
47
+ on_mouse_down: Optional[
48
+ Union[EventHandler, EventSpec, list, function, BaseVar]
49
+ ] = None,
50
+ on_mouse_enter: Optional[
51
+ Union[EventHandler, EventSpec, list, function, BaseVar]
52
+ ] = None,
53
+ on_mouse_leave: Optional[
54
+ Union[EventHandler, EventSpec, list, function, BaseVar]
55
+ ] = None,
56
+ on_mouse_move: Optional[
57
+ Union[EventHandler, EventSpec, list, function, BaseVar]
58
+ ] = None,
59
+ on_mouse_out: Optional[
60
+ Union[EventHandler, EventSpec, list, function, BaseVar]
61
+ ] = None,
62
+ on_mouse_over: Optional[
63
+ Union[EventHandler, EventSpec, list, function, BaseVar]
64
+ ] = None,
65
+ on_mouse_up: Optional[
66
+ Union[EventHandler, EventSpec, list, function, BaseVar]
67
+ ] = None,
68
+ on_scroll: Optional[
69
+ Union[EventHandler, EventSpec, list, function, BaseVar]
70
+ ] = None,
71
+ on_unmount: Optional[
72
+ Union[EventHandler, EventSpec, list, function, BaseVar]
73
+ ] = None,
74
+ **props
75
+ ) -> "Bare":
76
+ """Create a Bare component, with no tag.
77
+
78
+ Args:
79
+ contents: The contents of the component.
80
+
81
+ Returns:
82
+ The component.
83
+ """
84
+ ...
@@ -502,6 +502,14 @@ class Component(BaseComponent, ABC):
502
502
  if isinstance(child, Component):
503
503
  child.apply_theme(theme)
504
504
 
505
+ def _exclude_props(self) -> list[str]:
506
+ """Props to exclude when adding the component props to the Tag.
507
+
508
+ Returns:
509
+ A list of component props to exclude.
510
+ """
511
+ return []
512
+
505
513
  def _render(self, props: dict[str, Any] | None = None) -> Tag:
506
514
  """Define how to render the component in React.
507
515
 
@@ -544,6 +552,10 @@ class Component(BaseComponent, ABC):
544
552
  props.update(self._get_style())
545
553
  props.update(self.custom_attrs)
546
554
 
555
+ # remove excluded props from prop dict before adding to tag.
556
+ for prop_to_exclude in self._exclude_props():
557
+ props.pop(prop_to_exclude, None)
558
+
547
559
  return tag.add_props(**props)
548
560
 
549
561
  @classmethod
@@ -722,6 +734,11 @@ class Component(BaseComponent, ABC):
722
734
  children: The children of the component.
723
735
 
724
736
  """
737
+ from reflex.components.base.fragment import Fragment
738
+ from reflex.components.core.cond import Cond
739
+ from reflex.components.core.foreach import Foreach
740
+ from reflex.components.core.match import Match
741
+
725
742
  no_valid_parents_defined = all(child._valid_parents == [] for child in children)
726
743
  if (
727
744
  not self._invalid_children
@@ -731,21 +748,23 @@ class Component(BaseComponent, ABC):
731
748
  return
732
749
 
733
750
  comp_name = type(self).__name__
734
- allowed_components = ["Fragment", "Foreach", "Cond", "Match"]
751
+ allowed_components = [
752
+ comp.__name__ for comp in (Fragment, Foreach, Cond, Match)
753
+ ]
735
754
 
736
755
  def validate_child(child):
737
756
  child_name = type(child).__name__
738
757
 
739
758
  # Iterate through the immediate children of fragment
740
- if child_name == "Fragment":
759
+ if isinstance(child, Fragment):
741
760
  for c in child.children:
742
761
  validate_child(c)
743
762
 
744
- if child_name == "Cond":
763
+ if isinstance(child, Cond):
745
764
  validate_child(child.comp1)
746
765
  validate_child(child.comp2)
747
766
 
748
- if child_name == "Match":
767
+ if isinstance(child, Match):
749
768
  for cases in child.match_cases:
750
769
  validate_child(cases[-1])
751
770
  validate_child(child.default)
@@ -24,10 +24,10 @@ class Cond(MemoizationLeaf):
24
24
  cond: Var[Any]
25
25
 
26
26
  # The component to render if the cond is true.
27
- comp1: BaseComponent = Fragment.create()
27
+ comp1: BaseComponent = None # type: ignore
28
28
 
29
29
  # The component to render if the cond is false.
30
- comp2: BaseComponent = Fragment.create()
30
+ comp2: BaseComponent = None # type: ignore
31
31
 
32
32
  @classmethod
33
33
  def create(
@@ -95,7 +95,7 @@ class DebounceInput(Component):
95
95
  if child.class_name is not None:
96
96
  props["class_name"] = f"{props.get('class_name', '')} {child.class_name}"
97
97
  child_ref = child.get_ref()
98
- if not props.get("input_ref") and child_ref:
98
+ if props.get("input_ref") is None and child_ref:
99
99
  props["input_ref"] = Var.create_safe(child_ref, _var_is_local=False)
100
100
  props["id"] = child.id
101
101
 
@@ -8,7 +8,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Union
8
8
  from reflex import constants
9
9
  from reflex.components.chakra.forms.input import Input
10
10
  from reflex.components.chakra.layout.box import Box
11
- from reflex.components.component import Component
11
+ from reflex.components.component import Component, MemoizationLeaf
12
12
  from reflex.constants import Dirs
13
13
  from reflex.event import CallableEventSpec, EventChain, EventSpec, call_script
14
14
  from reflex.utils import imports
@@ -138,7 +138,7 @@ class UploadFilesProvider(Component):
138
138
  tag = "UploadFilesProvider"
139
139
 
140
140
 
141
- class Upload(Component):
141
+ class Upload(MemoizationLeaf):
142
142
  """A file upload component."""
143
143
 
144
144
  library = "react-dropzone@14.2.3"
@@ -13,7 +13,7 @@ from typing import Any, ClassVar, Dict, List, Optional, Union
13
13
  from reflex import constants
14
14
  from reflex.components.chakra.forms.input import Input
15
15
  from reflex.components.chakra.layout.box import Box
16
- from reflex.components.component import Component
16
+ from reflex.components.component import Component, MemoizationLeaf
17
17
  from reflex.constants import Dirs
18
18
  from reflex.event import CallableEventSpec, EventChain, EventSpec, call_script
19
19
  from reflex.utils import imports
@@ -114,7 +114,7 @@ class UploadFilesProvider(Component):
114
114
  """
115
115
  ...
116
116
 
117
- class Upload(Component):
117
+ class Upload(MemoizationLeaf):
118
118
  is_used: ClassVar[bool] = False
119
119
 
120
120
  @overload
@@ -15,7 +15,7 @@ from reflex.style import (
15
15
  format_as_emotion,
16
16
  )
17
17
  from reflex.utils import imports
18
- from reflex.vars import BaseVar, Var, VarData, get_unique_variable_name
18
+ from reflex.vars import BaseVar, Var, VarData, get_uuid_string_var
19
19
 
20
20
  LiteralAccordionType = Literal["single", "multiple"]
21
21
  LiteralAccordionDir = Literal["ltr", "rtl"]
@@ -463,6 +463,9 @@ to {
463
463
  `
464
464
  """
465
465
 
466
+ def _exclude_props(self) -> list[str]:
467
+ return ["color_scheme", "variant"]
468
+
466
469
 
467
470
  class AccordionItem(AccordionComponent):
468
471
  """An accordion component."""
@@ -512,7 +515,7 @@ class AccordionItem(AccordionComponent):
512
515
  The accordion item.
513
516
  """
514
517
  # The item requires a value to toggle (use a random unique name if not provided).
515
- value = props.pop("value", get_unique_variable_name())
518
+ value = props.pop("value", get_uuid_string_var())
516
519
 
517
520
  if "AccordionItem" not in (
518
521
  cls_name := props.pop("class_name", "AccordionItem")
@@ -19,7 +19,7 @@ from reflex.style import (
19
19
  format_as_emotion,
20
20
  )
21
21
  from reflex.utils import imports
22
- from reflex.vars import BaseVar, Var, VarData, get_unique_variable_name
22
+ from reflex.vars import BaseVar, Var, VarData, get_uuid_string_var
23
23
 
24
24
  LiteralAccordionType = Literal["single", "multiple"]
25
25
  LiteralAccordionDir = Literal["ltr", "rtl"]
@@ -2,11 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Optional
5
+ from typing import Optional, Union
6
6
 
7
7
  from reflex.components.component import Component, ComponentNamespace
8
+ from reflex.components.core.colors import color
8
9
  from reflex.components.radix.primitives.accordion import DEFAULT_ANIMATION_DURATION
9
10
  from reflex.components.radix.primitives.base import RadixPrimitiveComponentWithClassName
11
+ from reflex.components.radix.themes.base import LiteralAccentColor
10
12
  from reflex.style import Style
11
13
  from reflex.vars import Var
12
14
 
@@ -57,10 +59,26 @@ class ProgressIndicator(ProgressComponent):
57
59
  # The maximum progress value.
58
60
  max: Var[Optional[int]]
59
61
 
62
+ # The color scheme of the progress indicator.
63
+ color_scheme: Var[LiteralAccentColor]
64
+
60
65
  def _apply_theme(self, theme: Component):
66
+ global_color_scheme = getattr(theme, "accent_color", None)
67
+
68
+ if global_color_scheme is None and self.color_scheme is None:
69
+ raise ValueError(
70
+ "`color_scheme` cannot be None. Either set the `color_scheme` prop on the progress indicator "
71
+ "component or set the `accent_color` prop in your global theme."
72
+ )
73
+
74
+ color_scheme = color(
75
+ self.color_scheme if self.color_scheme is not None else global_color_scheme, # type: ignore
76
+ 9,
77
+ )
78
+
61
79
  self.style = Style(
62
80
  {
63
- "background-color": "var(--accent-9)",
81
+ "background-color": color_scheme,
64
82
  "width": "100%",
65
83
  "height": "100%",
66
84
  f"transition": f"transform {DEFAULT_ANIMATION_DURATION}ms linear",
@@ -72,20 +90,28 @@ class ProgressIndicator(ProgressComponent):
72
90
  }
73
91
  )
74
92
 
93
+ def _exclude_props(self) -> list[str]:
94
+ return ["color_scheme"]
95
+
75
96
 
76
97
  class Progress(ComponentNamespace):
77
- """High level API for progress bar."""
98
+ """High-level API for progress bar."""
78
99
 
79
100
  root = staticmethod(ProgressRoot.create)
80
101
  indicator = staticmethod(ProgressIndicator.create)
81
102
 
82
103
  @staticmethod
83
- def __call__(width: Optional[str] = "100%", **props) -> Component:
84
- """High level API for progress bar.
104
+ def __call__(
105
+ width: Optional[str] = "100%",
106
+ color_scheme: Optional[Union[Var, LiteralAccentColor]] = None,
107
+ **props,
108
+ ) -> Component:
109
+ """High-level API for progress bar.
85
110
 
86
111
  Args:
87
- width: The width of the progerss bar
88
- **props: The props of the progress bar
112
+ width: The width of the progress bar.
113
+ **props: The props of the progress bar.
114
+ color_scheme: The color scheme of the progress indicator.
89
115
 
90
116
  Returns:
91
117
  The progress bar.
@@ -93,9 +119,15 @@ class Progress(ComponentNamespace):
93
119
  style = props.setdefault("style", {})
94
120
  style.update({"width": width})
95
121
 
122
+ progress_indicator_props = (
123
+ {"color_scheme": color_scheme} if color_scheme is not None else {}
124
+ )
125
+
96
126
  return ProgressRoot.create(
97
127
  ProgressIndicator.create(
98
- value=props.get("value"), max=props.get("max", 100)
128
+ value=props.get("value"),
129
+ max=props.get("max", 100),
130
+ **progress_indicator_props, # type: ignore
99
131
  ),
100
132
  **props,
101
133
  )