fix(editor): repeat trigger keys of at-menu was added (#11631)
Close [BS-2716](https://linear.app/affine-design/issue/BS-2716/移动端通过toolpanel唤起的at-menu,出现两个)
This commit is contained in:
@@ -1,8 +1,5 @@
|
|||||||
import { EmbedLinkedDocBlockSchema } from '@blocksuite/affine-model';
|
import { EmbedLinkedDocBlockSchema } from '@blocksuite/affine-model';
|
||||||
import {
|
import { insertContent } from '@blocksuite/affine-rich-text';
|
||||||
getInlineEditorByModel,
|
|
||||||
insertContent,
|
|
||||||
} from '@blocksuite/affine-rich-text';
|
|
||||||
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
||||||
import { createDefaultDoc } from '@blocksuite/affine-shared/utils';
|
import { createDefaultDoc } from '@blocksuite/affine-shared/utils';
|
||||||
import {
|
import {
|
||||||
@@ -68,22 +65,7 @@ const linkedDocSlashMenuConfig: SlashMenuConfig = {
|
|||||||
if (!linkedDocWidget) return;
|
if (!linkedDocWidget) return;
|
||||||
// TODO(@L-Sun): make linked-doc-widget as extension
|
// TODO(@L-Sun): make linked-doc-widget as extension
|
||||||
// @ts-expect-error same as above
|
// @ts-expect-error same as above
|
||||||
const triggerKey = linkedDocWidget.config.triggerKeys[0];
|
|
||||||
|
|
||||||
insertContent(std, model, triggerKey);
|
|
||||||
|
|
||||||
const inlineEditor = getInlineEditorByModel(std, model);
|
|
||||||
if (inlineEditor) {
|
|
||||||
// Wait for range to be updated
|
|
||||||
const subscription = inlineEditor.slots.inlineRangeSync.subscribe(
|
|
||||||
() => {
|
|
||||||
// TODO(@L-Sun): make linked-doc-widget as extension
|
|
||||||
subscription.unsubscribe();
|
|
||||||
// @ts-expect-error same as above
|
|
||||||
linkedDocWidget.show({ addTriggerKey: true });
|
linkedDocWidget.show({ addTriggerKey: true });
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ import {
|
|||||||
toggleUnderline,
|
toggleUnderline,
|
||||||
} from '@blocksuite/affine-inline-preset';
|
} from '@blocksuite/affine-inline-preset';
|
||||||
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
import type { FrameBlockModel } from '@blocksuite/affine-model';
|
||||||
import {
|
import { insertContent } from '@blocksuite/affine-rich-text';
|
||||||
getInlineEditorByModel,
|
|
||||||
insertContent,
|
|
||||||
} from '@blocksuite/affine-rich-text';
|
|
||||||
import {
|
import {
|
||||||
copySelectedModelsCommand,
|
copySelectedModelsCommand,
|
||||||
deleteSelectedModelsCommand,
|
deleteSelectedModelsCommand,
|
||||||
@@ -348,35 +345,11 @@ const pageToolGroup: KeyboardToolPanelGroup = {
|
|||||||
);
|
);
|
||||||
if (!linkedDocWidget) return;
|
if (!linkedDocWidget) return;
|
||||||
assertType<AffineLinkedDocWidget>(linkedDocWidget);
|
assertType<AffineLinkedDocWidget>(linkedDocWidget);
|
||||||
|
|
||||||
const triggerKey = linkedDocWidget.config.triggerKeys[0];
|
|
||||||
|
|
||||||
std.command
|
|
||||||
.chain()
|
|
||||||
.pipe(getSelectedModelsCommand)
|
|
||||||
.pipe(ctx => {
|
|
||||||
const { selectedModels } = ctx;
|
|
||||||
if (!selectedModels?.length) return;
|
|
||||||
|
|
||||||
const currentModel = selectedModels[0];
|
|
||||||
insertContent(std, currentModel, triggerKey);
|
|
||||||
|
|
||||||
const inlineEditor = getInlineEditorByModel(std, currentModel);
|
|
||||||
// Wait for range to be updated
|
|
||||||
if (inlineEditor) {
|
|
||||||
const subscription = inlineEditor.slots.inlineRangeSync.subscribe(
|
|
||||||
() => {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
linkedDocWidget.show({
|
linkedDocWidget.show({
|
||||||
mode: 'mobile',
|
mode: 'mobile',
|
||||||
addTriggerKey: true,
|
addTriggerKey: true,
|
||||||
});
|
});
|
||||||
closeToolPanel();
|
closeToolPanel();
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ export type LinkedDocContext = {
|
|||||||
std: BlockStdScope;
|
std: BlockStdScope;
|
||||||
inlineEditor: AffineInlineEditor;
|
inlineEditor: AffineInlineEditor;
|
||||||
startRange: InlineRange;
|
startRange: InlineRange;
|
||||||
|
startNativeRange: Range;
|
||||||
triggerKey: string;
|
triggerKey: string;
|
||||||
config: LinkedWidgetConfig;
|
config: LinkedWidgetConfig;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
import { unsafeCSSVar } from '@blocksuite/affine-shared/theme';
|
import { unsafeCSSVar } from '@blocksuite/affine-shared/theme';
|
||||||
import {
|
import {
|
||||||
createKeydownObserver,
|
createKeydownObserver,
|
||||||
getCurrentNativeRange,
|
|
||||||
getPopperPosition,
|
getPopperPosition,
|
||||||
getViewportElement,
|
getViewportElement,
|
||||||
} from '@blocksuite/affine-shared/utils';
|
} from '@blocksuite/affine-shared/utils';
|
||||||
@@ -160,11 +159,15 @@ export class LinkedDocPopover extends SignalWatcher(
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
this._updateLinkedDocGroup().catch(console.error);
|
this._updateLinkedDocGroup().catch(console.error);
|
||||||
this._disposables.addFromEvent(this, 'mousedown', e => {
|
this._disposables.addFromEvent(this, 'pointerdown', e => {
|
||||||
// Prevent input from losing focus
|
// Prevent input from losing focus
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
this._disposables.addFromEvent(window, 'mousedown', e => {
|
this._disposables.addFromEvent(this, 'mousedown', e => {
|
||||||
|
// Prevent input from losing focus in electron
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
this._disposables.addFromEvent(window, 'pointerdown', e => {
|
||||||
if (e.target === this) return;
|
if (e.target === this) return;
|
||||||
// We don't clear the query when clicking outside the popover
|
// We don't clear the query when clicking outside the popover
|
||||||
this.context.close();
|
this.context.close();
|
||||||
@@ -338,11 +341,8 @@ export class LinkedDocPopover extends SignalWatcher(
|
|||||||
|
|
||||||
override willUpdate() {
|
override willUpdate() {
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated) {
|
||||||
const curRange = getCurrentNativeRange();
|
|
||||||
if (!curRange) return;
|
|
||||||
|
|
||||||
const updatePosition = throttle(() => {
|
const updatePosition = throttle(() => {
|
||||||
this._position = getPopperPosition(this, curRange);
|
this._position = getPopperPosition(this, this.context.startNativeRange);
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
this.disposables.addFromEvent(window, 'resize', updatePosition);
|
this.disposables.addFromEvent(window, 'resize', updatePosition);
|
||||||
|
|||||||
@@ -43,6 +43,19 @@ export class AffineLinkedDocWidget extends WidgetComponent<RootBlockModel> {
|
|||||||
|
|
||||||
private readonly _mode$ = signal<'desktop' | 'mobile' | 'none'>('none');
|
private readonly _mode$ = signal<'desktop' | 'mobile' | 'none'>('none');
|
||||||
|
|
||||||
|
private _addTriggerKey(inlineEditor: InlineEditor, triggerKey: string) {
|
||||||
|
const inlineRange = inlineEditor.getInlineRange();
|
||||||
|
if (!inlineRange) return;
|
||||||
|
inlineEditor.insertText(
|
||||||
|
{ index: inlineRange.index, length: 0 },
|
||||||
|
triggerKey
|
||||||
|
);
|
||||||
|
inlineEditor.setInlineRange({
|
||||||
|
index: inlineRange.index + triggerKey.length,
|
||||||
|
length: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private _updateInputRects() {
|
private _updateInputRects() {
|
||||||
if (!this._context) return;
|
if (!this._context) return;
|
||||||
const { inlineEditor, startRange, triggerKey } = this._context;
|
const { inlineEditor, startRange, triggerKey } = this._context;
|
||||||
@@ -258,27 +271,30 @@ export class AffineLinkedDocWidget extends WidgetComponent<RootBlockModel> {
|
|||||||
inlineEditor = props.inlineEditor;
|
inlineEditor = props.inlineEditor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const inlineRange = inlineEditor.getInlineRange();
|
|
||||||
if (!inlineRange) return;
|
|
||||||
|
|
||||||
if (addTriggerKey) {
|
if (addTriggerKey) {
|
||||||
inlineEditor.insertText(
|
this._addTriggerKey(inlineEditor, primaryTriggerKey);
|
||||||
{ index: inlineRange.index, length: 0 },
|
// we need to wait the range sync to get the correct startNativeRange
|
||||||
primaryTriggerKey
|
const subscription = inlineEditor.slots.inlineRangeSync.subscribe(() => {
|
||||||
);
|
this.show({ ...props, addTriggerKey: false });
|
||||||
inlineEditor.setInlineRange({
|
subscription.unsubscribe();
|
||||||
index: inlineRange.index + primaryTriggerKey.length,
|
|
||||||
length: 0,
|
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startRange = inlineEditor.getInlineRange();
|
||||||
|
if (!startRange) return;
|
||||||
|
|
||||||
|
const startNativeRange = inlineEditor.getNativeRange();
|
||||||
|
if (!startNativeRange) return;
|
||||||
|
|
||||||
const disposable = inlineEditor.slots.renderComplete.subscribe(() => {
|
const disposable = inlineEditor.slots.renderComplete.subscribe(() => {
|
||||||
this._updateInputRects();
|
this._updateInputRects();
|
||||||
});
|
});
|
||||||
this._context = {
|
this._context = {
|
||||||
std: this.std,
|
std: this.std,
|
||||||
inlineEditor,
|
inlineEditor,
|
||||||
startRange: inlineRange,
|
startRange,
|
||||||
|
startNativeRange,
|
||||||
triggerKey: primaryTriggerKey,
|
triggerKey: primaryTriggerKey,
|
||||||
config: this.config,
|
config: this.config,
|
||||||
close: () => {
|
close: () => {
|
||||||
|
|||||||
@@ -841,3 +841,21 @@ test('delete block by slash menu should remove children', async ({
|
|||||||
await redoByKeyboard(page);
|
await redoByKeyboard(page);
|
||||||
await assertRichTexts(page, ['123']);
|
await assertRichTexts(page, ['123']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should slash menu can trigger linked doc popover', async ({ page }) => {
|
||||||
|
await enterPlaygroundRoom(page);
|
||||||
|
await initEmptyParagraphState(page);
|
||||||
|
await focusRichText(page);
|
||||||
|
|
||||||
|
await type(page, '/linked');
|
||||||
|
await pressEnter(page);
|
||||||
|
await expect(page.locator('.linked-doc-popover')).toBeVisible();
|
||||||
|
await assertRichTexts(page, ['@']);
|
||||||
|
|
||||||
|
await type(page, 'doc');
|
||||||
|
await pressEnter(page);
|
||||||
|
await expect(page.locator('affine-reference')).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
page.locator('affine-reference .affine-reference-title')
|
||||||
|
).toHaveText('doc');
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user