refactor(mobile): impl all-docs masonry with css grid, close AF-1598 (#8731)

- responsive layout support
   ![CleanShot 2024-11-07 at 11.51.21.gif](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/38b3f11f-59a6-42cc-9a41-8b72ce0998ea.gif)
This commit is contained in:
CatsJuice
2024-11-08 00:48:32 +00:00
parent 06591db8d9
commit d8eda5e42d
4 changed files with 59 additions and 47 deletions

View File

@@ -10,21 +10,36 @@ import {
import type { DocMeta } from '@blocksuite/affine/store';
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
import clsx from 'clsx';
import { forwardRef, type ReactNode } from 'react';
import { forwardRef, type ReactNode, useMemo } from 'react';
import * as styles from './styles.css';
import { DocCardTags } from './tag';
const calcRowsById = (id: string) => {
const [MIN, MAX] = [2, 8];
const code = id.charCodeAt(0);
return Math.floor((code % (MAX - MIN)) + MIN);
};
export interface DocCardProps extends Omit<WorkbenchLinkProps, 'to'> {
meta: {
id: DocMeta['id'];
title?: ReactNode;
} & { [key: string]: any };
showTags?: boolean;
/**
* When enabled, preview's height will be calculated based on `meta.id`
*/
autoHeightById?: boolean;
}
export const DocCard = forwardRef<HTMLAnchorElement, DocCardProps>(
function DocCard({ showTags = true, meta, className, ...attrs }, ref) {
function DocCard(
{ showTags = true, meta, className, autoHeightById, ...attrs },
ref
) {
const favAdapter = useService(CompatibleFavoriteItemsAdapter);
const workspace = useService(WorkspaceService).workspace;
@@ -38,6 +53,12 @@ export const DocCard = forwardRef<HTMLAnchorElement, DocCardProps>(
[favAdapter, meta.id]
);
const contentStyle = useMemo(() => {
if (!autoHeightById) return { flex: 1 };
const rows = calcRowsById(meta.id);
return { height: `${rows * 18}px` };
}, [autoHeightById, meta.id]);
return (
<WorkbenchLink
to={`/${meta.id}`}
@@ -57,7 +78,7 @@ export const DocCard = forwardRef<HTMLAnchorElement, DocCardProps>(
}
/>
</header>
<main className={styles.content}>
<main className={styles.content} style={contentStyle}>
<PagePreview
docCollection={workspace.docCollection}
pageId={meta.id}

View File

@@ -43,7 +43,6 @@ export const untitled = style({
export const content = style([
footnoteRegular,
{
flex: 1,
overflow: 'hidden',
},
]);

View File

@@ -1,28 +1,13 @@
import { style } from '@vanilla-extract/css';
export const invisibleWrapper = style({
position: 'absolute',
padding: 'inherit',
width: '100%',
height: 0,
overflow: 'hidden',
visibility: 'hidden',
pointerEvents: 'none',
export const paddingX = 16;
export const columnGap = 17;
export const masonry = style({
padding: `16px ${paddingX}px`,
columnGap: columnGap,
});
export const invisibleList = style({
width: `calc(50% - 17px / 2)`,
});
export const stacks = style({
position: 'relative',
width: '100%',
display: 'flex',
gap: 17,
padding: 16,
});
export const stack = style({
width: 0,
flex: 1,
display: 'flex',
flexDirection: 'column',
gap: 10,
export const masonryItem = style({
breakInside: 'avoid',
marginBottom: 10,
});

View File

@@ -1,10 +1,19 @@
import { useGlobalEvent } from '@affine/core/mobile/hooks/use-global-events';
import type { DocMeta } from '@blocksuite/affine/store';
import { useMemo } from 'react';
import { useCallback, useState } from 'react';
import { DocCard } from '../../../components';
import * as styles from './masonry.css';
// TODO(@CatsJuice): Large amount docs performance
const calcColumnCount = () => {
const maxCardWidth = 220;
const windowWidth = window.innerWidth;
const newColumnCount = Math.floor(
(windowWidth - styles.paddingX * 2 - styles.columnGap) / maxCardWidth
);
return Math.max(newColumnCount, 2);
};
export const MasonryDocs = ({
items,
showTags,
@@ -12,25 +21,23 @@ export const MasonryDocs = ({
items: DocMeta[];
showTags?: boolean;
}) => {
// card preview is loaded lazily, it's meaningless to calculate height
const stacks = useMemo(() => {
return items.reduce(
(acc, item, i) => {
acc[i % 2].push(item);
return acc;
},
[[], []] as [DocMeta[], DocMeta[]]
);
}, [items]);
const [columnCount, setColumnCount] = useState(calcColumnCount);
const updateColumnCount = useCallback(() => {
setColumnCount(calcColumnCount());
}, []);
useGlobalEvent('resize', updateColumnCount);
return (
<div className={styles.stacks}>
{stacks.map((stack, i) => (
<ul key={i} className={styles.stack}>
{stack.map(item => (
<DocCard showTags={showTags} key={item.id} meta={item} />
))}
</ul>
<div className={styles.masonry} style={{ columnCount }}>
{items.map(item => (
<DocCard
key={item.id}
className={styles.masonryItem}
showTags={showTags}
meta={item}
autoHeightById
/>
))}
</div>
);