feat(mobile): search page ui (#8012)

feat(mobile): search page ui

fix(core): quick search tags performance issue
This commit is contained in:
CatsJuice
2024-08-29 09:05:23 +00:00
parent 5e8683c9be
commit f1bb1fc9b8
26 changed files with 731 additions and 50 deletions
@@ -1,10 +1,13 @@
import { IconButton } from '@affine/component';
import { WorkbenchService } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n';
import { SettingsIcon } from '@blocksuite/icons/rc';
import { useService } from '@toeverything/infra';
import clsx from 'clsx';
import { useCallback, useState } from 'react';
import { Link } from 'react-router-dom';
import { SearchButton, WorkspaceSelector } from '../../components';
import { SearchInput, WorkspaceSelector } from '../../components';
import { useGlobalEvent } from '../../hooks/use-global-events';
import * as styles from './styles.css';
@@ -15,6 +18,9 @@ import * as styles from './styles.css';
* - hide Search
*/
export const HomeHeader = () => {
const t = useI18n();
const workbench = useService(WorkbenchService).workbench;
const [dense, setDense] = useState(false);
useGlobalEvent(
@@ -24,6 +30,17 @@ export const HomeHeader = () => {
}, [])
);
const navSearch = useCallback(() => {
if (!document.startViewTransition) {
return workbench.open('/search');
}
document.startViewTransition(() => {
workbench.open('/search');
return new Promise(resolve => setTimeout(resolve, 150));
});
}, [workbench]);
return (
<div className={clsx(styles.root, { dense })}>
<div className={styles.float}>
@@ -42,7 +59,7 @@ export const HomeHeader = () => {
</div>
</div>
<div className={styles.searchWrapper}>
<SearchButton />
<SearchInput placeholder={t['Quick search']()} onClick={navSearch} />
</div>
</div>
<div className={styles.space} />
@@ -0,0 +1,87 @@
import { useI18n } from '@affine/i18n';
import { DocCard, type DocCardProps } from '../../components';
import {
UniversalSearchResultItem,
type UniversalSearchResultItemProps,
} from '../../components/search-result/universal-item';
import * as styles from './style.css';
export interface SearchResultsProps {
title: string;
docs?: DocCardProps['meta'][];
collections?: UniversalSearchResultItemProps['item'][];
tags?: UniversalSearchResultItemProps['item'][];
}
const Empty = () => {
const t = useI18n();
return (
<div className={styles.empty}>{t['com.affine.mobile.search.empty']()}</div>
);
};
export const SearchResults = ({
title,
docs,
collections,
tags,
}: SearchResultsProps) => {
return (
<>
<div className={styles.resTitle}>{title}</div>
{!docs?.length && !collections?.length && !tags?.length ? (
<Empty />
) : null}
{/* Doc Res */}
{docs?.length ? (
<div className={styles.resBlock} data-scroll>
<div className={styles.resBlockTitle}>Docs</div>
<div className={styles.resBlockScrollContent}>
<div className={styles.scrollDocsContent}>
{docs.map(doc => (
<DocCard meta={doc} key={doc.id} className={styles.docCard} />
))}
</div>
</div>
</div>
) : null}
{/* Collection Res */}
{collections?.length ? (
<div className={styles.resBlock}>
<div className={styles.resBlockTitle}>Collections</div>
<div className={styles.resBlockListContent}>
{collections.map(collection => (
<UniversalSearchResultItem
category="collection"
id={collection.payload.collectionId}
key={collection.id}
item={collection}
/>
))}
</div>
</div>
) : null}
{/* Tag Res */}
{tags?.length ? (
<div className={styles.resBlock}>
<div className={styles.resBlockTitle}>Tags</div>
<div className={styles.resBlockListContent}>
{tags.map(tag => (
<UniversalSearchResultItem
category="tag"
id={tag.payload.tagId}
key={tag.id}
item={tag}
/>
))}
</div>
</div>
) : null}
</>
);
};
@@ -0,0 +1,74 @@
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';
export const searchHeader = style({
padding: 16,
});
export const resTitle = style({
padding: '6px 16px',
marginBottom: 8,
height: 30,
fontSize: 13,
lineHeight: '18px',
fontWeight: 400,
letterSpacing: -0.08,
color: cssVarV2('text/secondary'),
});
export const resBlock = style({
paddingBottom: 32,
selectors: {
'&[data-scroll]': {
paddingBottom: 0,
},
},
});
export const resBlockTitle = style({
padding: '0 16px',
fontSize: 20,
lineHeight: '25px',
fontWeight: 400,
letterSpacing: -0.45,
color: cssVarV2('text/primary'),
});
const resBlockContent = style({
padding: '12px 0px',
});
export const resBlockListContent = style([
resBlockContent,
{
paddingLeft: 16,
paddingRight: 16,
},
]);
export const resBlockScrollContent = style([
resBlockContent,
{
width: '100%',
overflowX: 'auto',
paddingBottom: 32,
},
]);
export const scrollDocsContent = style({
display: 'flex',
gap: 12,
padding: '0 16px',
width: 'fit-content',
});
export const docCard = style({
width: 170,
height: 210,
flexShrink: 0,
});
export const empty = style({
padding: '0 16px',
fontSize: 20,
fontWeight: 400,
lineHeight: '25px',
letterSpacing: -0.45,
color: cssVarV2('text/primary'),
});