mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 09:20:15 +01:00
🔍 a11y: MultiSearch Clear Input (#5718)
* add accessibility features to model search * chore: linting * fix: Improve accessibility by adding aria-label to MultiSearch input * refactor: MultiSearch component as button * refactor: Update MultiSearch component styles for improved theming * refactor: Update MultiSearch component styles for improved visual consistency --------- Co-authored-by: Derek Jackson <derek_jackson@harvard.edu> Co-authored-by: derek jackson <63861027+derekjackson-das@users.noreply.github.com> Co-authored-by: Ruben Talstra <RubenTalstra1211@outlook.com>
This commit is contained in:
parent
18339ec7bb
commit
8b2ffa141e
1 changed files with 31 additions and 10 deletions
|
|
@ -1,10 +1,9 @@
|
||||||
import { Search, X } from 'lucide-react';
|
import { Search, X } from 'lucide-react';
|
||||||
import React, { useState, useMemo, useCallback } from 'react';
|
import React, { useState, useMemo, useCallback, useRef } from 'react';
|
||||||
import { useLocalize } from '~/hooks';
|
import { useLocalize } from '~/hooks';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
|
||||||
// This is a generic that can be added to Menu and Select components
|
/** This is a generic that can be added to Menu and Select components */
|
||||||
|
|
||||||
export default function MultiSearch({
|
export default function MultiSearch({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
|
@ -17,35 +16,57 @@ export default function MultiSearch({
|
||||||
className?: string;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const onChangeHandler: React.ChangeEventHandler<HTMLInputElement> = useCallback(
|
const onChangeHandler: React.ChangeEventHandler<HTMLInputElement> = useCallback(
|
||||||
(e) => onChange(e.target.value),
|
(e) => onChange(e.target.value),
|
||||||
[onChange],
|
[onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const clearSearch = () => {
|
||||||
|
onChange('');
|
||||||
|
setTimeout(() => {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'group sticky left-0 top-0 z-10 flex h-12 items-center gap-2 bg-gradient-to-b from-white from-65% to-transparent px-3 py-2 text-black transition-colors duration-300 focus:bg-gradient-to-b focus:from-white focus:to-white/50 dark:from-gray-700 dark:to-transparent dark:text-white dark:focus:from-white/10 dark:focus:to-white/20',
|
'focus:to-surface-primary/50 group sticky left-0 top-0 z-10 flex h-12 items-center gap-2 bg-gradient-to-b from-surface-tertiary-alt from-65% to-transparent px-3 py-2 text-text-primary transition-colors duration-300 focus:bg-gradient-to-b focus:from-surface-primary',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Search className="h-4 w-4 text-gray-500 transition-colors duration-300 dark:group-focus-within:text-gray-300 dark:group-hover:text-gray-300" />
|
<Search
|
||||||
|
className="h-4 w-4 text-text-secondary-alt transition-colors duration-300"
|
||||||
|
aria-hidden={'true'}
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
value={value ?? ''}
|
value={value ?? ''}
|
||||||
onChange={onChangeHandler}
|
onChange={onChangeHandler}
|
||||||
placeholder={placeholder ?? localize('com_ui_select_search_model')}
|
placeholder={placeholder ?? localize('com_ui_select_search_model')}
|
||||||
className="flex-1 rounded-md border-none bg-transparent px-2.5 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-gray-700/10 dark:focus:ring-gray-200/10"
|
aria-label="Search Model"
|
||||||
|
className="flex-1 rounded-md border-none bg-transparent px-2.5 py-2 text-sm placeholder-text-secondary focus:outline-none focus:ring-1 focus:ring-ring-primary"
|
||||||
/>
|
/>
|
||||||
<div className="relative flex h-5 w-5 items-center justify-end text-gray-500">
|
<button
|
||||||
<X
|
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-gray-500 dark:text-gray-300',
|
'relative flex h-5 w-5 items-center justify-end rounded-md text-text-secondary-alt',
|
||||||
|
value?.length ?? 0 ? 'cursor-pointer opacity-100' : 'hidden',
|
||||||
|
)}
|
||||||
|
aria-label={'Clear search'}
|
||||||
|
onClick={clearSearch}
|
||||||
|
tabIndex={0}
|
||||||
|
>
|
||||||
|
<X
|
||||||
|
aria-hidden={'true'}
|
||||||
|
className={cn(
|
||||||
|
'text-text-secondary-alt',
|
||||||
value?.length ?? 0 ? 'cursor-pointer opacity-100' : 'opacity-0',
|
value?.length ?? 0 ? 'cursor-pointer opacity-100' : 'opacity-0',
|
||||||
)}
|
)}
|
||||||
onClick={() => onChange('')}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue