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 { 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 { shallow } from 'zustand/shallow';
import { useSettingsStore } from '/@/renderer/store'; 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 { Icon } from '/@/shared/components/icon/icon';
import { TextInput, TextInputProps } from '/@/shared/components/text-input/text-input'; import { TextInput, TextInputProps } from '/@/shared/components/text-input/text-input';
interface SearchInputProps extends TextInputProps { interface SearchInputProps extends TextInputProps {
buttonProps?: Partial<ActionIconProps>;
enableHotkey?: boolean;
inputProps?: Partial<TextInputProps>;
value?: string; value?: string;
} }
export const SearchInput = ({ onChange, ...props }: SearchInputProps) => { export const SearchInput = ({
buttonProps,
enableHotkey = true,
inputProps,
onChange,
...props
}: SearchInputProps) => {
const ref = useRef<HTMLInputElement>(null); const ref = useRef<HTMLInputElement>(null);
const binding = useSettingsStore((state) => state.hotkeys.bindings.localSearch, shallow); 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>) => { const handleEscape = (e: KeyboardEvent<HTMLInputElement>) => {
if (e.code === 'Escape') { if (e.code === 'Escape') {
@@ -24,6 +48,7 @@ export const SearchInput = ({ onChange, ...props }: SearchInputProps) => {
ref.current.value = ''; ref.current.value = '';
ref.current.blur(); 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 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 ( return (
<TextInput <Box style={containerStyle}>
leftSection={<Icon icon="search" />} <TextInput
maw="20dvw" leftSection={<Icon icon="search" />}
onBlur={() => setIsFocused(false)} maw="20dvw"
onChange={onChange} {...inputProps}
onFocus={() => setIsFocused(true)} onBlur={handleBlur}
onKeyDown={handleEscape} onChange={onChange}
ref={ref} onFocus={() => setIsInputMode(true)}
size="sm" onKeyDown={handleEscape}
width={isFocused || hasValue ? '200px' : '100px'} ref={ref}
{...props} size="sm"
rightSection={ style={inputStyle}
ref.current?.value ? ( {...props}
<ActionIcon icon="x" onClick={handleClear} variant="transparent" /> rightSection={
) : null 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>
); );
}; };