feat(core): document search shows up to three results (#11002)

Close [BS-2828](https://linear.app/affine-design/issue/BS-2828).

![截屏2025-03-19 17.49.11.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/9548afcc-5707-435f-93b2-25e42fddc97f.png)
This commit is contained in:
akumatus
2025-03-20 06:55:56 +00:00
parent 8cfdc48b57
commit bd5d930490

View File

@@ -9,6 +9,7 @@ import { scrollbarStyle } from '@blocksuite/affine/shared/styles';
import { openFileOrFiles, type Signal } from '@blocksuite/affine/shared/utils';
import {
CollectionsIcon,
MoreHorizontalIcon,
SearchIcon,
TagsIcon,
UploadIcon,
@@ -16,6 +17,7 @@ import {
import type { DocMeta } from '@blocksuite/store';
import { css, html, type TemplateResult } from 'lit';
import { property, query, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import type { SearchMenuConfig } from '../chat-config';
import type { ChatChip } from '../chat-context';
@@ -30,6 +32,7 @@ export type MenuGroup = {
name: string;
items: MenuItem[] | Signal<MenuItem[]>;
maxDisplay?: number;
overflowText?: string | Signal<string>;
};
export type MenuItem = {
@@ -98,6 +101,9 @@ export class ChatPanelAddPopover extends SignalWatcher(
font-size: var(--affine-font-sm);
color: var(--affine-text-secondary-color);
}
.menu-items icon-button {
outline: none;
}
.item-suffix {
margin-left: auto;
font-size: var(--affine-font-xs);
@@ -121,10 +127,14 @@ export class ChatPanelAddPopover extends SignalWatcher(
this._activatedIndex = 0;
this._query = '';
this._updateSearchGroup();
this._focusSearchInput();
};
private _focusSearchInput() {
requestAnimationFrame(() => {
this.searchInput.focus();
});
};
}
private readonly tcGroup: MenuGroup = {
name: 'Tag & Collection',
@@ -175,17 +185,50 @@ export class ChatPanelAddPopover extends SignalWatcher(
};
private get _menuGroup() {
let groups: MenuGroup[] = [];
switch (this._mode) {
case AddPopoverMode.Tags:
return [this._searchGroup];
groups = [this._searchGroup];
break;
case AddPopoverMode.Collections:
return [this._searchGroup];
groups = [this._searchGroup];
break;
default:
if (this._query) {
return [this._searchGroup, this.uploadGroup];
groups = [this._searchGroup, this.uploadGroup];
} else {
groups = [this._searchGroup, this.tcGroup, this.uploadGroup];
}
return [this._searchGroup, this.tcGroup, this.uploadGroup];
}
// Process maxDisplay for each group
return groups.map(group => {
let items = Array.isArray(group.items) ? group.items : group.items.value;
const maxDisplay = group.maxDisplay ?? items.length;
const hasMore = items.length > maxDisplay;
if (!hasMore) {
return group;
}
return {
...group,
items: [
...items.slice(0, maxDisplay),
{
key: `${group.name} More`,
name:
typeof group.overflowText === 'string'
? group.overflowText
: (group.overflowText?.value ?? 'more'),
icon: MoreHorizontalIcon(),
action: () => {
this._resetMaxDisplay(group);
this._focusSearchInput();
},
},
],
};
});
}
private get _flattenMenuGroup() {
@@ -215,18 +258,16 @@ export class ChatPanelAddPopover extends SignalWatcher(
override connectedCallback() {
super.connectedCallback();
this._updateSearchGroup();
this.addEventListener('keydown', this._handleKeyDown);
document.addEventListener('keydown', this._handleKeyDown);
}
override firstUpdated() {
requestAnimationFrame(() => {
this.searchInput.focus();
});
this._focusSearchInput();
}
override disconnectedCallback() {
super.disconnectedCallback();
this.removeEventListener('keydown', this._handleKeyDown);
document.removeEventListener('keydown', this._handleKeyDown);
}
override render() {
@@ -266,38 +307,47 @@ export class ChatPanelAddPopover extends SignalWatcher(
private _renderMenuGroup(groups: MenuGroup[]) {
let startIndex = 0;
return groups.map((group, idx) => {
const items = Array.isArray(group.items)
? group.items
: group.items.value;
const menuGroup = html`<div class="menu-group">
${this._renderMenuItems(items, startIndex)}
${idx < groups.length - 1 ? this._renderDivider() : ''}
</div>`;
startIndex += items.length;
return menuGroup;
});
return repeat(
groups,
group => group.name,
(group, idx) => {
const items = Array.isArray(group.items)
? group.items
: group.items.value;
const menuGroup = html`<div class="menu-group">
${this._renderMenuItems(items, startIndex)}
${idx < groups.length - 1 ? this._renderDivider() : ''}
</div>`;
startIndex += items.length;
return menuGroup;
}
);
}
private _renderMenuItems(items: MenuItem[], startIndex: number) {
return html`<div>
return html`<div class="menu-items">
${items.length > 0
? items.map(({ key, name, icon, action, suffix }, idx) => {
const curIdx = startIndex + idx;
return html`<icon-button
width="280px"
height="30px"
data-id=${key}
data-index=${curIdx}
.text=${name}
hover=${this._activatedIndex === curIdx}
@click=${() => action()?.catch(console.error)}
@mousemove=${() => (this._activatedIndex = curIdx)}
>
${icon}
${suffix ? html`<div class="item-suffix">${suffix}</div>` : ''}
</icon-button>`;
})
? repeat(
items,
item => item.key,
({ key, name, icon, action, suffix }, idx) => {
const curIdx = startIndex + idx;
return html`<icon-button
width="280px"
height="30px"
data-id=${key}
data-index=${curIdx}
.text=${name}
hover=${this._activatedIndex === curIdx}
@click=${() => action()?.catch(console.error)}
@mousemove=${() => (this._activatedIndex = curIdx)}
>
${icon}
${suffix ? html`<div class="item-suffix">${suffix}</div>` : ''}
</icon-button>`;
}
)
: html`<div class="no-result">No Result</div>`}
</div>`;
}
@@ -308,6 +358,11 @@ export class ChatPanelAddPopover extends SignalWatcher(
this._updateSearchGroup();
}
private _resetMaxDisplay(group: MenuGroup) {
group.maxDisplay = undefined;
this.requestUpdate();
}
private _updateSearchGroup() {
switch (this._mode) {
case AddPopoverMode.Tags: