mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08: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 * as styles from './styles.css';
|
||||||
import { DocCardTags } from './tag';
|
import { DocCardTags } from './tag';
|
||||||
|
|
||||||
const calcRowsById = (id: string) => {
|
export const calcRowsById = (id: string, min = 2, max = 8) => {
|
||||||
const [MIN, MAX] = [2, 8];
|
|
||||||
|
|
||||||
const code = id.charCodeAt(0);
|
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'> {
|
export interface DocCardProps extends Omit<WorkbenchLinkProps, 'to'> {
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ import { style } from '@vanilla-extract/css';
|
|||||||
export const paddingX = 16;
|
export const paddingX = 16;
|
||||||
export const columnGap = 17;
|
export const columnGap = 17;
|
||||||
|
|
||||||
export const masonry = style({
|
export const columns = style({
|
||||||
padding: `16px ${paddingX}px`,
|
padding: `16px ${paddingX}px`,
|
||||||
columnGap: columnGap,
|
display: 'flex',
|
||||||
|
gap: columnGap,
|
||||||
});
|
});
|
||||||
export const masonryItem = style({
|
|
||||||
breakInside: 'avoid',
|
export const column = style({
|
||||||
marginBottom: 10,
|
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 { useGlobalEvent } from '@affine/core/mobile/hooks/use-global-events';
|
||||||
import type { DocMeta } from '@blocksuite/affine/store';
|
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';
|
import * as styles from './masonry.css';
|
||||||
|
|
||||||
const calcColumnCount = () => {
|
const calcColumnCount = () => {
|
||||||
@@ -14,6 +14,20 @@ const calcColumnCount = () => {
|
|||||||
return Math.max(newColumnCount, 2);
|
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 = ({
|
export const MasonryDocs = ({
|
||||||
items,
|
items,
|
||||||
showTags,
|
showTags,
|
||||||
@@ -28,16 +42,24 @@ export const MasonryDocs = ({
|
|||||||
}, []);
|
}, []);
|
||||||
useGlobalEvent('resize', updateColumnCount);
|
useGlobalEvent('resize', updateColumnCount);
|
||||||
|
|
||||||
|
const columns = useMemo(
|
||||||
|
() => calcColumns(items, columnCount),
|
||||||
|
[items, columnCount]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.masonry} style={{ columnCount }}>
|
<div className={styles.columns}>
|
||||||
{items.map(item => (
|
{columns.map((col, index) => (
|
||||||
<DocCard
|
<div key={`${columnCount}-${index}`} className={styles.column}>
|
||||||
key={item.id}
|
{col.map(item => (
|
||||||
className={styles.masonryItem}
|
<DocCard
|
||||||
showTags={showTags}
|
key={item.id}
|
||||||
meta={item}
|
showTags={showTags}
|
||||||
autoHeightById
|
meta={item}
|
||||||
/>
|
autoHeightById
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user