feat(component): support sort workspace card (#1837)

This commit is contained in:
Himself65
2023-04-06 13:21:45 -05:00
committed by GitHub
parent 773554bbac
commit b6bdf257e4
12 changed files with 366 additions and 32 deletions

View File

@@ -17,11 +17,14 @@
"@affine/debug": "workspace:*",
"@affine/i18n": "workspace:*",
"@affine/jotai": "workspace:*",
"@affine/workspace": "workspace:^",
"@blocksuite/blocks": "0.0.0-20230406032111-01cf598b-nightly",
"@blocksuite/editor": "0.0.0-20230406032111-01cf598b-nightly",
"@blocksuite/global": "0.0.0-20230406032111-01cf598b-nightly",
"@blocksuite/icons": "2.1.5",
"@blocksuite/store": "0.0.0-20230406032111-01cf598b-nightly",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@emotion/cache": "^11.10.7",
"@emotion/react": "^11.10.6",
"@emotion/server": "^11.10.0",

View File

@@ -4,7 +4,7 @@ import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { SettingsIcon } from '@blocksuite/icons';
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-blocksuite-workspace-name';
import type React from 'react';
import type { FC, MouseEvent } from 'react';
import { useCallback } from 'react';
import { WorkspaceAvatar } from '../workspace-avatar';
@@ -45,7 +45,7 @@ const PublishIcon = () => {
return <DefaultPublishIcon style={{ color: '#8699FF' }} />;
};
const WorkspaceType: React.FC<WorkspaceTypeProps> = ({ workspace }) => {
const WorkspaceType: FC<WorkspaceTypeProps> = ({ workspace }) => {
const { t } = useTranslation();
let isOwner = true;
if (workspace.flavour === WorkspaceFlavour.AFFINE) {
@@ -83,7 +83,7 @@ export type WorkspaceCardProps = {
onSettingClick: (workspace: AffineWorkspace | LocalWorkspace) => void;
};
export const WorkspaceCard: React.FC<WorkspaceCardProps> = ({
export const WorkspaceCard: FC<WorkspaceCardProps> = ({
workspace,
onClick,
onSettingClick,
@@ -95,9 +95,12 @@ export const WorkspaceCard: React.FC<WorkspaceCardProps> = ({
return (
<StyledCard
data-testid="workspace-card"
onClick={useCallback(() => {
onClick(workspace);
}, [onClick, workspace])}
onClick={useCallback(
(event: MouseEvent) => {
onClick(workspace);
},
[onClick, workspace]
)}
active={workspace.id === currentWorkspaceId}
>
<WorkspaceAvatar size={58} workspace={workspace} />

View File

@@ -0,0 +1,64 @@
import { WorkspaceFlavour } from '@affine/workspace/type';
import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils';
import { arrayMove } from '@dnd-kit/sortable';
import type { Meta } from '@storybook/react';
import { useState } from 'react';
import type { WorkspaceListProps } from './index';
import { WorkspaceList } from './index';
export default {
title: 'AFFiNE/WorkspaceList',
component: WorkspaceList,
} satisfies Meta<WorkspaceListProps>;
export const Default = () => {
const [items, setItems] = useState(() => {
const items = [
{
id: '1',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: createEmptyBlockSuiteWorkspace('1'),
providers: [],
},
{
id: '2',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: createEmptyBlockSuiteWorkspace('2'),
providers: [],
},
{
id: '3',
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace: createEmptyBlockSuiteWorkspace('3'),
providers: [],
},
] satisfies WorkspaceListProps['items'];
items.forEach(item => {
item.blockSuiteWorkspace.meta.setName(item.id);
});
return items;
});
return (
<WorkspaceList
currentWorkspaceId={null}
items={items}
onClick={() => {}}
onSettingClick={() => {}}
onDragEnd={event => {
const { active, over } = event;
if (active.id !== over?.id) {
setItems(items => {
const oldIndex = items.findIndex(item => item.id === active.id);
const newIndex = items.findIndex(item => item.id === over?.id);
return arrayMove(items, oldIndex, newIndex);
});
}
}}
/>
);
};

View File

@@ -0,0 +1,70 @@
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import type { DragEndEvent } from '@dnd-kit/core';
import {
DndContext,
PointerSensor,
useSensor,
useSensors,
} from '@dnd-kit/core';
import { SortableContext, useSortable } from '@dnd-kit/sortable';
import type { FC } from 'react';
import { WorkspaceCard } from '../workspace-card';
export type WorkspaceListProps = {
currentWorkspaceId: string | null;
items: (AffineWorkspace | LocalWorkspace)[];
onClick: (workspace: AffineWorkspace | LocalWorkspace) => void;
onSettingClick: (workspace: AffineWorkspace | LocalWorkspace) => void;
onDragEnd: (event: DragEndEvent) => void;
};
const SortableWorkspaceItem: FC<
Omit<WorkspaceListProps, 'items'> & {
item: AffineWorkspace | LocalWorkspace;
}
> = props => {
const { setNodeRef, attributes, listeners, transform } = useSortable({
id: props.item.id,
});
const style = transform
? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
}
: undefined;
return (
<div
data-testid="draggable-item"
style={style}
ref={setNodeRef}
{...attributes}
{...listeners}
>
<WorkspaceCard
currentWorkspaceId={props.currentWorkspaceId}
workspace={props.item}
onClick={props.onClick}
onSettingClick={props.onSettingClick}
/>
</div>
);
};
export const WorkspaceList: FC<WorkspaceListProps> = props => {
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 8,
},
})
);
return (
<DndContext sensors={sensors} onDragEnd={props.onDragEnd}>
<SortableContext items={props.items}>
{props.items.map(item => (
<SortableWorkspaceItem {...props} item={item} key={item.id} />
))}
</SortableContext>
</DndContext>
);
};