Skip to content

Commit

Permalink
feat(mobile): explorer create/rename operation
Browse files Browse the repository at this point in the history
  • Loading branch information
CatsJuice committed Oct 31, 2024
1 parent fffd60d commit 7a52a71
Show file tree
Hide file tree
Showing 34 changed files with 877 additions and 238 deletions.
2 changes: 1 addition & 1 deletion packages/frontend/component/src/ui/menu/desktop/sub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export const DesktopMenuSub = ({
} = {},
}: MenuSubProps) => {
const { className, children, otherProps } = useMenuItem({
...triggerOptions,
children: propsChildren,
suffixIcon: <ArrowRightSmallIcon />,
...triggerOptions,
});

return (
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/component/src/ui/menu/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ export {
};

export { Menu, MenuItem, MenuSeparator, MenuSub, MenuTrigger };
export * from './mobile/hook';
2 changes: 1 addition & 1 deletion packages/frontend/component/src/ui/menu/menu.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export interface MenuItemProps
export interface MenuSubProps {
children: ReactNode;
items: ReactNode;
triggerOptions?: Omit<MenuItemProps, 'onSelect' | 'children' | 'suffixIcon'>;
triggerOptions?: Omit<MenuItemProps, 'onSelect' | 'children'>;
portalOptions?: Omit<DropdownMenuPortalProps, 'children'>;
subOptions?: Omit<DropdownMenuSubProps, 'children'>;
subContentOptions?: Omit<DropdownMenuSubContentProps, 'children'>;
Expand Down
5 changes: 5 additions & 0 deletions packages/frontend/component/src/ui/menu/mobile/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import {
import type { MenuSubProps } from '../menu.types';

export type SubMenuContent = {
/**
* Customize submenu's title
* @default "Back"
*/
title?: string;
items: ReactNode;
contentOptions?: MenuSubProps['subContentOptions'];
};
Expand Down
18 changes: 18 additions & 0 deletions packages/frontend/component/src/ui/menu/mobile/hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useCallback, useContext } from 'react';

import { MobileMenuContext } from './context';

export const useMobileMenuController = () => {
const context = useContext(MobileMenuContext);

/**
* **A workaround to close mobile menu manually**
* By default, it will close automatically when `MenuItem` clicked.
* For custom menu content, you can use this method to close the menu.
*/
const close = useCallback(() => {
context.setOpen?.(false);
}, [context]);

return { close };
};
4 changes: 2 additions & 2 deletions packages/frontend/component/src/ui/menu/mobile/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ export const MobileMenu = ({
className={styles.backButton}
prefix={<ArrowLeftSmallIcon />}
onClick={() => setSubMenus(prev => prev.slice(0, index))}
prefixStyle={{ width: 20, height: 20 }}
prefixStyle={{ width: 24, height: 24 }}
>
{t['com.affine.backButton']()}
{sub.title || t['com.affine.backButton']()}
</Button>

{sub.items}
Expand Down
16 changes: 11 additions & 5 deletions packages/frontend/component/src/ui/menu/mobile/sub.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,28 @@ import { useMenuItem } from '../use-menu-item';
import { MobileMenuContext } from './context';

export const MobileMenuSub = ({
title,
children: propsChildren,
items,
triggerOptions,
subContentOptions: contentOptions = {},
}: MenuSubProps) => {
}: MenuSubProps & { title?: string }) => {
const {
className,
children,
otherProps: { onClick, ...otherTriggerOptions },
} = useMenuItem({
...triggerOptions,
children: propsChildren,
suffixIcon: <ArrowRightSmallPlusIcon />,
...triggerOptions,
});

return (
<MobileMenuSubRaw
onClick={onClick}
items={items}
subContentOptions={contentOptions}
title={title}
>
<div className={className} {...otherTriggerOptions}>
{children}
Expand All @@ -36,19 +38,23 @@ export const MobileMenuSub = ({
};

export const MobileMenuSubRaw = ({
title,
onClick,
children,
items,
subContentOptions: contentOptions = {},
}: MenuSubProps & { onClick?: (e: MouseEvent<HTMLDivElement>) => void }) => {
}: MenuSubProps & {
onClick?: (e: MouseEvent<HTMLDivElement>) => void;
title?: string;
}) => {
const { setSubMenus } = useContext(MobileMenuContext);

const onItemClick = useCallback(
(e: MouseEvent<HTMLDivElement>) => {
onClick?.(e);
setSubMenus(prev => [...prev, { items, contentOptions }]);
setSubMenus(prev => [...prev, { items, contentOptions, title }]);
},
[contentOptions, items, onClick, setSubMenus]
[contentOptions, items, onClick, setSubMenus, title]
);

return <Slot onClick={onItemClick}>{children}</Slot>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useI18n } from '@affine/i18n';

import {
RenameDialog,
type RenameDialogProps,
RenameSubMenu,
type RenameSubMenuProps,
} from '../../../rename';

export const CollectionRenameSubMenu = ({
title,
text,
...props
}: RenameSubMenuProps) => {
const t = useI18n();
return (
<RenameSubMenu
title={title || t['com.affine.m.explorer.collection.rename-menu-title']()}
text={text || t['com.affine.m.explorer.collection.rename']()}
{...props}
/>
);
};

const CollectionDesc = () => {
const t = useI18n();
return t['com.affine.collection.emptyCollectionDescription']();
};

export const CollectionRenameDialog = ({
title,
confirmText,
...props
}: RenameDialogProps) => {
return (
<RenameDialog
title={title}
confirmText={confirmText}
{...props}
descRenderer={CollectionDesc}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,23 +52,6 @@ export const ExplorerCollectionNode = ({

const collection = useLiveData(collectionService.collection$(collectionId));

const handleRename = useCallback(
(name: string) => {
if (collection && collection.name !== name) {
collectionService.updateCollection(collectionId, () => ({
...collection,
name,
}));

track.$.navigationPanel.organize.renameOrganizeItem({
type: 'collection',
});
notify.success({ message: t['com.affine.toastMessage.rename']() });
}
},
[collection, collectionId, collectionService, t]
);

const handleOpenCollapsed = useCallback(() => {
setCollapsed(false);
}, []);
Expand Down Expand Up @@ -105,7 +88,7 @@ export const ExplorerCollectionNode = ({
return [...additionalOperations, ...collectionOperations];
}
return collectionOperations;
}, [collectionOperations, additionalOperations]);
}, [additionalOperations, collectionOperations]);

if (!collection) {
return null;
Expand All @@ -115,12 +98,10 @@ export const ExplorerCollectionNode = ({
<ExplorerTreeNode
icon={CollectionIcon}
name={collection.name || t['Untitled']()}
renameable
collapsed={collapsed}
setCollapsed={setCollapsed}
to={`/collection/${collection.id}`}
active={active}
onRename={handleRename}
operations={finalOperations}
data-testid={`explorer-collection-${collectionId}`}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
IconButton,
MenuItem,
MenuSeparator,
notify,
useConfirmModal,
} from '@affine/component';
import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-page-list/utils';
Expand All @@ -28,6 +29,8 @@ import {
} from '@toeverything/infra';
import { useCallback, useMemo } from 'react';

import { CollectionRenameSubMenu } from './dialog';

export const useExplorerCollectionNodeOperations = (
collectionId: string,
onOpenCollapsed: () => void,
Expand Down Expand Up @@ -113,6 +116,24 @@ export const useExplorerCollectionNodeOperations = (
onOpenEdit();
}, [onOpenEdit]);

const handleRename = useCallback(
(name: string) => {
const collection = collectionService.collection$(collectionId).value;
if (collection && collection.name !== name) {
collectionService.updateCollection(collectionId, () => ({
...collection,
name,
}));

track.$.navigationPanel.organize.renameOrganizeItem({
type: 'collection',
});
notify.success({ message: t['com.affine.toastMessage.rename']() });
}
},
[collectionId, collectionService, t]
);

return useMemo(
() => ({
favorite,
Expand All @@ -122,13 +143,15 @@ export const useExplorerCollectionNodeOperations = (
handleOpenInSplitView,
handleShowEdit,
handleToggleFavoriteCollection,
handleRename,
}),
[
favorite,
handleAddDocToCollection,
handleDeleteCollection,
handleOpenInNewTab,
handleOpenInSplitView,
handleRename,
handleShowEdit,
handleToggleFavoriteCollection,
]
Expand All @@ -154,6 +177,7 @@ export const useExplorerCollectionNodeOperationsMenu = (
handleOpenInSplitView,
handleShowEdit,
handleToggleFavoriteCollection,
handleRename,
} = useExplorerCollectionNodeOperations(
collectionId,
onOpenCollapsed,
Expand All @@ -177,6 +201,14 @@ export const useExplorerCollectionNodeOperationsMenu = (
</IconButton>
),
},
{
index: 10,
view: <CollectionRenameSubMenu onConfirm={handleRename} />,
},
{
index: 11,
view: <MenuSeparator />,
},
{
index: 99,
view: (
Expand Down Expand Up @@ -256,6 +288,7 @@ export const useExplorerCollectionNodeOperationsMenu = (
handleDeleteCollection,
handleOpenInNewTab,
handleOpenInSplitView,
handleRename,
handleShowEdit,
handleToggleFavoriteCollection,
t,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useI18n } from '@affine/i18n';

import { RenameSubMenu, type RenameSubMenuProps } from '../../../rename';

export const DocRenameSubMenu = ({ title, text }: RenameSubMenuProps) => {
const t = useI18n();
return (
<RenameSubMenu
title={title || t['com.affine.m.explorer.doc.rename']()}
text={text || t['com.affine.m.explorer.doc.rename']()}
/>
);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Loading } from '@affine/component';
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { DocInfoService } from '@affine/core/modules/doc-info';
import { DocsSearchService } from '@affine/core/modules/docs-search';
import type { NodeOperation } from '@affine/core/modules/explorer';
import { useI18n } from '@affine/i18n';
import track from '@affine/track';
import {
DocsService,
FeatureFlagService,
Expand Down Expand Up @@ -92,14 +90,6 @@ export const ExplorerDocNode = ({
);
}, [indexerLoading]);

const handleRename = useAsyncCallback(
async (newName: string) => {
await docsService.changeDocTitle(docId, newName);
track.$.navigationPanel.organize.renameOrganizeItem({ type: 'doc' });
},
[docId, docsService]
);

const docInfoModal = useService(DocInfoService).modal;
const option = useMemo(
() => ({
Expand All @@ -126,7 +116,6 @@ export const ExplorerDocNode = ({
<ExplorerTreeNode
icon={Icon}
name={t.t(docTitle)}
renameable
extractEmojiAsIcon={enableEmojiIcon}
collapsed={collapsed}
setCollapsed={setCollapsed}
Expand All @@ -140,7 +129,6 @@ export const ExplorerDocNode = ({
</div>
)
}
onRename={handleRename}
operations={finalOperations}
data-testid={`explorer-doc-${docId}`}
>
Expand Down
Loading

0 comments on commit 7a52a71

Please sign in to comment.