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
@@ -92,7 +92,7 @@ export class AIItemList extends WithDisposable(LitElement) {
.onClick=${this.onClick}
.abortController=${this._abortController}
></ai-sub-item-list>`,
positionStrategy: 'fixed',
positionStrategy: 'absolute',
computePosition: {
referenceElement: aiItemContainer,
placement: 'right-start',
@@ -104,6 +104,11 @@ export class AIItemList extends WithDisposable(LitElement) {
});
};
override disconnectedCallback() {
super.disconnectedCallback();
this._closeSubMenu();
}
override render() {
return html`${repeat(this.groups, group => {
return html`
@@ -114,15 +119,14 @@ export class AIItemList extends WithDisposable(LitElement) {
: nothing}
${repeat(
group.items,
item => item.name,
item =>
html`<ai-item
.onClick=${this.onClick}
.item=${item}
.host=${this.host}
class=${this._itemClassName(item)}
@mouseover=${() => {
this._openSubMenu(item);
}}
@mouseenter=${() => this._openSubMenu(item)}
></ai-item>`
)}
`;
@@ -3,11 +3,12 @@ import {
PropTypes,
requiredProperties,
} 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 { baseTheme } from '@toeverything/theme';
import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
import { property } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { menuItemStyles } from './styles';
import type { AIItemConfig, AISubItemConfig } from './types';
@@ -54,10 +55,18 @@ export class AISubItemList extends WithDisposable(LitElement) {
this.abortController.abort();
};
override connectedCallback() {
super.connectedCallback();
this.disposables.addFromEvent(this, 'pointerdown', stopPropagation);
}
override render() {
if (!this.item.subItem || this.item.subItem.length <= 0) return nothing;
return html`<div class="ai-sub-menu">
${this.item.subItem?.map(
${repeat(
this.item.subItem,
subItem => subItem.type,
subItem =>
html`<div
class="menu-item"
@@ -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', () => {