mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
ui: enhanced auto-complete
This commit is contained in:
1 parent
c383c40477
commit
2b56444239
4 files changed
+37
-27
No files matched your search
@@ -38,6 +38,7 @@ export interface AutoCompleteHandle<Item> {
|
||||
setSelectedItems: (items: Item[]) => void;
|
||||
getQuery: () => string;
|
||||
setQuery: (query: string) => void;
|
||||
setSelectedKeys: (keys: string[]) => void;
|
||||
triggerQuery: () => any;
|
||||
closeList: () => void;
|
||||
/** returns comma seperated value text */
|
||||
@@ -69,7 +70,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
|
||||
const [focused, setFocused] = useState(false); // is focused
|
||||
const [selected, setSelected] = useState([]); // selected items
|
||||
const [selectedKeys, setSelectedKeys] = useState(props.selectedKeys); // keys of selected items
|
||||
const [selectedKeys, setSelectedKeys] = useState(props.selectedKeys || []); // keys of selected items
|
||||
const [itemList, setItemList] = useState([]); // items list
|
||||
const [currentItem, setCurrentItem] = useState(null); // index of current item (in item list)
|
||||
const [rerender, setRerender] = useState(false);
|
||||
@@ -81,16 +82,6 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
const queryCache = props.cacheKey ? superCache[props.cacheKey].query : useRef({}).current;
|
||||
const valueCache = props.cacheKey ? superCache[props.cacheKey].value : useRef({}).current;
|
||||
|
||||
useEffect(() => {
|
||||
const ids = [];
|
||||
for (const key of selectedKeys) if (!valueCache[key]) ids.push(key);
|
||||
if (!ids.length) return;
|
||||
Promise.resolve(props.fetchItems(ids)).then((items) => {
|
||||
for (const item of items) valueCache[itemKey(item)] = item;
|
||||
setRerender(!rerender);
|
||||
});
|
||||
}, [selectedKeys]);
|
||||
|
||||
const queryList = async (query) => {
|
||||
if (!query && !allowEmptyQuery) {
|
||||
setItemList([]);
|
||||
@@ -104,22 +95,25 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
};
|
||||
|
||||
const dispatchChange = () => {
|
||||
const query = inputRef.current?.value;
|
||||
const val = [...selectedKeys, query].filter((i) => i).join(',');
|
||||
onChange(val);
|
||||
if (!multi) onChange(inputRef.current?.value);
|
||||
else onChange(selectedKeys.join(','));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatchChange();
|
||||
const ids = [];
|
||||
for (const key of selectedKeys) if (!valueCache[key]) ids.push(key);
|
||||
if (!ids.length) return;
|
||||
Promise.resolve(props.fetchItems(ids)).then((items) => {
|
||||
for (const item of items) valueCache[itemKey(item)] = item;
|
||||
setRerender(!rerender);
|
||||
});
|
||||
}, [selectedKeys, multi]);
|
||||
|
||||
const handleInputChange = debounce(async (e) => {
|
||||
const { target } = e;
|
||||
const { value } = target;
|
||||
queryList(value);
|
||||
}, 300);
|
||||
const handleInputChange = debounce((e?) => queryList(e ? e.target.value : ''), 300);
|
||||
|
||||
const toggleItem = (item: T, key = itemKey(item)) => {
|
||||
const shouldKeepOpen = allowEmptyQuery && inputRef.current.value === '' && !multi;
|
||||
if (multi) {
|
||||
const idx = selectedKeys.indexOf(key);
|
||||
if (idx !== -1) {
|
||||
@@ -145,8 +139,10 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
inputRef.current.value = key;
|
||||
dispatchChange();
|
||||
}
|
||||
setItemList([]);
|
||||
setCurrentItem(null);
|
||||
if (!shouldKeepOpen) {
|
||||
setItemList([]);
|
||||
setCurrentItem(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputKeyDown = (e) => {
|
||||
@@ -169,7 +165,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
}
|
||||
if (key === 'Backspace') {
|
||||
if (target.value.length > 0) return;
|
||||
if (selected.length > 0) {
|
||||
if (selectedKeys.length > 0) {
|
||||
setSelected((s) => s.slice(0, -1));
|
||||
setSelectedKeys((s) => s.slice(0, -1));
|
||||
}
|
||||
@@ -209,6 +205,7 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
setQuery: (query) => {
|
||||
if (inputRef.current) inputRef.current.value = query;
|
||||
},
|
||||
setSelectedKeys: (keys) => setSelectedKeys(keys),
|
||||
triggerQuery: () => queryList(inputRef.current?.value),
|
||||
closeList: () => {
|
||||
setItemList([]);
|
||||
@@ -257,7 +254,10 @@ const AutoComplete = forwardRef(function Impl<T>(props: AutoCompleteProps<T>, re
|
||||
dispatchChange();
|
||||
handleInputChange(e);
|
||||
}}
|
||||
onFocus={() => setFocused(true)}
|
||||
onFocus={() => {
|
||||
if (allowEmptyQuery) handleInputChange();
|
||||
setFocused(true);
|
||||
}}
|
||||
onBlur={() => setFocused(false)}
|
||||
onKeyDown={handleInputKeyDown}
|
||||
defaultValue={multi ? '' : selectedKeys.join(',')}
|
||||
|
||||
+12
-3
@@ -2,13 +2,23 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AutoComplete from './AutoComplete';
|
||||
|
||||
type DefaultProps = {
|
||||
_id: string;
|
||||
name: string;
|
||||
} & string;
|
||||
|
||||
const CustomSelectAutoComplete = React.forwardRef<any, any>((props, ref) => (
|
||||
<AutoComplete<any>
|
||||
<AutoComplete<DefaultProps>
|
||||
ref={ref as any}
|
||||
queryItems={(query) => props.data.filter((i) => (i.name ? i.name.match(query) : i.match(query)))}
|
||||
fetchItems={(keys) => props.data.filter((i) => (i._id ? keys.includes(i._id) : keys.includes(i)))}
|
||||
queryItems={(query) => props.data.filter((i) => (i.name
|
||||
? i.name.toLowerCase().match(query.toLowerCase())
|
||||
: i.toString().toLowerCase().match(query.toLowerCase())
|
||||
))}
|
||||
itemText={(item) => `${item.name || item}`}
|
||||
itemKey={(item) => `${item._id?.toString() || item.name || item}`}
|
||||
renderItem={(item) => `${item.name || item}`}
|
||||
allowEmptyQuery
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
@@ -34,7 +44,6 @@ CustomSelectAutoComplete.defaultProps = {
|
||||
listStyle: {},
|
||||
multi: false,
|
||||
selectedKeys: [],
|
||||
allowEmptyQuery: false,
|
||||
freeSolo: false,
|
||||
freeSoloConverter: (input) => input,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.34.33",
|
||||
"version": "4.34.34",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
@@ -4,3 +4,4 @@ declare let UiContext: any;
|
||||
declare let Hydro: any;
|
||||
// eslint-disable-next-line camelcase
|
||||
declare let node_modules: any;
|
||||
declare let LANGS: Record<string, import('@hydrooj/utils/lib/lang').LangConfig>;
|
||||
Reference in new issue
Block a user