mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 23:07:02 +08:00
chore: move client folders (#948)
This commit is contained in:
27
apps/web/src/components/page-list/DateCell.tsx
Normal file
27
apps/web/src/components/page-list/DateCell.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import dayjs from 'dayjs';
|
||||
import { PageMeta } from '@/providers/app-state-provider';
|
||||
import { TableCell } from '@affine/component';
|
||||
import React from 'react';
|
||||
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
export const DateCell = ({
|
||||
pageMeta,
|
||||
dateKey,
|
||||
backupKey = '',
|
||||
}: {
|
||||
pageMeta: PageMeta;
|
||||
dateKey: keyof PageMeta;
|
||||
backupKey?: keyof PageMeta;
|
||||
}) => {
|
||||
// dayjs().format('L LT');
|
||||
const value = pageMeta[dateKey] ?? pageMeta[backupKey];
|
||||
return (
|
||||
<TableCell ellipsis={true}>
|
||||
{value ? dayjs(value as string).format('YYYY-MM-DD HH:mm') : '--'}
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateCell;
|
||||
21
apps/web/src/components/page-list/Empty.tsx
Normal file
21
apps/web/src/components/page-list/Empty.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { Empty } from '@affine/component';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
export const PageListEmpty = (props: { listType?: string }) => {
|
||||
const { listType } = props;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Empty
|
||||
width={800}
|
||||
height={300}
|
||||
sx={{ marginTop: '100px', marginBottom: '30px' }}
|
||||
/>
|
||||
{listType === 'all' && <p>{t('emptyAllPages')}</p>}
|
||||
{listType === 'favorite' && <p>{t('emptyFavorite')}</p>}
|
||||
{listType === 'trash' && <p>{t('emptyTrash')}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageListEmpty;
|
||||
113
apps/web/src/components/page-list/OperationCell.tsx
Normal file
113
apps/web/src/components/page-list/OperationCell.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { useConfirm } from '@/providers/ConfirmProvider';
|
||||
import { PageMeta } from '@/providers/app-state-provider';
|
||||
import { Menu, MenuItem } from '@affine/component';
|
||||
import { FlexWrapper } from '@affine/component';
|
||||
import { IconButton } from '@affine/component';
|
||||
import {
|
||||
MoreVerticalIcon,
|
||||
RestoreIcon,
|
||||
DeleteIcon,
|
||||
FavouritesIcon,
|
||||
FavouritedIcon,
|
||||
OpenInNewIcon,
|
||||
TrashIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { toast } from '@affine/component';
|
||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => {
|
||||
const { id, favorite } = pageMeta;
|
||||
const { openPage } = usePageHelper();
|
||||
const { toggleFavoritePage, toggleDeletePage } = usePageHelper();
|
||||
const { confirm } = useConfirm();
|
||||
const { t } = useTranslation();
|
||||
const OperationMenu = (
|
||||
<>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
toggleFavoritePage(id);
|
||||
toast(
|
||||
favorite ? t('Removed from Favorites') : t('Added to Favorites')
|
||||
);
|
||||
}}
|
||||
icon={favorite ? <FavouritedIcon /> : <FavouritesIcon />}
|
||||
>
|
||||
{favorite ? t('Remove from favorites') : t('Add to favorites')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
openPage(id, {}, true);
|
||||
}}
|
||||
icon={<OpenInNewIcon />}
|
||||
>
|
||||
{t('Open in new tab')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
confirm({
|
||||
title: t('Delete page?'),
|
||||
content: t('will be moved to Trash', {
|
||||
title: pageMeta.title || 'Untitled',
|
||||
}),
|
||||
confirmText: t('Delete'),
|
||||
confirmType: 'danger',
|
||||
}).then(confirm => {
|
||||
confirm && toggleDeletePage(id);
|
||||
toast(t('Moved to Trash'));
|
||||
});
|
||||
}}
|
||||
icon={<TrashIcon />}
|
||||
>
|
||||
{t('Delete')}
|
||||
</MenuItem>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<FlexWrapper alignItems="center" justifyContent="center">
|
||||
<Menu content={OperationMenu} placement="bottom-end" disablePortal={true}>
|
||||
<IconButton darker={true}>
|
||||
<MoreVerticalIcon />
|
||||
</IconButton>
|
||||
</Menu>
|
||||
</FlexWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const TrashOperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => {
|
||||
const { id } = pageMeta;
|
||||
const { openPage, getPageMeta } = usePageHelper();
|
||||
const { toggleDeletePage, permanentlyDeletePage } = usePageHelper();
|
||||
const { confirm } = useConfirm();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<FlexWrapper>
|
||||
<IconButton
|
||||
darker={true}
|
||||
style={{ marginRight: '12px' }}
|
||||
onClick={() => {
|
||||
toggleDeletePage(id);
|
||||
toast(t('restored', { title: getPageMeta(id)?.title || 'Untitled' }));
|
||||
openPage(id);
|
||||
}}
|
||||
>
|
||||
<RestoreIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
darker={true}
|
||||
onClick={() => {
|
||||
confirm({
|
||||
title: t('Delete permanently?'),
|
||||
content: t("Once deleted, you can't undo this action."),
|
||||
confirmText: t('Delete'),
|
||||
confirmType: 'danger',
|
||||
}).then(confirm => {
|
||||
confirm && permanentlyDeletePage(id);
|
||||
toast(t('Permanently deleted'));
|
||||
});
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</FlexWrapper>
|
||||
);
|
||||
};
|
||||
168
apps/web/src/components/page-list/index.tsx
Normal file
168
apps/web/src/components/page-list/index.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
import { PageMeta } from '@/providers/app-state-provider';
|
||||
import {
|
||||
FavouritedIcon,
|
||||
FavouritesIcon,
|
||||
PaperIcon,
|
||||
EdgelessIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import {
|
||||
StyledTableContainer,
|
||||
StyledTableRow,
|
||||
StyledTitleLink,
|
||||
StyledTitleWrapper,
|
||||
} from './styles';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
} from '@affine/component';
|
||||
import { OperationCell, TrashOperationCell } from './OperationCell';
|
||||
import Empty from './Empty';
|
||||
import { Content } from '@affine/component';
|
||||
import React from 'react';
|
||||
import DateCell from '@/components/page-list/DateCell';
|
||||
import { IconButton } from '@affine/component';
|
||||
import { Tooltip } from '@affine/component';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useAppState } from '@/providers/app-state-provider';
|
||||
import { toast } from '@affine/component';
|
||||
import { usePageHelper } from '@/hooks/use-page-helper';
|
||||
import { useTheme } from '@/providers/ThemeProvider';
|
||||
import { useTranslation } from '@affine/i18n';
|
||||
const FavoriteTag = ({
|
||||
pageMeta: { favorite, id },
|
||||
}: {
|
||||
pageMeta: PageMeta;
|
||||
}) => {
|
||||
const { toggleFavoritePage } = usePageHelper();
|
||||
const { theme } = useTheme();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Tooltip
|
||||
content={favorite ? t('Favorited') : t('Favorite')}
|
||||
placement="top-start"
|
||||
>
|
||||
<IconButton
|
||||
darker={true}
|
||||
iconSize={[20, 20]}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
toggleFavoritePage(id);
|
||||
toast(
|
||||
favorite ? t('Removed from Favorites') : t('Added to Favorites')
|
||||
);
|
||||
}}
|
||||
style={{
|
||||
color: favorite ? theme.colors.primaryColor : theme.colors.iconColor,
|
||||
}}
|
||||
className={favorite ? '' : 'favorite-button'}
|
||||
>
|
||||
{favorite ? (
|
||||
<FavouritedIcon data-testid="favorited-icon" />
|
||||
) : (
|
||||
<FavouritesIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export const PageList = ({
|
||||
pageList,
|
||||
showFavoriteTag = false,
|
||||
isTrash = false,
|
||||
isPublic = false,
|
||||
listType,
|
||||
}: {
|
||||
pageList: PageMeta[];
|
||||
showFavoriteTag?: boolean;
|
||||
isTrash?: boolean;
|
||||
isPublic?: boolean;
|
||||
listType?: 'all' | 'trash' | 'favorite';
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { currentWorkspace } = useAppState();
|
||||
const { t } = useTranslation();
|
||||
if (pageList.length === 0) {
|
||||
return <Empty listType={listType} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledTableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell proportion={0.5}>{t('Title')}</TableCell>
|
||||
<TableCell proportion={0.2}>{t('Created')}</TableCell>
|
||||
<TableCell proportion={0.2}>
|
||||
{isTrash ? t('Moved to Trash') : t('Updated')}
|
||||
</TableCell>
|
||||
<TableCell proportion={0.1}></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{pageList.map((pageMeta, index) => {
|
||||
return (
|
||||
<StyledTableRow
|
||||
data-testid="page-list-item"
|
||||
key={`${pageMeta.id}-${index}`}
|
||||
onClick={() => {
|
||||
if (isPublic) {
|
||||
router.push(
|
||||
`/public-workspace/${router.query.workspaceId}/${pageMeta.id}`
|
||||
);
|
||||
} else {
|
||||
router.push(
|
||||
`/workspace/${currentWorkspace?.id}/${pageMeta.id}`
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TableCell>
|
||||
<StyledTitleWrapper>
|
||||
<StyledTitleLink>
|
||||
{pageMeta.mode === 'edgeless' ? (
|
||||
<EdgelessIcon />
|
||||
) : (
|
||||
<PaperIcon />
|
||||
)}
|
||||
<Content ellipsis={true} color="inherit">
|
||||
{pageMeta.title || t('Untitled')}
|
||||
</Content>
|
||||
</StyledTitleLink>
|
||||
{showFavoriteTag && <FavoriteTag pageMeta={pageMeta} />}
|
||||
</StyledTitleWrapper>
|
||||
</TableCell>
|
||||
<DateCell pageMeta={pageMeta} dateKey="createDate" />
|
||||
<DateCell
|
||||
pageMeta={pageMeta}
|
||||
dateKey={isTrash ? 'trashDate' : 'updatedDate'}
|
||||
backupKey={isTrash ? 'trashDate' : 'createDate'}
|
||||
/>
|
||||
{!isPublic ? (
|
||||
<TableCell
|
||||
style={{ padding: 0 }}
|
||||
data-testid={`more-actions-${pageMeta.id}`}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{isTrash ? (
|
||||
<TrashOperationCell pageMeta={pageMeta} />
|
||||
) : (
|
||||
<OperationCell pageMeta={pageMeta} />
|
||||
)}
|
||||
</TableCell>
|
||||
) : null}
|
||||
</StyledTableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</StyledTableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageList;
|
||||
51
apps/web/src/components/page-list/styles.ts
Normal file
51
apps/web/src/components/page-list/styles.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { displayFlex, styled } from '@affine/component';
|
||||
import { TableRow } from '@affine/component';
|
||||
|
||||
export const StyledTableContainer = styled.div(() => {
|
||||
return {
|
||||
height: 'calc(100vh - 60px)',
|
||||
padding: '78px 72px',
|
||||
overflowY: 'auto',
|
||||
};
|
||||
});
|
||||
export const StyledTitleWrapper = styled.div(({ theme }) => {
|
||||
return {
|
||||
...displayFlex('flex-start', 'center'),
|
||||
a: {
|
||||
color: 'inherit',
|
||||
},
|
||||
'a:visited': {
|
||||
color: 'unset',
|
||||
},
|
||||
'a:hover': {
|
||||
color: theme.colors.primaryColor,
|
||||
},
|
||||
};
|
||||
});
|
||||
export const StyledTitleLink = styled.div(({ theme }) => {
|
||||
return {
|
||||
maxWidth: '80%',
|
||||
marginRight: '18px',
|
||||
...displayFlex('flex-start', 'center'),
|
||||
color: theme.colors.textColor,
|
||||
'>svg': {
|
||||
fontSize: '24px',
|
||||
marginRight: '12px',
|
||||
color: theme.colors.iconColor,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const StyledTableRow = styled(TableRow)(() => {
|
||||
return {
|
||||
cursor: 'pointer',
|
||||
'.favorite-button': {
|
||||
display: 'none',
|
||||
},
|
||||
'&:hover': {
|
||||
'.favorite-button': {
|
||||
display: 'flex',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user