fix(core): should invoke cleanup fn when ai sub item list is removed (#10685)

Closes: [BS-2755](https://linear.app/affine-design/issue/BS-2755/ai-translate-死循环报错)
This commit is contained in:
fundon
2025-03-07 03:35:59 +00:00
parent 916134b1da
commit 8da12025af
3 changed files with 57 additions and 6 deletions

View File

@@ -92,7 +92,7 @@ export class AIItemList extends WithDisposable(LitElement) {
.onClick=${this.onClick} .onClick=${this.onClick}
.abortController=${this._abortController} .abortController=${this._abortController}
></ai-sub-item-list>`, ></ai-sub-item-list>`,
positionStrategy: 'fixed', positionStrategy: 'absolute',
computePosition: { computePosition: {
referenceElement: aiItemContainer, referenceElement: aiItemContainer,
placement: 'right-start', placement: 'right-start',
@@ -104,6 +104,11 @@ export class AIItemList extends WithDisposable(LitElement) {
}); });
}; };
override disconnectedCallback() {
super.disconnectedCallback();
this._closeSubMenu();
}
override render() { override render() {
return html`${repeat(this.groups, group => { return html`${repeat(this.groups, group => {
return html` return html`
@@ -114,15 +119,14 @@ export class AIItemList extends WithDisposable(LitElement) {
: nothing} : nothing}
${repeat( ${repeat(
group.items, group.items,
item => item.name,
item => item =>
html`<ai-item html`<ai-item
.onClick=${this.onClick} .onClick=${this.onClick}
.item=${item} .item=${item}
.host=${this.host} .host=${this.host}
class=${this._itemClassName(item)} class=${this._itemClassName(item)}
@mouseover=${() => { @mouseenter=${() => this._openSubMenu(item)}
this._openSubMenu(item);
}}
></ai-item>` ></ai-item>`
)} )}
`; `;

View File

@@ -3,11 +3,12 @@ import {
PropTypes, PropTypes,
requiredProperties, requiredProperties,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import { EnterIcon } from '@blocksuite/affine/blocks'; import { EnterIcon, stopPropagation } from '@blocksuite/affine/blocks';
import { WithDisposable } from '@blocksuite/affine/global/lit'; import { WithDisposable } from '@blocksuite/affine/global/lit';
import { baseTheme } from '@toeverything/theme'; import { baseTheme } from '@toeverything/theme';
import { css, html, LitElement, nothing, unsafeCSS } from 'lit'; import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js'; import { property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { menuItemStyles } from './styles'; import { menuItemStyles } from './styles';
import type { AIItemConfig, AISubItemConfig } from './types'; import type { AIItemConfig, AISubItemConfig } from './types';
@@ -54,10 +55,18 @@ export class AISubItemList extends WithDisposable(LitElement) {
this.abortController.abort(); this.abortController.abort();
}; };
override connectedCallback() {
super.connectedCallback();
this.disposables.addFromEvent(this, 'pointerdown', stopPropagation);
}
override render() { override render() {
if (!this.item.subItem || this.item.subItem.length <= 0) return nothing; if (!this.item.subItem || this.item.subItem.length <= 0) return nothing;
return html`<div class="ai-sub-menu"> return html`<div class="ai-sub-menu">
${this.item.subItem?.map( ${repeat(
this.item.subItem,
subItem => subItem.type,
subItem => subItem =>
html`<div html`<div
class="menu-item" class="menu-item"

View File

@@ -783,6 +783,44 @@ test.describe('chat with block', () => {
} }
}); });
} }
test.describe('Translate to', () => {
test('should close all ai item list when clicking outside', async ({
page,
}) => {
const item = await page.waitForSelector('.ai-item-translate-to');
await item.hover();
const subItemList = await page.waitForSelector('ai-sub-item-list');
const bounds = await subItemList.boundingBox();
expect(bounds).toBeTruthy();
await page.mouse.click(bounds!.x + bounds!.width / 2, bounds!.y - 20);
await expect(page.locator('ai-sub-item-list')).toBeHidden();
await expect(page.locator('ai-item-list')).toBeHidden();
});
test('should close all ai item list when clicking sub item', async ({
page,
}) => {
const item = await page.waitForSelector(
'ask-ai-panel .ai-item-translate-to'
);
await item.hover();
const subItemList = await page.waitForSelector('ai-sub-item-list');
const bounds = await subItemList.boundingBox();
expect(bounds).toBeTruthy();
(await subItemList.waitForSelector('.menu-item:nth-child(1)')).click();
await expect(page.locator('ai-sub-item-list')).toBeHidden();
await expect(page.locator('ask-ai-panel ai-item-list')).toBeHidden();
});
});
}); });
test.describe('chat with image block', () => { test.describe('chat with image block', () => {