mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-27 17:22:36 +03:00
fix(mobile): fixed all docs header, remove doc card tags layout strategy (#8104)
close AF-1326
This commit is contained in:
parent
24acce2eac
commit
73dd1d3326
@ -55,6 +55,7 @@ export const AppTabs = () => {
|
||||
className={styles.tabItem}
|
||||
role="tab"
|
||||
aria-label={route.to.slice(1)}
|
||||
replaceHistory
|
||||
>
|
||||
<li>
|
||||
<route.Icon />
|
||||
|
@ -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,
|
||||
|
@ -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'),
|
||||
});
|
||||
|
@ -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} />;
|
||||
};
|
||||
|
@ -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} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user