diff --git a/apps/web/src/components/blocksuite/block-suite-page-list/utils.tsx b/apps/web/src/components/blocksuite/block-suite-page-list/utils.tsx index 93b90da4e..e6d807edb 100644 --- a/apps/web/src/components/blocksuite/block-suite-page-list/utils.tsx +++ b/apps/web/src/components/blocksuite/block-suite-page-list/utils.tsx @@ -10,7 +10,7 @@ import type { BlockSuiteWorkspace } from '../../../shared'; dayjs.extend(localizedFormat); export const formatDate = (date?: number | unknown) => { 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; }; diff --git a/packages/component/src/components/page-list/all-page.tsx b/packages/component/src/components/page-list/all-page.tsx index 945acadd7..6753ad387 100644 --- a/packages/component/src/components/page-list/all-page.tsx +++ b/packages/component/src/components/page-list/all-page.tsx @@ -1,72 +1,23 @@ -import type { IconButtonProps, TableCellProps } from '@affine/component'; import { - Content, - IconButton, Table, TableBody, TableCell, TableHead, TableRow, - Tooltip, } 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 { - ArrowDownBigIcon, - ArrowUpBigIcon, - FavoritedIcon, - FavoriteIcon, -} from '@blocksuite/icons'; +import { ArrowDownBigIcon, ArrowUpBigIcon } from '@blocksuite/icons'; import { useMediaQuery, useTheme } from '@mui/material'; import type { CSSProperties } from 'react'; -import { forwardRef } from 'react'; -import { NewPageButton } from './new-page-buttton'; -import { - StyledTableContainer, - StyledTableRow, - StyledTitleLink, - StyledTitleWrapper, -} from './styles'; +import { AllPagesBody } from './all-pages-body'; +import { NewPageButton } from './components/new-page-buttton'; +import { TitleCell } from './components/title-cell'; +import { AllPageListMobileView, TrashListMobileView } from './mobile'; +import { StyledTableContainer, StyledTableRow } from './styles'; import { useSorter } from './use-sorter'; -// eslint-disable-next-line react/display-name -const FavoriteTag = forwardRef< - HTMLButtonElement, - { - active: boolean; - } & Omit ->(({ active, onClick, ...props }, ref) => { - const t = useAFFiNEI18N(); - return ( - - { - e.stopPropagation(); - onClick?.(e); - }} - {...props} - > - {active ? ( - - ) : ( - - )} - - - ); -}); - export type PageListProps = { isPublicWorkspace?: boolean; list: ListData[]; @@ -74,36 +25,13 @@ export type PageListProps = { onCreateNewEdgeless: () => void; }; -const TitleCell = ({ - icon, - text, - suffix, - ...props -}: { - icon: JSX.Element; - text: string; - suffix?: JSX.Element; -} & TableCellProps) => { - return ( - - - - {icon} - - {text} - - - {suffix} - - - ); -}; - const AllPagesHead = ({ + isPublicWorkspace, sorter, createNewPage, createNewEdgeless, }: { + isPublicWorkspace: boolean; sorter: ReturnType>; createNewPage: () => void; createNewEdgeless: () => void; @@ -125,6 +53,7 @@ const AllPagesHead = ({ content: t['Updated'](), proportion: 0.2, }, + { key: 'unsortable_action', content: ( @@ -133,11 +62,10 @@ const AllPagesHead = ({ createNewEdgeless={createNewEdgeless} /> ), + showWhen: () => !isPublicWorkspace, sortable: false, styles: { - display: 'flex', - justifyContent: 'center', - alignItems: 'center', + justifyContent: 'flex-end', } satisfies CSSProperties, }, ]; @@ -145,8 +73,9 @@ const AllPagesHead = ({ return ( - {titleList.map( - ({ key, content, proportion, sortable = true, styles }) => ( + {titleList + .filter(({ showWhen = () => true }) => showWhen()) + .map(({ key, content, proportion, sortable = true, styles }) => ( sorter.shiftOrder(key as keyof ListData) : undefined } - style={styles} > - {content} - {sorter.key === key && - (sorter.order === 'asc' ? ( - - ) : ( - - ))} +
+ {content} + {sorter.key === key && + (sorter.order === 'asc' ? ( + + ) : ( + + ))} +
- ) - )} + ))}
); @@ -189,13 +124,12 @@ export type ListData = { onDisablePublicSharing: () => void; }; -export const PageList: React.FC = ({ +export const PageList = ({ isPublicWorkspace = false, list, onCreateNewPage, onCreateNewEdgeless, -}) => { - const t = useAFFiNEI18N(); +}: PageListProps) => { const sorter = useSorter({ data: list, key: 'createDate', @@ -205,93 +139,29 @@ export const PageList: React.FC = ({ const theme = useTheme(); const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm')); if (isSmallDevices) { - return ; + return ( + + ); } - const ListItems = sorter.data.map( - ( - { - pageId, - title, - icon, - isPublicPage, - favorite, - createDate, - updatedDate, - onClickPage, - bookmarkPage, - onOpenPageInNewTab, - removeToTrash, - onDisablePublicSharing, - }, - index - ) => { - return ( - - - - {createDate} - - - {updatedDate ?? createDate} - - {!isPublicWorkspace && ( - - - - - )} - - ); - } - ); - return ( - {ListItems} +
); @@ -338,7 +208,7 @@ export const PageListTrashView: React.FC<{ pageId, onClickPage, })); - return ; + return ; } 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 ( - - - - - {icon} - - {title || t['Untitled']()} - - - - - - ); - }); - - return ( - - - {ListItems} -
-
- ); -}; - export default PageList; diff --git a/packages/component/src/components/page-list/all-pages-body.tsx b/packages/component/src/components/page-list/all-pages-body.tsx new file mode 100644 index 000000000..c08765b42 --- /dev/null +++ b/packages/component/src/components/page-list/all-pages-body.tsx @@ -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 ( + + {data.map( + ( + { + pageId, + title, + icon, + isPublicPage, + favorite, + createDate, + updatedDate, + onClickPage, + bookmarkPage, + onOpenPageInNewTab, + removeToTrash, + onDisablePublicSharing, + }, + index + ) => { + return ( + + + + + {!isPublicWorkspace && ( + + + + + )} + + ); + } + )} + + ); +}; diff --git a/packages/component/src/components/page-list/components/favorite-tag.tsx b/packages/component/src/components/page-list/components/favorite-tag.tsx new file mode 100644 index 000000000..419e351dd --- /dev/null +++ b/packages/component/src/components/page-list/components/favorite-tag.tsx @@ -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 +>(({ active, onClick, ...props }, ref) => { + const t = useAFFiNEI18N(); + return ( + + { + e.stopPropagation(); + onClick?.(e); + }} + {...props} + > + {active ? ( + + ) : ( + + )} + + + ); +}); +FavoriteTag.displayName = 'FavoriteTag'; diff --git a/packages/component/src/components/page-list/new-page-buttton.tsx b/packages/component/src/components/page-list/components/new-page-buttton.tsx similarity index 92% rename from packages/component/src/components/page-list/new-page-buttton.tsx rename to packages/component/src/components/page-list/components/new-page-buttton.tsx index 424a7ef37..4109b57e3 100644 --- a/packages/component/src/components/page-list/new-page-buttton.tsx +++ b/packages/component/src/components/page-list/components/new-page-buttton.tsx @@ -2,9 +2,9 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { useState } from 'react'; -import { DropdownButton } from '../../ui/button/dropdown'; -import { Menu } from '../../ui/menu/menu'; -import { BlockCard } from '../card/block-card'; +import { DropdownButton } from '../../../ui/button/dropdown'; +import { Menu } from '../../../ui/menu/menu'; +import { BlockCard } from '../../card/block-card'; type NewPageButtonProps = { createNewPage: () => void; diff --git a/packages/component/src/components/page-list/components/title-cell.tsx b/packages/component/src/components/page-list/components/title-cell.tsx new file mode 100644 index 000000000..f116fb030 --- /dev/null +++ b/packages/component/src/components/page-list/components/title-cell.tsx @@ -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 ( + + + {icon} + + {text} + + + {suffix} + + ); +}; diff --git a/packages/component/src/components/page-list/mobile.tsx b/packages/component/src/components/page-list/mobile.tsx new file mode 100644 index 000000000..4a8cb0fb5 --- /dev/null +++ b/packages/component/src/components/page-list/mobile.tsx @@ -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 ( + + + {t['Title']()} + {!isPublicWorkspace && ( + +
+ +
+
+ )} +
+
+ ); +}; + +export const AllPageListMobileView = ({ + list, + isPublicWorkspace, + createNewPage, + createNewEdgeless, +}: { + isPublicWorkspace: boolean; + list: ListData[]; + createNewPage: () => void; + createNewEdgeless: () => void; +}) => { + return ( + + + + +
+
+ ); +}; + +// 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 ( + + + + {icon} + + {title || t['Untitled']()} + + + + + ); + }); + + return ( + + + {ListItems} +
+
+ ); +}; diff --git a/packages/component/src/components/page-list/styles.ts b/packages/component/src/components/page-list/styles.ts index 00f288c61..c6694abca 100644 --- a/packages/component/src/components/page-list/styles.ts +++ b/packages/component/src/components/page-list/styles.ts @@ -4,14 +4,26 @@ import { TableRow } from '@affine/component'; export const StyledTableContainer = styled('div')(({ theme }) => { return { height: 'calc(100vh - 52px)', - padding: '78px 72px', + padding: '52px 32px', maxWidth: '100%', overflowY: 'auto', - [theme.breakpoints.down('md')]: { - padding: '12px 24px', + [theme.breakpoints.down('sm')]: { + 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')(() => { return { ...displayFlex('flex-start', 'center'), @@ -28,8 +40,6 @@ export const StyledTitleWrapper = styled('div')(() => { }); export const StyledTitleLink = styled('div')(() => { return { - maxWidth: '80%', - marginRight: '18px', ...displayFlex('flex-start', 'center'), color: 'var(--affine-text-primary-color)', '>svg': { diff --git a/packages/component/src/stories/page-list.stories.tsx b/packages/component/src/stories/page-list.stories.tsx index 129db8a06..ce541c14c 100644 --- a/packages/component/src/stories/page-list.stories.tsx +++ b/packages/component/src/stories/page-list.stories.tsx @@ -10,7 +10,7 @@ import type { } from '../components/page-list/all-page'; import { PageListTrashView } 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 { OperationCell } from '../components/page-list/operation-cell'; import { toast } from '../ui/toast'; @@ -99,6 +99,14 @@ AffineAllPageList.args = { ], }; +export const AffinePublicPageList: StoryFn = ({ ...props }) => ( + +); +AffinePublicPageList.args = { + ...AffineAllPageList.args, + isPublicWorkspace: true, +}; + export const AffineAllPageMobileList: StoryFn = ({ ...props }) => ; diff --git a/packages/component/src/ui/menu/menu.tsx b/packages/component/src/ui/menu/menu.tsx index ca84b82ef..5fd6f18c2 100644 --- a/packages/component/src/ui/menu/menu.tsx +++ b/packages/component/src/ui/menu/menu.tsx @@ -16,10 +16,12 @@ export const Menu = (props: MenuProps) => { content, placement = 'bottom-start', children, + ...popperProps } = props; return content ? ( ( - ({ tableLayout }) => { - return { - fontSize: 'var(--affine-font-base)', - color: 'var(--affine-text-primary-color)', - tableLayout, - width: '100%', - borderCollapse: 'separate', - borderSpacing: '0', - }; - } -); +export const StyledTable = styled('table')(() => { + return { + fontSize: 'var(--affine-font-base)', + color: 'var(--affine-text-primary-color)', + tableLayout: 'fixed', + width: '100%', + borderCollapse: 'separate', + borderSpacing: '0', + }; +}); export const StyledTableBody = styled('tbody')(() => { return { @@ -37,7 +35,7 @@ export const StyledTableCell = styled('td')< return { width, height: '52px', - padding: '0 30px', + paddingLeft: '16px', boxSizing: 'border-box', textAlign: align, verticalAlign: 'middle', diff --git a/packages/component/src/ui/table/table-body.tsx b/packages/component/src/ui/table/table-body.tsx index 3ab84c568..50281e07f 100644 --- a/packages/component/src/ui/table/table-body.tsx +++ b/packages/component/src/ui/table/table-body.tsx @@ -1,11 +1,4 @@ -import type { HTMLAttributes, PropsWithChildren } from 'react'; - import { StyledTableBody } from './styles'; -export const TableBody = ({ - children, - ...props -}: PropsWithChildren>) => { - return {children}; -}; +export const TableBody = StyledTableBody; export default TableBody; diff --git a/packages/component/src/ui/table/table-cell.tsx b/packages/component/src/ui/table/table-cell.tsx index 02d1b4ae9..576d38d08 100644 --- a/packages/component/src/ui/table/table-cell.tsx +++ b/packages/component/src/ui/table/table-cell.tsx @@ -1,7 +1,4 @@ -import type { TableCellProps } from './interface'; import { StyledTableCell } from './styles'; -export const TableCell = ({ children, ...props }: TableCellProps) => { - return {children}; -}; +export const TableCell = StyledTableCell; export default TableCell; diff --git a/packages/component/src/ui/table/table-row.tsx b/packages/component/src/ui/table/table-row.tsx index d047e484d..740485e40 100644 --- a/packages/component/src/ui/table/table-row.tsx +++ b/packages/component/src/ui/table/table-row.tsx @@ -1,11 +1,4 @@ -import type { HTMLAttributes, PropsWithChildren } from 'react'; - import { StyledTableRow } from './styles'; -export const TableRow = ({ - children, - ...props -}: PropsWithChildren>) => { - return {children}; -}; +export const TableRow = StyledTableRow; export default TableRow; diff --git a/packages/component/src/ui/table/table.tsx b/packages/component/src/ui/table/table.tsx index 655269285..25caa149d 100644 --- a/packages/component/src/ui/table/table.tsx +++ b/packages/component/src/ui/table/table.tsx @@ -1,31 +1,5 @@ -import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; -import { Children } from 'react'; - 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 = ({ - children, - ...props -}: PropsWithChildren>) => { - const tableLayout = childrenHasEllipsis(children) ? 'fixed' : 'auto'; - - return ( - - {children} - - ); -}; +export const Table = StyledTable; export default Table;