mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-26 10:45:57 +08:00
feat(core): adjust split view ui (#6076)
This commit is contained in:
@@ -2,6 +2,7 @@ import { toast } from '@affine/component';
|
|||||||
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
||||||
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
||||||
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
|
import { Workbench } from '@affine/core/modules/workbench';
|
||||||
import type { Collection, Filter } from '@affine/env/filter';
|
import type { Collection, Filter } from '@affine/env/filter';
|
||||||
import { Trans } from '@affine/i18n';
|
import { Trans } from '@affine/i18n';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
@@ -33,6 +34,7 @@ const usePageOperationsRenderer = () => {
|
|||||||
currentWorkspace.docCollection
|
currentWorkspace.docCollection
|
||||||
);
|
);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
const workbench = useService(Workbench);
|
||||||
|
|
||||||
const pageOperationsRenderer = useCallback(
|
const pageOperationsRenderer = useCallback(
|
||||||
(page: DocMeta) => {
|
(page: DocMeta) => {
|
||||||
@@ -48,6 +50,7 @@ const usePageOperationsRenderer = () => {
|
|||||||
isPublic={!!page.isPublic}
|
isPublic={!!page.isPublic}
|
||||||
onDisablePublicSharing={onDisablePublicSharing}
|
onDisablePublicSharing={onDisablePublicSharing}
|
||||||
link={`/workspace/${currentWorkspace.id}/${page.id}`}
|
link={`/workspace/${currentWorkspace.id}/${page.id}`}
|
||||||
|
onOpenInSplitView={() => workbench.openPage(page.id, { at: 'tail' })}
|
||||||
onDuplicate={() => {
|
onDuplicate={() => {
|
||||||
duplicate(page.id, false);
|
duplicate(page.id, false);
|
||||||
}}
|
}}
|
||||||
@@ -70,7 +73,14 @@ const usePageOperationsRenderer = () => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[currentWorkspace.id, setTrashModal, t, toggleFavorite, duplicate]
|
[
|
||||||
|
currentWorkspace.id,
|
||||||
|
workbench,
|
||||||
|
duplicate,
|
||||||
|
setTrashModal,
|
||||||
|
toggleFavorite,
|
||||||
|
t,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
return pageOperationsRenderer;
|
return pageOperationsRenderer;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
toast,
|
toast,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import {
|
import {
|
||||||
@@ -20,6 +21,7 @@ import {
|
|||||||
MoreVerticalIcon,
|
MoreVerticalIcon,
|
||||||
OpenInNewIcon,
|
OpenInNewIcon,
|
||||||
ResetIcon,
|
ResetIcon,
|
||||||
|
SplitViewIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
@@ -45,6 +47,7 @@ export interface PageOperationCellProps {
|
|||||||
onRemoveToTrash: () => void;
|
onRemoveToTrash: () => void;
|
||||||
onDuplicate: () => void;
|
onDuplicate: () => void;
|
||||||
onDisablePublicSharing: () => void;
|
onDisablePublicSharing: () => void;
|
||||||
|
onOpenInSplitView: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PageOperationCell = ({
|
export const PageOperationCell = ({
|
||||||
@@ -55,8 +58,10 @@ export const PageOperationCell = ({
|
|||||||
onRemoveToTrash,
|
onRemoveToTrash,
|
||||||
onDuplicate,
|
onDuplicate,
|
||||||
onDisablePublicSharing,
|
onDisablePublicSharing,
|
||||||
|
onOpenInSplitView,
|
||||||
}: PageOperationCellProps) => {
|
}: PageOperationCellProps) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
const { appSettings } = useAppSettingHelper();
|
||||||
const [openDisableShared, setOpenDisableShared] = useState(false);
|
const [openDisableShared, setOpenDisableShared] = useState(false);
|
||||||
const OperationMenu = (
|
const OperationMenu = (
|
||||||
<>
|
<>
|
||||||
@@ -84,6 +89,20 @@ export const PageOperationCell = ({
|
|||||||
? t['com.affine.favoritePageOperation.remove']()
|
? t['com.affine.favoritePageOperation.remove']()
|
||||||
: t['com.affine.favoritePageOperation.add']()}
|
: t['com.affine.favoritePageOperation.add']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
|
{environment.isDesktop && appSettings.enableMultiView ? (
|
||||||
|
<MenuItem
|
||||||
|
onClick={onOpenInSplitView}
|
||||||
|
preFix={
|
||||||
|
<MenuIcon>
|
||||||
|
<SplitViewIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t['com.affine.workbench.split-view.page-menu-open']()}
|
||||||
|
</MenuItem>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{!environment.isDesktop && (
|
{!environment.isDesktop && (
|
||||||
<Link
|
<Link
|
||||||
className={styles.clearLinkStyle}
|
className={styles.clearLinkStyle}
|
||||||
|
|||||||
@@ -4,9 +4,16 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
type MenuItemProps,
|
type MenuItemProps,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
|
import { Workbench } from '@affine/core/modules/workbench';
|
||||||
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
import type { Collection, DeleteCollectionInfo } from '@affine/env/filter';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { DeleteIcon, EditIcon, FilterIcon } from '@blocksuite/icons';
|
import {
|
||||||
|
DeleteIcon,
|
||||||
|
EditIcon,
|
||||||
|
FilterIcon,
|
||||||
|
SplitViewIcon,
|
||||||
|
} from '@blocksuite/icons';
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
import {
|
import {
|
||||||
type PropsWithChildren,
|
type PropsWithChildren,
|
||||||
@@ -35,7 +42,9 @@ export const CollectionOperations = ({
|
|||||||
config: AllPageListConfig;
|
config: AllPageListConfig;
|
||||||
openRenameModal?: () => void;
|
openRenameModal?: () => void;
|
||||||
}>) => {
|
}>) => {
|
||||||
|
const { appSettings } = useAppSettingHelper();
|
||||||
const service = useService(CollectionService);
|
const service = useService(CollectionService);
|
||||||
|
const workbench = useService(Workbench);
|
||||||
const { open: openEditCollectionModal, node: editModal } =
|
const { open: openEditCollectionModal, node: editModal } =
|
||||||
useEditCollection(config);
|
useEditCollection(config);
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
@@ -71,6 +80,10 @@ export const CollectionOperations = ({
|
|||||||
});
|
});
|
||||||
}, [openEditCollectionModal, collection, service]);
|
}, [openEditCollectionModal, collection, service]);
|
||||||
|
|
||||||
|
const openCollectionSplitView = useCallback(() => {
|
||||||
|
workbench.openCollection(collection.id, { at: 'tail' });
|
||||||
|
}, [collection.id, workbench]);
|
||||||
|
|
||||||
const actions = useMemo<
|
const actions = useMemo<
|
||||||
Array<
|
Array<
|
||||||
| {
|
| {
|
||||||
@@ -104,6 +117,19 @@ export const CollectionOperations = ({
|
|||||||
name: t['com.affine.collection.menu.edit'](),
|
name: t['com.affine.collection.menu.edit'](),
|
||||||
click: showEdit,
|
click: showEdit,
|
||||||
},
|
},
|
||||||
|
...(appSettings.enableMultiView
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<MenuIcon>
|
||||||
|
<SplitViewIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
),
|
||||||
|
name: t['com.affine.workbench.split-view.page-menu-open'](),
|
||||||
|
click: openCollectionSplitView,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
element: <div key="divider" className={styles.divider}></div>,
|
element: <div key="divider" className={styles.divider}></div>,
|
||||||
},
|
},
|
||||||
@@ -120,7 +146,16 @@ export const CollectionOperations = ({
|
|||||||
type: 'danger',
|
type: 'danger',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[t, showEditName, showEdit, service, info, collection.id]
|
[
|
||||||
|
t,
|
||||||
|
showEditName,
|
||||||
|
showEdit,
|
||||||
|
appSettings.enableMultiView,
|
||||||
|
openCollectionSplitView,
|
||||||
|
service,
|
||||||
|
info,
|
||||||
|
collection.id,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
type MenuItemProps,
|
type MenuItemProps,
|
||||||
MenuSeparator,
|
MenuSeparator,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import {
|
import {
|
||||||
DeleteIcon,
|
DeleteIcon,
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
FavoriteIcon,
|
FavoriteIcon,
|
||||||
FilterMinusIcon,
|
FilterMinusIcon,
|
||||||
LinkedPageIcon,
|
LinkedPageIcon,
|
||||||
|
SplitViewIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { type ReactElement, useMemo } from 'react';
|
import { type ReactElement, useMemo } from 'react';
|
||||||
|
|
||||||
@@ -24,6 +26,7 @@ type OperationItemsProps = {
|
|||||||
onAddLinkedPage: () => void;
|
onAddLinkedPage: () => void;
|
||||||
onRemoveFromFavourites?: () => void;
|
onRemoveFromFavourites?: () => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
|
onOpenInSplitView: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OperationItems = ({
|
export const OperationItems = ({
|
||||||
@@ -35,7 +38,9 @@ export const OperationItems = ({
|
|||||||
onAddLinkedPage,
|
onAddLinkedPage,
|
||||||
onRemoveFromFavourites,
|
onRemoveFromFavourites,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
onOpenInSplitView,
|
||||||
}: OperationItemsProps) => {
|
}: OperationItemsProps) => {
|
||||||
|
const { appSettings } = useAppSettingHelper();
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const actions = useMemo<
|
const actions = useMemo<
|
||||||
Array<
|
Array<
|
||||||
@@ -81,9 +86,6 @@ export const OperationItems = ({
|
|||||||
name: t['Remove from favorites'](),
|
name: t['Remove from favorites'](),
|
||||||
click: onRemoveFromFavourites,
|
click: onRemoveFromFavourites,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
element: <MenuSeparator />,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...(inAllowList && onRemoveFromAllowList
|
...(inAllowList && onRemoveFromAllowList
|
||||||
@@ -97,18 +99,27 @@ export const OperationItems = ({
|
|||||||
name: t['Remove special filter'](),
|
name: t['Remove special filter'](),
|
||||||
click: onRemoveFromAllowList,
|
click: onRemoveFromAllowList,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
element: <MenuSeparator />,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...(isReferencePage
|
|
||||||
|
...(appSettings.enableMultiView
|
||||||
? [
|
? [
|
||||||
|
// open split view
|
||||||
{
|
{
|
||||||
element: <MenuSeparator />,
|
icon: (
|
||||||
|
<MenuIcon>
|
||||||
|
<SplitViewIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
),
|
||||||
|
name: t['com.affine.workbench.split-view.page-menu-open'](),
|
||||||
|
click: onOpenInSplitView,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|
||||||
|
{
|
||||||
|
element: <MenuSeparator />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<MenuIcon>
|
<MenuIcon>
|
||||||
@@ -121,14 +132,16 @@ export const OperationItems = ({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
t,
|
||||||
onRename,
|
onRename,
|
||||||
onAddLinkedPage,
|
onAddLinkedPage,
|
||||||
inFavorites,
|
inFavorites,
|
||||||
onRemoveFromFavourites,
|
onRemoveFromFavourites,
|
||||||
isReferencePage,
|
isReferencePage,
|
||||||
t,
|
|
||||||
inAllowList,
|
inAllowList,
|
||||||
onRemoveFromAllowList,
|
onRemoveFromAllowList,
|
||||||
|
appSettings.enableMultiView,
|
||||||
|
onOpenInSplitView,
|
||||||
onDelete,
|
onDelete,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { toast } from '@affine/component';
|
import { toast } from '@affine/component';
|
||||||
import { IconButton } from '@affine/component/ui/button';
|
import { IconButton } from '@affine/component/ui/button';
|
||||||
import { Menu } from '@affine/component/ui/menu';
|
import { Menu } from '@affine/component/ui/menu';
|
||||||
|
import { Workbench } from '@affine/core/modules/workbench';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import { MoreHorizontalIcon } from '@blocksuite/icons';
|
import { MoreHorizontalIcon } from '@blocksuite/icons';
|
||||||
import type { DocCollection } from '@blocksuite/store';
|
import type { DocCollection } from '@blocksuite/store';
|
||||||
|
import { useService } from '@toeverything/infra/di';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper';
|
import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper';
|
||||||
@@ -37,6 +39,7 @@ export const OperationMenuButton = ({ ...props }: OperationMenuButtonProps) => {
|
|||||||
const { createLinkedPage } = usePageHelper(docCollection);
|
const { createLinkedPage } = usePageHelper(docCollection);
|
||||||
const { setTrashModal } = useTrashModalHelper(docCollection);
|
const { setTrashModal } = useTrashModalHelper(docCollection);
|
||||||
const { removeFromFavorite } = useBlockSuiteMetaHelper(docCollection);
|
const { removeFromFavorite } = useBlockSuiteMetaHelper(docCollection);
|
||||||
|
const workbench = useService(Workbench);
|
||||||
|
|
||||||
const handleRename = useCallback(() => {
|
const handleRename = useCallback(() => {
|
||||||
setRenameModalOpen?.();
|
setRenameModalOpen?.();
|
||||||
@@ -64,6 +67,10 @@ export const OperationMenuButton = ({ ...props }: OperationMenuButtonProps) => {
|
|||||||
removeFromAllowList?.(pageId);
|
removeFromAllowList?.(pageId);
|
||||||
}, [pageId, removeFromAllowList]);
|
}, [pageId, removeFromAllowList]);
|
||||||
|
|
||||||
|
const handleOpenInSplitView = useCallback(() => {
|
||||||
|
workbench.openPage(pageId, { at: 'tail' });
|
||||||
|
}, [pageId, workbench]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
items={
|
items={
|
||||||
@@ -73,6 +80,7 @@ export const OperationMenuButton = ({ ...props }: OperationMenuButtonProps) => {
|
|||||||
onRemoveFromAllowList={handleRemoveFromAllowList}
|
onRemoveFromAllowList={handleRemoveFromAllowList}
|
||||||
onRemoveFromFavourites={handleRemoveFromFavourites}
|
onRemoveFromFavourites={handleRemoveFromFavourites}
|
||||||
onRename={handleRename}
|
onRename={handleRename}
|
||||||
|
onOpenInSplitView={handleOpenInSplitView}
|
||||||
inAllowList={inAllowList}
|
inAllowList={inAllowList}
|
||||||
inFavorites={inFavorites}
|
inFavorites={inFavorites}
|
||||||
isReferencePage={isReferencePage}
|
isReferencePage={isReferencePage}
|
||||||
|
|||||||
@@ -8,15 +8,24 @@ export const sidebarContainerInner = style({
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
borderRadius: 'inherit',
|
||||||
|
selectors: {
|
||||||
|
['[data-client-border=true][data-is-floating="true"] &']: {
|
||||||
|
boxShadow: cssVar('shadow3'),
|
||||||
|
border: `1px solid ${cssVar('borderColor')}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sidebarContainer = style({
|
export const sidebarContainer = style({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
right: 0,
|
||||||
selectors: {
|
selectors: {
|
||||||
[`&[data-client-border=true]`]: {
|
[`&[data-client-border=true]`]: {
|
||||||
paddingLeft: 9,
|
paddingLeft: 8,
|
||||||
|
borderRadius: 6,
|
||||||
},
|
},
|
||||||
[`&[data-client-border=false]`]: {
|
[`&[data-client-border=false]`]: {
|
||||||
borderLeft: `1px solid ${cssVar('borderColor')}`,
|
borderLeft: `1px solid ${cssVar('borderColor')}`,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { ResizePanel } from '@affine/component/resize-panel';
|
import { ResizePanel } from '@affine/component/resize-panel';
|
||||||
|
import { appSidebarOpenAtom } from '@affine/core/components/app-sidebar';
|
||||||
import { appSettingAtom } from '@toeverything/infra/atom';
|
import { appSettingAtom } from '@toeverything/infra/atom';
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
import { useLiveData } from '@toeverything/infra/livedata';
|
import { useLiveData } from '@toeverything/infra/livedata';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { RightSidebar } from '../entities/right-sidebar';
|
import { RightSidebar } from '../entities/right-sidebar';
|
||||||
import * as styles from './container.css';
|
import * as styles from './container.css';
|
||||||
@@ -20,6 +21,18 @@ export const RightSidebarContainer = () => {
|
|||||||
|
|
||||||
const frontView = useLiveData(rightSidebar.front);
|
const frontView = useLiveData(rightSidebar.front);
|
||||||
const open = useLiveData(rightSidebar.isOpen) && frontView !== undefined;
|
const open = useLiveData(rightSidebar.isOpen) && frontView !== undefined;
|
||||||
|
const [floating, setFloating] = useState(false);
|
||||||
|
const appSidebarOpened = useAtomValue(appSidebarOpenAtom);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onResize = () =>
|
||||||
|
setFloating(!!(window.innerWidth < 1200 && appSidebarOpened));
|
||||||
|
onResize();
|
||||||
|
window.addEventListener('resize', onResize);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', onResize);
|
||||||
|
};
|
||||||
|
}, [appSidebarOpened]);
|
||||||
|
|
||||||
const handleOpenChange = useCallback(
|
const handleOpenChange = useCallback(
|
||||||
(open: boolean) => {
|
(open: boolean) => {
|
||||||
@@ -38,8 +51,9 @@ export const RightSidebarContainer = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizePanel
|
<ResizePanel
|
||||||
|
floating={floating}
|
||||||
resizeHandlePos="left"
|
resizeHandlePos="left"
|
||||||
resizeHandleOffset={clientBorder ? 4 : 0}
|
resizeHandleOffset={clientBorder ? 3.5 : 0}
|
||||||
width={width}
|
width={width}
|
||||||
resizing={resizing}
|
resizing={resizing}
|
||||||
onResizing={setResizing}
|
onResizing={setResizing}
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import { combineLatest, map, switchMap } from 'rxjs';
|
|||||||
|
|
||||||
import { View } from './view';
|
import { View } from './view';
|
||||||
|
|
||||||
export type WorkbenchPosition = 'beside' | 'active' | number;
|
export type WorkbenchPosition = 'beside' | 'active' | 'head' | 'tail' | number;
|
||||||
|
|
||||||
|
interface WorkbenchOpenOptions {
|
||||||
|
at?: WorkbenchPosition;
|
||||||
|
replaceHistory?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class Workbench {
|
export class Workbench {
|
||||||
readonly views = new LiveData([new View()]);
|
readonly views = new LiveData([new View()]);
|
||||||
@@ -37,10 +42,7 @@ export class Workbench {
|
|||||||
|
|
||||||
open(
|
open(
|
||||||
to: To,
|
to: To,
|
||||||
{
|
{ at = 'active', replaceHistory = false }: WorkbenchOpenOptions = {}
|
||||||
at = 'active',
|
|
||||||
replaceHistory = false,
|
|
||||||
}: { at?: WorkbenchPosition; replaceHistory?: boolean } = {}
|
|
||||||
) {
|
) {
|
||||||
let view = this.viewAt(at);
|
let view = this.viewAt(at);
|
||||||
if (!view) {
|
if (!view) {
|
||||||
@@ -58,32 +60,32 @@ export class Workbench {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
openPage(pageId: string) {
|
openPage(pageId: string, options?: WorkbenchOpenOptions) {
|
||||||
this.open(`/${pageId}`);
|
this.open(`/${pageId}`, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
openCollections() {
|
openCollections(options?: WorkbenchOpenOptions) {
|
||||||
this.open('/collection');
|
this.open('/collection', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
openCollection(collectionId: string) {
|
openCollection(collectionId: string, options?: WorkbenchOpenOptions) {
|
||||||
this.open(`/collection/${collectionId}`);
|
this.open(`/collection/${collectionId}`, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
openAll() {
|
openAll(options?: WorkbenchOpenOptions) {
|
||||||
this.open('/all');
|
this.open('/all', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
openTrash() {
|
openTrash(options?: WorkbenchOpenOptions) {
|
||||||
this.open('/trash');
|
this.open('/trash', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
openTags() {
|
openTags(options?: WorkbenchOpenOptions) {
|
||||||
this.open('/tag');
|
this.open('/tag', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
openTag(tagId: string) {
|
openTag(tagId: string, options?: WorkbenchOpenOptions) {
|
||||||
this.open(`/tag/${tagId}`);
|
this.open(`/tag/${tagId}`, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
viewAt(positionIndex: WorkbenchPosition): View | undefined {
|
viewAt(positionIndex: WorkbenchPosition): View | undefined {
|
||||||
@@ -96,12 +98,16 @@ export class Workbench {
|
|||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
const newViews = [...this.views.value];
|
const newViews = [...this.views.value];
|
||||||
newViews.splice(index, 1);
|
newViews.splice(index, 1);
|
||||||
|
if (index !== 0) {
|
||||||
|
this.active(index - 1);
|
||||||
|
}
|
||||||
this.views.next(newViews);
|
this.views.next(newViews);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOthers(view: View) {
|
closeOthers(view: View) {
|
||||||
view.size.next(100);
|
view.size.next(100);
|
||||||
this.views.next([view]);
|
this.views.next([view]);
|
||||||
|
this.active(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
moveView(from: number, to: number) {
|
moveView(from: number, to: number) {
|
||||||
@@ -128,8 +134,15 @@ export class Workbench {
|
|||||||
0
|
0
|
||||||
);
|
);
|
||||||
const percentOfTotal = totalViewSize * percent;
|
const percentOfTotal = totalViewSize * percent;
|
||||||
view.setSize(Number((view.size.value + percentOfTotal).toFixed(4)));
|
const newSize = Number((view.size.value + percentOfTotal).toFixed(4));
|
||||||
nextView.setSize(Number((nextView.size.value - percentOfTotal).toFixed(4)));
|
const newNextSize = Number(
|
||||||
|
(nextView.size.value - percentOfTotal).toFixed(4)
|
||||||
|
);
|
||||||
|
// TODO: better strategy to limit size
|
||||||
|
if (newSize / totalViewSize < 0.2 || newNextSize / totalViewSize < 0.2)
|
||||||
|
return;
|
||||||
|
view.setSize(newSize);
|
||||||
|
nextView.setSize(newNextSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private indexAt(positionIndex: WorkbenchPosition): number {
|
private indexAt(positionIndex: WorkbenchPosition): number {
|
||||||
@@ -139,6 +152,12 @@ export class Workbench {
|
|||||||
if (positionIndex === 'beside') {
|
if (positionIndex === 'beside') {
|
||||||
return this.activeViewIndex.value + 1;
|
return this.activeViewIndex.value + 1;
|
||||||
}
|
}
|
||||||
|
if (positionIndex === 'head') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (positionIndex === 'tail') {
|
||||||
|
return this.views.value.length;
|
||||||
|
}
|
||||||
return positionIndex;
|
return positionIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,35 @@
|
|||||||
import { cssVar } from '@toeverything/theme';
|
import { cssVar } from '@toeverything/theme';
|
||||||
import { style } from '@vanilla-extract/css';
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const indicatorWrapper = style({
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 4,
|
||||||
|
top: 0,
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
width: '50%',
|
||||||
|
maxWidth: 300,
|
||||||
|
minWidth: 120,
|
||||||
|
height: 15,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
['WebkitAppRegion' as string]: 'no-drag',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const menuTrigger = style({
|
||||||
|
position: 'absolute',
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
pointerEvents: 'none',
|
||||||
|
});
|
||||||
export const indicator = style({
|
export const indicator = style({
|
||||||
width: 29,
|
width: 29,
|
||||||
height: 14,
|
height: 15,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
cursor: 'pointer',
|
cursor: 'grab',
|
||||||
['WebkitAppRegion' as string]: 'no-drag',
|
['WebkitAppRegion' as string]: 'no-drag',
|
||||||
color: cssVar('placeholderColor'),
|
color: cssVar('placeholderColor'),
|
||||||
|
|
||||||
@@ -19,8 +41,16 @@ export const indicator = style({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const indicatorInner = style({
|
export const indicatorInner = style({
|
||||||
width: 15,
|
width: 16,
|
||||||
height: 3,
|
height: 3,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
backgroundColor: 'currentColor',
|
backgroundColor: 'currentColor',
|
||||||
|
transition: 'all 0.5s cubic-bezier(0.16, 1, 0.3, 1)',
|
||||||
|
|
||||||
|
selectors: {
|
||||||
|
'[data-is-dragging="true"] &': {
|
||||||
|
width: 24,
|
||||||
|
height: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,23 +1,61 @@
|
|||||||
|
import { Menu, type MenuProps } from '@affine/component';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { forwardRef, type HTMLAttributes, memo } from 'react';
|
import {
|
||||||
|
forwardRef,
|
||||||
|
type HTMLAttributes,
|
||||||
|
memo,
|
||||||
|
type MouseEventHandler,
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import * as styles from './indicator.css';
|
import * as styles from './indicator.css';
|
||||||
|
|
||||||
export interface SplitViewMenuProps extends HTMLAttributes<HTMLDivElement> {
|
export interface SplitViewMenuProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
|
open?: boolean;
|
||||||
|
onOpenMenu?: () => void;
|
||||||
|
setPressed: (v: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SplitViewMenuIndicator = memo(
|
export const SplitViewMenuIndicator = memo(
|
||||||
forwardRef<HTMLDivElement, SplitViewMenuProps>(
|
forwardRef<HTMLDivElement, SplitViewMenuProps>(
|
||||||
function SplitViewMenuIndicator(
|
function SplitViewMenuIndicator(
|
||||||
{ className, active, ...attrs }: SplitViewMenuProps,
|
{
|
||||||
|
className,
|
||||||
|
active,
|
||||||
|
open,
|
||||||
|
setPressed,
|
||||||
|
onOpenMenu,
|
||||||
|
...attrs
|
||||||
|
}: SplitViewMenuProps,
|
||||||
ref
|
ref
|
||||||
) {
|
) {
|
||||||
|
// dnd's `isDragging` changes after mouseDown and mouseMoved
|
||||||
|
const onMouseDown = useCallback(() => {
|
||||||
|
const t = setTimeout(() => setPressed(true), 100);
|
||||||
|
window.addEventListener(
|
||||||
|
'mouseup',
|
||||||
|
() => {
|
||||||
|
clearTimeout(t);
|
||||||
|
setPressed(false);
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
}, [setPressed]);
|
||||||
|
|
||||||
|
const onClick: MouseEventHandler = useCallback(() => {
|
||||||
|
!open && onOpenMenu?.();
|
||||||
|
}, [onOpenMenu, open]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data-active={active}
|
data-active={active}
|
||||||
className={clsx(className, styles.indicator)}
|
className={clsx(className, styles.indicator)}
|
||||||
|
onClick={onClick}
|
||||||
|
onMouseDown={onMouseDown}
|
||||||
{...attrs}
|
{...attrs}
|
||||||
>
|
>
|
||||||
<div className={styles.indicatorInner} />
|
<div className={styles.indicatorInner} />
|
||||||
@@ -26,3 +64,66 @@ export const SplitViewMenuIndicator = memo(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface SplitViewIndicatorProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
isDragging?: boolean;
|
||||||
|
isActive?: boolean;
|
||||||
|
menuItems?: React.ReactNode;
|
||||||
|
// import type { SyntheticListenerMap } from '@dnd-kit/core/dist/hooks/utilities' is not allowed
|
||||||
|
listeners?: any;
|
||||||
|
setPressed?: (pressed: boolean) => void;
|
||||||
|
}
|
||||||
|
export const SplitViewIndicator = ({
|
||||||
|
isDragging,
|
||||||
|
isActive,
|
||||||
|
menuItems,
|
||||||
|
listeners,
|
||||||
|
setPressed,
|
||||||
|
}: SplitViewIndicatorProps) => {
|
||||||
|
const active = isActive || isDragging;
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
// prevent menu from opening when dragging
|
||||||
|
const setOpenMenuManually = useCallback((open: boolean) => {
|
||||||
|
if (open) return;
|
||||||
|
setMenuOpen(open);
|
||||||
|
}, []);
|
||||||
|
const openMenu = useCallback(() => {
|
||||||
|
setMenuOpen(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const menuRootOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
open: menuOpen,
|
||||||
|
onOpenChange: setOpenMenuManually,
|
||||||
|
}) satisfies MenuProps['rootOptions'],
|
||||||
|
[menuOpen, setOpenMenuManually]
|
||||||
|
);
|
||||||
|
const menuContentOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
align: 'center',
|
||||||
|
}) satisfies MenuProps['contentOptions'],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-is-dragging={isDragging} className={styles.indicatorWrapper}>
|
||||||
|
<Menu
|
||||||
|
contentOptions={menuContentOptions}
|
||||||
|
items={menuItems}
|
||||||
|
rootOptions={menuRootOptions}
|
||||||
|
>
|
||||||
|
<div className={styles.menuTrigger} />
|
||||||
|
</Menu>
|
||||||
|
<SplitViewMenuIndicator
|
||||||
|
open={menuOpen}
|
||||||
|
onOpenMenu={openMenu}
|
||||||
|
active={active}
|
||||||
|
setPressed={setPressed}
|
||||||
|
{...listeners}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Menu, MenuIcon, MenuItem, type MenuProps } from '@affine/component';
|
import { MenuIcon, MenuItem } from '@affine/component';
|
||||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
import {
|
import {
|
||||||
CloseIcon,
|
ExpandCloseIcon,
|
||||||
ExpandFullIcon,
|
KeepThisOneIcon,
|
||||||
InsertLeftIcon,
|
MoveToLeftIcon,
|
||||||
InsertRightIcon,
|
MoveToRightIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import { useSortable } from '@dnd-kit/sortable';
|
import { useSortable } from '@dnd-kit/sortable';
|
||||||
import { useService } from '@toeverything/infra/di';
|
import { useService } from '@toeverything/infra/di';
|
||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
|
|
||||||
import type { View } from '../../entities/view';
|
import type { View } from '../../entities/view';
|
||||||
import { Workbench } from '../../entities/workbench';
|
import { Workbench } from '../../entities/workbench';
|
||||||
import { SplitViewMenuIndicator } from './indicator';
|
import { SplitViewIndicator } from './indicator';
|
||||||
import * as styles from './split-view.css';
|
import * as styles from './split-view.css';
|
||||||
|
|
||||||
export interface SplitViewPanelProps
|
export interface SplitViewPanelProps
|
||||||
@@ -43,22 +43,24 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
view,
|
view,
|
||||||
setSlots,
|
setSlots,
|
||||||
}: SplitViewPanelProps) {
|
}: SplitViewPanelProps) {
|
||||||
|
const [indicatorPressed, setIndicatorPressed] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const size = useLiveData(view.size);
|
const size = useLiveData(view.size);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const activeView = useLiveData(workbench.activeView);
|
const activeView = useLiveData(workbench.activeView);
|
||||||
const views = useLiveData(workbench.views);
|
const views = useLiveData(workbench.views);
|
||||||
|
const isLast = views[views.length - 1] === view;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
listeners,
|
listeners,
|
||||||
transform,
|
transform,
|
||||||
transition,
|
transition,
|
||||||
isDragging,
|
isDragging: dndIsDragging,
|
||||||
setNodeRef,
|
setNodeRef,
|
||||||
setActivatorNodeRef,
|
|
||||||
} = useSortable({ id: view.id, attributes: { role: 'group' } });
|
} = useSortable({ id: view.id, attributes: { role: 'group' } });
|
||||||
|
|
||||||
|
const isDragging = dndIsDragging || indicatorPressed;
|
||||||
const isActive = activeView === view;
|
const isActive = activeView === view;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -67,12 +69,6 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
}
|
}
|
||||||
}, [setSlots, view.id]);
|
}, [setSlots, view.id]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isDragging) {
|
|
||||||
setMenuOpen(false);
|
|
||||||
}
|
|
||||||
}, [isDragging]);
|
|
||||||
|
|
||||||
const style = useMemo(
|
const style = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...assignInlineVars({ '--size': size.toString() }),
|
...assignInlineVars({ '--size': size.toString() }),
|
||||||
@@ -86,27 +82,14 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
}),
|
}),
|
||||||
[transform, transition]
|
[transform, transition]
|
||||||
);
|
);
|
||||||
const menuRootOptions = useMemo(
|
|
||||||
() =>
|
|
||||||
({
|
|
||||||
open: menuOpen,
|
|
||||||
onOpenChange: setMenuOpen,
|
|
||||||
}) satisfies MenuProps['rootOptions'],
|
|
||||||
[menuOpen]
|
|
||||||
);
|
|
||||||
const menuContentOptions = useMemo(
|
|
||||||
() =>
|
|
||||||
({
|
|
||||||
align: 'center',
|
|
||||||
}) satisfies MenuProps['contentOptions'],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={style}
|
style={style}
|
||||||
className={styles.splitViewPanel}
|
className={styles.splitViewPanel}
|
||||||
data-is-dragging={isDragging}
|
data-is-dragging={isDragging}
|
||||||
|
data-is-active={isActive && views.length > 1}
|
||||||
|
data-is-last={isLast}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
@@ -116,18 +99,13 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
>
|
>
|
||||||
<div className={styles.splitViewPanelContent} ref={ref} />
|
<div className={styles.splitViewPanelContent} ref={ref} />
|
||||||
{views.length > 1 ? (
|
{views.length > 1 ? (
|
||||||
<Menu
|
<SplitViewIndicator
|
||||||
contentOptions={menuContentOptions}
|
listeners={listeners}
|
||||||
items={<SplitViewMenu view={view} />}
|
isDragging={isDragging}
|
||||||
rootOptions={menuRootOptions}
|
isActive={isActive}
|
||||||
>
|
menuItems={<SplitViewMenu view={view} />}
|
||||||
<SplitViewMenuIndicator
|
setPressed={setIndicatorPressed}
|
||||||
ref={setActivatorNodeRef}
|
/>
|
||||||
active={isDragging || isActive}
|
|
||||||
className={styles.menuTrigger}
|
|
||||||
{...listeners}
|
|
||||||
/>
|
|
||||||
</Menu>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
@@ -135,10 +113,7 @@ export const SplitViewPanel = memo(function SplitViewPanel({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
interface SplitViewMenuProps {
|
const SplitViewMenu = ({ view }: { view: View }) => {
|
||||||
view: View;
|
|
||||||
}
|
|
||||||
const SplitViewMenu = ({ view }: SplitViewMenuProps) => {
|
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
const workbench = useService(Workbench);
|
const workbench = useService(Workbench);
|
||||||
const views = useLiveData(workbench.views);
|
const views = useLiveData(workbench.views);
|
||||||
@@ -155,14 +130,14 @@ const SplitViewMenu = ({ view }: SplitViewMenuProps) => {
|
|||||||
const handleMoveRight = useCallback(() => {
|
const handleMoveRight = useCallback(() => {
|
||||||
workbench.moveView(viewIndex, viewIndex + 1);
|
workbench.moveView(viewIndex, viewIndex + 1);
|
||||||
}, [viewIndex, workbench]);
|
}, [viewIndex, workbench]);
|
||||||
const handleFullScreen = useCallback(() => {
|
const handleCloseOthers = useCallback(() => {
|
||||||
workbench.closeOthers(view);
|
workbench.closeOthers(view);
|
||||||
}, [view, workbench]);
|
}, [view, workbench]);
|
||||||
|
|
||||||
const CloseItem =
|
const CloseItem =
|
||||||
views.length > 1 ? (
|
views.length > 1 ? (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
preFix={<MenuIcon icon={<CloseIcon />} />}
|
preFix={<MenuIcon icon={<ExpandCloseIcon />} />}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
>
|
>
|
||||||
{t['com.affine.workbench.split-view-menu.close']()}
|
{t['com.affine.workbench.split-view-menu.close']()}
|
||||||
@@ -173,7 +148,7 @@ const SplitViewMenu = ({ view }: SplitViewMenuProps) => {
|
|||||||
viewIndex > 0 && views.length > 1 ? (
|
viewIndex > 0 && views.length > 1 ? (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={handleMoveLeft}
|
onClick={handleMoveLeft}
|
||||||
preFix={<MenuIcon icon={<InsertLeftIcon />} />}
|
preFix={<MenuIcon icon={<MoveToLeftIcon />} />}
|
||||||
>
|
>
|
||||||
{t['com.affine.workbench.split-view-menu.move-left']()}
|
{t['com.affine.workbench.split-view-menu.move-left']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -182,10 +157,10 @@ const SplitViewMenu = ({ view }: SplitViewMenuProps) => {
|
|||||||
const FullScreenItem =
|
const FullScreenItem =
|
||||||
views.length > 1 ? (
|
views.length > 1 ? (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={handleFullScreen}
|
onClick={handleCloseOthers}
|
||||||
preFix={<MenuIcon icon={<ExpandFullIcon />} />}
|
preFix={<MenuIcon icon={<KeepThisOneIcon />} />}
|
||||||
>
|
>
|
||||||
{t['com.affine.workbench.split-view-menu.full-screen']()}
|
{t['com.affine.workbench.split-view-menu.keep-this-one']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
@@ -193,7 +168,7 @@ const SplitViewMenu = ({ view }: SplitViewMenuProps) => {
|
|||||||
viewIndex < views.length - 1 ? (
|
viewIndex < views.length - 1 ? (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={handleMoveRight}
|
onClick={handleMoveRight}
|
||||||
preFix={<MenuIcon icon={<InsertRightIcon />} />}
|
preFix={<MenuIcon icon={<MoveToRightIcon />} />}
|
||||||
>
|
>
|
||||||
{t['com.affine.workbench.split-view-menu.move-right']()}
|
{t['com.affine.workbench.split-view-menu.move-right']()}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const splitViewRoot = style({
|
|||||||
selectors: {
|
selectors: {
|
||||||
'&[data-client-border="true"]': {
|
'&[data-client-border="true"]': {
|
||||||
vars: {
|
vars: {
|
||||||
[gap]: '6px',
|
[gap]: '8px',
|
||||||
[borderRadius]: '6px',
|
[borderRadius]: '6px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -40,7 +40,7 @@ export const splitViewPanel = style({
|
|||||||
'[data-orientation="horizontal"] &': {
|
'[data-orientation="horizontal"] &': {
|
||||||
width: 0,
|
width: 0,
|
||||||
},
|
},
|
||||||
'[data-client-border="false"] &:not(:last-child):not([data-is-dragging="true"])':
|
'[data-client-border="false"] &:not([data-is-last="true"]):not([data-is-dragging="true"])':
|
||||||
{
|
{
|
||||||
borderRight: `1px solid ${cssVar('borderColor')}`,
|
borderRight: `1px solid ${cssVar('borderColor')}`,
|
||||||
},
|
},
|
||||||
@@ -63,6 +63,10 @@ export const splitViewPanelDrag = style({
|
|||||||
borderRadius: 'inherit',
|
borderRadius: 'inherit',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
|
|
||||||
|
// animate border in/out
|
||||||
|
boxShadow: `inset 0 0 0 0 transparent`,
|
||||||
|
transition: 'box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1)',
|
||||||
},
|
},
|
||||||
|
|
||||||
'[data-is-dragging="true"] &::after': {
|
'[data-is-dragging="true"] &::after': {
|
||||||
@@ -125,11 +129,3 @@ export const resizeHandle = style({
|
|||||||
// TODO
|
// TODO
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const menuTrigger = style({
|
|
||||||
position: 'absolute',
|
|
||||||
left: '50%',
|
|
||||||
top: 3,
|
|
||||||
transform: 'translateX(-50%)',
|
|
||||||
zIndex: 10,
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export const SplitView = ({
|
|||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor, {
|
useSensor(PointerSensor, {
|
||||||
activationConstraint: {
|
activationConstraint: {
|
||||||
distance: 2,
|
distance: 0,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1163,5 +1163,7 @@
|
|||||||
"com.affine.delete-tags.count": "{{count}} tag deleted",
|
"com.affine.delete-tags.count": "{{count}} tag deleted",
|
||||||
"com.affine.delete-tags.count_one": "{{count}} tag deleted",
|
"com.affine.delete-tags.count_one": "{{count}} tag deleted",
|
||||||
"com.affine.delete-tags.count_other": "{{count}} tags deleted",
|
"com.affine.delete-tags.count_other": "{{count}} tags deleted",
|
||||||
|
"com.affine.workbench.split-view-menu.keep-this-one": "Keep this one",
|
||||||
|
"com.affine.workbench.split-view.page-menu-open": "Split View",
|
||||||
"com.affine.search-tags.placeholder": "Type here ..."
|
"com.affine.search-tags.placeholder": "Type here ..."
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user