fix(mobile): fixed all docs header, remove doc card tags layout strategy (#8104)
close AF-1326
This commit is contained in:
@@ -55,6 +55,7 @@ export const AppTabs = () => {
|
|||||||
className={styles.tabItem}
|
className={styles.tabItem}
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-label={route.to.slice(1)}
|
aria-label={route.to.slice(1)}
|
||||||
|
replaceHistory
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<route.Icon />
|
<route.Icon />
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const appTabs = style({
|
|||||||
backgroundColor: cssVarV2('layer/background/secondary'),
|
backgroundColor: cssVarV2('layer/background/secondary'),
|
||||||
borderTop: `1px solid ${cssVarV2('layer/insideBorder/border')}`,
|
borderTop: `1px solid ${cssVarV2('layer/insideBorder/border')}`,
|
||||||
|
|
||||||
width: '100vw',
|
width: '100dvw',
|
||||||
height: globalVars.appTabHeight,
|
height: globalVars.appTabHeight,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
gap: 15.5,
|
gap: 15.5,
|
||||||
|
|||||||
@@ -7,18 +7,12 @@ export const tags = style({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'center',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
gap: 4,
|
||||||
transition: 'height 0.23s',
|
|
||||||
overflow: 'hidden',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const tag = style({
|
export const tag = style({
|
||||||
visibility: 'hidden',
|
|
||||||
position: 'absolute',
|
|
||||||
// transition: 'all 0.23s',
|
|
||||||
|
|
||||||
padding: '0px 8px',
|
padding: '0px 8px',
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@@ -43,3 +37,8 @@ export const tag = style({
|
|||||||
marginRight: 4,
|
marginRight: 4,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const more = style({
|
||||||
|
fontSize: 16,
|
||||||
|
color: cssVarV2('icon/primary'),
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { observeResize } from '@affine/component';
|
|
||||||
import type { Tag } from '@affine/core/modules/tag';
|
import type { Tag } from '@affine/core/modules/tag';
|
||||||
import { TagService } 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 { useLiveData, useService } from '@toeverything/infra';
|
||||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
|
||||||
|
|
||||||
import * as styles from './tag.css';
|
import * as styles from './tag.css';
|
||||||
|
|
||||||
@@ -23,121 +22,21 @@ const DocCardTag = ({ tag }: { tag: Tag }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GAP = 4;
|
const DocCardTagsRenderer = ({ tags }: { tags: Tag[] }) => {
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className={styles.tags} ref={ulRef} style={{ gap: GAP }}>
|
<ul className={styles.tags}>
|
||||||
{tags.map(tag => (
|
{tags.slice(0, 2).map(tag => (
|
||||||
<DocCardTag key={tag.id} tag={tag} />
|
<DocCardTag key={tag.id} tag={tag} />
|
||||||
))}
|
))}
|
||||||
{/* TODO(@CatsJuice): more icon */}
|
{tags.length > 2 ? <MoreHorizontalIcon className={styles.more} /> : null}
|
||||||
{/* <MoreHorizontalIcon /> */}
|
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DocCardTags = ({
|
export const DocCardTags = ({ docId }: { docId: string; rows?: number }) => {
|
||||||
docId,
|
|
||||||
rows = 2,
|
|
||||||
}: {
|
|
||||||
docId: string;
|
|
||||||
rows?: number;
|
|
||||||
}) => {
|
|
||||||
const tagService = useService(TagService);
|
const tagService = useService(TagService);
|
||||||
const tags = useLiveData(tagService.tagList.tagsByPageId$(docId));
|
const tags = useLiveData(tagService.tagList.tagsByPageId$(docId));
|
||||||
|
|
||||||
if (!tags.length) return null;
|
if (!tags.length) return null;
|
||||||
return <DocCardTagsRenderer tags={tags} rows={rows} />;
|
return <DocCardTagsRenderer tags={tags} />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { IconButton, MobileMenu } from '@affine/component';
|
import { IconButton, MobileMenu } from '@affine/component';
|
||||||
import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
import { MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
import { header } from './style.css';
|
import { header, headerSpace } from './style.css';
|
||||||
import { AllDocsTabs } from './tabs';
|
import { AllDocsTabs } from './tabs';
|
||||||
|
|
||||||
export interface AllDocsHeaderProps {
|
export interface AllDocsHeaderProps {
|
||||||
@@ -10,15 +10,18 @@ export interface AllDocsHeaderProps {
|
|||||||
|
|
||||||
export const AllDocsHeader = ({ operations }: AllDocsHeaderProps) => {
|
export const AllDocsHeader = ({ operations }: AllDocsHeaderProps) => {
|
||||||
return (
|
return (
|
||||||
<header className={header}>
|
<>
|
||||||
<AllDocsTabs />
|
<header className={header}>
|
||||||
<div>
|
<AllDocsTabs />
|
||||||
{operations ? (
|
<div>
|
||||||
<MobileMenu items={operations}>
|
{operations ? (
|
||||||
<IconButton icon={<MoreHorizontalIcon />} />
|
<MobileMenu items={operations}>
|
||||||
</MobileMenu>
|
<IconButton icon={<MoreHorizontalIcon />} />
|
||||||
) : null}
|
</MobileMenu>
|
||||||
</div>
|
) : null}
|
||||||
</header>
|
</div>
|
||||||
|
</header>
|
||||||
|
<div className={headerSpace} />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,32 @@
|
|||||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
export const header = style({
|
const headerContentHeight = 56;
|
||||||
display: 'flex',
|
const headerPaddingTop = 16;
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
gap: 16,
|
|
||||||
padding: '16px 16px 0px 16px',
|
|
||||||
|
|
||||||
position: 'sticky',
|
const basicHeader = style({
|
||||||
top: 0,
|
width: '100%',
|
||||||
backgroundColor: cssVarV2('layer/background/secondary'),
|
height: headerContentHeight + headerPaddingTop,
|
||||||
zIndex: 1,
|
|
||||||
});
|
});
|
||||||
|
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({
|
export const tabs = style({
|
||||||
height: 56,
|
height: headerContentHeight,
|
||||||
gap: 16,
|
gap: 16,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
Reference in New Issue
Block a user