mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 10:52:40 +08:00
style: add scrollbar (#2826)
This commit is contained in:
@@ -48,8 +48,7 @@ export const StyleWorkspaceTitle = styled('div')(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const StyledFooter = styled('div')({
|
export const StyledFooter = styled('div')({
|
||||||
height: '84px',
|
padding: '20px 40px',
|
||||||
padding: '0 40px',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
...displayFlex('space-between', 'center'),
|
...displayFlex('space-between', 'center'),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ModalWrapper,
|
ModalWrapper,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { ScrollableContainer } from '@affine/component';
|
||||||
import { WorkspaceList } from '@affine/component/workspace-list';
|
import { WorkspaceList } from '@affine/component/workspace-list';
|
||||||
import type {
|
import type {
|
||||||
AffineLegacyCloudWorkspace,
|
AffineLegacyCloudWorkspace,
|
||||||
@@ -105,7 +106,7 @@ export const WorkspaceListModal = ({
|
|||||||
/>
|
/>
|
||||||
</StyledOperationWrapper>
|
</StyledOperationWrapper>
|
||||||
</StyledModalHeader>
|
</StyledModalHeader>
|
||||||
|
<ScrollableContainer>
|
||||||
<StyledModalContent>
|
<StyledModalContent>
|
||||||
<WorkspaceList
|
<WorkspaceList
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -128,6 +129,7 @@ export const WorkspaceListModal = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{!environment.isDesktop && (
|
{!environment.isDesktop && (
|
||||||
|
<div>
|
||||||
<StyledCreateWorkspaceCard
|
<StyledCreateWorkspaceCard
|
||||||
onClick={onNewWorkspace}
|
onClick={onNewWorkspace}
|
||||||
data-testid="new-workspace"
|
data-testid="new-workspace"
|
||||||
@@ -143,6 +145,7 @@ export const WorkspaceListModal = ({
|
|||||||
<p>{t['Create Or Import']()}</p>
|
<p>{t['Create Or Import']()}</p>
|
||||||
</StyleWorkspaceInfo>
|
</StyleWorkspaceInfo>
|
||||||
</StyledCreateWorkspaceCard>
|
</StyledCreateWorkspaceCard>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{environment.isDesktop && (
|
{environment.isDesktop && (
|
||||||
@@ -218,7 +221,7 @@ export const WorkspaceListModal = ({
|
|||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
</StyledModalContent>
|
</StyledModalContent>
|
||||||
|
</ScrollableContainer>
|
||||||
<Footer user={user} onLogin={onClickLogin} onLogout={onClickLogout} />
|
<Footer user={user} onLogin={onClickLogin} onLogout={onClickLogout} />
|
||||||
</ModalWrapper>
|
</ModalWrapper>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export const StyledCreateWorkspaceCard = styled('div')(() => {
|
|||||||
return {
|
return {
|
||||||
width: '310px',
|
width: '310px',
|
||||||
height: '124px',
|
height: '124px',
|
||||||
|
marginBottom: '24px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
padding: '16px',
|
padding: '16px',
|
||||||
boxShadow: 'var(--affine-shadow-1)',
|
boxShadow: 'var(--affine-shadow-1)',
|
||||||
@@ -133,10 +134,8 @@ export const StyledHelperContainer = styled('div')(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const StyledModalContent = styled('div')({
|
export const StyledModalContent = styled('div')({
|
||||||
height: '534px',
|
height: '540px',
|
||||||
padding: '8px 40px',
|
padding: '8px 40px',
|
||||||
marginTop: '72px',
|
|
||||||
overflow: 'auto',
|
|
||||||
...displayFlex('space-between', 'flex-start', 'flex-start'),
|
...displayFlex('space-between', 'flex-start', 'flex-start'),
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
});
|
});
|
||||||
@@ -163,12 +162,11 @@ export const StyleWorkspaceAdd = styled('div')(() => {
|
|||||||
export const StyledModalHeader = styled('div')(() => {
|
export const StyledModalHeader = styled('div')(() => {
|
||||||
return {
|
return {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '72px',
|
marginTop: '10px',
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
borderRadius: '24px 24px 0 0',
|
borderRadius: '24px 24px 0 0',
|
||||||
padding: '0 40px',
|
padding: '10px 40px',
|
||||||
...displayFlex('space-between', 'center'),
|
...displayFlex('space-between', 'center'),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ 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, TableHeadRow } from '../..';
|
import { ScrollableContainer, Table, TableBody, TableCell, TableHead, TableHeadRow } from '../..';
|
||||||
import { TableBodyRow } from '../../ui/table';
|
import { TableBodyRow } from '../../ui/table';
|
||||||
import { useHasScrollTop } from '../app-sidebar/sidebar-containers/use-has-scroll-top';
|
import { useHasScrollTop } from '../app-sidebar/sidebar-containers/use-has-scroll-top';
|
||||||
import { AllPagesBody } from './all-pages-body';
|
import { AllPagesBody } from './all-pages-body';
|
||||||
@@ -121,13 +121,15 @@ export const PageList = ({
|
|||||||
const isSmallDevices = useIsSmallDevices();
|
const isSmallDevices = useIsSmallDevices();
|
||||||
if (isSmallDevices) {
|
if (isSmallDevices) {
|
||||||
return (
|
return (
|
||||||
<AllPageListMobileView
|
<ScrollableContainer inTableView>
|
||||||
isPublicWorkspace={isPublicWorkspace}
|
<AllPageListMobileView
|
||||||
createNewPage={onCreateNewPage}
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
createNewEdgeless={onCreateNewEdgeless}
|
createNewPage={onCreateNewPage}
|
||||||
importFile={onImportFile}
|
createNewEdgeless={onCreateNewEdgeless}
|
||||||
list={sorter.data}
|
importFile={onImportFile}
|
||||||
/>
|
list={sorter.data}
|
||||||
|
/>
|
||||||
|
</ScrollableContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,23 +142,30 @@ export const PageList = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTableContainer ref={ref}>
|
sorter.data.length === 0 && fallback ?
|
||||||
<Table showBorder={hasScrollTop} style={{ maxHeight: '100%' }}>
|
<StyledTableContainer>
|
||||||
<AllPagesHead
|
{fallback}
|
||||||
isPublicWorkspace={isPublicWorkspace}
|
|
||||||
sorter={sorter}
|
|
||||||
createNewPage={onCreateNewPage}
|
|
||||||
createNewEdgeless={onCreateNewEdgeless}
|
|
||||||
importFile={onImportFile}
|
|
||||||
/>
|
|
||||||
<AllPagesBody
|
|
||||||
isPublicWorkspace={isPublicWorkspace}
|
|
||||||
groupKey={groupKey}
|
|
||||||
data={sorter.data}
|
|
||||||
/>
|
|
||||||
</Table>
|
|
||||||
{sorter.data.length === 0 && fallback ? fallback : null}
|
|
||||||
</StyledTableContainer>
|
</StyledTableContainer>
|
||||||
|
:
|
||||||
|
<ScrollableContainer inTableView>
|
||||||
|
<StyledTableContainer ref={ref}>
|
||||||
|
<Table showBorder={hasScrollTop} style={{ maxHeight: '100%' }}>
|
||||||
|
<AllPagesHead
|
||||||
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
|
sorter={sorter}
|
||||||
|
createNewPage={onCreateNewPage}
|
||||||
|
createNewEdgeless={onCreateNewEdgeless}
|
||||||
|
importFile={onImportFile}
|
||||||
|
/>
|
||||||
|
<AllPagesBody
|
||||||
|
|
||||||
|
isPublicWorkspace={isPublicWorkspace}
|
||||||
|
groupKey={groupKey}
|
||||||
|
data={sorter.data}
|
||||||
|
/>
|
||||||
|
</Table>
|
||||||
|
</StyledTableContainer>
|
||||||
|
</ScrollableContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -238,12 +247,18 @@ export const PageListTrashView: React.FC<{
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTableContainer ref={ref}>
|
list.length === 0 && fallback ?
|
||||||
<Table showBorder={hasScrollTop}>
|
<StyledTableContainer>
|
||||||
<TrashListHead />
|
{fallback}
|
||||||
<TableBody>{ListItems}</TableBody>
|
|
||||||
</Table>
|
|
||||||
{list.length === 0 && fallback ? fallback : null}
|
|
||||||
</StyledTableContainer>
|
</StyledTableContainer>
|
||||||
|
:
|
||||||
|
<ScrollableContainer inTableView>
|
||||||
|
<StyledTableContainer ref={ref}>
|
||||||
|
<Table showBorder={hasScrollTop}>
|
||||||
|
<TrashListHead />
|
||||||
|
<TableBody>{ListItems}</TableBody>
|
||||||
|
</Table>
|
||||||
|
</StyledTableContainer>
|
||||||
|
</ScrollableContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export const StyledTableContainer = styled('div')(({ theme }) => {
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
padding: '0 32px 180px 32px',
|
padding: '0 32px 180px 32px',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
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': {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export * from './ui/menu';
|
|||||||
export * from './ui/modal';
|
export * from './ui/modal';
|
||||||
export * from './ui/mui';
|
export * from './ui/mui';
|
||||||
export * from './ui/popper';
|
export * from './ui/popper';
|
||||||
|
export * from './ui/scrollbar';
|
||||||
export * from './ui/shared/container';
|
export * from './ui/shared/container';
|
||||||
export * from './ui/switch';
|
export * from './ui/switch';
|
||||||
export * from './ui/table';
|
export * from './ui/table';
|
||||||
|
|||||||
@@ -187,14 +187,24 @@ input[type='number']::-webkit-outer-spin-button {
|
|||||||
-ms-overflow-style: none; /* IE 10+ */
|
-ms-overflow-style: none; /* IE 10+ */
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
display: none; /* Chrome Safari */
|
width: 0; /* Chrome Safari */
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
|
.affine-default-viewport::-webkit-scrollbar {
|
||||||
|
width: 8px; /* Chrome Safari */
|
||||||
|
}
|
||||||
|
.affine-default-viewport::-webkit-scrollbar-thumb {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.affine-default-viewport:hover::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--affine-black-30);
|
||||||
|
}
|
||||||
.editor-wrapper {
|
.editor-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0 2rem;
|
padding: 0 1rem;
|
||||||
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* issue: https://github.com/toeverything/AFFiNE/issues/2004 */
|
/* issue: https://github.com/toeverything/AFFiNE/issues/2004 */
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const dropdownBtn = style({
|
|||||||
export const divider = style({
|
export const divider = style({
|
||||||
width: '0.5px',
|
width: '0.5px',
|
||||||
height: '16px',
|
height: '16px',
|
||||||
background: 'var(--affine-border-color)',
|
background: 'var(--affine-divider-color)',
|
||||||
// fix dropdown button click area
|
// fix dropdown button click area
|
||||||
margin: '0 4px',
|
margin: '0 4px',
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
|
|||||||
85
packages/component/src/ui/scrollbar/index.css.ts
Normal file
85
packages/component/src/ui/scrollbar/index.css.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { globalStyle, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const scrollableContainerRoot = style({
|
||||||
|
width: '100%',
|
||||||
|
vars: {
|
||||||
|
'--scrollbar-width': '10px',
|
||||||
|
},
|
||||||
|
height: '100%',
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scrollTopBorder = style({
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: '16px',
|
||||||
|
right: '16px',
|
||||||
|
height: '1px',
|
||||||
|
transition: 'opacity .3s .2s',
|
||||||
|
opacity: 0,
|
||||||
|
background: 'var(--affine-border-color)',
|
||||||
|
selectors: {
|
||||||
|
'&[data-has-scroll-top="true"]': {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scrollableViewport = style({
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
globalStyle(`${scrollableViewport} > div`, {
|
||||||
|
maxWidth: '100%',
|
||||||
|
display: 'block !important',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scrollableContainer = style({
|
||||||
|
height: '100%',
|
||||||
|
marginBottom: '4px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scrollbar = style({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
userSelect: 'none',
|
||||||
|
touchAction: 'none',
|
||||||
|
padding: '0 2px',
|
||||||
|
marginRight: '4px',
|
||||||
|
width: 'var(--scrollbar-width)',
|
||||||
|
height: '100%',
|
||||||
|
opacity: 1,
|
||||||
|
transition: 'opacity .15s',
|
||||||
|
selectors: {
|
||||||
|
'&[data-state="hidden"]': {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const TableScrollbar = style({
|
||||||
|
paddingTop: '60px',
|
||||||
|
paddingBottom: '60px',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scrollbarThumb = style({
|
||||||
|
position: 'relative',
|
||||||
|
background: 'var(--affine-black-30)',
|
||||||
|
borderRadius: '4px',
|
||||||
|
selectors: {
|
||||||
|
'&::before': {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
minWidth: '44px',
|
||||||
|
minHeight: '44px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
1
packages/component/src/ui/scrollbar/index.ts
Normal file
1
packages/component/src/ui/scrollbar/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './scrollbar';
|
||||||
43
packages/component/src/ui/scrollbar/scrollbar.tsx
Normal file
43
packages/component/src/ui/scrollbar/scrollbar.tsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import * as ScrollArea from '@radix-ui/react-scroll-area';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
import { useHasScrollTop } from '../../components/app-sidebar/sidebar-containers/use-has-scroll-top';
|
||||||
|
import * as styles from './index.css';
|
||||||
|
|
||||||
|
export type ScrollableContainerProps = {
|
||||||
|
showScrollTopBorder?: boolean;
|
||||||
|
inTableView?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScrollableContainer = ({
|
||||||
|
children,
|
||||||
|
showScrollTopBorder = false,
|
||||||
|
inTableView = false,
|
||||||
|
}: PropsWithChildren<ScrollableContainerProps>) => {
|
||||||
|
const [hasScrollTop, ref] = useHasScrollTop();
|
||||||
|
return (
|
||||||
|
<ScrollArea.Root className={styles.scrollableContainerRoot}>
|
||||||
|
<div
|
||||||
|
data-has-scroll-top={hasScrollTop}
|
||||||
|
className={clsx({[styles.scrollTopBorder]:showScrollTopBorder})}
|
||||||
|
/>
|
||||||
|
<ScrollArea.Viewport
|
||||||
|
className={clsx([styles.scrollableViewport])}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
<div className={styles.scrollableContainer}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</ScrollArea.Viewport>
|
||||||
|
<ScrollArea.Scrollbar
|
||||||
|
|
||||||
|
orientation="vertical"
|
||||||
|
className={clsx(styles.scrollbar,{[styles.TableScrollbar]:inTableView})}
|
||||||
|
>
|
||||||
|
<ScrollArea.Thumb className={styles.scrollbarThumb} />
|
||||||
|
</ScrollArea.Scrollbar>
|
||||||
|
</ScrollArea.Root>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user