Skip to content

Commit

Permalink
Merge pull request victoryhb#39 from fgdvir/fix_dynamic_item_add
Browse files Browse the repository at this point in the history
Fix dynamic item add
  • Loading branch information
victoryhb authored May 30, 2023
2 parents a4849da + 8f372cc commit b84a4ef
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 3 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ The `option_menu` function accepts the following parameters:
* "separator": the <hr> element separating the options
- manual_select: Pass to manually change the menu item selection.
The function returns the (string) option currently selected
- on_change: A callback that will happen when the selection changes. The callback function should accept one argument "key". You can use it to fetch the value of the menu (see [example 5](#examples))



### Manual Selection
This option was added to allow the user to manually move to a specific option in the menu. This could be useful when the user wants to move to another option automatically after finishing with one option (for example, if settings are approved, then move back to the main option).
Expand All @@ -44,7 +47,7 @@ To use this option, you need to pass the index of the desired option as `manual_


## Examples
```
```python
import streamlit as st
from streamlit_option_menu import option_menu

Expand Down Expand Up @@ -84,4 +87,14 @@ selected4 = option_menu(None, ["Home", "Upload", "Tasks", 'Settings'],
orientation="horizontal", manual_select=manual_select, key='menu_4')
st.button(f"Move to Next {st.session_state.get('menu_option',1)}", key='switch_button')
selected4

# 5. Add on_change callback
def on_change(key):
selection = st.session_state[key]
st.write(f"Selection changed to {selection}")

selected5 = option_menu(None, ["Home", "Upload", "Tasks", 'Settings'],
icons=['house', 'cloud-upload', "list-task", 'gear'],
on_change=on_change, key='menu_5', orientation="horizontal")
selected5
```
11 changes: 9 additions & 2 deletions streamlit_option_menu/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import streamlit as st
import streamlit.components.v1 as components

from streamlit_option_menu.streamlit_callback import register_callback
import os

_RELEASE = True
Expand Down Expand Up @@ -35,7 +35,7 @@


def option_menu(menu_title, options, default_index=0, menu_icon=None, icons=None, orientation="vertical",
styles=None, manual_select=None, key=None):
styles=None, manual_select=None, key=None, on_change=None):
"""_summary_
Args:
Expand All @@ -48,10 +48,17 @@ def option_menu(menu_title, options, default_index=0, menu_icon=None, icons=None
styles (_type_, optional): You can add a css style here. Defaults to None.
manual_select (_type_, optional): An index to select. If passed, will change current selection to the passsed.
key (_type_, optional): The component key. Defaults to None.
on_change (_type_, optional): A callback function to call when the selection changes. Defaults to None. The callback function must accept a single argument, which will be the key of the option menu. You can fetch current selection by calling st.session_state[key]
Returns:
str: The selected option
"""
if on_change is not None:
if key is None:
st.error("You must pass a key if you want to use the on_change callback for the option menu")
else:
register_callback(key, on_change, key)

if manual_select is not None and key is None:
default_index = manual_select

Expand Down
19 changes: 19 additions & 0 deletions streamlit_option_menu/frontend/src/MyComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ export default {
icons.value[i] = getFullIconName(icons.value[i]);
}
const selectedIndex = ref(props.args.defaultIndex)
const updateIcons = () => {
for (let i = 0; i < props.args.options.length; i++) {
if (!icons.value[i]) {
icons.value[i] = "bi-caret-right";
}
icons.value[i] = getFullIconName(icons.value[i]);
}
}
updateIcons()
const onClicked = (index, option) => {
selectedIndex.value = index
Streamlit.setComponentValue(option)
Expand Down Expand Up @@ -87,6 +98,14 @@ export default {
}
}
watch(
() => props.args.icons,
() => {
// reset icons array and then update it
icons.value = props.args.icons || []
updateIcons()
}
)
watch(
() => props.args.manualSelect,
Expand Down
37 changes: 37 additions & 0 deletions streamlit_option_menu/streamlit_callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from streamlit import session_state as _state
from streamlit.components.v1 import components as _components


def _patch_register_widget(register_widget):
def wrapper_register_widget(*args, **kwargs):
user_key = kwargs.get("user_key", None)
callbacks = _state.get("_components_callbacks", None)

# Check if a callback was registered for that user_key.
if user_key and callbacks and user_key in callbacks:
callback = callbacks[user_key]

# Add callback-specific args for the real register_widget function.
kwargs["on_change_handler"] = callback[0]
kwargs["args"] = callback[1]
kwargs["kwargs"] = callback[2]

# Call the original function with updated kwargs.
return register_widget(*args, **kwargs)

return wrapper_register_widget


# Patch function only once.
if not hasattr(_components.register_widget, "__callbacks_patched__"):
setattr(_components.register_widget, "__callbacks_patched__", True)
_components.register_widget = _patch_register_widget(_components.register_widget)


def register_callback(element_key, callback, *callback_args, **callback_kwargs):
# Initialize callbacks store.
if "_components_callbacks" not in _state:
_state._components_callbacks = {}

# Register a callback for a given element_key.
_state._components_callbacks[element_key] = (callback, callback_args, callback_kwargs)

0 comments on commit b84a4ef

Please sign in to comment.