Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add search sort filter #155

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useRouter } from 'next/router';
import { ReactNode, useMemo } from 'react';

import Header from '@/components/layout/Header';
import SearchTagList from '@/components/search/SearchTagList';
import { Side } from '@/components/side/Side';

import TagList from '../side/TagList';
Expand Down Expand Up @@ -32,6 +33,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
) : (
<div className='flex flex-row md:border-none'>
{pathname === '/' && <TagList />}
{pathname === '/search/result' && <SearchTagList />}
<div
className={`relative ${
pathname === '/'
Expand Down
76 changes: 76 additions & 0 deletions src/components/search/SearchNavbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useRouter } from 'next/router';
import { AiOutlineArrowLeft } from 'react-icons/ai';

import useFilterHandling from '@/hooks/useFilterHandling';

import { TagLinkListSkeleton } from '@/components/loading/skeleton';
import SearchTagLink from '@/components/search/SearchTagLink';

interface Props {
middleText: string;
endText?: string;
t: (key: string) => string;
i18n_lang: string;
tid?: string;
sortBy: string;
rankBy: string;
}

const SearchNavbar = ({
middleText = '',
endText = '',
i18n_lang,
tid,
sortBy,
rankBy,
}: Props) => {
const router = useRouter();

const goBack = () => {
if (window.history.length < 2) {
router.push('/');
} else {
router.back();
}
};

const { tagItems } = useFilterHandling(tid, sortBy, rankBy, i18n_lang);

return (
<div className='relative my-2 bg-white dark:bg-gray-800 md:rounded-lg'>
<div className='flex h-12 items-center justify-between py-2 px-4'>
<div className='cursor-pointer' onClick={goBack}>
<AiOutlineArrowLeft
className='text-gray-500 hover:text-blue-400'
size={18}
/>
</div>
<div className='w-3/4 truncate text-center font-bold dark:text-gray-300'>
{middleText}
</div>
{endText ? (
<div className='justify-end text-sm text-gray-500 dark:text-gray-400'>
{endText}
</div>
) : (
<div className='flex w-5' />
)}
</div>
{/* 移动端标签 */}
<div className='flex border-t border-gray-100 px-3 pb-2 dark:border-gray-700 lg:hidden'>
{tagItems.length > 0 ? (
<SearchTagLink
items={tagItems}
tid={tid}
sort_by={sortBy}
rank_by={rankBy}
/>
) : (
<TagLinkListSkeleton />
)}
</div>
</div>
);
};

export default SearchNavbar;
47 changes: 47 additions & 0 deletions src/components/search/SearchTagLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { NoPrefetchLink } from '@/components/links/CustomLink';

import { makeUrl } from '@/utils/util';

import { TagType } from '@/types/tag';

interface Props {
tid?: string;
sort_by: string;
items: TagType[];
rank_by: string;
}

export default function SearchTagLink({ tid, sort_by, items, rank_by }: Props) {
return (
<div className='custom-scrollbar mt-2 overflow-y-auto'>
<ul className='flex text-xs font-bold'>
{items.map((item: TagType) => {
return (
<li className='shrink-0 grow-0 basis-auto' key={item.tid}>
<NoPrefetchLink
href={makeUrl('/search/result', {
sort_by,
rank_by,
tid: item.tid,
})}
>
<a
className={`
mr-1 inline-flex h-6 items-center justify-center rounded-xl px-2
${
tid === item.tid
? 'bg-gray-100 text-blue-500 dark:bg-gray-700'
: 'text-gray-500 hover:bg-gray-100 hover:text-blue-500 dark:text-gray-200 dark:hover:bg-gray-700'
}
`}
>
{item.name}
</a>
</NoPrefetchLink>
</li>
);
})}
</ul>
</div>
);
}
122 changes: 122 additions & 0 deletions src/components/search/SearchTagList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import classNames from 'classnames';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { useEffect, useState } from 'react';
import { AiOutlineAppstore, AiOutlineSetting } from 'react-icons/ai';

import { TagModal } from '@/components/dialog/TagModal';

import { getTags } from '@/services/tag';
import { isMobile, makeUrl } from '@/utils/util';

import { TagListSkeleton } from '../loading/skeleton';

import { Tag } from '@/types/tag';

export default function SearchTagList() {
const { t, i18n } = useTranslation('home');
const defaultTag: Tag = {
name: '综合',
name_en: 'All',
tid: 'all',
icon_name: 'find',
};

const router = useRouter();
const {
tid = 'all',
sort_by = 'featured',
rank_by,
year,
month,
} = router.query;
const [tags, setTags] = useState<Tag[]>([]);
const [maxHeight, setMaxHeight] = useState<number>(444);

const updateMaxHeight = () => {
const calculatedHeight = Math.max(window.innerHeight * 0.6, 300);
setMaxHeight(calculatedHeight);
};

useEffect(() => {
const initTags = async () => {
const res = await getTags();
if (res.success) {
res.data.unshift(defaultTag);
setTags(res.data);
}
};

if (!isMobile()) {
initTags();
updateMaxHeight();
window.addEventListener('resize', updateMaxHeight);
return () => {
window.removeEventListener('resize', updateMaxHeight);
};
}
}, []);

const iconClassName = (iconName: string) => `iconfont icon-${iconName} mr-1`;

const tagClassName = (itemTid: string) =>
classNames(
'flex flex-row w-[115px] items-center my-1 py-2 px-3 rounded text-[14px] cursor-pointer hover:bg-gray-100 hover:text-blue-500 dark:hover:bg-gray-700',
{
'text-gray-500 dark:text-gray-400': tid !== itemTid,
'bg-gray-100 dark:bg-gray-700 text-blue-500': tid === itemTid,
}
);

return (
<div className='hidden max-w-[162px] shrink-0 lg:block lg:w-2/12 lg:grow-0'>
<div className='fixed top-16 pl-2'>
<div className='w-[140px] rounded-lg bg-white px-3 py-2 dark:bg-gray-800'>
<div className='px-1 pb-1'>
<div className='border-b border-b-gray-200 pb-2 dark:border-b-gray-600 dark:text-gray-300'>
<div className='flex w-[104px] flex-row items-center p-1'>
<AiOutlineAppstore size={16} />
<div className='ml-1 font-medium'>{t('tag_side.title')}</div>
</div>
</div>
</div>
<div
className='hidden-scrollbar overflow-y-auto'
style={{ maxHeight: `${maxHeight}px` }}
>
{!tags.length && <TagListSkeleton />}
{tags.map((item: Tag) => (
<Link
prefetch={false}
key={item.tid}
href={makeUrl('/search/result', {
sort_by,
rank_by,
tid: item.tid,
year,
month,
})}
>
<div className={tagClassName(item.tid)}>
<div className={iconClassName(item.icon_name)}></div>
<div className='truncate text-ellipsis'>
{i18n.language === 'zh'
? item.name
: item.name_en || item.name}
</div>
</div>
</Link>
))}
</div>
<TagModal updateTags={setTags} t={t} i18n_lang={i18n.language}>
<div className='flex cursor-pointer flex-row items-center border-t border-t-gray-200 px-3 pt-2 pb-1 text-gray-500 hover:text-blue-500 dark:border-t-gray-600 dark:text-gray-300 dark:hover:text-blue-500'>
<AiOutlineSetting size={15} />
<div className='ml-0.5 text-sm'>{t('tag_side.manage')}</div>
</div>
</TagModal>
</div>
</div>
</div>
);
}
12 changes: 9 additions & 3 deletions src/pages/search/result/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import RepositoryItem from '@/components/home/Item';
import ItemBottom from '@/components/home/ItemBottom';
import Loading from '@/components/loading/Loading';
import { SearchSkeleton } from '@/components/loading/skeleton';
import Navbar from '@/components/navbar/Navbar';
import SearchNavbar from '@/components/search/SearchNavbar';
import Seo from '@/components/Seo';
import ToTop from '@/components/toTop/ToTop';

Expand Down Expand Up @@ -53,11 +53,17 @@ const Result: NextPage = () => {
onLoadMore: () => setSize(pageIndex + 1),
rootMargin: '0px 0px 100px 0px',
});

// TODO: SearchNavbar
return (
<>
<Seo title={t('title')} description={t('description')} />
<Navbar middleText={t('navbar')} />
<SearchNavbar
middleText={t('navbar')}
t={t}
i18n_lang={''}
sortBy={''}
rankBy={''}
/>
<div className='h-screen'>
<div className='divide-y divide-gray-100 overflow-y-hidden bg-white dark:divide-gray-700 md:rounded-lg'>
{list.map((item) => (
Expand Down
9 changes: 9 additions & 0 deletions src/utils/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,12 @@ export const constructURL = (params: { [key: string]: any }) => {

return `/?${queryString}`;
};

export const makeUrl = (path: string, params: { [key: string]: any }) => {
const queryString = Object.entries(params)
.filter(([_, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${key}=${value}`)
.join('&');

return `${path}?${queryString}`;
};