Skip to content

Commit 95b5109

Browse files
committed
Add option to disable automatic anchor updates on user scroll
1 parent c497e2f commit 95b5109

File tree

4 files changed

+56
-17
lines changed

4 files changed

+56
-17
lines changed

example.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,23 @@
6969
orientation="horizontal",
7070
force_anchor=force_settings)
7171

72-
# 6. Retrieve active anchor
72+
# 6. Retrieve active anchor
7373
with st.sidebar:
7474
st.subheader("Example 6")
7575
active_anchor = scroll_navbar(
7676
anchor_ids,
7777
key="navbar6",
7878
orientation="vertical")
7979
st.write(f"{active_anchor} is active in Example 6")
80+
81+
# 7. Disable automatic active anchor updates
82+
with st.sidebar:
83+
st.subheader("Example 7")
84+
scroll_navbar(
85+
anchor_ids=anchor_ids,
86+
key="navbar7",
87+
orientation="vertical",
88+
auto_update_anchor=False)
8089

8190
# Dummy page setup
8291
for anchor_id in anchor_ids:

streamlit_scroll_navigation/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ def scroll_navbar(
6868
force_anchor: str = None,
6969
orientation: Literal['vertical', 'horizontal'] = 'vertical',
7070
override_styles: Dict[str, str] = {},
71+
auto_update_anchor: bool = True
7172
) -> str:
7273
"""
7374
Creates a scroll navigation bar component.
@@ -89,7 +90,11 @@ def scroll_navbar(
8990
Setting this will simulate clicking on an anchor. Defaults to None.
9091
orientation (Literal['vertical', 'horizontal'], optional):
9192
The orientation of the navigation bar. Defaults to 'vertical'.
92-
override_styles (Dict[str, str], optional): A dictionary of styles to override default styles. Defaults to {}.
93+
override_styles (Dict[str, str], optional):
94+
A dictionary of styles to override default styles. Defaults to {}.
95+
auto_update_anchor (bool, optional):
96+
If true, the highlighted anchor will automatically update to the next nearest anchor when the current one is scrolled out of view.
97+
Defaults to true.
9398
Returns:
9499
str: The ID of the anchor that is currently selected.
95100
Example:
@@ -99,6 +104,7 @@ def scroll_navbar(
99104
for anchor in anchor_ids:
100105
st.subheader(anchor,anchor=anchor)
101106
st.write(["content "]*100)
107+
102108
# Add a scroll navigation bar for anchors
103109
from screamlit_scroll_navigation import scroll_navbar
104110
with st.sidebar():
@@ -115,5 +121,6 @@ def scroll_navbar(
115121
force_anchor=force_anchor,
116122
orientation=orientation,
117123
override_styles=override_styles,
124+
auto_update_anchor=auto_update_anchor
118125
)
119126
return component_value

streamlit_scroll_navigation/frontend/public/CrossOriginInterface.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@ class CrossOriginInterface {
1515
this.anchorVisibleStates = {};
1616
this.activeAnchorId = null;
1717
this.component = null;
18+
this.autoUpdateAnchor = false;
1819
this.key = key;
1920
window.addEventListener("message", this.handleMessage.bind(this));
2021
}
21-
22+
23+
register(component, autoUpdateAnchor) {
24+
this.component = component;
25+
this.autoUpdateAnchor = autoUpdateAnchor;
26+
console.debug('Registered component', component, autoUpdateAnchor);
27+
}
2228
scroll(anchorId) {
2329
const element = document.getElementById(anchorId);
2430
console.debug('Scrolling to', anchorId);
@@ -162,7 +168,8 @@ class CrossOriginInterface {
162168
//If component is not registered, only allow registration method
163169
if (this.component === null) {
164170
if (COI_method === 'register') {
165-
this.component = event.source;
171+
const {auto_update_anchor} = event.data;
172+
this.register(event.source, auto_update_anchor);
166173
}
167174
else {
168175
console.error('Must register component with this CrossOriginInterface before calling other methods');

streamlit_scroll_navigation/frontend/src/ScrollNavigationBar.tsx

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@ class ScrollNavigationBar extends StreamlitComponentBase<State> {
1717
public state = { activeAnchorId: "" };
1818

1919
// Send message to COI
20-
postMessage(COI_method: string, data?: { anchor_id?: string; anchor_ids?: string[] }) {
20+
postMessage(COI_method: string,
21+
data?: {
22+
anchor_id?: string;
23+
anchor_ids?:string[];
24+
auto_update_anchor?: boolean
25+
}): void {
2126
const { key } = this.props.args;
2227
if (key == null || typeof key !== "string") {
2328
throw new Error("Invalid key: key must be a string.");
@@ -27,21 +32,23 @@ class ScrollNavigationBar extends StreamlitComponentBase<State> {
2732
window.parent.postMessage({ COI_method, key, anchor_id, anchor_ids }, "*");
2833
}
2934

35+
postRegister(auto_update_anchor:boolean): void {
36+
this.postMessage("register", { auto_update_anchor } );
37+
console.debug("postRegister");
38+
}
3039
postScroll(anchor_id: string): void {
3140
this.postMessage("scroll", { anchor_id });
3241
console.debug("postScroll", anchor_id);
3342
}
34-
postRegister(): void {
35-
this.postMessage("register");
36-
console.debug("postRegister");
37-
}
3843
postTrackAnchors(anchor_ids: string[]): void {
3944
this.postMessage("trackAnchors", { anchor_ids });
4045
console.debug("postTrackAnchors", anchor_ids);
4146
}
4247
postUpdateActiveAnchor(anchor_id: string): void {
43-
this.postMessage("updateActiveAnchor", { anchor_id });
44-
console.debug("postUpdateActiveAnchor", anchor_id);
48+
const { auto_update_anchor } = this.getCleanedArgs();
49+
if (auto_update_anchor)
50+
this.postMessage("updateActiveAnchor", { anchor_id });
51+
console.debug("postUpdateActiveAnchor", anchor_id, "; autoupdate", auto_update_anchor);
4552
}
4653

4754
// Handle menu item click
@@ -58,11 +65,11 @@ class ScrollNavigationBar extends StreamlitComponentBase<State> {
5865
};
5966

6067
public componentDidMount(): void {
61-
const { anchor_ids } = this.getCleanedArgs();
68+
const { anchor_ids, auto_update_anchor } = this.getCleanedArgs();
6269
const initialAnchorId = anchor_ids[0];
6370

6471
// Register component
65-
this.postRegister();
72+
this.postRegister(auto_update_anchor);
6673
// Tell COI to track anchors for visibility
6774
this.postTrackAnchors(anchor_ids);
6875
// Set initial active anchor for component and COI
@@ -114,7 +121,7 @@ class ScrollNavigationBar extends StreamlitComponentBase<State> {
114121
}
115122

116123
private getCleanedArgs() {
117-
let { key, anchor_ids, anchor_labels, anchor_icons, force_anchor, orientation, override_styles } = this.props.args;
124+
let { key, anchor_ids, anchor_labels, anchor_icons, force_anchor, orientation, override_styles, auto_update_anchor} = this.props.args;
118125
//key is required
119126
if (key == null || typeof key !== "string") {
120127
throw new Error("Invalid key: key must be a string.");
@@ -175,7 +182,18 @@ class ScrollNavigationBar extends StreamlitComponentBase<State> {
175182
}
176183
}
177184

178-
return { anchor_ids, anchor_labels, anchor_icons, force_anchor, key, orientation, override_styles };
185+
//auto_update_active is an optional boolean
186+
//If not provided, default to true
187+
//If provided, it must be a boolean
188+
if (auto_update_anchor == null) {
189+
auto_update_anchor = true;
190+
} else {
191+
if (typeof auto_update_anchor !== "boolean") {
192+
throw new Error("Invalid auto_update_anchor: auto_update_anchor must be a boolean.");
193+
}
194+
}
195+
196+
return { anchor_ids, anchor_labels, anchor_icons, force_anchor, key, orientation, override_styles, auto_update_anchor };
179197
}
180198

181199
static getBiName(icon: string) {
@@ -228,8 +246,6 @@ class ScrollNavigationBar extends StreamlitComponentBase<State> {
228246
e.currentTarget.style.color = newStyle.color || "";
229247
}}
230248
>
231-
{/* Render Bootstrap icon if provided */}
232-
233249
<span>
234250
{anchor_icons && anchor_icons[index] && (
235251
<i className={`${ScrollNavigationBar.getBiName(anchor_icons[index])}`}

0 commit comments

Comments
 (0)