mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-11 22:18:54 +08:00
feat(core): document search shows up to three results (#11002)
Close [BS-2828](https://linear.app/affine-design/issue/BS-2828). 
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user