update search input styling

This commit is contained in:
jeffvli
2025-11-13 18:47:39 -08:00
parent c7dc2d4969
commit 5e45897b8e
@@ -1,21 +1,45 @@
import { useHotkeys } from '@mantine/hooks';
import { ChangeEvent, KeyboardEvent, useRef, useState } from 'react';
import { ChangeEvent, CSSProperties, KeyboardEvent, useRef, useState } from 'react';
import { shallow } from 'zustand/shallow';
import { useSettingsStore } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { ActionIcon, ActionIconProps } from '/@/shared/components/action-icon/action-icon';
import { Box } from '/@/shared/components/box/box';
import { Icon } from '/@/shared/components/icon/icon';
import { TextInput, TextInputProps } from '/@/shared/components/text-input/text-input';
interface SearchInputProps extends TextInputProps {
buttonProps?: Partial<ActionIconProps>;
enableHotkey?: boolean;
inputProps?: Partial<TextInputProps>;
value?: string;
}
export const SearchInput = ({ onChange, ...props }: SearchInputProps) => {
export const SearchInput = ({
buttonProps,
enableHotkey = true,
inputProps,
onChange,
...props
}: SearchInputProps) => {
const ref = useRef<HTMLInputElement>(null);
const binding = useSettingsStore((state) => state.hotkeys.bindings.localSearch, shallow);
const [isInputMode, setIsInputMode] = useState(false);
useHotkeys([[binding.hotkey, () => ref?.current?.select()]]);
useHotkeys([
[
binding.hotkey,
() => {
if (enableHotkey) {
setIsInputMode(true);
setTimeout(() => {
ref?.current?.focus();
ref?.current?.select();
}, 0);
}
},
],
]);
const handleEscape = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.code === 'Escape') {
@@ -24,6 +48,7 @@ export const SearchInput = ({ onChange, ...props }: SearchInputProps) => {
ref.current.value = '';
ref.current.blur();
}
setIsInputMode(false);
}
};
@@ -35,27 +60,76 @@ export const SearchInput = ({ onChange, ...props }: SearchInputProps) => {
}
};
const [isFocused, setIsFocused] = useState(false);
const handleButtonClick = () => {
setIsInputMode(true);
setTimeout(() => {
ref?.current?.focus();
}, 0);
};
const handleBlur = () => {
const hasValue = props.value || ref.current?.value;
if (!hasValue) {
setIsInputMode(false);
}
};
const hasValue = props.value || ref.current?.value;
const shouldShowInput = isInputMode || hasValue;
const containerStyle: CSSProperties = {
display: 'inline-flex',
overflow: 'hidden',
position: 'relative',
transition: 'width 0.3s ease-in-out',
width: shouldShowInput ? '200px' : '40px',
};
const buttonStyle: CSSProperties = {
left: 0,
opacity: shouldShowInput ? 0 : 1,
pointerEvents: shouldShowInput ? 'none' : 'auto',
position: 'absolute',
top: 0,
transition: 'opacity 0.2s ease-in-out',
zIndex: 10,
};
const inputStyle: CSSProperties = {
opacity: shouldShowInput ? 1 : 0,
transition: 'opacity 0.2s ease-in-out',
width: '100%',
};
return (
<TextInput
leftSection={<Icon icon="search" />}
maw="20dvw"
onBlur={() => setIsFocused(false)}
onChange={onChange}
onFocus={() => setIsFocused(true)}
onKeyDown={handleEscape}
ref={ref}
size="sm"
width={isFocused || hasValue ? '200px' : '100px'}
{...props}
rightSection={
ref.current?.value ? (
<ActionIcon icon="x" onClick={handleClear} variant="transparent" />
) : null
}
/>
<Box style={containerStyle}>
<TextInput
leftSection={<Icon icon="search" />}
maw="20dvw"
{...inputProps}
onBlur={handleBlur}
onChange={onChange}
onFocus={() => setIsInputMode(true)}
onKeyDown={handleEscape}
ref={ref}
size="sm"
style={inputStyle}
{...props}
rightSection={
ref.current?.value ? (
<ActionIcon icon="x" onClick={handleClear} variant="transparent" />
) : null
}
/>
<ActionIcon
{...buttonProps}
icon="search"
iconProps={{ size: 'lg' }}
onClick={handleButtonClick}
style={buttonStyle}
tooltip={{ label: 'Search' }}
variant="subtle"
/>
</Box>
);
};