diff --git a/web/app/components/base/portal-to-follow-elem/index.tsx b/web/app/components/base/portal-to-follow-elem/index.tsx index 3d24c6ee99..9d48ba20bc 100644 --- a/web/app/components/base/portal-to-follow-elem/index.tsx +++ b/web/app/components/base/portal-to-follow-elem/index.tsx @@ -6,6 +6,7 @@ import { flip, offset, shift, + size, useDismiss, useFloating, useFocus, @@ -27,6 +28,7 @@ export type PortalToFollowElemOptions = { open?: boolean offset?: number | OffsetOptions onOpenChange?: (open: boolean) => void + triggerPopupSameWidth?: boolean } export function usePortalToFollowElem({ @@ -34,6 +36,7 @@ export function usePortalToFollowElem({ open, offset: offsetValue = 0, onOpenChange: setControlledOpen, + triggerPopupSameWidth, }: PortalToFollowElemOptions = {}) { const setOpen = setControlledOpen @@ -50,6 +53,12 @@ export function usePortalToFollowElem({ padding: 5, }), shift({ padding: 5 }), + size({ + apply({ rects, elements }) { + if (triggerPopupSameWidth) + elements.floating.style.width = `${rects.reference.width}px` + }, + }), ], }) diff --git a/web/app/education-apply/components/hooks.ts b/web/app/education-apply/components/hooks.ts index 244a5f47d7..6ecb9dab32 100644 --- a/web/app/education-apply/components/hooks.ts +++ b/web/app/education-apply/components/hooks.ts @@ -10,15 +10,16 @@ export const useEducation = () => { const { mutateAsync, isPending, + data, } = useEducationAutocomplete() const [prevSchools, setPrevSchools] = useState([]) const handleUpdateSchools = useCallback((searchParams: SearchParams) => { if (searchParams.keywords) { mutateAsync(searchParams).then((res) => { - const currentPage = searchParams.page || 1 + const currentPage = searchParams.page || 0 const resSchools = res.data - if (currentPage > 1) + if (currentPage > 0) setPrevSchools(prevSchools => [...(prevSchools || []), ...resSchools]) else setPrevSchools(resSchools) @@ -29,12 +30,15 @@ export const useEducation = () => { const { run: querySchoolsWithDebounced } = useDebounceFn((searchParams: SearchParams) => { handleUpdateSchools(searchParams) }, { - wait: 1000, + wait: 300, }) return { schools: prevSchools, + setSchools: setPrevSchools, querySchoolsWithDebounced, + handleUpdateSchools, isLoading: isPending, + hasNext: data?.has_next, } } diff --git a/web/app/education-apply/components/search-input.tsx b/web/app/education-apply/components/search-input.tsx index 88a0db3711..2a4eb96fb9 100644 --- a/web/app/education-apply/components/search-input.tsx +++ b/web/app/education-apply/components/search-input.tsx @@ -1,10 +1,9 @@ import { useCallback, - useEffect, + useRef, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { useDebounceFn } from 'ahooks' import { useEducation } from './hooks' import Input from '@/app/components/base/input' import { @@ -25,29 +24,53 @@ const SearchInput = ({ const [open, setOpen] = useState(false) const { schools, - isLoading, + setSchools, querySchoolsWithDebounced, + handleUpdateSchools, + hasNext, } = useEducation() + const pageRef = useRef(0) + const valueRef = useRef(value) - const { - run: handleSearch, - } = useDebounceFn(() => { - querySchoolsWithDebounced({ - keywords: value, - page: 1, + const handleSearch = useCallback((debounced?: boolean) => { + const keywords = valueRef.current + const page = pageRef.current + if (debounced) { + querySchoolsWithDebounced({ + keywords, + page, + }) + return + } + + handleUpdateSchools({ + keywords, + page, }) - }, { - wait: 300, - }) - const handleValueChange = useCallback((e: { target: { value: string } }) => { - onChange(e.target.value) - handleSearch() - }, [handleSearch, onChange]) + }, [querySchoolsWithDebounced, handleUpdateSchools]) - useEffect(() => { - if (!isLoading && !open && schools.length) - setOpen(true) - }, [isLoading, open, schools]) + const handleValueChange = useCallback((e: any) => { + setOpen(true) + setSchools([]) + pageRef.current = 0 + const inputValue = e.target.value + valueRef.current = inputValue + onChange(inputValue) + handleSearch(true) + }, [onChange, handleSearch, setSchools]) + + const handleScroll = useCallback((e: Event) => { + const target = e.target as HTMLDivElement + const { + scrollTop, + scrollHeight, + clientHeight, + } = target + if (scrollTop + clientHeight >= scrollHeight - 5 && scrollTop > 0 && hasNext) { + pageRef.current += 1 + handleSearch() + } + }, [handleSearch, hasNext]) return ( - -
- { - schools.map((school, index) => ( -
- {school} -
- )) - } -
+ + { + !!schools.length && value && ( +
+ { + schools.map((school, index) => ( +
{ + onChange(school) + setOpen(false) + }} + > + {school} +
+ )) + } +
+ ) + }
) diff --git a/web/service/use-education.ts b/web/service/use-education.ts index 314813d7b9..73e037c3f9 100644 --- a/web/service/use-education.ts +++ b/web/service/use-education.ts @@ -44,8 +44,8 @@ export const useEducationAutocomplete = () => { mutationFn: (searchParams: SearchParams) => { const { keywords = '', - page = 1, - limit = 20, + page = 0, + limit = 40, } = searchParams return get<{ data: string[]; has_next: boolean; curr_page: number }>(`/account/education/autocomplete?keywords=${keywords}&page=${page}&limit=${limit}`) },