mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-10 15:44:12 +03:00
feat: sticky table head in page list (#2668)
Co-authored-by: Himself65 <himself65@outlook.com>
This commit is contained in:
parent
b461a684ad
commit
efae4cccd6
@ -79,6 +79,7 @@ export const sidebarButtonStyle = style({
|
|||||||
width: 'auto',
|
width: 'auto',
|
||||||
height: '32px',
|
height: '32px',
|
||||||
color: 'var(--affine-icon-color)',
|
color: 'var(--affine-icon-color)',
|
||||||
|
zIndex: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sidebarFloatMaskStyle = style({
|
export const sidebarFloatMaskStyle = style({
|
||||||
|
@ -1,41 +1,14 @@
|
|||||||
import * as ScrollArea from '@radix-ui/react-scroll-area';
|
import * as ScrollArea from '@radix-ui/react-scroll-area';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { type PropsWithChildren, useEffect, useRef, useState } from 'react';
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
|
import { useHasScrollTop } from './use-has-scroll-top';
|
||||||
|
|
||||||
export function SidebarContainer({ children }: PropsWithChildren) {
|
export function SidebarContainer({ children }: PropsWithChildren) {
|
||||||
return <div className={clsx([styles.baseContainer])}>{children}</div>;
|
return <div className={clsx([styles.baseContainer])}>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function useHasScrollTop() {
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
const [hasScrollTop, setHasScrollTop] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!ref.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const container = ref.current;
|
|
||||||
|
|
||||||
function updateScrollTop() {
|
|
||||||
if (container) {
|
|
||||||
const hasScrollTop = container.scrollTop > 0;
|
|
||||||
setHasScrollTop(hasScrollTop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
container.addEventListener('scroll', updateScrollTop);
|
|
||||||
updateScrollTop();
|
|
||||||
return () => {
|
|
||||||
container.removeEventListener('scroll', updateScrollTop);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [hasScrollTop, ref] as const;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SidebarScrollableContainer({ children }: PropsWithChildren) {
|
export function SidebarScrollableContainer({ children }: PropsWithChildren) {
|
||||||
const [hasScrollTop, ref] = useHasScrollTop();
|
const [hasScrollTop, ref] = useHasScrollTop();
|
||||||
return (
|
return (
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
export function useHasScrollTop() {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [hasScrollTop, setHasScrollTop] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = ref.current;
|
||||||
|
|
||||||
|
function updateScrollTop() {
|
||||||
|
if (container) {
|
||||||
|
const hasScrollTop = container.scrollTop > 0;
|
||||||
|
setHasScrollTop(hasScrollTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
container.addEventListener('scroll', updateScrollTop);
|
||||||
|
updateScrollTop();
|
||||||
|
return () => {
|
||||||
|
container.removeEventListener('scroll', updateScrollTop);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [hasScrollTop, ref] as const;
|
||||||
|
}
|
@ -5,13 +5,15 @@ import { useMediaQuery, useTheme } from '@mui/material';
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { type CSSProperties } from 'react';
|
import { type CSSProperties } from 'react';
|
||||||
|
|
||||||
import { Table, TableBody, TableCell, TableHead, TableRow } from '../..';
|
import { Table, TableBody, TableCell, TableHead, TableHeadRow } from '../..';
|
||||||
|
import { TableBodyRow } from '../../ui/table';
|
||||||
|
import { useHasScrollTop } from '../app-sidebar/sidebar-containers/use-has-scroll-top';
|
||||||
import { AllPagesBody } from './all-pages-body';
|
import { AllPagesBody } from './all-pages-body';
|
||||||
import { NewPageButton } from './components/new-page-buttton';
|
import { NewPageButton } from './components/new-page-buttton';
|
||||||
import { TitleCell } from './components/title-cell';
|
import { TitleCell } from './components/title-cell';
|
||||||
import { AllPageListMobileView, TrashListMobileView } from './mobile';
|
import { AllPageListMobileView, TrashListMobileView } from './mobile';
|
||||||
import { TrashOperationCell } from './operation-cell';
|
import { TrashOperationCell } from './operation-cell';
|
||||||
import { StyledTableContainer, StyledTableRow } from './styles';
|
import { StyledTableContainer } from './styles';
|
||||||
import type { ListData, PageListProps, TrashListData } from './type';
|
import type { ListData, PageListProps, TrashListData } from './type';
|
||||||
import { useSorter } from './use-sorter';
|
import { useSorter } from './use-sorter';
|
||||||
import { formatDate, useIsSmallDevices } from './utils';
|
import { formatDate, useIsSmallDevices } from './utils';
|
||||||
@ -66,7 +68,7 @@ const AllPagesHead = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableHeadRow>
|
||||||
{titleList
|
{titleList
|
||||||
.filter(({ showWhen = () => true }) => showWhen())
|
.filter(({ showWhen = () => true }) => showWhen())
|
||||||
.map(({ key, content, proportion, sortable = true, styles }) => (
|
.map(({ key, content, proportion, sortable = true, styles }) => (
|
||||||
@ -97,7 +99,7 @@ const AllPagesHead = ({
|
|||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableHeadRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -115,7 +117,7 @@ export const PageList = ({
|
|||||||
key: DEFAULT_SORT_KEY,
|
key: DEFAULT_SORT_KEY,
|
||||||
order: 'desc',
|
order: 'desc',
|
||||||
});
|
});
|
||||||
|
const [hasScrollTop, ref] = useHasScrollTop();
|
||||||
const isSmallDevices = useIsSmallDevices();
|
const isSmallDevices = useIsSmallDevices();
|
||||||
if (isSmallDevices) {
|
if (isSmallDevices) {
|
||||||
return (
|
return (
|
||||||
@ -138,8 +140,8 @@ export const PageList = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTableContainer>
|
<StyledTableContainer ref={ref}>
|
||||||
<Table>
|
<Table showBorder={hasScrollTop} style={{ maxHeight: '100%' }}>
|
||||||
<AllPagesHead
|
<AllPagesHead
|
||||||
isPublicWorkspace={isPublicWorkspace}
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
sorter={sorter}
|
sorter={sorter}
|
||||||
@ -162,12 +164,12 @@ const TrashListHead = () => {
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
return (
|
return (
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableHeadRow>
|
||||||
<TableCell proportion={0.5}>{t['Title']()}</TableCell>
|
<TableCell proportion={0.5}>{t['Title']()}</TableCell>
|
||||||
<TableCell proportion={0.2}>{t['Created']()}</TableCell>
|
<TableCell proportion={0.2}>{t['Created']()}</TableCell>
|
||||||
<TableCell proportion={0.2}>{t['Moved to Trash']()}</TableCell>
|
<TableCell proportion={0.2}>{t['Moved to Trash']()}</TableCell>
|
||||||
<TableCell proportion={0.1}></TableCell>
|
<TableCell proportion={0.1}></TableCell>
|
||||||
</TableRow>
|
</TableHeadRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -179,6 +181,7 @@ export const PageListTrashView: React.FC<{
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const [hasScrollTop, ref] = useHasScrollTop();
|
||||||
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
const isSmallDevices = useMediaQuery(theme.breakpoints.down('sm'));
|
||||||
if (isSmallDevices) {
|
if (isSmallDevices) {
|
||||||
const mobileList = list.map(({ pageId, icon, title, onClickPage }) => ({
|
const mobileList = list.map(({ pageId, icon, title, onClickPage }) => ({
|
||||||
@ -205,7 +208,7 @@ export const PageListTrashView: React.FC<{
|
|||||||
index
|
index
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<StyledTableRow
|
<TableBodyRow
|
||||||
data-testid={`page-list-item-${pageId}`}
|
data-testid={`page-list-item-${pageId}`}
|
||||||
key={`${pageId}-${index}`}
|
key={`${pageId}-${index}`}
|
||||||
>
|
>
|
||||||
@ -229,14 +232,14 @@ export const PageListTrashView: React.FC<{
|
|||||||
onOpenPage={onClickPage}
|
onOpenPage={onClickPage}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</StyledTableRow>
|
</TableBodyRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTableContainer>
|
<StyledTableContainer ref={ref}>
|
||||||
<Table>
|
<Table showBorder={hasScrollTop}>
|
||||||
<TrashListHead />
|
<TrashListHead />
|
||||||
<TableBody>{ListItems}</TableBody>
|
<TableBody>{ListItems}</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
|
@ -3,18 +3,19 @@ import { useDraggable } from '@dnd-kit/core';
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
|
|
||||||
import { styled, TableBody, TableCell } from '../../..';
|
import { styled } from '../../styles';
|
||||||
|
import { TableBody, TableCell } from '../../ui/table';
|
||||||
import { FavoriteTag } from './components/favorite-tag';
|
import { FavoriteTag } from './components/favorite-tag';
|
||||||
import { TitleCell } from './components/title-cell';
|
import { TitleCell } from './components/title-cell';
|
||||||
import { OperationCell } from './operation-cell';
|
import { OperationCell } from './operation-cell';
|
||||||
import { StyledTableRow } from './styles';
|
import { StyledTableBodyRow } from './styles';
|
||||||
import type { DateKey, DraggableTitleCellData, ListData } from './type';
|
import type { DateKey, DraggableTitleCellData, ListData } from './type';
|
||||||
import { useDateGroup } from './use-date-group';
|
import { useDateGroup } from './use-date-group';
|
||||||
import { formatDate, useIsSmallDevices } from './utils';
|
import { formatDate, useIsSmallDevices } from './utils';
|
||||||
|
|
||||||
export const GroupRow = ({ children }: { children: ReactNode }) => {
|
export const GroupRow = ({ children }: { children: ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<StyledTableRow>
|
<StyledTableBodyRow>
|
||||||
<TableCell
|
<TableCell
|
||||||
style={{
|
style={{
|
||||||
color: 'var(--affine-text-secondary-color)',
|
color: 'var(--affine-text-secondary-color)',
|
||||||
@ -25,7 +26,7 @@ export const GroupRow = ({ children }: { children: ReactNode }) => {
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</StyledTableRow>
|
</StyledTableBodyRow>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ export const AllPagesBody = ({
|
|||||||
const isSmallDevices = useIsSmallDevices();
|
const isSmallDevices = useIsSmallDevices();
|
||||||
const dataWithGroup = useDateGroup({ data, key: groupKey });
|
const dataWithGroup = useDateGroup({ data, key: groupKey });
|
||||||
return (
|
return (
|
||||||
<TableBody>
|
<TableBody style={{ overflowY: 'auto', height: '100%' }}>
|
||||||
{dataWithGroup.map(
|
{dataWithGroup.map(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@ -71,7 +72,7 @@ export const AllPagesBody = ({
|
|||||||
dataWithGroup[index - 1].groupName !== groupName) && (
|
dataWithGroup[index - 1].groupName !== groupName) && (
|
||||||
<GroupRow>{groupName}</GroupRow>
|
<GroupRow>{groupName}</GroupRow>
|
||||||
)}
|
)}
|
||||||
<StyledTableRow data-testid={`page-list-item-${pageId}`}>
|
<StyledTableBodyRow data-testid={`page-list-item-${pageId}`}>
|
||||||
<DraggableTitleCell
|
<DraggableTitleCell
|
||||||
pageId={pageId}
|
pageId={pageId}
|
||||||
draggableData={{
|
draggableData={{
|
||||||
@ -130,7 +131,7 @@ export const AllPagesBody = ({
|
|||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
</StyledTableRow>
|
</StyledTableBodyRow>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,13 @@ import {
|
|||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableHeadRow,
|
||||||
} from '../../..';
|
} from '../../..';
|
||||||
import { AllPagesBody } from './all-pages-body';
|
import { AllPagesBody } from './all-pages-body';
|
||||||
import { NewPageButton } from './components/new-page-buttton';
|
import { NewPageButton } from './components/new-page-buttton';
|
||||||
import {
|
import {
|
||||||
|
StyledTableBodyRow,
|
||||||
StyledTableContainer,
|
StyledTableContainer,
|
||||||
StyledTableRow,
|
|
||||||
StyledTitleLink,
|
StyledTitleLink,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import type { ListData } from './type';
|
import type { ListData } from './type';
|
||||||
@ -31,7 +31,7 @@ const MobileHead = ({
|
|||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
return (
|
return (
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableHeadRow>
|
||||||
<TableCell proportion={0.8}>{t['Title']()}</TableCell>
|
<TableCell proportion={0.8}>{t['Title']()}</TableCell>
|
||||||
{!isPublicWorkspace && (
|
{!isPublicWorkspace && (
|
||||||
<TableCell>
|
<TableCell>
|
||||||
@ -50,7 +50,7 @@ const MobileHead = ({
|
|||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
)}
|
)}
|
||||||
</TableRow>
|
</TableHeadRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -103,7 +103,7 @@ export const TrashListMobileView = ({
|
|||||||
|
|
||||||
const ListItems = list.map(({ pageId, title, icon, onClickPage }, index) => {
|
const ListItems = list.map(({ pageId, title, icon, onClickPage }, index) => {
|
||||||
return (
|
return (
|
||||||
<StyledTableRow
|
<StyledTableBodyRow
|
||||||
data-testid={`page-list-item-${pageId}`}
|
data-testid={`page-list-item-${pageId}`}
|
||||||
key={`${pageId}-${index}`}
|
key={`${pageId}-${index}`}
|
||||||
>
|
>
|
||||||
@ -115,7 +115,7 @@ export const TrashListMobileView = ({
|
|||||||
</Content>
|
</Content>
|
||||||
</StyledTitleLink>
|
</StyledTitleLink>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</StyledTableRow>
|
</StyledTableBodyRow>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { displayFlex, styled } from '../../styles';
|
import { displayFlex, styled } from '../../styles';
|
||||||
import { Content } from '../../ui/layout/content';
|
import { Content } from '../../ui/layout/content';
|
||||||
import { TableRow } from '../../ui/table/table-row';
|
import { TableBodyRow } from '../../ui/table/table-row';
|
||||||
|
|
||||||
export const StyledTableContainer = styled('div')(({ theme }) => {
|
export const StyledTableContainer = styled('div')(({ theme }) => {
|
||||||
return {
|
return {
|
||||||
height: 'calc(100vh - 52px)',
|
height: '100%',
|
||||||
padding: '18px 32px 80px 32px',
|
padding: '0 32px 180px 32px',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
overflowY: 'auto',
|
overflowY: 'scroll',
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
padding: '52px 0px',
|
padding: '52px 0px',
|
||||||
'tr > td:first-of-type': {
|
'tr > td:first-of-type': {
|
||||||
@ -69,7 +69,7 @@ export const StyledTitlePreview = styled(Content)(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const StyledTableRow = styled(TableRow)(() => {
|
export const StyledTableBodyRow = styled(TableBodyRow)(() => {
|
||||||
return {
|
return {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'.favorite-button': {
|
'.favorite-button': {
|
||||||
|
@ -113,7 +113,7 @@ AffineAllPageList.args = {
|
|||||||
removeToTrash: () => toast('Remove to trash'),
|
removeToTrash: () => toast('Remove to trash'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pageId: '3',
|
pageId: '4',
|
||||||
favorite: false,
|
favorite: false,
|
||||||
isPublicPage: false,
|
isPublicPage: false,
|
||||||
icon: <PageIcon />,
|
icon: <PageIcon />,
|
||||||
|
@ -24,7 +24,11 @@ export const DropdownButton = forwardRef<
|
|||||||
<span>{children}</span>
|
<span>{children}</span>
|
||||||
<span className={styles.divider} />
|
<span className={styles.divider} />
|
||||||
<span className={styles.dropdownWrapper} onClick={handleClickDropDown}>
|
<span className={styles.dropdownWrapper} onClick={handleClickDropDown}>
|
||||||
<ArrowDownSmallIcon className={styles.icon} width={16} height={16} />
|
<ArrowDownSmallIcon
|
||||||
|
className={styles.dropdownIcon}
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
@ -44,10 +44,10 @@ export const dropdownWrapper = style({
|
|||||||
paddingRight: '10px',
|
paddingRight: '10px',
|
||||||
});
|
});
|
||||||
|
|
||||||
export const icon = style({
|
export const dropdownIcon = style({
|
||||||
borderRadius: '4px',
|
borderRadius: '4px',
|
||||||
selectors: {
|
selectors: {
|
||||||
'&:hover': {
|
[`${dropdownWrapper}:hover &`]: {
|
||||||
background: 'var(--affine-hover-color)',
|
background: 'var(--affine-hover-color)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties } from 'react';
|
||||||
|
|
||||||
import { absoluteCenter, displayInlineFlex, styled } from '../../styles';
|
import { displayInlineFlex, styled } from '../../styles';
|
||||||
import type { ButtonProps } from './interface';
|
import type { ButtonProps } from './interface';
|
||||||
import { getButtonColors, getSize } from './utils';
|
import { getButtonColors, getSize } from './utils';
|
||||||
|
|
||||||
@ -46,30 +46,13 @@ export const StyledIconButton = styled('button', {
|
|||||||
WebkitAppRegion: 'no-drag',
|
WebkitAppRegion: 'no-drag',
|
||||||
color: 'var(--affine-icon-color)',
|
color: 'var(--affine-icon-color)',
|
||||||
...displayInlineFlex('center', 'center'),
|
...displayInlineFlex('center', 'center'),
|
||||||
position: 'relative',
|
|
||||||
...(disabled ? { cursor: 'not-allowed', pointerEvents: 'none' } : {}),
|
...(disabled ? { cursor: 'not-allowed', pointerEvents: 'none' } : {}),
|
||||||
transition: 'background .15s',
|
transition: 'background .15s',
|
||||||
|
borderRadius,
|
||||||
// TODO: we need to add @emotion/babel-plugin
|
|
||||||
'::after': {
|
|
||||||
content: '""',
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
borderRadius,
|
|
||||||
transition: 'background .15s',
|
|
||||||
...absoluteCenter({ horizontal: true, vertical: true }),
|
|
||||||
},
|
|
||||||
|
|
||||||
svg: {
|
|
||||||
position: 'relative',
|
|
||||||
zIndex: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
':hover': {
|
':hover': {
|
||||||
color: hoverColor ?? 'var(--affine-primary-color)',
|
color: hoverColor ?? 'var(--affine-primary-color)',
|
||||||
'::after': {
|
background: hoverBackground || 'var(--affine-hover-color)',
|
||||||
background: hoverBackground || 'var(--affine-hover-color)',
|
|
||||||
},
|
|
||||||
...(hoverStyle ?? {}),
|
...(hoverStyle ?? {}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@ export const StyledEmptyContainer = styled('div')<{ style?: CSSProperties }>(
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
...displayFlex('center', 'center'),
|
...displayFlex('center', 'center'),
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
color: 'var(--affine-text-secondary-color)',
|
||||||
svg: {
|
svg: {
|
||||||
color: 'transparent',
|
color: 'transparent',
|
||||||
width: style?.width ?? '248px',
|
width: style?.width ?? '248px',
|
||||||
|
@ -18,6 +18,7 @@ const StyledIconButton = styled(IconButton)<
|
|||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: top ?? 24,
|
top: top ?? 24,
|
||||||
right: right ?? 40,
|
right: right ?? 40,
|
||||||
|
zIndex: 1,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,16 +1,36 @@
|
|||||||
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')(() => {
|
export const StyledTable = styled('table')<{ showBorder?: boolean }>(
|
||||||
return {
|
({ showBorder }) => {
|
||||||
fontSize: 'var(--affine-font-base)',
|
return {
|
||||||
color: 'var(--affine-text-primary-color)',
|
fontSize: 'var(--affine-font-base)',
|
||||||
tableLayout: 'fixed',
|
color: 'var(--affine-text-primary-color)',
|
||||||
width: '100%',
|
tableLayout: 'fixed',
|
||||||
borderCollapse: 'separate',
|
width: '100%',
|
||||||
borderSpacing: '0',
|
borderCollapse: 'collapse',
|
||||||
};
|
borderSpacing: '0',
|
||||||
});
|
|
||||||
|
...(typeof showBorder === 'boolean'
|
||||||
|
? {
|
||||||
|
thead: {
|
||||||
|
'::after': {
|
||||||
|
display: 'block',
|
||||||
|
position: 'absolute',
|
||||||
|
content: '""',
|
||||||
|
width: '100%',
|
||||||
|
height: '1px',
|
||||||
|
left: 0,
|
||||||
|
background: 'var(--affine-border-color)',
|
||||||
|
transition: 'opacity .15s',
|
||||||
|
opacity: showBorder ? 1 : 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export const StyledTableBody = styled('tbody')(() => {
|
export const StyledTableBody = styled('tbody')(() => {
|
||||||
return {
|
return {
|
||||||
@ -53,24 +73,29 @@ export const StyledTableHead = styled('thead')(() => {
|
|||||||
return {
|
return {
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
color: 'var(--affine-text-secondary-color)',
|
color: 'var(--affine-text-secondary-color)',
|
||||||
tr: {
|
};
|
||||||
td: {
|
});
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
},
|
export const StyledTHeadRow = styled('tr')(() => {
|
||||||
':hover': {
|
return {
|
||||||
td: {
|
td: {
|
||||||
background: 'unset',
|
whiteSpace: 'nowrap',
|
||||||
},
|
// How to set tbody height with overflow scroll
|
||||||
},
|
// see https://stackoverflow.com/questions/23989463/how-to-set-tbody-height-with-overflow-scroll
|
||||||
|
position: 'sticky',
|
||||||
|
top: 0,
|
||||||
|
background: 'var(--affine-background-primary-color)',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export const StyledTableRow = styled('tr')(() => {
|
export const StyledTBodyRow = styled('tr')(() => {
|
||||||
return {
|
return {
|
||||||
td: {
|
td: {
|
||||||
transition: 'background .15s',
|
transition: 'background .15s',
|
||||||
},
|
},
|
||||||
|
// Add border radius to table row
|
||||||
|
// see https://stackoverflow.com/questions/4094126/how-to-add-border-radius-on-table-row
|
||||||
'td:first-of-type': {
|
'td:first-of-type': {
|
||||||
borderTopLeftRadius: '10px',
|
borderTopLeftRadius: '10px',
|
||||||
borderBottomLeftRadius: '10px',
|
borderBottomLeftRadius: '10px',
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
|
||||||
|
|
||||||
import { StyledTableHead } from './styles';
|
import { StyledTableHead } from './styles';
|
||||||
|
|
||||||
export const TableHead = ({
|
export const TableHead = StyledTableHead;
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: PropsWithChildren<HTMLAttributes<HTMLTableSectionElement>>) => {
|
|
||||||
return <StyledTableHead {...props}>{children}</StyledTableHead>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TableHead;
|
export default TableHead;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { StyledTableRow } from './styles';
|
import { StyledTBodyRow, StyledTHeadRow } from './styles';
|
||||||
export const TableRow = StyledTableRow;
|
export const TableHeadRow = StyledTHeadRow;
|
||||||
|
export const TableBodyRow = StyledTBodyRow;
|
||||||
|
|
||||||
export default TableRow;
|
export default TableHeadRow;
|
||||||
|
Loading…
Reference in New Issue
Block a user