feat: add block card component (#2398)
This commit is contained in:
25
packages/component/src/components/card/block-card/index.tsx
Normal file
25
packages/component/src/components/card/block-card/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
|
||||||
|
|
||||||
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
|
export const BlockCard = forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
{
|
||||||
|
left?: ReactNode;
|
||||||
|
title: string;
|
||||||
|
desc?: string;
|
||||||
|
right?: ReactNode;
|
||||||
|
} & HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ left, title, desc, right, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<div ref={ref} className={styles.blockCard} {...props}>
|
||||||
|
{left && <div className={styles.blockCardAround}>{left}</div>}
|
||||||
|
<div className={styles.blockCardContent}>
|
||||||
|
<div>{title}</div>
|
||||||
|
<div className={styles.blockCardDesc}>{desc}</div>
|
||||||
|
</div>
|
||||||
|
{right && <div className={styles.blockCardAround}>{right}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
BlockCard.displayName = 'BlockCard';
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const blockCard = style({
|
||||||
|
display: 'flex',
|
||||||
|
gap: '12px',
|
||||||
|
padding: '8px 12px',
|
||||||
|
color: 'var(--affine-text-primary-color)',
|
||||||
|
backgroundColor: 'var(--affine-background-primary-color)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
userSelect: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
selectors: {
|
||||||
|
'&:hover': {
|
||||||
|
boxShadow: 'var(--affine-shadow-1)',
|
||||||
|
},
|
||||||
|
// TODO active styles
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const blockCardAround = style({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const blockCardContent = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flex: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const blockCardDesc = style({
|
||||||
|
color: 'var(--affine-text-secondary-color)',
|
||||||
|
fontSize: 'var(--affine-font-xs)',
|
||||||
|
});
|
||||||
@@ -10,7 +10,7 @@ import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-
|
|||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { WorkspaceAvatar } from '../workspace-avatar';
|
import { WorkspaceAvatar } from '../../workspace-avatar';
|
||||||
import {
|
import {
|
||||||
StyledCard,
|
StyledCard,
|
||||||
StyledSettingLink,
|
StyledSettingLink,
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { displayFlex, styled, textEllipsis } from '../..';
|
import { displayFlex, styled, textEllipsis } from '../../../styles';
|
||||||
import { IconButton } from '../..';
|
import { IconButton } from '../../../ui/button/icon-button';
|
||||||
|
|
||||||
export const StyleWorkspaceInfo = styled('div')(() => {
|
export const StyleWorkspaceInfo = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
@@ -9,7 +9,7 @@ import type { FC } from 'react';
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { Menu } from '../..';
|
import { Menu } from '../../ui/menu/menu';
|
||||||
import { Export } from './export';
|
import { Export } from './export';
|
||||||
import { containerStyle, indicatorContainerStyle, tabStyle } from './index.css';
|
import { containerStyle, indicatorContainerStyle, tabStyle } from './index.css';
|
||||||
import { SharePage } from './share-page';
|
import { SharePage } from './share-page';
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import { SortableContext, useSortable } from '@dnd-kit/sortable';
|
import { SortableContext, useSortable } from '@dnd-kit/sortable';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
|
|
||||||
import { WorkspaceCard } from '../workspace-card';
|
import { WorkspaceCard } from '../../components/card/workspace-card';
|
||||||
import { workspaceItemStyle } from './index.css';
|
import { workspaceItemStyle } from './index.css';
|
||||||
|
|
||||||
export type WorkspaceListProps = {
|
export type WorkspaceListProps = {
|
||||||
|
|||||||
55
packages/component/src/stories/card.stories.tsx
Normal file
55
packages/component/src/stories/card.stories.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { BlockCard } from '@affine/component/card/block-card';
|
||||||
|
import { WorkspaceFlavour } from '@affine/workspace/type';
|
||||||
|
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||||
|
import { Workspace } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import { WorkspaceCard } from '../components/card/workspace-card';
|
||||||
|
import { toast } from '../ui/toast';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'AFFiNE/Card',
|
||||||
|
component: WorkspaceCard,
|
||||||
|
};
|
||||||
|
|
||||||
|
const blockSuiteWorkspace = new Workspace({
|
||||||
|
id: 'blocksuite-local',
|
||||||
|
});
|
||||||
|
|
||||||
|
blockSuiteWorkspace.meta.setName('Hello World');
|
||||||
|
|
||||||
|
export const AffineWorkspaceCard = () => {
|
||||||
|
return (
|
||||||
|
<WorkspaceCard
|
||||||
|
workspace={{
|
||||||
|
flavour: WorkspaceFlavour.LOCAL,
|
||||||
|
id: 'local',
|
||||||
|
blockSuiteWorkspace,
|
||||||
|
providers: [],
|
||||||
|
}}
|
||||||
|
onClick={() => {}}
|
||||||
|
onSettingClick={() => {}}
|
||||||
|
currentWorkspaceId={null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AffineBlockCard = () => {
|
||||||
|
return (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
|
||||||
|
<BlockCard title={'New Page'} onClick={() => toast('clicked')} />
|
||||||
|
<BlockCard
|
||||||
|
title={'New Page'}
|
||||||
|
desc={'Write with a blank page'}
|
||||||
|
right={<PageIcon width={20} height={20} />}
|
||||||
|
onClick={() => toast('clicked page')}
|
||||||
|
/>
|
||||||
|
<BlockCard
|
||||||
|
title={'New Edgeless'}
|
||||||
|
desc={'Draw with a blank whiteboard'}
|
||||||
|
left={<PageIcon width={20} height={20} />}
|
||||||
|
right={<EdgelessIcon width={20} height={20} />}
|
||||||
|
onClick={() => toast('clicked edgeless')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { WorkspaceFlavour } from '@affine/workspace/type';
|
|
||||||
import { Workspace } from '@blocksuite/store';
|
|
||||||
|
|
||||||
import { WorkspaceCard } from '../components/workspace-card';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: 'AFFiNE/WorkspaceCard',
|
|
||||||
component: WorkspaceCard,
|
|
||||||
};
|
|
||||||
|
|
||||||
const blockSuiteWorkspace = new Workspace({
|
|
||||||
id: 'blocksuite-local',
|
|
||||||
});
|
|
||||||
|
|
||||||
blockSuiteWorkspace.meta.setName('Hello World');
|
|
||||||
|
|
||||||
export const Basic = () => {
|
|
||||||
return (
|
|
||||||
<WorkspaceCard
|
|
||||||
workspace={{
|
|
||||||
flavour: WorkspaceFlavour.LOCAL,
|
|
||||||
id: 'local',
|
|
||||||
blockSuiteWorkspace,
|
|
||||||
providers: [],
|
|
||||||
}}
|
|
||||||
onClick={() => {}}
|
|
||||||
onSettingClick={() => {}}
|
|
||||||
currentWorkspaceId={null}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user