style: move trash button group to page bottom (#3352)

This commit is contained in:
JimmFly
2023-07-25 13:21:16 +08:00
committed by GitHub
parent be81e63eed
commit e3f66d7e22
12 changed files with 136 additions and 103 deletions

View File

@@ -43,6 +43,9 @@ export const EditorModeSwitch = ({
assertExists(pageMeta); assertExists(pageMeta);
const { trash } = pageMeta; const { trash } = pageMeta;
useEffect(() => { useEffect(() => {
if (trash) {
return;
}
const keydown = (e: KeyboardEvent) => { const keydown = (e: KeyboardEvent) => {
if ( if (
!environment.isServer && environment.isMacOs !environment.isServer && environment.isMacOs
@@ -64,7 +67,7 @@ export const EditorModeSwitch = ({
document.addEventListener('keydown', keydown, { capture: true }); document.addEventListener('keydown', keydown, { capture: true });
return () => return () =>
document.removeEventListener('keydown', keydown, { capture: true }); document.removeEventListener('keydown', keydown, { capture: true });
}, [setSetting, t]); }, [setSetting, t, trash]);
return ( return (
<Tooltip content={<TooltipContent />}> <Tooltip content={<TooltipContent />}>
@@ -77,6 +80,7 @@ export const EditorModeSwitch = ({
data-testid="switch-page-mode-button" data-testid="switch-page-mode-button"
active={currentMode === 'page'} active={currentMode === 'page'}
hide={trash && currentMode !== 'page'} hide={trash && currentMode !== 'page'}
trash={trash}
onClick={() => { onClick={() => {
setSetting(setting => { setSetting(setting => {
if (setting?.mode !== 'page') { if (setting?.mode !== 'page') {
@@ -90,6 +94,7 @@ export const EditorModeSwitch = ({
data-testid="switch-edgeless-mode-button" data-testid="switch-edgeless-mode-button"
active={currentMode === 'edgeless'} active={currentMode === 'edgeless'}
hide={trash && currentMode !== 'edgeless'} hide={trash && currentMode !== 'edgeless'}
trash={trash}
onClick={() => { onClick={() => {
setSetting(setting => { setSetting(setting => {
if (setting?.mode !== 'edgeless') { if (setting?.mode !== 'edgeless') {

View File

@@ -34,14 +34,19 @@ export const StyledEditorModeSwitch = styled('div')<{
export const StyledSwitchItem = styled('button')<{ export const StyledSwitchItem = styled('button')<{
active?: boolean; active?: boolean;
hide?: boolean; hide?: boolean;
}>(({ active = false, hide = false }) => { trash?: boolean;
}>(({ active = false, hide = false, trash = false }) => {
return { return {
width: '24px', width: '24px',
height: '24px', height: '24px',
borderRadius: '8px', borderRadius: '8px',
WebkitAppRegion: 'no-drag', WebkitAppRegion: 'no-drag',
boxShadow: active ? 'var(--affine-shadow-1)' : 'none', boxShadow: active ? 'var(--affine-shadow-1)' : 'none',
color: active ? 'var(--affine-primary-color)' : 'var(--affine-icon-color)', color: active
? trash
? 'var(--affine-error-color)'
: 'var(--affine-primary-color)'
: 'var(--affine-icon-color)',
display: hide ? 'none' : 'inline-flex', display: hide ? 'none' : 'inline-flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',

View File

@@ -8,12 +8,14 @@ import { StyledSwitchItem } from './style';
type HoverAnimateControllerProps = { type HoverAnimateControllerProps = {
active?: boolean; active?: boolean;
hide?: boolean; hide?: boolean;
trash?: boolean;
children: React.ReactElement; children: React.ReactElement;
} & HTMLAttributes<HTMLButtonElement>; } & HTMLAttributes<HTMLButtonElement>;
const HoverAnimateController = ({ const HoverAnimateController = ({
active, active,
hide, hide,
trash,
children, children,
...props ...props
}: HoverAnimateControllerProps) => { }: HoverAnimateControllerProps) => {
@@ -22,6 +24,7 @@ const HoverAnimateController = ({
<StyledSwitchItem <StyledSwitchItem
hide={hide} hide={hide}
active={active} active={active}
trash={trash}
onMouseEnter={() => { onMouseEnter={() => {
setStartAnimate(true); setStartAnimate(true);
}} }}

View File

@@ -0,0 +1,15 @@
import { style } from '@vanilla-extract/css';
export const group = style({
width: '100%',
position: 'absolute',
bottom: '100px',
left: '0',
display: 'flex',
gap: '24px',
justifyContent: 'center',
});
export const buttonContainer = style({
boxShadow: 'var(--affine-float-button-shadow-2)',
borderRadius: '8px',
});

View File

@@ -10,7 +10,7 @@ import { useCallback, useState } from 'react';
import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper'; import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper';
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace'; import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
import { useNavigateHelper } from '../../../../hooks/use-navigate-helper'; import { useNavigateHelper } from '../../../../hooks/use-navigate-helper';
import { buttonContainer, group } from './styles.css';
export const TrashButtonGroup = () => { export const TrashButtonGroup = () => {
// fixme(himself65): remove these hooks ASAP // fixme(himself65): remove these hooks ASAP
const [workspace] = useCurrentWorkspace(); const [workspace] = useCurrentWorkspace();
@@ -29,25 +29,29 @@ export const TrashButtonGroup = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<> <div className={group}>
<Button <div className={buttonContainer}>
shape="round" <Button
style={{ marginRight: '24px' }} type="processing"
onClick={() => { onClick={() => {
restoreFromTrash(pageId); restoreFromTrash(pageId);
}} }}
> size="large"
{t['Restore it']()} >
</Button> {t['Restore it']()}
<Button </Button>
shape="round" </div>
type="error" <div className={buttonContainer}>
onClick={() => { <Button
setOpen(true); type="error"
}} onClick={() => {
> setOpen(true);
{t['Delete permanently']()} }}
</Button> size="large"
>
{t['Delete permanently']()}
</Button>
</div>
<Confirm <Confirm
title={t['TrashButtonGroupTitle']()} title={t['TrashButtonGroupTitle']()}
content={t['TrashButtonGroupDescription']()} content={t['TrashButtonGroupDescription']()}
@@ -65,7 +69,7 @@ export const TrashButtonGroup = () => {
setOpen(false); setOpen(false);
}} }}
/> />
</> </div>
); );
}; };

View File

@@ -23,7 +23,6 @@ import { currentModeAtom } from '../../../atoms/mode';
import type { AffineOfficialWorkspace } from '../../../shared'; import type { AffineOfficialWorkspace } from '../../../shared';
import DownloadClientTip from './download-tips'; import DownloadClientTip from './download-tips';
import { EditorOptionMenu } from './header-right-items/editor-option-menu'; import { EditorOptionMenu } from './header-right-items/editor-option-menu';
import TrashButtonGroup from './header-right-items/trash-button-group';
import * as styles from './styles.css'; import * as styles from './styles.css';
import { OSWarningMessage, shouldShowWarning } from './utils'; import { OSWarningMessage, shouldShowWarning } from './utils';
@@ -38,7 +37,6 @@ export type BaseHeaderProps<
export enum HeaderRightItemName { export enum HeaderRightItemName {
EditorOptionMenu = 'editorOptionMenu', EditorOptionMenu = 'editorOptionMenu',
TrashButtonGroup = 'trashButtonGroup',
// some windows only items // some windows only items
WindowsAppControls = 'windowsAppControls', WindowsAppControls = 'windowsAppControls',
} }
@@ -56,16 +54,10 @@ type HeaderItem = {
}; };
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = { const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
[HeaderRightItemName.TrashButtonGroup]: {
Component: TrashButtonGroup,
availableWhen: (_, currentPage) => {
return currentPage?.meta.trash === true;
},
},
[HeaderRightItemName.EditorOptionMenu]: { [HeaderRightItemName.EditorOptionMenu]: {
Component: EditorOptionMenu, Component: EditorOptionMenu,
availableWhen: (_, currentPage, { isPublic }) => { availableWhen: (_, currentPage, { isPublic }) => {
return !isPublic; return !isPublic && currentPage?.meta.trash !== true;
}, },
}, },
[HeaderRightItemName.WindowsAppControls]: { [HeaderRightItemName.WindowsAppControls]: {

View File

@@ -25,6 +25,7 @@ import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { pageSettingFamily } from '../atoms'; import { pageSettingFamily } from '../atoms';
import { fontStyleOptions, useAppSetting } from '../atoms/settings'; import { fontStyleOptions, useAppSetting } from '../atoms/settings';
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
import TrashButtonGroup from './blocksuite/workspace-header/header-right-items/trash-button-group';
import * as styles from './page-detail-editor.css'; import * as styles from './page-detail-editor.css';
import { pluginContainer } from './page-detail-editor.css'; import { pluginContainer } from './page-detail-editor.css';
@@ -67,62 +68,65 @@ const EditorWrapper = memo(function EditorWrapper({
}, [appSettings.fontStyle]); }, [appSettings.fontStyle]);
return ( return (
<Editor <>
className={clsx(styles.editor, { <Editor
'full-screen': appSettings.fullWidthLayout, className={clsx(styles.editor, {
})} 'full-screen': appSettings.fullWidthLayout,
style={ })}
{ style={
'--affine-font-family': value, {
} as CSSProperties '--affine-font-family': value,
} } as CSSProperties
key={`${workspace.id}-${pageId}`} }
mode={isPublic ? 'page' : currentMode} key={`${workspace.id}-${pageId}`}
page={page} mode={isPublic ? 'page' : currentMode}
onInit={useCallback( page={page}
(page: Page, editor: Readonly<EditorContainer>) => { onInit={useCallback(
onInit(page, editor); (page: Page, editor: Readonly<EditorContainer>) => {
}, onInit(page, editor);
[onInit] },
)} [onInit]
setBlockHub={setBlockHub} )}
onLoad={useCallback( setBlockHub={setBlockHub}
(page: Page, editor: EditorContainer) => { onLoad={useCallback(
page.workspace.setPageMeta(page.id, { (page: Page, editor: EditorContainer) => {
updatedDate: Date.now(), page.workspace.setPageMeta(page.id, {
}); updatedDate: Date.now(),
localStorage.setItem('last_page_id', page.id); });
let dispose = () => {}; localStorage.setItem('last_page_id', page.id);
if (onLoad) { let dispose = () => {};
dispose = onLoad(page, editor); if (onLoad) {
} dispose = onLoad(page, editor);
const editorItems = rootStore.get(editorItemsAtom); }
let disposes: (() => void)[] = []; const editorItems = rootStore.get(editorItemsAtom);
const renderTimeout = setTimeout(() => { let disposes: (() => void)[] = [];
disposes = Object.entries(editorItems).map(([id, editorItem]) => { const renderTimeout = setTimeout(() => {
const div = document.createElement('div'); disposes = Object.entries(editorItems).map(([id, editorItem]) => {
div.setAttribute('plugin-id', id); const div = document.createElement('div');
const cleanup = editorItem(div, editor); div.setAttribute('plugin-id', id);
assertExists(parent); const cleanup = editorItem(div, editor);
document.body.appendChild(div); assertExists(parent);
return () => { document.body.appendChild(div);
cleanup(); return () => {
document.body.removeChild(div); cleanup();
}; document.body.removeChild(div);
};
});
}); });
});
return () => { return () => {
dispose(); dispose();
clearTimeout(renderTimeout); clearTimeout(renderTimeout);
setTimeout(() => { setTimeout(() => {
disposes.forEach(dispose => dispose()); disposes.forEach(dispose => dispose());
}); });
}; };
}, },
[onLoad] [onLoad]
)} )}
/> />
{meta.trash && <TrashButtonGroup />}
</>
); );
}); });

View File

@@ -37,7 +37,7 @@
"@radix-ui/react-toast": "^1.1.4", "@radix-ui/react-toast": "^1.1.4",
"@toeverything/hooks": "workspace:*", "@toeverything/hooks": "workspace:*",
"@toeverything/plugin-infra": "workspace:*", "@toeverything/plugin-infra": "workspace:*",
"@toeverything/theme": "^0.7.7", "@toeverything/theme": "^0.7.9",
"@vanilla-extract/dynamic": "^2.0.3", "@vanilla-extract/dynamic": "^2.0.3",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"dayjs": "^1.11.9", "dayjs": "^1.11.9",

View File

@@ -70,7 +70,7 @@ export const button = style({
color: 'var(--affine-white)', color: 'var(--affine-white)',
background: 'var(--affine-primary-color)', background: 'var(--affine-primary-color)',
borderColor: 'var(--affine-black-10)', borderColor: 'var(--affine-black-10)',
boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', boxShadow: 'var(--affine-button-inner-shadow)',
}, },
'&.primary:hover': { '&.primary:hover': {
background: background:
@@ -85,10 +85,10 @@ export const button = style({
}, },
'&.error': { '&.error': {
color: 'var(--affine-white)', color: 'var(--affine-pure-white)',
background: 'var(--affine-error-color)', background: 'var(--affine-error-color)',
borderColor: 'var(--affine-black-10)', borderColor: 'var(--affine-black-10)',
boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', boxShadow: 'var(--affine-button-inner-shadow)',
}, },
'&.error:hover': { '&.error:hover': {
background: background:
@@ -106,7 +106,7 @@ export const button = style({
color: 'var(--affine-white)', color: 'var(--affine-white)',
background: 'var(--affine-warning-color)', background: 'var(--affine-warning-color)',
borderColor: 'var(--affine-black-10)', borderColor: 'var(--affine-black-10)',
boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', boxShadow: 'var(--affine-button-inner-shadow)',
}, },
'&.warning:hover': { '&.warning:hover': {
background: background:
@@ -121,10 +121,10 @@ export const button = style({
}, },
'&.success': { '&.success': {
color: 'var(--affine-white)', color: 'var(--affine-pure-white)',
background: 'var(--affine-success-color)', background: 'var(--affine-success-color)',
borderColor: 'var(--affine-black-10)', borderColor: 'var(--affine-black-10)',
boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', boxShadow: 'var(--affine-button-inner-shadow)',
}, },
'&.success:hover': { '&.success:hover': {
background: background:
@@ -139,10 +139,10 @@ export const button = style({
}, },
'&.processing': { '&.processing': {
color: 'var(--affine-white)', color: 'var(--affine-pure-white)',
background: 'var(--affine-processing-color)', background: 'var(--affine-processing-color)',
borderColor: 'var(--affine-black-10)', borderColor: 'var(--affine-black-10)',
boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', boxShadow: 'var(--affine-button-inner-shadow)',
}, },
'&.processing:hover': { '&.processing:hover': {
background: background:

View File

@@ -52,10 +52,10 @@ export const Confirm = ({
{buttonDirection === 'row' ? ( {buttonDirection === 'row' ? (
<StyledRowButtonWrapper> <StyledRowButtonWrapper>
<Button <Button
shape="round"
onClick={() => { onClick={() => {
onCancel?.(); onCancel?.();
}} }}
size="large"
style={{ marginRight: '24px' }} style={{ marginRight: '24px' }}
data-testid={cancelButtonTestId} data-testid={cancelButtonTestId}
> >
@@ -63,10 +63,10 @@ export const Confirm = ({
</Button> </Button>
<Button <Button
type={confirmType} type={confirmType}
shape="round"
onClick={() => { onClick={() => {
onConfirm?.(); onConfirm?.();
}} }}
size="large"
data-testid={confirmButtonTestId} data-testid={confirmButtonTestId}
> >
{confirmText} {confirmText}
@@ -76,7 +76,6 @@ export const Confirm = ({
<StyledColumnButtonWrapper> <StyledColumnButtonWrapper>
<Button <Button
type={confirmType} type={confirmType}
shape="round"
onClick={() => { onClick={() => {
onConfirm?.(); onConfirm?.();
}} }}
@@ -86,7 +85,6 @@ export const Confirm = ({
{confirmText} {confirmText}
</Button> </Button>
<Button <Button
shape="round"
onClick={() => { onClick={() => {
onCancel?.(); onCancel?.();
}} }}

View File

@@ -11,7 +11,7 @@ export const ModalWrapper = styled('div')<{
width, width,
height, height,
minHeight, minHeight,
backgroundColor: 'var(--affine-background-secondary-color)', backgroundColor: 'var(--affine-background-overlay-panel-color)',
boxShadow: 'var(--affine-shadow-3)', boxShadow: 'var(--affine-shadow-3)',
borderRadius: '12px', borderRadius: '12px',
position: 'relative', position: 'relative',

View File

@@ -132,7 +132,7 @@ __metadata:
"@radix-ui/react-toast": ^1.1.4 "@radix-ui/react-toast": ^1.1.4
"@toeverything/hooks": "workspace:*" "@toeverything/hooks": "workspace:*"
"@toeverything/plugin-infra": "workspace:*" "@toeverything/plugin-infra": "workspace:*"
"@toeverything/theme": ^0.7.7 "@toeverything/theme": ^0.7.9
"@types/react": ^18.2.15 "@types/react": ^18.2.15
"@types/react-datepicker": ^4.11.2 "@types/react-datepicker": ^4.11.2
"@types/react-dnd": ^3.0.2 "@types/react-dnd": ^3.0.2
@@ -10977,13 +10977,20 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@toeverything/theme@npm:0.7.7, @toeverything/theme@npm:^0.7.7": "@toeverything/theme@npm:0.7.7":
version: 0.7.7 version: 0.7.7
resolution: "@toeverything/theme@npm:0.7.7" resolution: "@toeverything/theme@npm:0.7.7"
checksum: 5a449eb440c49c712069203d85acc73613a000324807ce2e42c3b441b74b74e54ba76292144b60e3900d54bf41a13f98923174fee527c20303ad6c68e26e6176 checksum: 5a449eb440c49c712069203d85acc73613a000324807ce2e42c3b441b74b74e54ba76292144b60e3900d54bf41a13f98923174fee527c20303ad6c68e26e6176
languageName: node languageName: node
linkType: hard linkType: hard
"@toeverything/theme@npm:^0.7.9":
version: 0.7.9
resolution: "@toeverything/theme@npm:0.7.9"
checksum: ac7eeea49f9099e34e8aacbd327ef5e48cc9fc02c32a20309da3dbcb4363c8dda3d2c887cf0e8eb8bb0fa2c50e73f123332f7009d2926961ef7ecbbb921a856d
languageName: node
linkType: hard
"@toeverything/y-indexeddb@workspace:*, @toeverything/y-indexeddb@workspace:packages/y-indexeddb": "@toeverything/y-indexeddb@workspace:*, @toeverything/y-indexeddb@workspace:packages/y-indexeddb":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@toeverything/y-indexeddb@workspace:packages/y-indexeddb" resolution: "@toeverything/y-indexeddb@workspace:packages/y-indexeddb"