feat(core): adjust ai help island style and behavior, add animation (#7310)
- Move right-sidebar activeTab state into `RightSidebarService` - Remove `styled` usage and adjust the UI - Fix the issue that ai-button clicking not work when sidebar opened - Add an animation if AI-Chat panel hasn't been opened. 
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
export { styled };
|
export {
|
||||||
|
/**
|
||||||
|
* @deprecated `@emotion/styled` is planned to be removed in the future. Do not use for new components.
|
||||||
|
**/
|
||||||
|
styled,
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,65 +1,66 @@
|
|||||||
import type { SidebarTabName } from '@affine/core/modules/multi-tab-sidebar';
|
|
||||||
import { RightSidebarService } from '@affine/core/modules/right-sidebar';
|
import { RightSidebarService } from '@affine/core/modules/right-sidebar';
|
||||||
import {
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
DocsService,
|
import { useEffect, useState } from 'react';
|
||||||
GlobalContextService,
|
|
||||||
GlobalStateService,
|
|
||||||
LiveData,
|
|
||||||
useLiveData,
|
|
||||||
useService,
|
|
||||||
} from '@toeverything/infra';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { ToolContainer } from '../../workspace';
|
import { ToolContainer } from '../../workspace';
|
||||||
import { AIIcon } from './icons';
|
import { AIIcon } from './icons';
|
||||||
import { StyledIsland, StyledTriggerWrapper } from './style';
|
import {
|
||||||
|
aiIslandAnimationBg,
|
||||||
|
aiIslandBtn,
|
||||||
|
aiIslandWrapper,
|
||||||
|
borderAngle1,
|
||||||
|
borderAngle2,
|
||||||
|
borderAngle3,
|
||||||
|
} from './styles.css';
|
||||||
|
|
||||||
export const RIGHT_SIDEBAR_TABS_ACTIVE_KEY =
|
if (typeof window !== 'undefined' && window.CSS) {
|
||||||
'app:settings:rightsidebar:tabs:active';
|
const getName = (nameWithVar: string) => nameWithVar.slice(4, -1);
|
||||||
|
const registerAngle = (varName: string, initialValue: number) => {
|
||||||
|
window.CSS.registerProperty({
|
||||||
|
name: getName(varName),
|
||||||
|
syntax: '<angle>',
|
||||||
|
inherits: false,
|
||||||
|
initialValue: `${initialValue}deg`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
registerAngle(borderAngle1, 0);
|
||||||
|
registerAngle(borderAngle2, 90);
|
||||||
|
registerAngle(borderAngle3, 180);
|
||||||
|
}
|
||||||
|
|
||||||
export const AIIsland = () => {
|
export const AIIsland = () => {
|
||||||
const docId = useLiveData(
|
// to make sure ai island is hidden first and animate in
|
||||||
useService(GlobalContextService).globalContext.docId.$
|
const [hide, setHide] = useState(true);
|
||||||
);
|
|
||||||
const docRecordList = useService(DocsService).list;
|
|
||||||
const doc = useLiveData(docId ? docRecordList.doc$(docId) : undefined);
|
|
||||||
const mode = useLiveData(doc?.mode$);
|
|
||||||
|
|
||||||
const globalState = useService(GlobalStateService).globalState;
|
|
||||||
const activeTabName = useLiveData(
|
|
||||||
LiveData.from(
|
|
||||||
globalState.watch<SidebarTabName>(RIGHT_SIDEBAR_TABS_ACTIVE_KEY),
|
|
||||||
'journal'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const setActiveTabName = useCallback(
|
|
||||||
(name: string) => {
|
|
||||||
globalState.set(RIGHT_SIDEBAR_TABS_ACTIVE_KEY, name);
|
|
||||||
},
|
|
||||||
[globalState]
|
|
||||||
);
|
|
||||||
const rightSidebar = useService(RightSidebarService).rightSidebar;
|
const rightSidebar = useService(RightSidebarService).rightSidebar;
|
||||||
|
const activeTabName = useLiveData(rightSidebar.activeTabName$);
|
||||||
const rightSidebarOpen = useLiveData(rightSidebar.isOpen$);
|
const rightSidebarOpen = useLiveData(rightSidebar.isOpen$);
|
||||||
|
const aiChatHasEverOpened = useLiveData(rightSidebar.aiChatHasEverOpened$);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHide(rightSidebarOpen && activeTabName === 'chat');
|
||||||
|
}, [activeTabName, rightSidebarOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolContainer>
|
<ToolContainer>
|
||||||
<StyledIsland
|
<div
|
||||||
spread={true}
|
className={aiIslandWrapper}
|
||||||
data-testid="ai-island"
|
data-hide={hide}
|
||||||
onClick={() => {
|
data-animation={!aiChatHasEverOpened}
|
||||||
if (rightSidebarOpen) return;
|
|
||||||
rightSidebar.isOpen$;
|
|
||||||
rightSidebar.open();
|
|
||||||
if (activeTabName !== 'chat') {
|
|
||||||
setActiveTabName('chat');
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
inEdgelessPage={!!docId && mode === 'edgeless'}
|
|
||||||
>
|
>
|
||||||
<StyledTriggerWrapper data-testid="faq-icon">
|
<div className={aiIslandAnimationBg} />
|
||||||
|
<button
|
||||||
|
className={aiIslandBtn}
|
||||||
|
data-testid="ai-island"
|
||||||
|
onClick={() => {
|
||||||
|
if (hide) return;
|
||||||
|
rightSidebar.open();
|
||||||
|
rightSidebar.setActiveTabName('chat');
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AIIcon />
|
<AIIcon />
|
||||||
</StyledTriggerWrapper>
|
</button>
|
||||||
</StyledIsland>
|
</div>
|
||||||
</ToolContainer>
|
</ToolContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import { displayFlex, positionAbsolute, styled } from '@affine/component';
|
|
||||||
|
|
||||||
export const StyledIsland = styled('div')<{
|
|
||||||
spread: boolean;
|
|
||||||
inEdgelessPage?: boolean;
|
|
||||||
}>(({ spread, inEdgelessPage }) => {
|
|
||||||
return {
|
|
||||||
width: '44px',
|
|
||||||
position: 'relative',
|
|
||||||
boxShadow: spread
|
|
||||||
? 'var(--affine-menu-shadow)'
|
|
||||||
: inEdgelessPage
|
|
||||||
? 'var(--affine-menu-shadow)'
|
|
||||||
: 'unset',
|
|
||||||
padding: '0 4px 44px',
|
|
||||||
borderRadius: '50%',
|
|
||||||
background: spread
|
|
||||||
? 'var(--affine-background-overlay-panel-color)'
|
|
||||||
: 'var(--affine-background-primary-color)',
|
|
||||||
':hover': {
|
|
||||||
background: spread ? undefined : 'var(--affine-white)',
|
|
||||||
boxShadow: spread ? undefined : 'var(--affine-menu-shadow)',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const StyledTriggerWrapper = styled('div')<{
|
|
||||||
spread?: boolean;
|
|
||||||
}>(({ spread }) => {
|
|
||||||
return {
|
|
||||||
width: '36px',
|
|
||||||
height: '36px',
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: 'var(--affine-icon-color)',
|
|
||||||
borderRadius: '5px',
|
|
||||||
fontSize: '24px',
|
|
||||||
...displayFlex('center', 'center'),
|
|
||||||
...positionAbsolute({ left: '4px', bottom: '4px' }),
|
|
||||||
':hover': {
|
|
||||||
backgroundColor: spread ? 'var(--affine-hover-color)' : undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { cssVar } from '@toeverything/theme';
|
||||||
|
import { createVar, keyframes, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const aiIslandWrapper = style({
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
position: 'relative',
|
||||||
|
transform: 'translateY(0)',
|
||||||
|
transition: 'transform 0.2s ease',
|
||||||
|
|
||||||
|
selectors: {
|
||||||
|
'&[data-hide="true"]': {
|
||||||
|
transform: 'translateY(120px)',
|
||||||
|
transitionDelay: '0.2s',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export const aiIslandBtn = style({
|
||||||
|
width: 'inherit',
|
||||||
|
height: 'inherit',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
borderRadius: '50%',
|
||||||
|
color: cssVar('iconColor'),
|
||||||
|
border: `0.5px solid ${cssVar('borderColor')}`,
|
||||||
|
boxShadow: '0px 2px 2px rgba(0,0,0,0.05)',
|
||||||
|
background: cssVar('backgroundOverlayPanelColor'),
|
||||||
|
position: 'relative',
|
||||||
|
|
||||||
|
selectors: {
|
||||||
|
[`${aiIslandWrapper}[data-animation="true"] &`]: {
|
||||||
|
borderColor: 'transparent',
|
||||||
|
},
|
||||||
|
'&:hover::after': {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: cssVar('hoverColor'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------- animation --------
|
||||||
|
export const borderAngle1 = createVar('border-angle-1');
|
||||||
|
export const borderAngle2 = createVar('border-angle-2');
|
||||||
|
export const borderAngle3 = createVar('border-angle-3');
|
||||||
|
const brightBlue = createVar('bright-blue');
|
||||||
|
const brightGreen = createVar('bright-green');
|
||||||
|
const brightRed = createVar('bright-red');
|
||||||
|
const borderWidth = createVar('border-width');
|
||||||
|
|
||||||
|
const rotateBg1 = keyframes({
|
||||||
|
to: { [borderAngle1.slice(4, -1)]: '360deg' },
|
||||||
|
});
|
||||||
|
const rotateBg2 = keyframes({
|
||||||
|
to: { [borderAngle2.slice(4, -1)]: '450deg' },
|
||||||
|
});
|
||||||
|
const rotateBg3 = keyframes({
|
||||||
|
to: { [borderAngle3.slice(4, -1)]: '540deg' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const aiIslandAnimationBg = style({
|
||||||
|
width: 'inherit',
|
||||||
|
height: 'inherit',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
position: 'absolute',
|
||||||
|
borderRadius: '50%',
|
||||||
|
|
||||||
|
vars: {
|
||||||
|
[borderAngle1]: '0deg',
|
||||||
|
[borderAngle2]: '90deg',
|
||||||
|
[borderAngle3]: '180deg',
|
||||||
|
[brightBlue]: 'rgb(0, 100, 255)',
|
||||||
|
[brightGreen]: '#1E96EB',
|
||||||
|
[brightRed]: 'rgb(0, 200, 255)',
|
||||||
|
[borderWidth]: '1.5px',
|
||||||
|
},
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
backgroundImage: `conic-gradient(from ${borderAngle1} at 50% 50%,
|
||||||
|
transparent,
|
||||||
|
${brightBlue} 10%,
|
||||||
|
transparent 30%,
|
||||||
|
transparent),
|
||||||
|
conic-gradient(from ${borderAngle2} at 50% 50%,
|
||||||
|
transparent,
|
||||||
|
${brightGreen} 10%,
|
||||||
|
transparent 60%,
|
||||||
|
transparent),
|
||||||
|
conic-gradient(from ${borderAngle3} at 50% 50%,
|
||||||
|
transparent,
|
||||||
|
${brightRed} 10%,
|
||||||
|
transparent 50%,
|
||||||
|
transparent)`,
|
||||||
|
|
||||||
|
selectors: {
|
||||||
|
[`${aiIslandWrapper}[data-animation="true"] &`]: {
|
||||||
|
width: `calc(100% + 2 * ${borderWidth})`,
|
||||||
|
height: `calc(100% + 2 * ${borderWidth})`,
|
||||||
|
top: `calc(-1 * ${borderWidth})`,
|
||||||
|
left: `calc(-1 * ${borderWidth})`,
|
||||||
|
animation: `${rotateBg1} 3s linear infinite, ${rotateBg2} 8s linear infinite, ${rotateBg3} 13s linear infinite`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -87,27 +87,16 @@ export const mainContainerStyle = style({
|
|||||||
});
|
});
|
||||||
export const toolStyle = style({
|
export const toolStyle = style({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: '30px',
|
right: 16,
|
||||||
bottom: '30px',
|
bottom: 16,
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
gap: '12px',
|
gap: '12px',
|
||||||
selectors: {
|
selectors: {
|
||||||
'&.trash': {
|
'&.trash': {
|
||||||
bottom: '78px',
|
bottom: '78px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'@media': {
|
|
||||||
'screen and (max-width: 960px)': {
|
|
||||||
right: 'calc((100vw - 640px) * 3 / 19 + 14px)',
|
|
||||||
},
|
|
||||||
'screen and (max-width: 640px)': {
|
|
||||||
right: '5px',
|
|
||||||
bottom: '5px',
|
|
||||||
},
|
|
||||||
print: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
import type { GlobalState } from '@toeverything/infra';
|
import type { GlobalState } from '@toeverything/infra';
|
||||||
import { Entity, LiveData } from '@toeverything/infra';
|
import { Entity, LiveData } from '@toeverything/infra';
|
||||||
|
|
||||||
|
import type { SidebarTabName } from '../../multi-tab-sidebar';
|
||||||
import { RightSidebarView } from './right-sidebar-view';
|
import { RightSidebarView } from './right-sidebar-view';
|
||||||
|
|
||||||
const RIGHT_SIDEBAR_KEY = 'app:settings:rightsidebar';
|
const RIGHT_SIDEBAR_KEY = 'app:settings:rightsidebar';
|
||||||
|
const RIGHT_SIDEBAR_TABS_ACTIVE_KEY = 'app:settings:rightsidebar:tabs:active';
|
||||||
|
const RIGHT_SIDEBAR_AI_HAS_EVER_OPENED_KEY =
|
||||||
|
'app:settings:rightsidebar:ai:has-ever-opened';
|
||||||
|
|
||||||
export class RightSidebar extends Entity {
|
export class RightSidebar extends Entity {
|
||||||
|
_disposables: Array<() => void> = [];
|
||||||
constructor(private readonly globalState: GlobalState) {
|
constructor(private readonly globalState: GlobalState) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
const sub = this.activeTabName$.subscribe(name => {
|
||||||
|
if (name === 'chat') {
|
||||||
|
this.globalState.set(RIGHT_SIDEBAR_AI_HAS_EVER_OPENED_KEY, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._disposables.push(() => sub.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly isOpen$ = LiveData.from(
|
readonly isOpen$ = LiveData.from(
|
||||||
@@ -19,6 +31,25 @@ export class RightSidebar extends Entity {
|
|||||||
stack => stack[0] as RightSidebarView | undefined
|
stack => stack[0] as RightSidebarView | undefined
|
||||||
);
|
);
|
||||||
readonly hasViews$ = this.views$.map(stack => stack.length > 0);
|
readonly hasViews$ = this.views$.map(stack => stack.length > 0);
|
||||||
|
readonly activeTabName$ = LiveData.from(
|
||||||
|
this.globalState.watch<SidebarTabName>(RIGHT_SIDEBAR_TABS_ACTIVE_KEY),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
/** To determine if AI chat has ever been opened, used to show the animation for the first time */
|
||||||
|
readonly aiChatHasEverOpened$ = LiveData.from(
|
||||||
|
this.globalState.watch<boolean>(RIGHT_SIDEBAR_AI_HAS_EVER_OPENED_KEY),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
override dispose() {
|
||||||
|
super.dispose();
|
||||||
|
this._disposables.forEach(dispose => dispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveTabName(name: SidebarTabName) {
|
||||||
|
this.globalState.set(RIGHT_SIDEBAR_TABS_ACTIVE_KEY, name);
|
||||||
|
}
|
||||||
|
|
||||||
open() {
|
open() {
|
||||||
this._set(true);
|
this._set(true);
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ import { Scrollable } from '@affine/component';
|
|||||||
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
||||||
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
|
||||||
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
import { PageAIOnboarding } from '@affine/core/components/affine/ai-onboarding';
|
||||||
import {
|
import { AIIsland } from '@affine/core/components/pure/ai-island';
|
||||||
AIIsland,
|
|
||||||
RIGHT_SIDEBAR_TABS_ACTIVE_KEY,
|
|
||||||
} from '@affine/core/components/pure/ai-island';
|
|
||||||
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper';
|
||||||
import { RecentPagesService } from '@affine/core/modules/cmdk';
|
import { RecentPagesService } from '@affine/core/modules/cmdk';
|
||||||
import type { PageRootService } from '@blocksuite/blocks';
|
import type { PageRootService } from '@blocksuite/blocks';
|
||||||
@@ -27,8 +24,6 @@ import {
|
|||||||
FrameworkScope,
|
FrameworkScope,
|
||||||
globalBlockSuiteSchema,
|
globalBlockSuiteSchema,
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
GlobalStateService,
|
|
||||||
LiveData,
|
|
||||||
useLiveData,
|
useLiveData,
|
||||||
useService,
|
useService,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
@@ -49,7 +44,6 @@ import { useRegisterBlocksuiteEditorCommands } from '../../../hooks/affine/use-r
|
|||||||
import { useActiveBlocksuiteEditor } from '../../../hooks/use-block-suite-editor';
|
import { useActiveBlocksuiteEditor } from '../../../hooks/use-block-suite-editor';
|
||||||
import { usePageDocumentTitle } from '../../../hooks/use-global-state';
|
import { usePageDocumentTitle } from '../../../hooks/use-global-state';
|
||||||
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../../../hooks/use-navigate-helper';
|
||||||
import type { SidebarTabName } from '../../../modules/multi-tab-sidebar';
|
|
||||||
import {
|
import {
|
||||||
MultiTabSidebarBody,
|
MultiTabSidebarBody,
|
||||||
MultiTabSidebarHeaderSwitcher,
|
MultiTabSidebarHeaderSwitcher,
|
||||||
@@ -70,19 +64,8 @@ import * as styles from './detail-page.css';
|
|||||||
import { DetailPageHeader } from './detail-page-header';
|
import { DetailPageHeader } from './detail-page-header';
|
||||||
|
|
||||||
const DetailPageImpl = memo(function DetailPageImpl() {
|
const DetailPageImpl = memo(function DetailPageImpl() {
|
||||||
const globalState = useService(GlobalStateService).globalState;
|
const rightSidebar = useService(RightSidebarService).rightSidebar;
|
||||||
const activeTabName = useLiveData(
|
const activeTabName = useLiveData(rightSidebar.activeTabName$);
|
||||||
LiveData.from(
|
|
||||||
globalState.watch<SidebarTabName>(RIGHT_SIDEBAR_TABS_ACTIVE_KEY),
|
|
||||||
'journal'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
const setActiveTabName = useCallback(
|
|
||||||
(name: string) => {
|
|
||||||
globalState.set(RIGHT_SIDEBAR_TABS_ACTIVE_KEY, name);
|
|
||||||
},
|
|
||||||
[globalState]
|
|
||||||
);
|
|
||||||
|
|
||||||
const doc = useService(DocService).doc;
|
const doc = useService(DocService).doc;
|
||||||
const docRecordList = useService(DocsService).list;
|
const docRecordList = useService(DocsService).list;
|
||||||
@@ -90,7 +73,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
const [editor, setEditor] = useState<AffineEditorContainer | null>(null);
|
||||||
const workspace = useService(WorkspaceService).workspace;
|
const workspace = useService(WorkspaceService).workspace;
|
||||||
const globalContext = useService(GlobalContextService).globalContext;
|
const globalContext = useService(GlobalContextService).globalContext;
|
||||||
const rightSidebar = useService(RightSidebarService).rightSidebar;
|
|
||||||
const docCollection = workspace.docCollection;
|
const docCollection = workspace.docCollection;
|
||||||
const mode = useLiveData(doc.mode$);
|
const mode = useLiveData(doc.mode$);
|
||||||
const { appSettings } = useAppSettingHelper();
|
const { appSettings } = useAppSettingHelper();
|
||||||
@@ -99,6 +81,12 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
|||||||
// TODO(@eyhn): remove jotai here
|
// TODO(@eyhn): remove jotai here
|
||||||
const [_, setActiveBlockSuiteEditor] = useActiveBlocksuiteEditor();
|
const [_, setActiveBlockSuiteEditor] = useActiveBlocksuiteEditor();
|
||||||
|
|
||||||
|
const setActiveTabName = useCallback(
|
||||||
|
(...args: Parameters<typeof rightSidebar.setActiveTabName>) =>
|
||||||
|
rightSidebar.setActiveTabName(...args),
|
||||||
|
[rightSidebar]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isActiveView) {
|
if (isActiveView) {
|
||||||
setActiveBlockSuiteEditor(editor);
|
setActiveBlockSuiteEditor(editor);
|
||||||
|
|||||||
Reference in New Issue
Block a user