feat(core): add new doc button to collection page (#6423)
close TOV-64 You can add this document to the collection while creating a new document on the collection page. <img width="790" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/828f80af-a1a5-47c2-98c2-a574cf79052c">
This commit is contained in:
@@ -22,7 +22,7 @@ export const newCollectionButton = style({
|
|||||||
padding: '6px 10px',
|
padding: '6px 10px',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
background: cssVar('backgroundPrimaryColor'),
|
background: cssVar('backgroundPrimaryColor'),
|
||||||
fontSize: cssVar('fontSm'),
|
fontSize: cssVar('fontXs'),
|
||||||
fontWeight: 600,
|
fontWeight: 500,
|
||||||
height: '32px',
|
height: '28px',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { menuContent } from './new-page-button.css';
|
|||||||
type NewPageButtonProps = {
|
type NewPageButtonProps = {
|
||||||
createNewPage: () => void;
|
createNewPage: () => void;
|
||||||
createNewEdgeless: () => void;
|
createNewEdgeless: () => void;
|
||||||
importFile: () => void;
|
importFile?: () => void;
|
||||||
size?: 'small' | 'default';
|
size?: 'small' | 'default';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,13 +43,15 @@ export const CreateNewPagePopup = ({
|
|||||||
onClick={createNewEdgeless}
|
onClick={createNewEdgeless}
|
||||||
data-testid="new-edgeless-button-in-all-page"
|
data-testid="new-edgeless-button-in-all-page"
|
||||||
/>
|
/>
|
||||||
<BlockCard
|
{importFile ? (
|
||||||
title={t['com.affine.new_import']()}
|
<BlockCard
|
||||||
desc={t['com.affine.import_file']()}
|
title={t['com.affine.new_import']()}
|
||||||
right={<ImportIcon width={20} height={20} />}
|
desc={t['com.affine.import_file']()}
|
||||||
onClick={importFile}
|
right={<ImportIcon width={20} height={20} />}
|
||||||
data-testid="import-button-in-all-page"
|
onClick={importFile}
|
||||||
/>
|
data-testid="import-button-in-all-page"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
{/* TODO Import */}
|
{/* TODO Import */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -63,22 +65,29 @@ export const NewPageButton = ({
|
|||||||
children,
|
children,
|
||||||
}: PropsWithChildren<NewPageButtonProps>) => {
|
}: PropsWithChildren<NewPageButtonProps>) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleCreateNewPage = useCallback(() => {
|
||||||
|
createNewPage();
|
||||||
|
setOpen(false);
|
||||||
|
}, [createNewPage]);
|
||||||
|
|
||||||
|
const handleCreateNewEdgeless = useCallback(() => {
|
||||||
|
createNewEdgeless();
|
||||||
|
setOpen(false);
|
||||||
|
}, [createNewEdgeless]);
|
||||||
|
|
||||||
|
const handleImportFile = useCallback(() => {
|
||||||
|
importFile?.();
|
||||||
|
setOpen(false);
|
||||||
|
}, [importFile]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
items={
|
items={
|
||||||
<CreateNewPagePopup
|
<CreateNewPagePopup
|
||||||
createNewPage={useCallback(() => {
|
createNewPage={handleCreateNewPage}
|
||||||
createNewPage();
|
createNewEdgeless={handleCreateNewEdgeless}
|
||||||
setOpen(false);
|
importFile={importFile ? handleImportFile : undefined}
|
||||||
}, [createNewPage])}
|
|
||||||
createNewEdgeless={useCallback(() => {
|
|
||||||
createNewEdgeless();
|
|
||||||
setOpen(false);
|
|
||||||
}, [createNewEdgeless])}
|
|
||||||
importFile={useCallback(() => {
|
|
||||||
importFile();
|
|
||||||
setOpen(false);
|
|
||||||
}, [importFile])}
|
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rootOptions={{
|
rootOptions={{
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ export const addPageButton = style({
|
|||||||
padding: '6px 10px',
|
padding: '6px 10px',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
background: cssVar('backgroundPrimaryColor'),
|
background: cssVar('backgroundPrimaryColor'),
|
||||||
fontSize: cssVar('fontSm'),
|
fontSize: cssVar('fontXs'),
|
||||||
fontWeight: 600,
|
fontWeight: 500,
|
||||||
height: '32px',
|
height: '28px',
|
||||||
});
|
});
|
||||||
export const tagSticky = style({
|
export const tagSticky = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -162,3 +162,16 @@ export const tagSelectorItemText = style({
|
|||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const rightButtonGroup = style({
|
||||||
|
display: 'flex',
|
||||||
|
gap: '12px',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const buttonText = style({
|
||||||
|
fontSize: cssVar('fontXs'),
|
||||||
|
fontWeight: 500,
|
||||||
|
color: cssVar('textPrimaryColor'),
|
||||||
|
});
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import {
|
|||||||
SearchIcon,
|
SearchIcon,
|
||||||
ViewLayersIcon,
|
ViewLayersIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService, Workspace } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { CollectionService } from '../../../modules/collection';
|
import { CollectionService } from '../../../modules/collection';
|
||||||
|
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
||||||
import { createTagFilter } from '../filter/utils';
|
import { createTagFilter } from '../filter/utils';
|
||||||
import { createEmptyCollection } from '../use-collection-manager';
|
import { createEmptyCollection } from '../use-collection-manager';
|
||||||
import type { AllPageListConfig } from '../view/edit-collection/edit-collection';
|
import type { AllPageListConfig } from '../view/edit-collection/edit-collection';
|
||||||
@@ -29,6 +30,10 @@ import { PageListNewPageButton } from './page-list-new-page-button';
|
|||||||
|
|
||||||
export const PageListHeader = () => {
|
export const PageListHeader = () => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
const workspace = useService(Workspace);
|
||||||
|
const { importFile, createEdgeless, createPage } = usePageHelper(
|
||||||
|
workspace.docCollection
|
||||||
|
);
|
||||||
|
|
||||||
const title = useMemo(() => {
|
const title = useMemo(() => {
|
||||||
return t['com.affine.all-pages.header']();
|
return t['com.affine.all-pages.header']();
|
||||||
@@ -37,8 +42,14 @@ export const PageListHeader = () => {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.docListHeader}>
|
<div className={styles.docListHeader}>
|
||||||
<div className={styles.docListHeaderTitle}>{title}</div>
|
<div className={styles.docListHeaderTitle}>{title}</div>
|
||||||
<PageListNewPageButton testId="new-page-button-trigger">
|
<PageListNewPageButton
|
||||||
{t['New Page']()}
|
size="small"
|
||||||
|
testId="new-page-button-trigger"
|
||||||
|
onCreateEdgeless={createEdgeless}
|
||||||
|
onCreatePage={createPage}
|
||||||
|
onImportFile={importFile}
|
||||||
|
>
|
||||||
|
<div className={styles.buttonText}>{t['New Page']()}</div>
|
||||||
</PageListNewPageButton>
|
</PageListNewPageButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -62,11 +73,23 @@ export const CollectionPageListHeader = ({
|
|||||||
const collectionService = useService(CollectionService);
|
const collectionService = useService(CollectionService);
|
||||||
const { node, open } = useEditCollection(config);
|
const { node, open } = useEditCollection(config);
|
||||||
|
|
||||||
const handleAddPage = useAsyncCallback(async () => {
|
const handleEdit = useAsyncCallback(async () => {
|
||||||
const ret = await open({ ...collection }, 'page');
|
const ret = await open({ ...collection }, 'page');
|
||||||
collectionService.updateCollection(collection.id, () => ret);
|
collectionService.updateCollection(collection.id, () => ret);
|
||||||
}, [collection, collectionService, open]);
|
}, [collection, collectionService, open]);
|
||||||
|
|
||||||
|
const workspace = useService(Workspace);
|
||||||
|
const { createEdgeless, createPage } = usePageHelper(workspace.docCollection);
|
||||||
|
|
||||||
|
const handleCreateEdgeless = useCallback(() => {
|
||||||
|
const newDoc = createEdgeless();
|
||||||
|
collectionService.addPageToCollection(collection.id, newDoc.id);
|
||||||
|
}, [collection.id, collectionService, createEdgeless]);
|
||||||
|
const handleCreatePage = useCallback(() => {
|
||||||
|
const newDoc = createPage();
|
||||||
|
collectionService.addPageToCollection(collection.id, newDoc.id);
|
||||||
|
}, [collection.id, collectionService, createPage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{node}
|
{node}
|
||||||
@@ -80,9 +103,19 @@ export const CollectionPageListHeader = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.titleCollectionName}>{collection.name}</div>
|
<div className={styles.titleCollectionName}>{collection.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<Button className={styles.addPageButton} onClick={handleAddPage}>
|
<div className={styles.rightButtonGroup}>
|
||||||
{t['com.affine.collection.addPages']()}
|
<Button className={styles.addPageButton} onClick={handleEdit}>
|
||||||
</Button>
|
{t['Edit']()}
|
||||||
|
</Button>
|
||||||
|
<PageListNewPageButton
|
||||||
|
size="small"
|
||||||
|
testId="new-page-button-trigger"
|
||||||
|
onCreateEdgeless={handleCreateEdgeless}
|
||||||
|
onCreatePage={handleCreatePage}
|
||||||
|
>
|
||||||
|
<div className={styles.buttonText}>{t['New Page']()}</div>
|
||||||
|
</PageListNewPageButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export const dateCell = style({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
fontSize: cssVar('fontXs'),
|
fontSize: cssVar('fontXs'),
|
||||||
color: cssVar('textSecondaryColor'),
|
color: cssVar('textPrimaryColor'),
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
padding: '0 8px',
|
padding: '0 8px',
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { useService, Workspace } from '@toeverything/infra';
|
|
||||||
import type { PropsWithChildren } from 'react';
|
import type { PropsWithChildren } from 'react';
|
||||||
|
|
||||||
import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils';
|
|
||||||
import { NewPageButton } from '../components/new-page-button';
|
import { NewPageButton } from '../components/new-page-button';
|
||||||
import * as styles from './page-list-new-page-button.css';
|
import * as styles from './page-list-new-page-button.css';
|
||||||
|
|
||||||
@@ -10,22 +8,24 @@ export const PageListNewPageButton = ({
|
|||||||
children,
|
children,
|
||||||
size,
|
size,
|
||||||
testId,
|
testId,
|
||||||
|
onCreatePage,
|
||||||
|
onCreateEdgeless,
|
||||||
|
onImportFile,
|
||||||
}: PropsWithChildren<{
|
}: PropsWithChildren<{
|
||||||
className?: string;
|
className?: string;
|
||||||
size?: 'small' | 'default';
|
size?: 'small' | 'default';
|
||||||
testId?: string;
|
testId?: string;
|
||||||
|
onCreatePage: () => void;
|
||||||
|
onCreateEdgeless: () => void;
|
||||||
|
onImportFile?: () => void;
|
||||||
}>) => {
|
}>) => {
|
||||||
const currentWorkspace = useService(Workspace);
|
|
||||||
const { importFile, createEdgeless, createPage } = usePageHelper(
|
|
||||||
currentWorkspace.docCollection
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div className={className} data-testid={testId}>
|
<div className={className} data-testid={testId}>
|
||||||
<NewPageButton
|
<NewPageButton
|
||||||
size={size}
|
size={size}
|
||||||
importFile={importFile}
|
importFile={onImportFile}
|
||||||
createNewEdgeless={createEdgeless}
|
createNewEdgeless={onCreateEdgeless}
|
||||||
createNewPage={createPage}
|
createNewPage={onCreatePage}
|
||||||
>
|
>
|
||||||
<div className={styles.newPageButtonLabel}>{children}</div>
|
<div className={styles.newPageButtonLabel}>{children}</div>
|
||||||
</NewPageButton>
|
</NewPageButton>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const newTagButton = style({
|
|||||||
padding: '6px 10px',
|
padding: '6px 10px',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
background: cssVar('backgroundPrimaryColor'),
|
background: cssVar('backgroundPrimaryColor'),
|
||||||
fontSize: cssVar('fontSm'),
|
fontSize: cssVar('fontXs'),
|
||||||
fontWeight: 600,
|
fontWeight: 500,
|
||||||
height: '32px',
|
height: '28px',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
export const button = style({
|
||||||
|
padding: '6px 10px',
|
||||||
|
borderRadius: '8px',
|
||||||
|
background: cssVar('backgroundPrimaryColor'),
|
||||||
|
fontSize: cssVar('fontXs'),
|
||||||
|
fontWeight: 500,
|
||||||
|
height: '28px',
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import { nanoid } from 'nanoid';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { createEmptyCollection } from '../use-collection-manager';
|
import { createEmptyCollection } from '../use-collection-manager';
|
||||||
|
import * as styles from './save-as-collection-button.css';
|
||||||
import { useEditCollectionName } from './use-edit-collection';
|
import { useEditCollectionName } from './use-edit-collection';
|
||||||
|
|
||||||
interface SaveAsCollectionButtonProps {
|
interface SaveAsCollectionButtonProps {
|
||||||
@@ -35,8 +36,7 @@ export const SaveAsCollectionButton = ({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
data-testid="save-as-collection"
|
data-testid="save-as-collection"
|
||||||
icon={<SaveIcon />}
|
icon={<SaveIcon />}
|
||||||
size="large"
|
className={styles.button}
|
||||||
style={{ padding: '7px 8px' }}
|
|
||||||
>
|
>
|
||||||
{t['com.affine.editCollection.saveCollection']()}
|
{t['com.affine.editCollection.saveCollection']()}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -147,6 +147,6 @@ export const emptyCollectionMessage = style({
|
|||||||
});
|
});
|
||||||
export const emptyCollectionNewButton = style({
|
export const emptyCollectionNewButton = style({
|
||||||
padding: '0 8px',
|
padding: '0 8px',
|
||||||
height: '30px',
|
height: '28px',
|
||||||
fontSize: cssVar('fontSm'),
|
fontSize: cssVar('fontXs'),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { usePageHelper } from '@affine/core/components/blocksuite/block-suite-page-list/utils';
|
||||||
import {
|
import {
|
||||||
AllPageListOperationsMenu,
|
AllPageListOperationsMenu,
|
||||||
PageDisplayMenu,
|
PageDisplayMenu,
|
||||||
@@ -22,7 +23,9 @@ export const AllPageHeader = ({
|
|||||||
onChangeFilters: (filters: Filter[]) => void;
|
onChangeFilters: (filters: Filter[]) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const workspace = useService(Workspace);
|
const workspace = useService(Workspace);
|
||||||
|
const { importFile, createEdgeless, createPage } = usePageHelper(
|
||||||
|
workspace.docCollection
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<Header
|
<Header
|
||||||
left={
|
left={
|
||||||
@@ -40,6 +43,9 @@ export const AllPageHeader = ({
|
|||||||
styles.headerCreateNewButton,
|
styles.headerCreateNewButton,
|
||||||
!showCreateNew && styles.headerCreateNewButtonHidden
|
!showCreateNew && styles.headerCreateNewButtonHidden
|
||||||
)}
|
)}
|
||||||
|
onCreateEdgeless={createEdgeless}
|
||||||
|
onCreatePage={createPage}
|
||||||
|
onImportFile={importFile}
|
||||||
>
|
>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
</PageListNewPageButton>
|
</PageListNewPageButton>
|
||||||
|
|||||||
@@ -892,7 +892,7 @@
|
|||||||
"com.affine.payment.member-limit.pro.confirm": "知道了",
|
"com.affine.payment.member-limit.pro.confirm": "知道了",
|
||||||
"com.affine.payment.member-limit.pro.description": "每个 {{planName}} 用户最多可以邀请 {{quota}} 个成员加入他们的工作区。 如果您想继续添加协作成员,可以创建新的工作区。",
|
"com.affine.payment.member-limit.pro.description": "每个 {{planName}} 用户最多可以邀请 {{quota}} 个成员加入他们的工作区。 如果您想继续添加协作成员,可以创建新的工作区。",
|
||||||
"com.affine.payment.member-limit.title": "成员数量已达到极限",
|
"com.affine.payment.member-limit.title": "成员数量已达到极限",
|
||||||
"com.affine.payment.member.description": "在此处管理成员。{{planName}} 用户可以邀请最多 {{memberLimit}} 人。",
|
"com.affine.payment.member.description": "在此处管理成员。{{planName}} 用户可以邀请最多 {{memberLimit}} 人",
|
||||||
"com.affine.payment.member.description.go-upgrade": "前往升级",
|
"com.affine.payment.member.description.go-upgrade": "前往升级",
|
||||||
"com.affine.payment.modal.change.cancel": "取消",
|
"com.affine.payment.modal.change.cancel": "取消",
|
||||||
"com.affine.payment.modal.change.confirm": "更改",
|
"com.affine.payment.modal.change.confirm": "更改",
|
||||||
|
|||||||
Reference in New Issue
Block a user