fix(mobile): fixed all docs header, remove doc card tags layout strategy (#8104)

close AF-1326
This commit is contained in:
CatsJuice 2024-09-05 06:29:18 +00:00
parent 24acce2eac
commit 73dd1d3326
No known key found for this signature in database
GPG Key ID: 1C1E76924FAFDDE4
6 changed files with 53 additions and 139 deletions

View File

@ -55,6 +55,7 @@ export const AppTabs = () => {
className={styles.tabItem}
role="tab"
aria-label={route.to.slice(1)}
replaceHistory
>
<li>
<route.Icon />

View File

@ -11,7 +11,7 @@ export const appTabs = style({
backgroundColor: cssVarV2('layer/background/secondary'),
borderTop: `1px solid ${cssVarV2('layer/insideBorder/border')}`,
width: '100vw',
width: '100dvw',
height: globalVars.appTabHeight,
padding: 16,
gap: 15.5,

View File

@ -7,18 +7,12 @@ export const tags = style({
width: '100%',
display: 'flex',
flexWrap: 'wrap',
alignItems: 'flex-start',
alignItems: 'center',
position: 'relative',
transition: 'height 0.23s',
overflow: 'hidden',
gap: 4,
});
export const tag = style({
visibility: 'hidden',
position: 'absolute',
// transition: 'all 0.23s',
padding: '0px 8px',
borderRadius: 10,
alignItems: 'center',
@ -43,3 +37,8 @@ export const tag = style({
marginRight: 4,
},
});
export const more = style({
fontSize: 16,
color: cssVarV2('icon/primary'),
});

View File

@ -1,9 +1,8 @@
import { observeResize } from '@affine/component';
import type { Tag } from '@affine/core/modules/tag';
import { TagService } from '@affine/core/modules/tag';
import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { useCallback, useEffect, useRef } from 'react';
import * as styles from './tag.css';
@ -23,121 +22,21 @@ const DocCardTag = ({ tag }: { tag: Tag }) => {
);
};
const GAP = 4;
const MIN_WIDTH = 32;
const DocCardTagsRenderer = ({ tags, rows }: { tags: Tag[]; rows: number }) => {
const ulRef = useRef<HTMLUListElement>(null);
// A strategy to layout tags
const layoutTags = useCallback(
(entry: ResizeObserverEntry) => {
const availableWidth = entry.contentRect.width;
const lis = Array.from(ulRef.current?.querySelectorAll('li') ?? []);
const tagGrid: Array<{
x: number;
y: number;
w: number;
el: HTMLLIElement;
}> = [];
for (let i = 0; i < rows; i++) {
let width = 0;
let restSpace = availableWidth - width;
while (restSpace >= MIN_WIDTH) {
const li = lis.shift();
if (!li) break;
const liWidth = li.scrollWidth + 2; // 2 is for border
let liDisplayWidth = Math.min(liWidth, restSpace);
restSpace = restSpace - liDisplayWidth - GAP;
if (restSpace < MIN_WIDTH) {
liDisplayWidth += restSpace;
}
tagGrid.push({
x: width,
y: i * (22 + GAP),
w: liDisplayWidth,
el: li,
});
width += liDisplayWidth + GAP;
}
}
const lastItem = tagGrid[tagGrid.length - 1];
tagGrid.forEach(({ el, x, y, w }) => {
Object.assign(el.style, {
width: `${w}px`,
transform: `translate(${x}px, ${y}px)`,
visibility: 'visible',
});
});
// hide rest
lis.forEach(li =>
Object.assign(li.style, {
visibility: 'hidden',
width: '0px',
transform: `translate(0px, ${lastItem.y + 22 + GAP}px)`,
})
);
// update ul height
// to avoid trigger resize immediately
setTimeout(() => {
if (ulRef.current) {
ulRef.current.style.height = `${lastItem.y + 22}px`;
}
});
},
[rows]
);
const prevEntryRef = useRef<ResizeObserverEntry | null>(null);
useEffect(() => {
tags; // make sure tags is in deps
const ul = ulRef.current;
if (!ul) return;
const dispose = observeResize(ul, entry => {
if (entry.contentRect.width === prevEntryRef.current?.contentRect.width) {
return;
}
layoutTags(entry);
prevEntryRef.current = entry;
});
return () => {
dispose();
prevEntryRef.current = null;
};
}, [layoutTags, tags]);
const DocCardTagsRenderer = ({ tags }: { tags: Tag[] }) => {
return (
<ul className={styles.tags} ref={ulRef} style={{ gap: GAP }}>
{tags.map(tag => (
<ul className={styles.tags}>
{tags.slice(0, 2).map(tag => (
<DocCardTag key={tag.id} tag={tag} />
))}
{/* TODO(@CatsJuice): more icon */}
{/* <MoreHorizontalIcon /> */}
{tags.length > 2 ? <MoreHorizontalIcon className={styles.more} /> : null}
</ul>
);
};
export const DocCardTags = ({
docId,
rows = 2,
}: {
docId: string;
rows?: number;
}) => {
export const DocCardTags = ({ docId }: { docId: string; rows?: number }) => {
const tagService = useService(TagService);
const tags = useLiveData(tagService.tagList.tagsByPageId$(docId));
if (!tags.length) return null;
return <DocCardTagsRenderer tags={tags} rows={rows} />;
return <DocCardTagsRenderer tags={tags} />;
};

View File

@ -1,7 +1,7 @@
import { IconButton, MobileMenu } from '@affine/component';
import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
import { header } from './style.css';
import { header, headerSpace } from './style.css';
import { AllDocsTabs } from './tabs';
export interface AllDocsHeaderProps {
@ -10,15 +10,18 @@ export interface AllDocsHeaderProps {
export const AllDocsHeader = ({ operations }: AllDocsHeaderProps) => {
return (
<header className={header}>
<AllDocsTabs />
<div>
{operations ? (
<MobileMenu items={operations}>
<IconButton icon={<MoreHorizontalIcon />} />
</MobileMenu>
) : null}
</div>
</header>
<>
<header className={header}>
<AllDocsTabs />
<div>
{operations ? (
<MobileMenu items={operations}>
<IconButton icon={<MoreHorizontalIcon />} />
</MobileMenu>
) : null}
</div>
</header>
<div className={headerSpace} />
</>
);
};

View File

@ -1,20 +1,32 @@
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';
export const header = style({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 16,
padding: '16px 16px 0px 16px',
const headerContentHeight = 56;
const headerPaddingTop = 16;
position: 'sticky',
top: 0,
backgroundColor: cssVarV2('layer/background/secondary'),
zIndex: 1,
const basicHeader = style({
width: '100%',
height: headerContentHeight + headerPaddingTop,
});
export const header = style([
basicHeader,
{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 16,
padding: `${headerPaddingTop}px 16px 0px 16px`,
position: 'fixed',
top: 0,
backgroundColor: cssVarV2('layer/background/secondary'),
zIndex: 1,
},
]);
export const headerSpace = style([basicHeader]);
export const tabs = style({
height: 56,
height: headerContentHeight,
gap: 16,
display: 'flex',
alignItems: 'center',