mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 19:46:32 +08:00
feat(electron): multi tabs support (#7440)
use https://www.electronjs.org/docs/latest/api/web-contents-view to serve different tab views added tabs view manager in electron to handle multi-view actions and events. fix AF-1111 fix AF-999 fix PD-1459 fix AF-964 PD-1458
This commit is contained in:
@@ -98,7 +98,7 @@ export const tableHeaderTimestamp = style({
|
||||
|
||||
export const tableHeaderDivider = style({
|
||||
height: 0,
|
||||
borderTop: `1px solid ${cssVar('borderColor')}`,
|
||||
borderTop: `0.5px solid ${cssVar('borderColor')}`,
|
||||
width: '100%',
|
||||
margin: '8px 0',
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
export const floatingMaxWidth = 768;
|
||||
export const navWrapperStyle = style({
|
||||
paddingBottom: '8px',
|
||||
@@ -11,7 +11,7 @@ export const navWrapperStyle = style({
|
||||
},
|
||||
selectors: {
|
||||
'&[data-has-border=true]': {
|
||||
borderRight: `1px solid ${cssVar('borderColor')}`,
|
||||
borderRight: `0.5px solid ${cssVar('borderColor')}`,
|
||||
},
|
||||
'&[data-is-floating="true"]': {
|
||||
backgroundColor: cssVar('backgroundPrimaryColor'),
|
||||
@@ -45,14 +45,6 @@ export const navHeaderStyle = style({
|
||||
['WebkitAppRegion' as string]: 'drag',
|
||||
});
|
||||
|
||||
globalStyle(
|
||||
`html[data-fullscreen="false"]
|
||||
${navHeaderStyle}[data-is-macos-electron="true"]`,
|
||||
{
|
||||
paddingLeft: '90px',
|
||||
}
|
||||
);
|
||||
|
||||
export const navBodyStyle = style({
|
||||
flex: '1 1 auto',
|
||||
height: 'calc(100% - 52px)',
|
||||
|
||||
@@ -2,7 +2,7 @@ import { atom } from 'jotai';
|
||||
import { atomWithStorage } from 'jotai/utils';
|
||||
|
||||
export const APP_SIDEBAR_OPEN = 'app-sidebar-open';
|
||||
export const isMobile = window.innerWidth < 768;
|
||||
export const isMobile = window.innerWidth < 768 && !environment.isDesktop;
|
||||
|
||||
export const appSidebarOpenAtom = atomWithStorage(APP_SIDEBAR_OPEN, !isMobile);
|
||||
export const appSidebarFloatingAtom = atom(isMobile);
|
||||
|
||||
@@ -41,7 +41,6 @@ const MIN_WIDTH = 248;
|
||||
export function AppSidebar({
|
||||
children,
|
||||
clientBorder,
|
||||
translucentUI,
|
||||
}: AppSidebarProps): ReactElement {
|
||||
const [open, setOpen] = useAtom(appSidebarOpenAtom);
|
||||
const [width, setWidth] = useAtom(appSidebarWidthAtom);
|
||||
@@ -49,6 +48,11 @@ export function AppSidebar({
|
||||
const [resizing, setResizing] = useAtom(appSidebarResizingAtom);
|
||||
|
||||
useEffect(() => {
|
||||
// do not float app sidebar on desktop
|
||||
if (environment.isDesktop) {
|
||||
return;
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
const isFloatingMaxWidth = window.matchMedia(
|
||||
`(max-width: ${floatingMaxWidth}px)`
|
||||
@@ -75,10 +79,8 @@ export function AppSidebar({
|
||||
};
|
||||
}, [open, setFloating, setOpen, width]);
|
||||
|
||||
const hasRightBorder = !environment.isDesktop && !clientBorder;
|
||||
const isMacosDesktop = environment.isDesktop && environment.isMacOs;
|
||||
const hasRightBorder =
|
||||
!environment.isDesktop || (!clientBorder && !translucentUI);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResizePanel
|
||||
@@ -96,12 +98,13 @@ export function AppSidebar({
|
||||
resizeHandleOffset={clientBorder ? 8 : 0}
|
||||
resizeHandleVerticalPadding={clientBorder ? 16 : 0}
|
||||
data-transparent
|
||||
data-open={open}
|
||||
data-has-border={hasRightBorder}
|
||||
data-testid="app-sidebar-wrapper"
|
||||
data-is-macos-electron={isMacosDesktop}
|
||||
>
|
||||
<nav className={navStyle} data-testid="app-sidebar">
|
||||
<SidebarHeader />
|
||||
{!environment.isDesktop && <SidebarHeader />}
|
||||
<div className={navBodyStyle} data-testid="sliderBar-inner">
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useHasScrollTop } from '@affine/component';
|
||||
import * as ScrollArea from '@radix-ui/react-scroll-area';
|
||||
import clsx from 'clsx';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useRef } from 'react';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
import * as styles from './index.css';
|
||||
|
||||
@@ -11,8 +10,7 @@ export function SidebarContainer({ children }: PropsWithChildren) {
|
||||
}
|
||||
|
||||
export function SidebarScrollableContainer({ children }: PropsWithChildren) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const hasScrollTop = useHasScrollTop(ref);
|
||||
const [setContainer, hasScrollTop] = useHasScrollTop();
|
||||
return (
|
||||
<ScrollArea.Root className={styles.scrollableContainerRoot}>
|
||||
<div
|
||||
@@ -21,7 +19,7 @@ export function SidebarScrollableContainer({ children }: PropsWithChildren) {
|
||||
/>
|
||||
<ScrollArea.Viewport
|
||||
className={clsx([styles.scrollableViewport])}
|
||||
ref={ref}
|
||||
ref={setContainer}
|
||||
>
|
||||
<div className={clsx([styles.scrollableContainer])}>{children}</div>
|
||||
</ScrollArea.Viewport>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
|
||||
import { NavigationButtons } from '../../../modules/navigation';
|
||||
import { navHeaderStyle } from '../index.css';
|
||||
import { appSidebarOpenAtom } from '../index.jotai';
|
||||
import { SidebarSwitch } from './sidebar-switch';
|
||||
@@ -9,13 +8,8 @@ export const SidebarHeader = () => {
|
||||
const open = useAtomValue(appSidebarOpenAtom);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={navHeaderStyle}
|
||||
data-open={open}
|
||||
data-is-macos-electron={environment.isDesktop && environment.isMacOs}
|
||||
>
|
||||
<div className={navHeaderStyle} data-open={open}>
|
||||
<SidebarSwitch show={open} />
|
||||
<NavigationButtons />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
} from 'react';
|
||||
|
||||
import { usePageHeaderColsDef } from './header-col-def';
|
||||
@@ -156,8 +155,7 @@ export const ListScrollContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
PropsWithChildren<ListScrollContainerProps>
|
||||
>(({ className, children, style }, ref) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const hasScrollTop = useHasScrollTop(containerRef);
|
||||
const [setContainer, hasScrollTop] = useHasScrollTop();
|
||||
|
||||
const setNodeRef = useCallback(
|
||||
(r: HTMLDivElement) => {
|
||||
@@ -168,9 +166,9 @@ export const ListScrollContainer = forwardRef<
|
||||
ref.current = r;
|
||||
}
|
||||
}
|
||||
containerRef.current = r;
|
||||
return setContainer(r);
|
||||
},
|
||||
[ref]
|
||||
[ref, setContainer]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -19,7 +19,7 @@ export const tableHeader = style({
|
||||
transform: 'translateY(-0.5px)', // fix sticky look through issue
|
||||
});
|
||||
globalStyle(`[data-has-scroll-top=true] ${tableHeader}`, {
|
||||
boxShadow: `0 1px ${cssVar('borderColor')}`,
|
||||
boxShadow: `0 0.5px ${cssVar('borderColor')}`,
|
||||
});
|
||||
export const headerTitleSelectionIconWrapper = style({
|
||||
display: 'flex',
|
||||
|
||||
@@ -56,6 +56,7 @@ export const headerSideContainer = style({
|
||||
|
||||
export const windowAppControlsWrapper = style({
|
||||
display: 'flex',
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
export const windowAppControl = style({
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { openSettingModalAtom } from '@affine/core/atoms';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
||||
import {
|
||||
ExplorerCollections,
|
||||
ExplorerFavorites,
|
||||
@@ -7,17 +9,19 @@ import {
|
||||
ExplorerOrganize,
|
||||
} from '@affine/core/modules/explorer';
|
||||
import { ExplorerTags } from '@affine/core/modules/explorer/views/sections/tags';
|
||||
import { CMDKQuickSearchService } from '@affine/core/modules/quicksearch/services/cmdk';
|
||||
import { TelemetryWorkspaceContextService } from '@affine/core/modules/telemetry/services/telemetry';
|
||||
import { pathGenerator } from '@affine/core/shared';
|
||||
import { mixpanel } from '@affine/core/utils';
|
||||
import { apis, events } from '@affine/electron-api';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import { FolderIcon, SettingsIcon } from '@blocksuite/icons/rc';
|
||||
import type { Doc } from '@blocksuite/store';
|
||||
import type { Workspace } from '@toeverything/infra';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useLiveData, useService, WorkspaceService } from '@toeverything/infra';
|
||||
import { useAtomValue, useSetAtom } from 'jotai';
|
||||
import type { ReactElement } from 'react';
|
||||
import { memo, useEffect } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
import { useAppSettingHelper } from '../../hooks/affine/use-app-setting-helper';
|
||||
import { WorkbenchService } from '../../modules/workbench';
|
||||
@@ -33,6 +37,7 @@ import {
|
||||
SidebarContainer,
|
||||
SidebarScrollableContainer,
|
||||
} from '../app-sidebar';
|
||||
import { usePageHelper } from '../blocksuite/block-suite-page-list/utils';
|
||||
import { WorkspaceSelector } from '../workspace-selector';
|
||||
import ImportPage from './import-page';
|
||||
import {
|
||||
@@ -64,127 +69,147 @@ export type RootAppSidebarProps = {
|
||||
* This is for the whole affine app sidebar.
|
||||
* This component wraps the app sidebar in `@affine/component` with logic and data.
|
||||
*
|
||||
* @todo(himself65): rewrite all styled component into @vanilla-extract/css
|
||||
*/
|
||||
export const RootAppSidebar = memo(
|
||||
({
|
||||
currentWorkspace,
|
||||
openPage,
|
||||
createPage,
|
||||
paths,
|
||||
onOpenQuickSearchModal,
|
||||
onOpenSettingModal,
|
||||
}: RootAppSidebarProps): ReactElement => {
|
||||
const currentWorkspaceId = currentWorkspace.id;
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
const docCollection = currentWorkspace.docCollection;
|
||||
const t = useI18n();
|
||||
const currentPath = useLiveData(
|
||||
useService(WorkbenchService).workbench.location$.map(
|
||||
location => location.pathname
|
||||
)
|
||||
);
|
||||
export const RootAppSidebar = (): ReactElement => {
|
||||
const currentWorkspace = useService(WorkspaceService).workspace;
|
||||
const currentWorkspaceId = currentWorkspace.id;
|
||||
const { openPage } = useNavigateHelper();
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
const docCollection = currentWorkspace.docCollection;
|
||||
const t = useI18n();
|
||||
const currentPath = useLiveData(
|
||||
useService(WorkbenchService).workbench.location$.map(
|
||||
location => location.pathname
|
||||
)
|
||||
);
|
||||
const cmdkQuickSearchService = useService(CMDKQuickSearchService);
|
||||
const onOpenQuickSearchModal = useCallback(() => {
|
||||
cmdkQuickSearchService.toggle();
|
||||
mixpanel.track('QuickSearchOpened', {
|
||||
segment: 'navigation panel',
|
||||
control: 'search button',
|
||||
});
|
||||
}, [cmdkQuickSearchService]);
|
||||
|
||||
const telemetry = useService(TelemetryWorkspaceContextService);
|
||||
const telemetry = useService(TelemetryWorkspaceContextService);
|
||||
|
||||
const allPageActive = currentPath === '/all';
|
||||
const allPageActive = currentPath === '/all';
|
||||
|
||||
const onClickNewPage = useAsyncCallback(async () => {
|
||||
const page = createPage();
|
||||
page.load();
|
||||
openPage(page.id);
|
||||
mixpanel.track('DocCreated', {
|
||||
page: telemetry.getPageContext(),
|
||||
segment: 'navigation panel',
|
||||
module: 'bottom button',
|
||||
control: 'new doc button',
|
||||
category: 'page',
|
||||
type: 'doc',
|
||||
const pageHelper = usePageHelper(currentWorkspace.docCollection);
|
||||
const createPage = useCallback(() => {
|
||||
return pageHelper.createPage();
|
||||
}, [pageHelper]);
|
||||
|
||||
const onClickNewPage = useAsyncCallback(async () => {
|
||||
const page = createPage();
|
||||
page.load();
|
||||
openPage(currentWorkspaceId, page.id);
|
||||
mixpanel.track('DocCreated', {
|
||||
page: telemetry.getPageContext(),
|
||||
segment: 'navigation panel',
|
||||
module: 'bottom button',
|
||||
control: 'new doc button',
|
||||
category: 'page',
|
||||
type: 'doc',
|
||||
});
|
||||
}, [createPage, currentWorkspaceId, openPage, telemetry]);
|
||||
|
||||
// Listen to the "New Page" action from the menu
|
||||
useEffect(() => {
|
||||
if (environment.isDesktop) {
|
||||
return events?.applicationMenu.onNewPageAction(onClickNewPage);
|
||||
}
|
||||
return;
|
||||
}, [onClickNewPage]);
|
||||
|
||||
const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom);
|
||||
|
||||
const onOpenSettingModal = useCallback(() => {
|
||||
setOpenSettingModalAtom({
|
||||
activeTab: 'appearance',
|
||||
open: true,
|
||||
});
|
||||
mixpanel.track('SettingsViewed', {
|
||||
// page:
|
||||
segment: 'navigation panel',
|
||||
module: 'general list',
|
||||
control: 'settings button',
|
||||
});
|
||||
}, [setOpenSettingModalAtom]);
|
||||
|
||||
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
|
||||
useEffect(() => {
|
||||
if (environment.isDesktop) {
|
||||
apis?.ui.handleSidebarVisibilityChange(sidebarOpen).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}, [createPage, openPage, telemetry]);
|
||||
}
|
||||
}, [sidebarOpen]);
|
||||
|
||||
// Listen to the "New Page" action from the menu
|
||||
useEffect(() => {
|
||||
if (environment.isDesktop) {
|
||||
return events?.applicationMenu.onNewPageAction(onClickNewPage);
|
||||
}
|
||||
return;
|
||||
}, [onClickNewPage]);
|
||||
|
||||
const sidebarOpen = useAtomValue(appSidebarOpenAtom);
|
||||
useEffect(() => {
|
||||
if (environment.isDesktop) {
|
||||
apis?.ui.handleSidebarVisibilityChange(sidebarOpen).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
}, [sidebarOpen]);
|
||||
|
||||
return (
|
||||
<AppSidebar
|
||||
clientBorder={appSettings.clientBorder}
|
||||
translucentUI={appSettings.enableBlurBackground}
|
||||
>
|
||||
<SidebarContainer>
|
||||
<div className={workspaceAndUserWrapper}>
|
||||
<div className={workspaceWrapper}>
|
||||
<WorkspaceSelector />
|
||||
</div>
|
||||
<UserInfo />
|
||||
return (
|
||||
<AppSidebar
|
||||
clientBorder={appSettings.clientBorder}
|
||||
translucentUI={appSettings.enableBlurBackground}
|
||||
>
|
||||
<SidebarContainer>
|
||||
<div className={workspaceAndUserWrapper}>
|
||||
<div className={workspaceWrapper}>
|
||||
<WorkspaceSelector />
|
||||
</div>
|
||||
<div className={quickSearchAndNewPage}>
|
||||
<QuickSearchInput
|
||||
className={quickSearch}
|
||||
data-testid="slider-bar-quick-search-button"
|
||||
onClick={onOpenQuickSearchModal}
|
||||
/>
|
||||
<AddPageButton onClick={onClickNewPage} />
|
||||
</div>
|
||||
<MenuLinkItem
|
||||
icon={<FolderIcon />}
|
||||
active={allPageActive}
|
||||
to={paths.all(currentWorkspaceId)}
|
||||
>
|
||||
<span data-testid="all-pages">
|
||||
{t['com.affine.workspaceSubPath.all']()}
|
||||
</span>
|
||||
</MenuLinkItem>
|
||||
<AppSidebarJournalButton
|
||||
docCollection={currentWorkspace.docCollection}
|
||||
<UserInfo />
|
||||
</div>
|
||||
<div className={quickSearchAndNewPage}>
|
||||
<QuickSearchInput
|
||||
className={quickSearch}
|
||||
data-testid="slider-bar-quick-search-button"
|
||||
onClick={onOpenQuickSearchModal}
|
||||
/>
|
||||
<MenuItem
|
||||
data-testid="slider-bar-workspace-setting-button"
|
||||
icon={<SettingsIcon />}
|
||||
onClick={onOpenSettingModal}
|
||||
>
|
||||
<span data-testid="settings-modal-trigger">
|
||||
{t['com.affine.settingSidebar.title']()}
|
||||
</span>
|
||||
</MenuItem>
|
||||
</SidebarContainer>
|
||||
<SidebarScrollableContainer>
|
||||
{runtimeConfig.enableNewFavorite && <ExplorerFavorites />}
|
||||
{runtimeConfig.enableOrganize && <ExplorerOrganize />}
|
||||
{runtimeConfig.enableNewFavorite && <ExplorerMigrationFavorites />}
|
||||
{runtimeConfig.enableOldFavorite && (
|
||||
<ExplorerOldFavorites defaultCollapsed />
|
||||
)}
|
||||
<ExplorerCollections defaultCollapsed />
|
||||
<ExplorerTags defaultCollapsed />
|
||||
<CategoryDivider label={t['com.affine.rootAppSidebar.others']()} />
|
||||
{/* fixme: remove the following spacer */}
|
||||
<div style={{ height: '4px' }} />
|
||||
<div style={{ padding: '0 8px' }}>
|
||||
<TrashButton />
|
||||
<ImportPage docCollection={docCollection} />
|
||||
</div>
|
||||
</SidebarScrollableContainer>
|
||||
<SidebarContainer>
|
||||
{environment.isDesktop ? <UpdaterButton /> : <AppDownloadButton />}
|
||||
</SidebarContainer>
|
||||
</AppSidebar>
|
||||
);
|
||||
}
|
||||
);
|
||||
<AddPageButton onClick={onClickNewPage} />
|
||||
</div>
|
||||
<MenuLinkItem
|
||||
icon={<FolderIcon />}
|
||||
active={allPageActive}
|
||||
to={pathGenerator.all(currentWorkspaceId)}
|
||||
>
|
||||
<span data-testid="all-pages">
|
||||
{t['com.affine.workspaceSubPath.all']()}
|
||||
</span>
|
||||
</MenuLinkItem>
|
||||
<AppSidebarJournalButton
|
||||
docCollection={currentWorkspace.docCollection}
|
||||
/>
|
||||
<MenuItem
|
||||
data-testid="slider-bar-workspace-setting-button"
|
||||
icon={<SettingsIcon />}
|
||||
onClick={onOpenSettingModal}
|
||||
>
|
||||
<span data-testid="settings-modal-trigger">
|
||||
{t['com.affine.settingSidebar.title']()}
|
||||
</span>
|
||||
</MenuItem>
|
||||
</SidebarContainer>
|
||||
<SidebarScrollableContainer>
|
||||
{runtimeConfig.enableNewFavorite && <ExplorerFavorites />}
|
||||
{runtimeConfig.enableOrganize && <ExplorerOrganize />}
|
||||
{runtimeConfig.enableNewFavorite && <ExplorerMigrationFavorites />}
|
||||
{runtimeConfig.enableOldFavorite && (
|
||||
<ExplorerOldFavorites defaultCollapsed />
|
||||
)}
|
||||
<ExplorerCollections defaultCollapsed />
|
||||
<ExplorerTags defaultCollapsed />
|
||||
<CategoryDivider label={t['com.affine.rootAppSidebar.others']()} />
|
||||
{/* fixme: remove the following spacer */}
|
||||
<div style={{ height: '4px' }} />
|
||||
<div style={{ padding: '0 8px' }}>
|
||||
<TrashButton />
|
||||
<ImportPage docCollection={docCollection} />
|
||||
</div>
|
||||
</SidebarScrollableContainer>
|
||||
<SidebarContainer>
|
||||
{environment.isDesktop ? <UpdaterButton /> : <AppDownloadButton />}
|
||||
</SidebarContainer>
|
||||
</AppSidebar>
|
||||
);
|
||||
};
|
||||
|
||||
RootAppSidebar.displayName = 'memo(RootAppSidebar)';
|
||||
|
||||
@@ -4,9 +4,8 @@ export const appStyle = style({
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
flexGrow: '1',
|
||||
flexDirection: 'row',
|
||||
display: 'flex',
|
||||
backgroundColor: cssVar('backgroundPrimaryColor'),
|
||||
selectors: {
|
||||
'&[data-is-resizing="true"]': {
|
||||
@@ -42,11 +41,11 @@ globalStyle(`html[data-theme="dark"] ${appStyle}`, {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const mainContainerStyle = style({
|
||||
position: 'relative',
|
||||
zIndex: 0,
|
||||
// it will create stacking context to limit layer of child elements and be lower than after auto zIndex
|
||||
width: 0,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
overflow: 'clip',
|
||||
@@ -71,14 +70,16 @@ export const mainContainerStyle = style({
|
||||
'&[data-client-border="true"][data-side-bar-open="true"]': {
|
||||
marginLeft: 0,
|
||||
},
|
||||
'&[data-client-border="true"]:before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
height: '8px',
|
||||
width: '100%',
|
||||
top: '-8px',
|
||||
left: 0,
|
||||
['WebkitAppRegion' as string]: 'drag',
|
||||
'&[data-client-border="true"][data-is-desktop="true"]': {
|
||||
marginTop: 0,
|
||||
},
|
||||
'&[data-client-border="false"][data-is-desktop="true"][data-side-bar-open="true"]':
|
||||
{
|
||||
borderTopLeftRadius: 6,
|
||||
},
|
||||
'&[data-client-border="false"][data-is-desktop="true"]': {
|
||||
borderTop: `0.5px solid ${cssVar('borderColor')}`,
|
||||
borderLeft: `0.5px solid ${cssVar('borderColor')}`,
|
||||
},
|
||||
'&[data-transparent=true]': {
|
||||
backgroundColor: 'transparent',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||
import {
|
||||
DocsService,
|
||||
GlobalContextService,
|
||||
@@ -26,45 +27,44 @@ export const AppContainer = ({
|
||||
...rest
|
||||
}: WorkspaceRootProps) => {
|
||||
const noisyBackground = useNoisyBackground && environment.isDesktop;
|
||||
const blurBackground = environment.isDesktop && useBlurBackground;
|
||||
return (
|
||||
<div
|
||||
{...rest}
|
||||
className={clsx(appStyle, {
|
||||
'noisy-background': noisyBackground,
|
||||
'blur-background': environment.isDesktop && useBlurBackground,
|
||||
'blur-background': blurBackground,
|
||||
})}
|
||||
data-noise-background={noisyBackground}
|
||||
data-is-resizing={resizing}
|
||||
data-blur-background={blurBackground}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface MainContainerProps extends HTMLAttributes<HTMLDivElement> {
|
||||
clientBorder?: boolean;
|
||||
}
|
||||
export interface MainContainerProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const MainContainer = forwardRef<
|
||||
HTMLDivElement,
|
||||
PropsWithChildren<MainContainerProps>
|
||||
>(function MainContainer(
|
||||
{ className, children, clientBorder, ...props },
|
||||
ref
|
||||
): ReactElement {
|
||||
>(function MainContainer({ className, children, ...props }, ref): ReactElement {
|
||||
const appSideBarOpen = useAtomValue(appSidebarOpenAtom);
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
|
||||
return (
|
||||
<div
|
||||
{...props}
|
||||
className={clsx(mainContainerStyle, className)}
|
||||
data-is-macos={environment.isDesktop && environment.isMacOs}
|
||||
data-is-desktop={environment.isDesktop}
|
||||
data-transparent={false}
|
||||
data-client-border={clientBorder}
|
||||
data-client-border={appSettings.clientBorder}
|
||||
data-side-bar-open={appSideBarOpen}
|
||||
data-testid="main-container"
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
<div className={mainContainerStyle}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user