From 79054bc12170fd2e5b036ebe32163be22f2b4aca Mon Sep 17 00:00:00 2001 From: Maksim Karmatskikh Date: Mon, 27 Nov 2023 10:16:46 +0100 Subject: [PATCH] UBERF-4165: Add search to actions popup (#4057) Signed-off-by: Maxim Karmatskikh --- models/contact/src/index.ts | 4 +- models/recruit/src/index.ts | 4 +- models/tracker/src/index.ts | 2 +- packages/presentation/src/types.ts | 2 +- .../src/components/MentionPopup.svelte | 80 +--- .../src/components/MentionResult.svelte | 2 +- packages/text-editor/src/index.ts | 2 + packages/text-editor/src/search.ts | 100 +++++ plugins/view-assets/lang/en.json | 8 +- plugins/view-assets/lang/ru.json | 7 +- .../src/components/ActionsPopup.svelte | 389 +++++++++++++----- .../src/components/icons/ChevronDown.svelte | 22 + .../src/components/icons/ChevronUp.svelte | 22 + plugins/view-resources/src/plugin.ts | 4 +- tests/sanity/tests/actions.spec.ts | 6 +- 15 files changed, 472 insertions(+), 182 deletions(-) create mode 100644 packages/text-editor/src/search.ts create mode 100644 plugins/view-resources/src/components/icons/ChevronDown.svelte create mode 100644 plugins/view-resources/src/components/icons/ChevronUp.svelte diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index 99d6bf393b..b5b2562fc0 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -741,7 +741,7 @@ export function createModel (builder: Builder): void { label: contact.string.SearchPerson, title: contact.string.People, query: contact.completion.PersonQuery, - context: ['search', 'mention'], + context: ['search', 'mention', 'spotlight'], classToSearch: contact.class.Person }, contact.completion.PersonCategory @@ -755,7 +755,7 @@ export function createModel (builder: Builder): void { label: contact.string.SearchOrganization, title: contact.string.Organizations, query: contact.completion.OrganizationQuery, - context: ['search', 'mention'], + context: ['search', 'mention', 'spotlight'], classToSearch: contact.class.Organization }, contact.completion.OrganizationCategory diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index 511d9f59b7..e1715270cd 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -1200,7 +1200,7 @@ export function createModel (builder: Builder): void { label: recruit.string.SearchApplication, title: recruit.string.Applications, query: recruit.completion.ApplicationQuery, - context: ['search', 'mention'], + context: ['search', 'mention', 'spotlight'], classToSearch: recruit.class.Applicant }, recruit.completion.ApplicationCategory @@ -1214,7 +1214,7 @@ export function createModel (builder: Builder): void { label: recruit.string.SearchVacancy, title: recruit.string.Vacancies, query: recruit.completion.VacancyQuery, - context: ['search', 'mention'], + context: ['search', 'mention', 'spotlight'], classToSearch: recruit.class.Vacancy }, recruit.completion.VacancyCategory diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index 1f7a4846ff..5c9be41e08 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -509,7 +509,7 @@ export function createModel (builder: Builder): void { label: tracker.string.SearchIssue, title: tracker.string.Issues, query: tracker.completion.IssueQuery, - context: ['search', 'mention'], + context: ['search', 'mention', 'spotlight'], classToSearch: tracker.class.Issue }, tracker.completion.IssueCategory diff --git a/packages/presentation/src/types.ts b/packages/presentation/src/types.ts index 783f5b0cfb..605a5bb1c0 100644 --- a/packages/presentation/src/types.ts +++ b/packages/presentation/src/types.ts @@ -56,7 +56,7 @@ export type ObjectSearchFactory = ( * search - show in search popup * mention - show in mentions */ -export type ObjectSearchContext = 'search' | 'mention' +export type ObjectSearchContext = 'search' | 'mention' | 'spotlight' /** * @public diff --git a/packages/text-editor/src/components/MentionPopup.svelte b/packages/text-editor/src/components/MentionPopup.svelte index 15f726a92d..ed492febe9 100644 --- a/packages/text-editor/src/components/MentionPopup.svelte +++ b/packages/text-editor/src/components/MentionPopup.svelte @@ -16,23 +16,16 @@ -
+
{#if icon !== undefined} diff --git a/packages/text-editor/src/index.ts b/packages/text-editor/src/index.ts index 5b88b32f8b..8160185065 100644 --- a/packages/text-editor/src/index.ts +++ b/packages/text-editor/src/index.ts @@ -30,10 +30,12 @@ export { default as TextEditor } from './components/TextEditor.svelte' export { default as TextEditorStyleToolbar } from './components/TextEditorStyleToolbar.svelte' export { default as AttachIcon } from './components/icons/Attach.svelte' export { default as TableOfContents } from './components/toc/TableOfContents.svelte' +export { default as MentionResult } from './components/MentionResult.svelte' export * from './components/node-view' export { default } from './plugin' export * from './types' export * from './utils' +export * from './search' export { FocusExtension, type FocusOptions, type FocusStorage } from './components/extension/focus' export { HeadingsExtension, type HeadingsOptions, type HeadingsStorage } from './components/extension/headings' diff --git a/packages/text-editor/src/search.ts b/packages/text-editor/src/search.ts new file mode 100644 index 0000000000..7a8fec54db --- /dev/null +++ b/packages/text-editor/src/search.ts @@ -0,0 +1,100 @@ +// +// Copyright © 2023 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { Class, Ref, Doc, SearchResultDoc, TxOperations } from '@hcengineering/core' +import { type ObjectSearchCategory } from '@hcengineering/presentation' + +/** + * @public + */ +export interface SearchSection { + category: ObjectSearchCategory + items: SearchResultDoc[] +} + +/** + * @public + */ +export interface SearchItem { + num: number + item: SearchResultDoc + category: ObjectSearchCategory +} + +export function findCategoryByClass ( + categories: ObjectSearchCategory[], + _class: Ref> +): ObjectSearchCategory | undefined { + for (const category of categories) { + if (category.classToSearch === _class) { + return category + } + } + return undefined +} + +export function packSearchResultsForListView (sections: SearchSection[]): SearchItem[] { + let results: SearchItem[] = [] + for (const section of sections) { + const category = section.category + const items = section.items + + if (category.classToSearch !== undefined) { + results = results.concat( + items.map((item, num) => { + return { num, category, item } + }) + ) + } + } + return results +} + +export async function doFulltextSearch ( + client: TxOperations, + classes: Array>>, + query: string, + categories: ObjectSearchCategory[] +): Promise { + const result = await client.searchFulltext( + { + query: `${query}*`, + classes + }, + { + limit: 10 + } + ) + + const itemsByClass = new Map>, SearchResultDoc[]>() + for (const item of result.docs) { + const list = itemsByClass.get(item.doc._class) + if (list === undefined) { + itemsByClass.set(item.doc._class, [item]) + } else { + list.push(item) + } + } + + const sections: SearchSection[] = [] + for (const [_class, items] of itemsByClass.entries()) { + const category = findCategoryByClass(categories, _class) + if (category !== undefined) { + sections.push({ category, items }) + } + } + + return sections +} diff --git a/plugins/view-assets/lang/en.json b/plugins/view-assets/lang/en.json index 6404060274..2fbba75393 100644 --- a/plugins/view-assets/lang/en.json +++ b/plugins/view-assets/lang/en.json @@ -36,11 +36,11 @@ "Assigned": "Assigned", "Created": "Created", "Subscribed": "Subscribed", - + "ShowPreview": "Show document preview", "ShowActions": "Show actions popup", - "ActionPlaceholder": "type to filter...", + "ActionPlaceholder": "Search or run a command...", "General": "General", "Navigation": "Navigation", "Editor": "Editor", @@ -111,6 +111,8 @@ "ChooseIcon": "Choose icon", "IconColor": "Choose icon color", "IconCategory": "Icon", - "EmojiCategory": "Emoji" + "EmojiCategory": "Emoji", + "Type": "Type", + "ToViewCommands": "to view available commands" } } diff --git a/plugins/view-assets/lang/ru.json b/plugins/view-assets/lang/ru.json index c2bbcf70fe..254b44623e 100644 --- a/plugins/view-assets/lang/ru.json +++ b/plugins/view-assets/lang/ru.json @@ -23,7 +23,7 @@ "Open": "Открыть", "Assignees": "Исполнители", "Labels": "Метки", - "ActionPlaceholder": "введите для фильтрации...", + "ActionPlaceholder": "Поиск или команда...", "General": "Общее", "Navigation": "Навигация", "Editor": "Редактор", @@ -107,6 +107,9 @@ "ChooseIcon": "Выбрать иконку", "IconColor": "Выберите цвет", "IconCategory": "Иконка", - "EmojiCategory": "Эмодзи" + "EmojiCategory": "Эмодзи", + + "Type": "Введите", + "ToViewCommands": "чтобы увидеть команды" } } diff --git a/plugins/view-resources/src/components/ActionsPopup.svelte b/plugins/view-resources/src/components/ActionsPopup.svelte index 87235ef3fc..3504f85527 100644 --- a/plugins/view-resources/src/components/ActionsPopup.svelte +++ b/plugins/view-resources/src/components/ActionsPopup.svelte @@ -13,9 +13,14 @@ // limitations under the License. -->
dispatch('changeContent')} > {#if $selectionStore.docs.length > 0 || $focusStore.focus !== undefined || activeAction?.actionPopup !== undefined} -
+
{#if $selectionStore.docs.length > 0}