Skip to content

Commit

Permalink
reset function callback; add hover effect to icons;
Browse files Browse the repository at this point in the history
  • Loading branch information
m-wrzr committed Sep 1, 2024
1 parent c28d48c commit c6934e9
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 51 deletions.
6 changes: 6 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ def search_kwargs(searchterm: str, **kwargs) -> List[str]:
a=1,
b=2,
),
dict(
search_function=search,
reset_function=lambda: print("reset function called"),
key=f"{search.__name__}_reset_function",
label=f"{search.__name__}_reset_function",
),
dict(
search_function=search,
default=None,
Expand Down
6 changes: 6 additions & 0 deletions streamlit_searchbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def st_searchbox(
style_overrides: StyleOverrides | None = None,
debounce: int = 150,
min_execution_time: int = MIN_EXECUTION_TIME_DEFAULT,
reset_function: Callable[[], None] = None,
key: str = "searchbox",
rerun_scope: Literal["app", "fragment"] = "app",
**kwargs,
Expand Down Expand Up @@ -243,6 +244,8 @@ def st_searchbox(
Minimal execution time for the search function in milliseconds. This is used
to avoid fast consecutive reruns, where fast reruns can lead to resets
within the component in some streamlit versions. Defaults to 0.
reset_function (Callable[[], None], optional):
Function that is called after the user reset the combobox. Defaults to None.
key (str, optional):
Streamlit session key. Defaults to "searchbox".
Expand Down Expand Up @@ -310,6 +313,9 @@ def st_searchbox(
if interaction == "reset":
_set_defaults(key, default, default_options)

if reset_function is not None:
reset_function()

if rerun_on_update:
# only pass scope if the version is >= 1.37
if st.__version__ >= "1.37":
Expand Down
121 changes: 74 additions & 47 deletions streamlit_searchbox/frontend/src/icons.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";

/*
This file contains the icons used in the searchbox component. See https://lucide.dev/icons
Expand All @@ -24,54 +24,81 @@ export const DropdownIcon: React.FC<React.SVGProps<SVGSVGElement>> = (
</svg>
);

interface ClearIconProps extends React.SVGProps<SVGSVGElement> {
fillHover?: string;
strokeHover?: string;
}

// default icon
export const ClearIconCircularFilled: React.FC<
React.SVGProps<SVGSVGElement>
> = (props) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58173 16.4183 4 12 4C7.58173 4 4 7.58173 4 12C4 16.4183 7.58173 20 12 20ZM10.0303 8.96967C9.73743 8.67679 9.26257 8.67679 8.96967 8.96967C8.67676 9.26257 8.67676 9.73743 8.96967 10.0303L10.9393 12L8.96967 13.9697C8.67676 14.2626 8.67676 14.7374 8.96967 15.0303C9.26257 15.3232 9.73743 15.3232 10.0303 15.0303L12 13.0607L13.9697 15.0303C14.2626 15.3232 14.7374 15.3232 15.0303 15.0303C15.3232 14.7374 15.3232 14.2626 15.0303 13.9697L13.0607 12L15.0303 10.0303C15.3232 9.73743 15.3232 9.26257 15.0303 8.96967C14.7374 8.67679 14.2626 8.67679 13.9697 8.96967L12 10.9393L10.0303 8.96967Z"
></path>
</svg>
);
export const ClearIconCircularFilled: React.FC<ClearIconProps> = (props) => {
const [hover, setHover] = useState(false);

return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
{...props}
fill={hover && props.fillHover ? props.fillHover! : props.fill}
style={{ transition: "fill 0.3s ease-in-out" }}
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58173 16.4183 4 12 4C7.58173 4 4 7.58173 4 12C4 16.4183 7.58173 20 12 20ZM10.0303 8.96967C9.73743 8.67679 9.26257 8.67679 8.96967 8.96967C8.67676 9.26257 8.67676 9.73743 8.96967 10.0303L10.9393 12L8.96967 13.9697C8.67676 14.2626 8.67676 14.7374 8.96967 15.0303C9.26257 15.3232 9.73743 15.3232 10.0303 15.0303L12 13.0607L13.9697 15.0303C14.2626 15.3232 14.7374 15.3232 15.0303 15.0303C15.3232 14.7374 15.3232 14.2626 15.0303 13.9697L13.0607 12L15.0303 10.0303C15.3232 9.73743 15.3232 9.26257 15.0303 8.96967C14.7374 8.67679 14.2626 8.67679 13.9697 8.96967L12 10.9393L10.0303 8.96967Z"
></path>
</svg>
);
};

// optional icon
export const ClearIconCircularUnfilled: React.FC<
React.SVGProps<SVGSVGElement>
> = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
{...props}
>
<circle cx="12" cy="12" r="10" />
<path d="m15 9-6 6" />
<path d="m9 9 6 6" />
</svg>
);
export const ClearIconCircularUnfilled: React.FC<ClearIconProps> = (props) => {
const [hover, setHover] = useState(false);

return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
{...props}
stroke={hover && props.strokeHover ? props.strokeHover : props.stroke}
style={{ transition: "fill 0.3s ease-in-out" }}
>
<circle cx="12" cy="12" r="10" />
<path d="m15 9-6 6" />
<path d="m9 9 6 6" />
</svg>
);
};

// optional icon
export const ClearIconCross: React.FC<React.SVGProps<SVGSVGElement>> = (
props,
) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
// default, overwritten by styles
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
{...props}
>
<path d="M18 6 6 18" stroke={props.stroke} />
<path d="m6 6 12 12" stroke={props.stroke} />
</svg>
);
export const ClearIconCross: React.FC<ClearIconProps> = (props) => {
const [hover, setHover] = useState(false);

return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
// default, overwritten by styles
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
{...props}
stroke={hover && props.strokeHover ? props.strokeHover : props.stroke}
style={{ transition: "fill 0.3s ease-in-out" }}
>
<path d="M18 6 6 18" stroke={props.stroke} />
<path d="m6 6 12 12" stroke={props.stroke} />
</svg>
);
};
20 changes: 16 additions & 4 deletions streamlit_searchbox/frontend/src/styling.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class SearchboxStyle {
alignItems: "center",
justifyContent: "center",
marginRight: "8px",
marginLeft: "2px",
}}
>
<DropdownIcon
Expand All @@ -142,21 +143,25 @@ class SearchboxStyle {
innerProps: { ref, ...iconProps },
} = props;

// fill / stroke color might be passed by react-select
iconProps = {
...iconProps,
ref: ref,
fill: this.theme.fadedText60,
// streamlit has fixed icon sizes at 15x15
width: 15,
height: 15,
width: 22,
height: 22,
...overrides,
};

// if stroke or fill in iconProps don't set hover color
const isColorOverride = "stroke" in iconProps || "fill" in iconProps;

if (iconProps.icon === "cross") {
return (
<ClearIconCross
// replace opacity single overlapping strokes will look weird
stroke={this.theme.textColor}
strokeHover={isColorOverride ? undefined : this.theme.primaryColor}
{...iconProps}
/>
);
Expand All @@ -167,14 +172,21 @@ class SearchboxStyle {
<ClearIconCircularUnfilled
// replace opacity single overlapping strokes will look weird
stroke={this.theme.textColor}
strokeHover={isColorOverride ? undefined : this.theme.primaryColor}
{...iconProps}
// icon can't be filled
fill="none"
/>
);
}

return <ClearIconCircularFilled {...iconProps} />;
return (
<ClearIconCircularFilled
fill={this.theme.fadedText60}
fillHover={isColorOverride ? undefined : this.theme.textColor}
{...iconProps}
/>
);
}
}

Expand Down

0 comments on commit c6934e9

Please sign in to comment.