mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 03:33:06 +08:00
feat: add responvise page view (#2453)
This commit is contained in:
@@ -10,7 +10,7 @@ import type { BlockSuiteWorkspace } from '../../../shared';
|
|||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
export const formatDate = (date?: number | unknown) => {
|
export const formatDate = (date?: number | unknown) => {
|
||||||
const dateStr =
|
const dateStr =
|
||||||
typeof date === 'number' ? dayjs(date).format('YYYY-MM-DD HH:mm') : '--';
|
typeof date === 'number' ? dayjs(date).format('MM-DD HH:mm') : '--';
|
||||||
return dateStr;
|
return dateStr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,72 +1,23 @@
|
|||||||
import type { IconButtonProps, TableCellProps } from '@affine/component';
|
|
||||||
import {
|
import {
|
||||||
Content,
|
|
||||||
IconButton,
|
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
Tooltip,
|
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { OperationCell, TrashOperationCell } from '@affine/component/page-list';
|
import { TrashOperationCell } from '@affine/component/page-list';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import {
|
import { ArrowDownBigIcon, ArrowUpBigIcon } from '@blocksuite/icons';
|
||||||
ArrowDownBigIcon,
|
|
||||||
ArrowUpBigIcon,
|
|
||||||
FavoritedIcon,
|
|
||||||
FavoriteIcon,
|
|
||||||
} from '@blocksuite/icons';
|
|
||||||
import { useMediaQuery, useTheme } from '@mui/material';
|
import { useMediaQuery, useTheme } from '@mui/material';
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
import { forwardRef } from 'react';
|
|
||||||
|
|
||||||
import { NewPageButton } from './new-page-buttton';
|
import { AllPagesBody } from './all-pages-body';
|
||||||
import {
|
import { NewPageButton } from './components/new-page-buttton';
|
||||||
StyledTableContainer,
|
import { TitleCell } from './components/title-cell';
|
||||||
StyledTableRow,
|
import { AllPageListMobileView, TrashListMobileView } from './mobile';
|
||||||
StyledTitleLink,
|
import { StyledTableContainer, StyledTableRow } from './styles';
|
||||||
StyledTitleWrapper,
|
|
||||||
} from './styles';
|
|
||||||
import { useSorter } from './use-sorter';
|
import { useSorter } from './use-sorter';
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
const FavoriteTag = forwardRef<
|
|
||||||
HTMLButtonElement,
|
|
||||||
{
|
|
||||||
active: boolean;
|
|
||||||
} & Omit<IconButtonProps, 'children'>
|
|
||||||
>(({ active, onClick, ...props }, ref) => {
|
|
||||||
const t = useAFFiNEI18N();
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
content={active ? t['Favorited']() : t['Favorite']()}
|
|
||||||
placement="top-start"
|
|
||||||
>
|
|
||||||
<IconButton
|
|
||||||
ref={ref}
|
|
||||||
iconSize={[20, 20]}
|
|
||||||
style={{
|
|
||||||
color: active
|
|
||||||
? 'var(--affine-primary-color)'
|
|
||||||
: 'var(--affine-icon-color)',
|
|
||||||
}}
|
|
||||||
onClick={e => {
|
|
||||||
e.stopPropagation();
|
|
||||||
onClick?.(e);
|
|
||||||
}}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{active ? (
|
|
||||||
<FavoritedIcon data-testid="favorited-icon" />
|
|
||||||
) : (
|
|
||||||
<FavoriteIcon />
|
|
||||||
)}
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export type PageListProps = {
|
export type PageListProps = {
|
||||||
isPublicWorkspace?: boolean;
|
isPublicWorkspace?: boolean;
|
||||||
list: ListData[];
|
list: ListData[];
|
||||||
@@ -74,36 +25,13 @@ export type PageListProps = {
|
|||||||
onCreateNewEdgeless: () => void;
|
onCreateNewEdgeless: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TitleCell = ({
|
|
||||||
icon,
|
|
||||||
text,
|
|
||||||
suffix,
|
|
||||||
...props
|
|
||||||
}: {
|
|
||||||
icon: JSX.Element;
|
|
||||||
text: string;
|
|
||||||
suffix?: JSX.Element;
|
|
||||||
} & TableCellProps) => {
|
|
||||||
return (
|
|
||||||
<TableCell {...props}>
|
|
||||||
<StyledTitleWrapper>
|
|
||||||
<StyledTitleLink>
|
|
||||||
{icon}
|
|
||||||
<Content ellipsis={true} color="inherit">
|
|
||||||
{text}
|
|
||||||
</Content>
|
|
||||||
</StyledTitleLink>
|
|
||||||
{suffix}
|
|
||||||
</StyledTitleWrapper>
|
|
||||||
</TableCell>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AllPagesHead = ({
|
const AllPagesHead = ({
|
||||||
|
isPublicWorkspace,
|
||||||
sorter,
|
sorter,
|
||||||
createNewPage,
|
createNewPage,
|
||||||
createNewEdgeless,
|
createNewEdgeless,
|
||||||
}: {
|
}: {
|
||||||
|
isPublicWorkspace: boolean;
|
||||||
sorter: ReturnType<typeof useSorter<ListData>>;
|
sorter: ReturnType<typeof useSorter<ListData>>;
|
||||||
createNewPage: () => void;
|
createNewPage: () => void;
|
||||||
createNewEdgeless: () => void;
|
createNewEdgeless: () => void;
|
||||||
@@ -125,6 +53,7 @@ const AllPagesHead = ({
|
|||||||
content: t['Updated'](),
|
content: t['Updated'](),
|
||||||
proportion: 0.2,
|
proportion: 0.2,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'unsortable_action',
|
key: 'unsortable_action',
|
||||||
content: (
|
content: (
|
||||||
@@ -133,11 +62,10 @@ const AllPagesHead = ({
|
|||||||
createNewEdgeless={createNewEdgeless}
|
createNewEdgeless={createNewEdgeless}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
showWhen: () => !isPublicWorkspace,
|
||||||
sortable: false,
|
sortable: false,
|
||||||
styles: {
|
styles: {
|
||||||
display: 'flex',
|
justifyContent: 'flex-end',
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
} satisfies CSSProperties,
|
} satisfies CSSProperties,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -145,8 +73,9 @@ const AllPagesHead = ({
|
|||||||
return (
|
return (
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{titleList.map(
|
{titleList
|
||||||
({ key, content, proportion, sortable = true, styles }) => (
|
.filter(({ showWhen = () => true }) => showWhen())
|
||||||
|
.map(({ key, content, proportion, sortable = true, styles }) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
key={key}
|
key={key}
|
||||||
proportion={proportion}
|
proportion={proportion}
|
||||||
@@ -156,18 +85,24 @@ const AllPagesHead = ({
|
|||||||
? () => sorter.shiftOrder(key as keyof ListData)
|
? () => sorter.shiftOrder(key as keyof ListData)
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
style={styles}
|
|
||||||
>
|
>
|
||||||
{content}
|
<div
|
||||||
{sorter.key === key &&
|
style={{
|
||||||
(sorter.order === 'asc' ? (
|
display: 'flex',
|
||||||
<ArrowUpBigIcon width={24} height={24} />
|
alignItems: 'center',
|
||||||
) : (
|
...styles,
|
||||||
<ArrowDownBigIcon width={24} height={24} />
|
}}
|
||||||
))}
|
>
|
||||||
|
{content}
|
||||||
|
{sorter.key === key &&
|
||||||
|
(sorter.order === 'asc' ? (
|
||||||
|
<ArrowUpBigIcon width={24} height={24} />
|
||||||
|
) : (
|
||||||
|
<ArrowDownBigIcon width={24} height={24} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)
|
))}
|
||||||
)}
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
@@ -189,13 +124,12 @@ export type ListData = {
|
|||||||
onDisablePublicSharing: () => void;
|
onDisablePublicSharing: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PageList: React.FC<PageListProps> = ({
|
export const PageList = ({
|
||||||
isPublicWorkspace = false,
|
isPublicWorkspace = false,
|
||||||
list,
|
list,
|
||||||
onCreateNewPage,
|
onCreateNewPage,
|
||||||
onCreateNewEdgeless,
|
onCreateNewEdgeless,
|
||||||
}) => {
|
}: PageListProps) => {
|
||||||
const t = useAFFiNEI18N();
|
|
||||||
const sorter = useSorter<ListData>({
|
const sorter = useSorter<ListData>({
|
||||||
data: list,
|
data: list,
|
||||||
key: 'createDate',
|
key: 'createDate',
|
||||||
@@ -205,93 +139,29 @@ export const PageList: React.FC<PageListProps> = ({
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
if (isSmallDevices) {
|
if (isSmallDevices) {
|
||||||
return <PageListMobileView list={sorter.data} />;
|
return (
|
||||||
|
<AllPageListMobileView
|
||||||
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
|
createNewPage={onCreateNewPage}
|
||||||
|
createNewEdgeless={onCreateNewEdgeless}
|
||||||
|
list={sorter.data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListItems = sorter.data.map(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
pageId,
|
|
||||||
title,
|
|
||||||
icon,
|
|
||||||
isPublicPage,
|
|
||||||
favorite,
|
|
||||||
createDate,
|
|
||||||
updatedDate,
|
|
||||||
onClickPage,
|
|
||||||
bookmarkPage,
|
|
||||||
onOpenPageInNewTab,
|
|
||||||
removeToTrash,
|
|
||||||
onDisablePublicSharing,
|
|
||||||
},
|
|
||||||
index
|
|
||||||
) => {
|
|
||||||
return (
|
|
||||||
<StyledTableRow
|
|
||||||
data-testid={`page-list-item-${pageId}`}
|
|
||||||
key={`${pageId}-${index}`}
|
|
||||||
>
|
|
||||||
<TitleCell
|
|
||||||
icon={icon}
|
|
||||||
text={title || t['Untitled']()}
|
|
||||||
data-testid="title"
|
|
||||||
onClick={onClickPage}
|
|
||||||
/>
|
|
||||||
<TableCell
|
|
||||||
data-testid="created-date"
|
|
||||||
ellipsis={true}
|
|
||||||
onClick={onClickPage}
|
|
||||||
>
|
|
||||||
{createDate}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
data-testid="updated-date"
|
|
||||||
ellipsis={true}
|
|
||||||
onClick={onClickPage}
|
|
||||||
>
|
|
||||||
{updatedDate ?? createDate}
|
|
||||||
</TableCell>
|
|
||||||
{!isPublicWorkspace && (
|
|
||||||
<TableCell
|
|
||||||
style={{
|
|
||||||
padding: 0,
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '10px',
|
|
||||||
}}
|
|
||||||
data-testid={`more-actions-${pageId}`}
|
|
||||||
>
|
|
||||||
<FavoriteTag
|
|
||||||
className={favorite ? '' : 'favorite-button'}
|
|
||||||
onClick={bookmarkPage}
|
|
||||||
active={!!favorite}
|
|
||||||
/>
|
|
||||||
<OperationCell
|
|
||||||
title={title}
|
|
||||||
favorite={favorite}
|
|
||||||
isPublic={isPublicPage}
|
|
||||||
onOpenPageInNewTab={onOpenPageInNewTab}
|
|
||||||
onToggleFavoritePage={bookmarkPage}
|
|
||||||
onRemoveToTrash={removeToTrash}
|
|
||||||
onDisablePublicSharing={onDisablePublicSharing}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
)}
|
|
||||||
</StyledTableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTableContainer>
|
<StyledTableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
<AllPagesHead
|
<AllPagesHead
|
||||||
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
sorter={sorter}
|
sorter={sorter}
|
||||||
createNewPage={onCreateNewPage}
|
createNewPage={onCreateNewPage}
|
||||||
createNewEdgeless={onCreateNewEdgeless}
|
createNewEdgeless={onCreateNewEdgeless}
|
||||||
/>
|
/>
|
||||||
<TableBody>{ListItems}</TableBody>
|
<AllPagesBody
|
||||||
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
|
data={sorter.data}
|
||||||
|
/>
|
||||||
</Table>
|
</Table>
|
||||||
</StyledTableContainer>
|
</StyledTableContainer>
|
||||||
);
|
);
|
||||||
@@ -338,7 +208,7 @@ export const PageListTrashView: React.FC<{
|
|||||||
pageId,
|
pageId,
|
||||||
onClickPage,
|
onClickPage,
|
||||||
}));
|
}));
|
||||||
return <PageListMobileView list={mobileList} />;
|
return <TrashListMobileView list={mobileList} />;
|
||||||
}
|
}
|
||||||
const ListItems = list.map(
|
const ListItems = list.map(
|
||||||
(
|
(
|
||||||
@@ -395,43 +265,4 @@ export const PageListTrashView: React.FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PageListMobileView: React.FC<{
|
|
||||||
list: {
|
|
||||||
pageId: string;
|
|
||||||
title: string;
|
|
||||||
icon: JSX.Element;
|
|
||||||
onClickPage: () => void;
|
|
||||||
}[];
|
|
||||||
}> = ({ list }) => {
|
|
||||||
const t = useAFFiNEI18N();
|
|
||||||
|
|
||||||
const ListItems = list.map(({ pageId, title, icon, onClickPage }, index) => {
|
|
||||||
return (
|
|
||||||
<StyledTableRow
|
|
||||||
data-testid={`page-list-item-${pageId}`}
|
|
||||||
key={`${pageId}-${index}`}
|
|
||||||
>
|
|
||||||
<TableCell onClick={onClickPage}>
|
|
||||||
<StyledTitleWrapper>
|
|
||||||
<StyledTitleLink>
|
|
||||||
{icon}
|
|
||||||
<Content ellipsis={true} color="inherit">
|
|
||||||
{title || t['Untitled']()}
|
|
||||||
</Content>
|
|
||||||
</StyledTitleLink>
|
|
||||||
</StyledTitleWrapper>
|
|
||||||
</TableCell>
|
|
||||||
</StyledTableRow>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledTableContainer>
|
|
||||||
<Table>
|
|
||||||
<TableBody>{ListItems}</TableBody>
|
|
||||||
</Table>
|
|
||||||
</StyledTableContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PageList;
|
export default PageList;
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { TableBody, TableCell } from '@affine/component';
|
||||||
|
import { OperationCell } from '@affine/component/page-list';
|
||||||
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { useMediaQuery, useTheme } from '@mui/material';
|
||||||
|
|
||||||
|
import type { ListData } from './all-page';
|
||||||
|
import { FavoriteTag } from './components/favorite-tag';
|
||||||
|
import { TitleCell } from './components/title-cell';
|
||||||
|
import { StyledTableRow } from './styles';
|
||||||
|
|
||||||
|
export const AllPagesBody = ({
|
||||||
|
isPublicWorkspace,
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
isPublicWorkspace: boolean;
|
||||||
|
data: ListData[];
|
||||||
|
}) => {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
const theme = useTheme();
|
||||||
|
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
|
return (
|
||||||
|
<TableBody>
|
||||||
|
{data.map(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
pageId,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
isPublicPage,
|
||||||
|
favorite,
|
||||||
|
createDate,
|
||||||
|
updatedDate,
|
||||||
|
onClickPage,
|
||||||
|
bookmarkPage,
|
||||||
|
onOpenPageInNewTab,
|
||||||
|
removeToTrash,
|
||||||
|
onDisablePublicSharing,
|
||||||
|
},
|
||||||
|
index
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<StyledTableRow
|
||||||
|
data-testid={`page-list-item-${pageId}`}
|
||||||
|
key={`${pageId}-${index}`}
|
||||||
|
>
|
||||||
|
<TitleCell
|
||||||
|
icon={icon}
|
||||||
|
text={title || t['Untitled']()}
|
||||||
|
data-testid="title"
|
||||||
|
onClick={onClickPage}
|
||||||
|
/>
|
||||||
|
<TableCell
|
||||||
|
data-testid="created-date"
|
||||||
|
ellipsis={true}
|
||||||
|
hidden={isSmallDevices}
|
||||||
|
onClick={onClickPage}
|
||||||
|
>
|
||||||
|
{createDate}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
data-testid="updated-date"
|
||||||
|
ellipsis={true}
|
||||||
|
hidden={isSmallDevices}
|
||||||
|
onClick={onClickPage}
|
||||||
|
>
|
||||||
|
{updatedDate ?? createDate}
|
||||||
|
</TableCell>
|
||||||
|
{!isPublicWorkspace && (
|
||||||
|
<TableCell
|
||||||
|
style={{
|
||||||
|
padding: 0,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '10px',
|
||||||
|
}}
|
||||||
|
data-testid={`more-actions-${pageId}`}
|
||||||
|
>
|
||||||
|
<FavoriteTag
|
||||||
|
className={favorite ? '' : 'favorite-button'}
|
||||||
|
onClick={bookmarkPage}
|
||||||
|
active={!!favorite}
|
||||||
|
/>
|
||||||
|
<OperationCell
|
||||||
|
title={title}
|
||||||
|
favorite={favorite}
|
||||||
|
isPublic={isPublicPage}
|
||||||
|
onOpenPageInNewTab={onOpenPageInNewTab}
|
||||||
|
onToggleFavoritePage={bookmarkPage}
|
||||||
|
onRemoveToTrash={removeToTrash}
|
||||||
|
onDisablePublicSharing={onDisablePublicSharing}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import type { IconButtonProps } from '@affine/component';
|
||||||
|
import { IconButton, Tooltip } from '@affine/component';
|
||||||
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons';
|
||||||
|
import { forwardRef } from 'react';
|
||||||
|
|
||||||
|
export const FavoriteTag = forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
{
|
||||||
|
active: boolean;
|
||||||
|
} & Omit<IconButtonProps, 'children'>
|
||||||
|
>(({ active, onClick, ...props }, ref) => {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content={active ? t['Favorited']() : t['Favorite']()}
|
||||||
|
placement="top-start"
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
ref={ref}
|
||||||
|
iconSize={[20, 20]}
|
||||||
|
style={{
|
||||||
|
color: active
|
||||||
|
? 'var(--affine-primary-color)'
|
||||||
|
: 'var(--affine-icon-color)',
|
||||||
|
}}
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick?.(e);
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{active ? (
|
||||||
|
<FavoritedIcon data-testid="favorited-icon" />
|
||||||
|
) : (
|
||||||
|
<FavoriteIcon />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
FavoriteTag.displayName = 'FavoriteTag';
|
||||||
+3
-3
@@ -2,9 +2,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|||||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
import { EdgelessIcon, PageIcon } from '@blocksuite/icons';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { DropdownButton } from '../../ui/button/dropdown';
|
import { DropdownButton } from '../../../ui/button/dropdown';
|
||||||
import { Menu } from '../../ui/menu/menu';
|
import { Menu } from '../../../ui/menu/menu';
|
||||||
import { BlockCard } from '../card/block-card';
|
import { BlockCard } from '../../card/block-card';
|
||||||
|
|
||||||
type NewPageButtonProps = {
|
type NewPageButtonProps = {
|
||||||
createNewPage: () => void;
|
createNewPage: () => void;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import type { TableCellProps } from '@affine/component';
|
||||||
|
import { Content, TableCell } from '@affine/component';
|
||||||
|
|
||||||
|
import { StyledTitleLink } from '../styles';
|
||||||
|
|
||||||
|
export const TitleCell = ({
|
||||||
|
icon,
|
||||||
|
text,
|
||||||
|
suffix,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
icon: JSX.Element;
|
||||||
|
text: string;
|
||||||
|
suffix?: JSX.Element;
|
||||||
|
} & TableCellProps) => {
|
||||||
|
return (
|
||||||
|
<TableCell {...props}>
|
||||||
|
<StyledTitleLink>
|
||||||
|
{icon}
|
||||||
|
<Content ellipsis={true} color="inherit">
|
||||||
|
{text}
|
||||||
|
</Content>
|
||||||
|
</StyledTitleLink>
|
||||||
|
{suffix}
|
||||||
|
</TableCell>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import {
|
||||||
|
Content,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
} from '@affine/component';
|
||||||
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
|
||||||
|
import type { ListData } from './all-page';
|
||||||
|
import { AllPagesBody } from './all-pages-body';
|
||||||
|
import { NewPageButton } from './components/new-page-buttton';
|
||||||
|
import {
|
||||||
|
StyledTableContainer,
|
||||||
|
StyledTableRow,
|
||||||
|
StyledTitleLink,
|
||||||
|
} from './styles';
|
||||||
|
|
||||||
|
const MobileHead = ({
|
||||||
|
isPublicWorkspace,
|
||||||
|
createNewPage,
|
||||||
|
createNewEdgeless,
|
||||||
|
}: {
|
||||||
|
isPublicWorkspace: boolean;
|
||||||
|
createNewPage: () => void;
|
||||||
|
createNewEdgeless: () => void;
|
||||||
|
}) => {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
return (
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell proportion={0.8}>{t['Title']()}</TableCell>
|
||||||
|
{!isPublicWorkspace && (
|
||||||
|
<TableCell>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<NewPageButton
|
||||||
|
createNewPage={createNewPage}
|
||||||
|
createNewEdgeless={createNewEdgeless}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
|
)}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AllPageListMobileView = ({
|
||||||
|
list,
|
||||||
|
isPublicWorkspace,
|
||||||
|
createNewPage,
|
||||||
|
createNewEdgeless,
|
||||||
|
}: {
|
||||||
|
isPublicWorkspace: boolean;
|
||||||
|
list: ListData[];
|
||||||
|
createNewPage: () => void;
|
||||||
|
createNewEdgeless: () => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<StyledTableContainer>
|
||||||
|
<Table>
|
||||||
|
<MobileHead
|
||||||
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
|
createNewPage={createNewPage}
|
||||||
|
createNewEdgeless={createNewEdgeless}
|
||||||
|
/>
|
||||||
|
<AllPagesBody isPublicWorkspace={isPublicWorkspace} data={list} />
|
||||||
|
</Table>
|
||||||
|
</StyledTableContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO align to {@link AllPageListMobileView}
|
||||||
|
export const TrashListMobileView = ({
|
||||||
|
list,
|
||||||
|
}: {
|
||||||
|
list: {
|
||||||
|
pageId: string;
|
||||||
|
title: string;
|
||||||
|
icon: JSX.Element;
|
||||||
|
onClickPage: () => void;
|
||||||
|
}[];
|
||||||
|
}) => {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
|
||||||
|
const ListItems = list.map(({ pageId, title, icon, onClickPage }, index) => {
|
||||||
|
return (
|
||||||
|
<StyledTableRow
|
||||||
|
data-testid={`page-list-item-${pageId}`}
|
||||||
|
key={`${pageId}-${index}`}
|
||||||
|
>
|
||||||
|
<TableCell onClick={onClickPage}>
|
||||||
|
<StyledTitleLink>
|
||||||
|
{icon}
|
||||||
|
<Content ellipsis={true} color="inherit">
|
||||||
|
{title || t['Untitled']()}
|
||||||
|
</Content>
|
||||||
|
</StyledTitleLink>
|
||||||
|
</TableCell>
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledTableContainer>
|
||||||
|
<Table>
|
||||||
|
<TableBody>{ListItems}</TableBody>
|
||||||
|
</Table>
|
||||||
|
</StyledTableContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,14 +4,26 @@ import { TableRow } from '@affine/component';
|
|||||||
export const StyledTableContainer = styled('div')(({ theme }) => {
|
export const StyledTableContainer = styled('div')(({ theme }) => {
|
||||||
return {
|
return {
|
||||||
height: 'calc(100vh - 52px)',
|
height: 'calc(100vh - 52px)',
|
||||||
padding: '78px 72px',
|
padding: '52px 32px',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
[theme.breakpoints.down('md')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
padding: '12px 24px',
|
padding: '52px 0px',
|
||||||
|
'tr > td:first-of-type': {
|
||||||
|
borderTopLeftRadius: '0px',
|
||||||
|
borderBottomLeftRadius: '0px',
|
||||||
|
},
|
||||||
|
'tr > td:last-of-type': {
|
||||||
|
borderTopRightRadius: '0px',
|
||||||
|
borderBottomRightRadius: '0px',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
export const StyledTitleWrapper = styled('div')(() => {
|
export const StyledTitleWrapper = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
...displayFlex('flex-start', 'center'),
|
...displayFlex('flex-start', 'center'),
|
||||||
@@ -28,8 +40,6 @@ export const StyledTitleWrapper = styled('div')(() => {
|
|||||||
});
|
});
|
||||||
export const StyledTitleLink = styled('div')(() => {
|
export const StyledTitleLink = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
maxWidth: '80%',
|
|
||||||
marginRight: '18px',
|
|
||||||
...displayFlex('flex-start', 'center'),
|
...displayFlex('flex-start', 'center'),
|
||||||
color: 'var(--affine-text-primary-color)',
|
color: 'var(--affine-text-primary-color)',
|
||||||
'>svg': {
|
'>svg': {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
} from '../components/page-list/all-page';
|
} from '../components/page-list/all-page';
|
||||||
import { PageListTrashView } from '../components/page-list/all-page';
|
import { PageListTrashView } from '../components/page-list/all-page';
|
||||||
import PageList from '../components/page-list/all-page';
|
import PageList from '../components/page-list/all-page';
|
||||||
import { NewPageButton } from '../components/page-list/new-page-buttton';
|
import { NewPageButton } from '../components/page-list/components/new-page-buttton';
|
||||||
import type { OperationCellProps } from '../components/page-list/operation-cell';
|
import type { OperationCellProps } from '../components/page-list/operation-cell';
|
||||||
import { OperationCell } from '../components/page-list/operation-cell';
|
import { OperationCell } from '../components/page-list/operation-cell';
|
||||||
import { toast } from '../ui/toast';
|
import { toast } from '../ui/toast';
|
||||||
@@ -99,6 +99,14 @@ AffineAllPageList.args = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AffinePublicPageList: StoryFn<PageListProps> = ({ ...props }) => (
|
||||||
|
<PageList {...props} />
|
||||||
|
);
|
||||||
|
AffinePublicPageList.args = {
|
||||||
|
...AffineAllPageList.args,
|
||||||
|
isPublicWorkspace: true,
|
||||||
|
};
|
||||||
|
|
||||||
export const AffineAllPageMobileList: StoryFn<PageListProps> = ({
|
export const AffineAllPageMobileList: StoryFn<PageListProps> = ({
|
||||||
...props
|
...props
|
||||||
}) => <PageList {...props} />;
|
}) => <PageList {...props} />;
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ export const Menu = (props: MenuProps) => {
|
|||||||
content,
|
content,
|
||||||
placement = 'bottom-start',
|
placement = 'bottom-start',
|
||||||
children,
|
children,
|
||||||
|
...popperProps
|
||||||
} = props;
|
} = props;
|
||||||
return content ? (
|
return content ? (
|
||||||
<Popper
|
<Popper
|
||||||
{...props}
|
placement={placement}
|
||||||
|
{...popperProps}
|
||||||
showArrow={false}
|
showArrow={false}
|
||||||
content={
|
content={
|
||||||
<StyledMenuWrapper
|
<StyledMenuWrapper
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import { styled, textEllipsis } from '../../styles';
|
import { styled, textEllipsis } from '../../styles';
|
||||||
import type { TableCellProps } from './interface';
|
import type { TableCellProps } from './interface';
|
||||||
|
|
||||||
export const StyledTable = styled('table')<{ tableLayout: 'auto' | 'fixed' }>(
|
export const StyledTable = styled('table')(() => {
|
||||||
({ tableLayout }) => {
|
return {
|
||||||
return {
|
fontSize: 'var(--affine-font-base)',
|
||||||
fontSize: 'var(--affine-font-base)',
|
color: 'var(--affine-text-primary-color)',
|
||||||
color: 'var(--affine-text-primary-color)',
|
tableLayout: 'fixed',
|
||||||
tableLayout,
|
width: '100%',
|
||||||
width: '100%',
|
borderCollapse: 'separate',
|
||||||
borderCollapse: 'separate',
|
borderSpacing: '0',
|
||||||
borderSpacing: '0',
|
};
|
||||||
};
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const StyledTableBody = styled('tbody')(() => {
|
export const StyledTableBody = styled('tbody')(() => {
|
||||||
return {
|
return {
|
||||||
@@ -37,7 +35,7 @@ export const StyledTableCell = styled('td')<
|
|||||||
return {
|
return {
|
||||||
width,
|
width,
|
||||||
height: '52px',
|
height: '52px',
|
||||||
padding: '0 30px',
|
paddingLeft: '16px',
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
textAlign: align,
|
textAlign: align,
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
|
||||||
|
|
||||||
import { StyledTableBody } from './styles';
|
import { StyledTableBody } from './styles';
|
||||||
export const TableBody = ({
|
export const TableBody = StyledTableBody;
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: PropsWithChildren<HTMLAttributes<HTMLTableSectionElement>>) => {
|
|
||||||
return <StyledTableBody {...props}>{children}</StyledTableBody>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TableBody;
|
export default TableBody;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
import type { TableCellProps } from './interface';
|
|
||||||
import { StyledTableCell } from './styles';
|
import { StyledTableCell } from './styles';
|
||||||
export const TableCell = ({ children, ...props }: TableCellProps) => {
|
export const TableCell = StyledTableCell;
|
||||||
return <StyledTableCell {...props}>{children}</StyledTableCell>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TableCell;
|
export default TableCell;
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
|
||||||
|
|
||||||
import { StyledTableRow } from './styles';
|
import { StyledTableRow } from './styles';
|
||||||
export const TableRow = ({
|
export const TableRow = StyledTableRow;
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: PropsWithChildren<HTMLAttributes<HTMLTableRowElement>>) => {
|
|
||||||
return <StyledTableRow {...props}>{children}</StyledTableRow>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TableRow;
|
export default TableRow;
|
||||||
|
|||||||
@@ -1,31 +1,5 @@
|
|||||||
import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react';
|
|
||||||
import { Children } from 'react';
|
|
||||||
|
|
||||||
import { StyledTable } from './styles';
|
import { StyledTable } from './styles';
|
||||||
const childrenHasEllipsis = (children: ReactNode | ReactNode[]): boolean => {
|
|
||||||
return Children.toArray(children).some(child => {
|
|
||||||
if (typeof child === 'object' && 'props' in child) {
|
|
||||||
if (!child.props.ellipsis && child.props.children) {
|
|
||||||
return childrenHasEllipsis(child.props.children);
|
|
||||||
}
|
|
||||||
return child.props.ellipsis ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
export const Table = StyledTable;
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Table = ({
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: PropsWithChildren<HTMLAttributes<HTMLTableElement>>) => {
|
|
||||||
const tableLayout = childrenHasEllipsis(children) ? 'fixed' : 'auto';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledTable tableLayout={tableLayout} {...props}>
|
|
||||||
{children}
|
|
||||||
</StyledTable>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Table;
|
export default Table;
|
||||||
|
|||||||
Reference in New Issue
Block a user