mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
feat(mobile): impl masonry docs with flex and predict card height (#8849)
previous `columns` implementation has some limitation: - the card order is not as expected - there may be strange shadow on top
This commit is contained in:
@@ -17,11 +17,9 @@ 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];
|
||||
|
||||
export const calcRowsById = (id: string, min = 2, max = 8) => {
|
||||
const code = id.charCodeAt(0);
|
||||
return Math.floor((code % (MAX - MIN)) + MIN);
|
||||
return Math.floor((code % (max - min)) + min);
|
||||
};
|
||||
|
||||
export interface DocCardProps extends Omit<WorkbenchLinkProps, 'to'> {
|
||||
|
||||
@@ -3,11 +3,16 @@ import { style } from '@vanilla-extract/css';
|
||||
export const paddingX = 16;
|
||||
export const columnGap = 17;
|
||||
|
||||
export const masonry = style({
|
||||
export const columns = style({
|
||||
padding: `16px ${paddingX}px`,
|
||||
columnGap: columnGap,
|
||||
display: 'flex',
|
||||
gap: columnGap,
|
||||
});
|
||||
export const masonryItem = style({
|
||||
breakInside: 'avoid',
|
||||
marginBottom: 10,
|
||||
|
||||
export const column = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 10,
|
||||
width: 0,
|
||||
flex: 1,
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useGlobalEvent } from '@affine/core/mobile/hooks/use-global-events';
|
||||
import type { DocMeta } from '@blocksuite/affine/store';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { DocCard } from '../../../components';
|
||||
import { calcRowsById, DocCard } from '../../../components';
|
||||
import * as styles from './masonry.css';
|
||||
|
||||
const calcColumnCount = () => {
|
||||
@@ -14,6 +14,20 @@ const calcColumnCount = () => {
|
||||
return Math.max(newColumnCount, 2);
|
||||
};
|
||||
|
||||
const calcColumns = (items: DocMeta[], length: number) => {
|
||||
const columns = Array.from({ length }, () => [] as DocMeta[]);
|
||||
const heights = Array.from({ length }, () => 0);
|
||||
|
||||
items.forEach(item => {
|
||||
const itemHeight = calcRowsById(item.id);
|
||||
const minHeightIndex = heights.indexOf(Math.min(...heights));
|
||||
heights[minHeightIndex] += itemHeight;
|
||||
columns[minHeightIndex].push(item);
|
||||
});
|
||||
|
||||
return columns;
|
||||
};
|
||||
|
||||
export const MasonryDocs = ({
|
||||
items,
|
||||
showTags,
|
||||
@@ -28,16 +42,24 @@ export const MasonryDocs = ({
|
||||
}, []);
|
||||
useGlobalEvent('resize', updateColumnCount);
|
||||
|
||||
const columns = useMemo(
|
||||
() => calcColumns(items, columnCount),
|
||||
[items, columnCount]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.masonry} style={{ columnCount }}>
|
||||
{items.map(item => (
|
||||
<DocCard
|
||||
key={item.id}
|
||||
className={styles.masonryItem}
|
||||
showTags={showTags}
|
||||
meta={item}
|
||||
autoHeightById
|
||||
/>
|
||||
<div className={styles.columns}>
|
||||
{columns.map((col, index) => (
|
||||
<div key={`${columnCount}-${index}`} className={styles.column}>
|
||||
{col.map(item => (
|
||||
<DocCard
|
||||
key={item.id}
|
||||
showTags={showTags}
|
||||
meta={item}
|
||||
autoHeightById
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user