mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 05:14:54 +00:00
feat(editor): add at member highlight (#12535)
Closes: BS-2896 <img width="468" alt="image" src="https://github.com/user-attachments/assets/2b84c484-29b8-4650-b74c-da7afd3a1e41" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced member search results with highlighted text, making it easier to visually identify matched parts of member names during searches. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -31,8 +31,11 @@ import {
|
||||
import { computed, Signal } from '@preact/signals-core';
|
||||
import { Service } from '@toeverything/infra';
|
||||
import { cssVarV2 } from '@toeverything/theme/v2';
|
||||
import type { FuseResultMatch } from 'fuse.js';
|
||||
import Fuse from 'fuse.js';
|
||||
import { html } from 'lit';
|
||||
import { styleMap } from 'lit/directives/style-map.js';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
import {
|
||||
createAbsolutePositionFromRelativePosition,
|
||||
createRelativePositionFromTypeIndex,
|
||||
@@ -46,6 +49,7 @@ import { type JournalService, suggestJournalDate } from '../../journal';
|
||||
import { NotificationService } from '../../notification';
|
||||
import type { GuardService, MemberSearchService } from '../../permissions';
|
||||
import type { DocGrantedUsersService } from '../../permissions/services/doc-granted-users';
|
||||
import { highlighter } from '../../quicksearch/utils/highlighter';
|
||||
import type { SearchMenuService } from '../../search-menu/services';
|
||||
|
||||
function resolveSignal<T>(data: T | Signal<T>): T {
|
||||
@@ -327,6 +331,32 @@ export class AtMenuConfigService extends Service {
|
||||
return result;
|
||||
}
|
||||
|
||||
private highlightFuseTitle(
|
||||
matches: readonly FuseResultMatch[] | undefined,
|
||||
title: string,
|
||||
key: string
|
||||
): string {
|
||||
if (!matches) {
|
||||
return title;
|
||||
}
|
||||
const normalizedRange = ([start, end]: [number, number]) =>
|
||||
[
|
||||
start,
|
||||
end + 1 /* in fuse, the `end` is different from the `substring` */,
|
||||
] as [number, number];
|
||||
const titleMatches = matches
|
||||
?.filter(match => match.key === key)
|
||||
.flatMap(match => match.indices.map(normalizedRange));
|
||||
return (
|
||||
highlighter(
|
||||
title,
|
||||
`<span style="color: ${cssVarV2('text/emphasis')}">`,
|
||||
'</span>',
|
||||
titleMatches ?? []
|
||||
) ?? title
|
||||
);
|
||||
}
|
||||
|
||||
private memberGroup(
|
||||
query: string,
|
||||
close: () => void,
|
||||
@@ -350,9 +380,10 @@ export class AtMenuConfigService extends Service {
|
||||
? html`<img style=${avatarStyle} src="${avatar}" />`
|
||||
: UserIcon();
|
||||
|
||||
let displayName = name ?? 'Unknown';
|
||||
return {
|
||||
key: id,
|
||||
name: name ?? 'Unknown',
|
||||
name: html`${unsafeHTML(displayName)}`,
|
||||
icon,
|
||||
action: () => {
|
||||
const root = inlineEditor.rootElement;
|
||||
@@ -567,15 +598,34 @@ export class AtMenuConfigService extends Service {
|
||||
];
|
||||
}
|
||||
|
||||
// Create a single Fuse instance for all members
|
||||
const fuse = new Fuse(members, {
|
||||
keys: ['name'],
|
||||
includeMatches: true,
|
||||
includeScore: true,
|
||||
ignoreLocation: true,
|
||||
threshold: 0.0,
|
||||
});
|
||||
const searchResults = fuse.search(query);
|
||||
|
||||
return [
|
||||
...members.map(member =>
|
||||
getMenuItem(
|
||||
member.id,
|
||||
member.name,
|
||||
member.avatarUrl,
|
||||
member.id !== currentUser?.id
|
||||
)
|
||||
),
|
||||
...searchResults.map(result => {
|
||||
const member = result.item;
|
||||
const displayName = this.highlightFuseTitle(
|
||||
result.matches,
|
||||
member.name ?? 'Unknown',
|
||||
'name'
|
||||
);
|
||||
return {
|
||||
...getMenuItem(
|
||||
member.id,
|
||||
member.name,
|
||||
member.avatarUrl,
|
||||
member.id !== currentUser?.id
|
||||
),
|
||||
name: html`${unsafeHTML(displayName)}`,
|
||||
};
|
||||
}),
|
||||
...(canUserManage ? [inviteItem] : []),
|
||||
];
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user