import { AutoTooltip, Input as CInput, Cascader as SealCascader } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import _ from 'lodash'; import { useEffect, useMemo, useState } from 'react'; import useQueryModelLoraList, { LoraOptionGroup } from '../services/use-query-lora-list'; import loraSelectionStyles from '../style/lora-selection.less'; interface LoraListItemProps { item: { value: any[]; lora_name: string; source: string }; base: string; defaultDataList: LoraOptionGroup[]; selectedRepoNames: Set; duplicateNames: Set; validated: boolean; onChange: (partial: { value?: any[]; lora_name?: string; source?: string; }) => void; } const LoraListItem: React.FC = ({ item, base, defaultDataList, selectedRepoNames, duplicateNames, validated, onChange }) => { const intl = useIntl(); const { dataList: ownSearchList, fetchData } = useQueryModelLoraList(); const [hasSearched, setHasSearched] = useState(false); const itemDataList = hasSearched ? ownSearchList : defaultDataList; const debouncedSearch = useMemo( () => _.debounce((q: string) => { fetchData({ base, q }); }, 300), [base] ); useEffect(() => { return () => { debouncedSearch.cancel(); }; }, [debouncedSearch]); useEffect(() => { setHasSearched(false); }, [base]); const groupedOptions = useMemo(() => { const currentRepo = item.value?.[1]; return itemDataList .map((group) => ({ ...group, children: group.children.filter( (child) => !selectedRepoNames.has(child.value) || child.value === currentRepo ) })) .filter((group) => group.children.length > 0); }, [itemDataList, selectedRepoNames, item.value]); const handleSearch = (q: string) => { if (!base || !q) { setHasSearched(false); debouncedSearch.cancel(); return; } setHasSearched(true); debouncedSearch(q); }; const handleCascaderChange = (value: any, selectedOptions?: any[]) => { const leaf = selectedOptions?.[selectedOptions.length - 1]; onChange({ value: value || [], source: leaf?.source || '' }); }; const handleNameChange = (e: React.ChangeEvent) => { onChange({ lora_name: e.target.value }); }; const cascaderEmpty = !item.value || item.value.length === 0; const nameEmpty = !item.lora_name?.trim(); const isDuplicate = !!item.lora_name?.trim() && duplicateNames.has(item.lora_name.trim()); const cascaderStatus = validated && cascaderEmpty ? ('error' as const) : 'success'; const inputStatus = validated && (nameEmpty || isDuplicate) ? ('error' as const) : 'success'; const displayRender = (labels: any[]) => { const left = typeof labels[0] === 'string' ? labels[0].replace(/\/+$/, '') : labels[0]; const right = typeof labels[1] === 'string' ? labels[1].replace(/^\/+/, '') : labels[1]; const content = ( {left}/{right} ); return ( {content} ); }; const optionNode = (option: any) => { const { data } = option; return ( {data.label} ); }; return (
triggerNode.parentNode} >
); }; export default LoraListItem;