mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-03 18:40:00 +08:00
feat(mobile): search page ui (#8012)
feat(mobile): search page ui fix(core): quick search tags performance issue
This commit is contained in:
@@ -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'),
|
||||
});
|
||||
Reference in New Issue
Block a user