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 Nov 1, 2024
1 parent 1f6cce2 commit 9b09a3d
Show file tree
Hide file tree
Showing 44 changed files with 1,135 additions and 252 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const InviteModal = ({
confirmButtonOptions={{
loading: isMutating,
variant: 'primary',
['data-testid' as string]: 'confirm-enable-affine-cloud-button',
'data-testid': 'confirm-enable-affine-cloud-button',
}}
onConfirm={handleConfirm}
>
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/component/src/ui/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface ButtonProps
tooltip?: TooltipProps['content'];
tooltipShortcut?: TooltipProps['shortcut'];
tooltipOptions?: Partial<Omit<TooltipProps, 'content' | 'shortcut'>>;
[key: `data-${string}`]: string;
}

const IconSlot = ({
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/component/src/ui/input/row-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type RowInputProps = {
type?: HTMLInputElement['type'];
style?: CSSProperties;
onEnter?: () => void;
[key: `data-${string}`]: string;
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size' | 'onBlur'>;

// RowInput component that is used in the selector layout for search input
Expand Down
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';
4 changes: 3 additions & 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,9 @@ export interface MenuItemProps
export interface MenuSubProps {
children: ReactNode;
items: ReactNode;
triggerOptions?: Omit<MenuItemProps, 'onSelect' | 'children' | 'suffixIcon'>;
triggerOptions?: Omit<MenuItemProps, 'onSelect' | 'children'> & {
[key: `data-${string}`]: string;
};
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
Expand Up @@ -40,7 +40,7 @@ export const WorkspaceDeleteModal = ({
confirmButtonOptions={{
variant: 'error',
disabled: !allowDelete,
['data-testid' as string]: 'delete-workspace-confirm-button',
'data-testid': 'delete-workspace-confirm-button',
}}
{...props}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const Export = ({ exportHandler, className, pageMode }: ExportProps) => {
triggerOptions={{
className: transitionStyle,
prefixIcon: <ExportIcon />,
['data-testid' as string]: 'export-menu',
'data-testid': 'export-menu',
}}
subOptions={{
onOpenChange: handleExportMenuOpenChange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export const Snapshot = ({ className }: SnapshotProps) => {
triggerOptions={{
className: transitionStyle,
prefixIcon: <ToneIcon />,
['data-testid' as string]: 'snapshot-menu',
'data-testid': 'snapshot-menu',
}}
subOptions={{}}
>
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']()}
/>
);
};
Loading

0 comments on commit 9b09a3d

Please sign in to comment.