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