mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-19 02:51:47 +08:00
feat(core): move collapsed groups state to masonry
This commit is contained in:
@@ -7,7 +7,6 @@ import type { ExplorerPreference } from './types';
|
||||
export type DocExplorerContextType = {
|
||||
view$: LiveData<DocListItemView>;
|
||||
groups$: LiveData<Array<{ key: string; items: string[] }>>;
|
||||
collapsedGroups$: LiveData<string[]>;
|
||||
selectMode$?: LiveData<boolean>;
|
||||
selectedDocIds$: LiveData<string[]>;
|
||||
prevCheckAnchorId$?: LiveData<string | null>;
|
||||
@@ -25,7 +24,6 @@ export const createDocExplorerContext = () =>
|
||||
({
|
||||
view$: new LiveData<DocListItemView>('list'),
|
||||
groups$: new LiveData<Array<{ key: string; items: string[] }>>([]),
|
||||
collapsedGroups$: new LiveData<string[]>([]),
|
||||
selectMode$: new LiveData<boolean>(false),
|
||||
selectedDocIds$: new LiveData<string[]>([]),
|
||||
prevCheckAnchorId$: new LiveData<string | null>(null),
|
||||
|
||||
@@ -16,16 +16,19 @@ import * as styles from './group-header.css';
|
||||
export const DocGroupHeader = ({
|
||||
className,
|
||||
groupId,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
...props
|
||||
}: HTMLAttributes<HTMLDivElement> & {
|
||||
groupId: string;
|
||||
collapsed: boolean;
|
||||
onCollapse: (collapsed: boolean) => void;
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const contextValue = useContext(DocExplorerContext);
|
||||
|
||||
const groups = useLiveData(contextValue.groups$);
|
||||
const selectedDocIds = useLiveData(contextValue.selectedDocIds$);
|
||||
const collapsedGroups = useLiveData(contextValue.collapsedGroups$);
|
||||
const selectMode = useLiveData(contextValue.selectMode$);
|
||||
|
||||
const group = groups.find(g => g.key === groupId);
|
||||
@@ -34,13 +37,8 @@ export const DocGroupHeader = ({
|
||||
);
|
||||
|
||||
const handleToggleCollapse = useCallback(() => {
|
||||
const prev = contextValue.collapsedGroups$.value;
|
||||
contextValue.collapsedGroups$.next(
|
||||
prev.includes(groupId)
|
||||
? prev.filter(id => id !== groupId)
|
||||
: [...prev, groupId]
|
||||
);
|
||||
}, [groupId, contextValue]);
|
||||
onCollapse(!collapsed);
|
||||
}, [collapsed, onCollapse]);
|
||||
|
||||
const handleSelectAll = useCallback(() => {
|
||||
const prev = contextValue.selectedDocIds$.value;
|
||||
@@ -64,10 +62,7 @@ export const DocGroupHeader = ({
|
||||
).length;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.groupHeader}
|
||||
data-collapsed={collapsedGroups.includes(groupId)}
|
||||
>
|
||||
<div className={styles.groupHeader} data-collapsed={collapsed}>
|
||||
<div className={clsx(styles.content, className)} {...props} />
|
||||
{selectMode ? (
|
||||
<div className={styles.selectInfo}>
|
||||
@@ -105,6 +100,8 @@ export const PlainTextDocGroupHeader = ({
|
||||
...props
|
||||
}: HTMLAttributes<HTMLDivElement> & {
|
||||
groupId: string;
|
||||
collapsed: boolean;
|
||||
onCollapse: (collapsed: boolean) => void;
|
||||
docCount: number;
|
||||
icon?: ReactNode;
|
||||
}) => {
|
||||
|
||||
@@ -30,4 +30,5 @@ export interface GroupHeaderProps {
|
||||
groupId: string;
|
||||
docCount: number;
|
||||
collapsed: boolean;
|
||||
onCollapse: (collapsed: boolean) => void;
|
||||
}
|
||||
|
||||
@@ -100,10 +100,17 @@ export const CheckboxDocListProperty = ({
|
||||
export const CheckboxGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const text = groupId === 'true' ? 'Checked' : 'Unchecked';
|
||||
return (
|
||||
<PlainTextDocGroupHeader docCount={docCount} groupId={groupId}>
|
||||
<PlainTextDocGroupHeader
|
||||
docCount={docCount}
|
||||
groupId={groupId}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
{text}
|
||||
</PlainTextDocGroupHeader>
|
||||
);
|
||||
|
||||
+8
-1
@@ -166,11 +166,18 @@ export const UpdatedByDocListInlineProperty = ({
|
||||
export const ModifiedByGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const userId = groupId;
|
||||
|
||||
return (
|
||||
<PlainTextDocGroupHeader groupId={groupId} docCount={docCount}>
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
<div className={styles.userLabelContainer}>
|
||||
<PublicUserLabel id={userId} size={20} showName={false} />
|
||||
</div>
|
||||
|
||||
@@ -243,11 +243,21 @@ export const UpdatedDateDocListProperty = ({ doc }: DocListPropertyProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const DateGroupHeader = ({ groupId, docCount }: GroupHeaderProps) => {
|
||||
export const DateGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const date = groupId || 'No Date';
|
||||
|
||||
return (
|
||||
<PlainTextDocGroupHeader groupId={groupId} docCount={docCount}>
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
{date}
|
||||
</PlainTextDocGroupHeader>
|
||||
);
|
||||
|
||||
@@ -137,6 +137,8 @@ export const DocPrimaryModeDocListProperty = ({
|
||||
export const DocPrimaryModeGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const t = useI18n();
|
||||
const text =
|
||||
@@ -147,7 +149,12 @@ export const DocPrimaryModeGroupHeader = ({
|
||||
: 'Default';
|
||||
|
||||
return (
|
||||
<PlainTextDocGroupHeader groupId={groupId} docCount={docCount}>
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
{text}
|
||||
</PlainTextDocGroupHeader>
|
||||
);
|
||||
|
||||
@@ -236,10 +236,20 @@ export const JournalDocListProperty = ({ doc }: DocListPropertyProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const JournalGroupHeader = ({ groupId, docCount }: GroupHeaderProps) => {
|
||||
export const JournalGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const text = groupId === 'true' ? 'Journal' : 'Not Journal';
|
||||
return (
|
||||
<PlainTextDocGroupHeader groupId={groupId} docCount={docCount}>
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
{text}
|
||||
</PlainTextDocGroupHeader>
|
||||
);
|
||||
|
||||
@@ -202,7 +202,12 @@ export const TagsDocListProperty = ({ doc }: DocListPropertyProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const TagsGroupHeader = ({ groupId, docCount }: GroupHeaderProps) => {
|
||||
export const TagsGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const t = useI18n();
|
||||
const tagService = useService(TagService);
|
||||
const tag = useLiveData(tagService.tagList.tagByTagId$(groupId));
|
||||
@@ -212,6 +217,8 @@ export const TagsGroupHeader = ({ groupId, docCount }: GroupHeaderProps) => {
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
icon={
|
||||
<div
|
||||
style={{
|
||||
@@ -231,6 +238,8 @@ export const TagsGroupHeader = ({ groupId, docCount }: GroupHeaderProps) => {
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
icon={<TagIcon tag={tag} />}
|
||||
>
|
||||
<TagName tag={tag} />
|
||||
|
||||
@@ -260,10 +260,20 @@ export const TextDocListProperty = ({ value }: { value: string }) => {
|
||||
return <StackProperty icon={<TextTypeIcon />}>{value}</StackProperty>;
|
||||
};
|
||||
|
||||
export const TextGroupHeader = ({ groupId, docCount }: GroupHeaderProps) => {
|
||||
export const TextGroupHeader = ({
|
||||
groupId,
|
||||
docCount,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
}: GroupHeaderProps) => {
|
||||
const text = groupId || 'No Text';
|
||||
return (
|
||||
<PlainTextDocGroupHeader groupId={groupId} docCount={docCount}>
|
||||
<PlainTextDocGroupHeader
|
||||
groupId={groupId}
|
||||
docCount={docCount}
|
||||
collapsed={collapsed}
|
||||
onCollapse={onCollapse}
|
||||
>
|
||||
{text}
|
||||
</PlainTextDocGroupHeader>
|
||||
);
|
||||
|
||||
@@ -50,10 +50,12 @@ import { PinnedCollections } from './pinned-collections';
|
||||
const GroupHeader = memo(function GroupHeader({
|
||||
groupId,
|
||||
collapsed,
|
||||
onCollapse,
|
||||
itemCount,
|
||||
}: {
|
||||
groupId: string;
|
||||
collapsed?: boolean;
|
||||
onCollapse: (collapsed: boolean) => void;
|
||||
itemCount: number;
|
||||
}) {
|
||||
const contextValue = useContext(DocExplorerContext);
|
||||
@@ -76,12 +78,21 @@ const GroupHeader = memo(function GroupHeader({
|
||||
groupId={groupId}
|
||||
docCount={itemCount}
|
||||
collapsed={!!collapsed}
|
||||
onCollapse={onCollapse}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return '// TODO: ' + groupType;
|
||||
}
|
||||
}, [allProperties, collapsed, groupId, groupKey, groupType, itemCount]);
|
||||
}, [
|
||||
allProperties,
|
||||
collapsed,
|
||||
groupId,
|
||||
groupKey,
|
||||
groupType,
|
||||
itemCount,
|
||||
onCollapse,
|
||||
]);
|
||||
|
||||
if (!groupType) {
|
||||
return null;
|
||||
@@ -166,7 +177,6 @@ export const AllPage = () => {
|
||||
const orderBy = useLiveData(explorerContextValue.orderBy$);
|
||||
const groups = useLiveData(explorerContextValue.groups$);
|
||||
const selectedDocIds = useLiveData(explorerContextValue.selectedDocIds$);
|
||||
const collapsedGroups = useLiveData(explorerContextValue.collapsedGroups$);
|
||||
const selectMode = useLiveData(explorerContextValue.selectMode$);
|
||||
|
||||
const { openPromptModal } = usePromptModal();
|
||||
@@ -175,27 +185,15 @@ export const AllPage = () => {
|
||||
const items = groups.map((group: any) => {
|
||||
return {
|
||||
id: group.key,
|
||||
Component: groups.length > 1 ? GroupHeader : undefined,
|
||||
height: groups.length > 1 ? 24 : 0,
|
||||
className: styles.groupHeader,
|
||||
items: group.items.map((docId: string) => {
|
||||
return {
|
||||
id: docId,
|
||||
Component: DocListItemComponent,
|
||||
height:
|
||||
view === 'list'
|
||||
? 42
|
||||
: view === 'grid'
|
||||
? 280
|
||||
: calcCardHeightById(docId),
|
||||
'data-view': view,
|
||||
className: styles.docItem,
|
||||
};
|
||||
}),
|
||||
} satisfies MasonryGroup;
|
||||
});
|
||||
return items;
|
||||
}, [groups, view]);
|
||||
}, [groups]);
|
||||
|
||||
const collectionRulesService = useService(CollectionRulesService);
|
||||
useEffect(() => {
|
||||
@@ -443,11 +441,24 @@ export const AllPage = () => {
|
||||
preloadHeight={100}
|
||||
itemWidth={'stretch'}
|
||||
virtualScroll
|
||||
collapsedGroups={collapsedGroups}
|
||||
groupComponent={GroupHeader}
|
||||
itemComponent={DocListItemComponent}
|
||||
groupClassName={styles.groupHeader}
|
||||
itemClassName={styles.docItem}
|
||||
paddingX={useCallback(
|
||||
(w: number) => (w > 500 ? 24 : w > 393 ? 20 : 16),
|
||||
[]
|
||||
)}
|
||||
groupHeight={groupBy ? 24 : 0}
|
||||
itemHeight={useMemo(
|
||||
() =>
|
||||
view === 'list'
|
||||
? 42
|
||||
: view === 'grid'
|
||||
? 280
|
||||
: item => calcCardHeightById(item.id),
|
||||
[view]
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -136,7 +136,8 @@ export class DocCreatedByUpdatedBySyncService extends Service {
|
||||
workspaceRootDocSynced &&
|
||||
isOwnerOrAdmin &&
|
||||
missingCreatedBy &&
|
||||
!markedSynced
|
||||
!markedSynced &&
|
||||
this.workspaceService.workspace.flavour !== 'local'
|
||||
)
|
||||
),
|
||||
false
|
||||
|
||||
Reference in New Issue
Block a user