feat(core): move collapsed groups state to masonry

This commit is contained in:
EYHN
2025-05-15 15:36:42 +09:00
parent afa108d517
commit 68b57cdee7
15 changed files with 191 additions and 91 deletions
@@ -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>
);
@@ -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