From 18bd545c25de5970708d38b85c55a17fbb7b64ce Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Thu, 28 Apr 2022 15:11:09 +0700 Subject: [PATCH] Skills search update (#1573) Signed-off-by: Andrey Sobolev --- models/board/src/index.ts | 3 +- models/calendar/src/index.ts | 4 +- models/tags/src/index.ts | 4 +- models/tracker/src/index.ts | 7 +- packages/model/src/dsl.ts | 2 +- packages/ui/src/components/Button.svelte | 5 +- .../src/components/Candidates.svelte | 2 +- .../src/components/SkillsView.svelte | 26 ++- plugins/tags-assets/lang/en.json | 5 +- plugins/tags-assets/lang/ru.json | 5 +- .../src/components/CategoryBar.svelte | 72 ++++++-- .../TagElementCountPresenter.svelte | 31 ++++ .../src/components/TagsCategoryPopup.svelte | 162 ++++++++++++++++++ .../src/components/TagsEditor.svelte | 11 +- .../src/components/TagsPopup.svelte | 6 +- .../src/components/TagsView.svelte | 34 ++++ plugins/tags-resources/src/index.ts | 4 +- plugins/tags-resources/src/plugin.ts | 8 +- 18 files changed, 348 insertions(+), 43 deletions(-) create mode 100644 plugins/tags-resources/src/components/TagElementCountPresenter.svelte create mode 100644 plugins/tags-resources/src/components/TagsCategoryPopup.svelte diff --git a/models/board/src/index.ts b/models/board/src/index.ts index 1b6b1a5ffb..7e88a4ea78 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -18,6 +18,7 @@ import type { Board, Card, CardAction, CardDate, CardLabel } from '@anticrm/boar import type { Employee } from '@anticrm/contact' import { TxOperations as Client, Doc, DOMAIN_MODEL, FindOptions, IndexKind, Ref, Type, Timestamp } from '@anticrm/core' import { + ArrOf, Builder, Collection, Index, @@ -103,7 +104,7 @@ export class TCard extends TTask implements Card { @Prop(TypeRef(contact.class.Employee), board.string.Assignee) declare assignee: Ref | null - @Prop(Collection(contact.class.Employee), board.string.Members) + @Prop(ArrOf(TypeRef(contact.class.Employee)), board.string.Members) members?: Ref[] } diff --git a/models/calendar/src/index.ts b/models/calendar/src/index.ts index adce68a03d..c7219daede 100644 --- a/models/calendar/src/index.ts +++ b/models/calendar/src/index.ts @@ -18,7 +18,7 @@ import { Calendar, Event, Reminder } from '@anticrm/calendar' import { Employee } from '@anticrm/contact' import type { Domain, Markup, Ref, Timestamp } from '@anticrm/core' import { IndexKind } from '@anticrm/core' -import { Builder, Collection, Index, Mixin, Model, Prop, TypeDate, TypeMarkup, TypeString, UX } from '@anticrm/model' +import { ArrOf, Builder, Collection, Index, Mixin, Model, Prop, TypeDate, TypeMarkup, TypeRef, TypeString, UX } from '@anticrm/model' import attachment from '@anticrm/model-attachment' import chunter from '@anticrm/model-chunter' import contact from '@anticrm/model-contact' @@ -64,7 +64,7 @@ export class TEvent extends TAttachedDoc implements Event { @Prop(Collection(chunter.class.Comment), chunter.string.Comments) comments?: number - @Prop(Collection(contact.class.Employee), calendar.string.Participants) + @Prop(ArrOf(TypeRef(contact.class.Employee)), calendar.string.Participants) participants!: Ref[] } diff --git a/models/tags/src/index.ts b/models/tags/src/index.ts index f4acc0a3d9..e1a5d6ae1f 100644 --- a/models/tags/src/index.ts +++ b/models/tags/src/index.ts @@ -13,7 +13,7 @@ // limitations under the License. import { Class, Doc, Domain, IndexKind, Ref } from '@anticrm/core' -import { Builder, Collection, Index, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model' +import { ArrOf, Builder, Index, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model' import core, { TAttachedDoc, TDoc } from '@anticrm/model-core' import view from '@anticrm/model-view' import { Asset, IntlString } from '@anticrm/platform' @@ -74,7 +74,7 @@ export class TTagCategory extends TDoc implements TagCategory { @Prop(TypeString(), tags.string.CategoryTargetClass) targetClass!: Ref> - @Prop(Collection(core.class.TypeString), tags.string.CategoryTagsLabel) + @Prop(ArrOf(TypeRef(core.class.TypeString)), tags.string.CategoryTagsLabel) tags!: string[] @Prop(TypeString(), tags.string.DefaultLabel) diff --git a/models/tracker/src/index.ts b/models/tracker/src/index.ts index a26a21e31d..756ec2f2b3 100644 --- a/models/tracker/src/index.ts +++ b/models/tracker/src/index.ts @@ -17,6 +17,7 @@ import type { Employee } from '@anticrm/contact' import contact from '@anticrm/contact' import { Domain, DOMAIN_MODEL, IndexKind, Markup, Ref, Timestamp } from '@anticrm/core' import { + ArrOf, Builder, Collection, Hidden, @@ -126,10 +127,10 @@ export class TIssue extends TDoc implements Issue { @Prop(TypeRef(tracker.class.Issue), tracker.string.Parent) parentIssue!: Ref - @Prop(Collection(tracker.class.Issue), tracker.string.BlockedBy) + @Prop(ArrOf(TypeRef(tracker.class.Issue)), tracker.string.BlockedBy) blockedBy!: Ref[] - @Prop(Collection(tracker.class.Issue), tracker.string.RelatedTo) + @Prop(ArrOf(TypeRef(tracker.class.Issue)), tracker.string.RelatedTo) relatedIssue!: Ref[] @Prop(Collection(chunter.class.Comment), tracker.string.Comments) @@ -193,7 +194,7 @@ export class TProject extends TDoc implements Project { @Prop(TypeRef(contact.class.Employee), tracker.string.ProjectLead) lead!: Ref | null - @Prop(Collection(contact.class.Employee), tracker.string.Members) + @Prop(ArrOf(TypeRef(contact.class.Employee)), tracker.string.Members) members!: Ref[] @Prop(Collection(chunter.class.Comment), chunter.string.Comments) diff --git a/packages/model/src/dsl.ts b/packages/model/src/dsl.ts index c7db4eddd8..59661490f4 100644 --- a/packages/model/src/dsl.ts +++ b/packages/model/src/dsl.ts @@ -392,7 +392,7 @@ export function Collection (clazz: Ref>, itemLab /** * @public */ -export function ArrOf (type: Type): TypeArrOf { +export function ArrOf> (type: Type): TypeArrOf { return { _class: core.class.ArrOf, of: type, label: 'Array' as IntlString } } diff --git a/packages/ui/src/components/Button.svelte b/packages/ui/src/components/Button.svelte index 45bd9c425d..320ed0c2f0 100644 --- a/packages/ui/src/components/Button.svelte +++ b/packages/ui/src/components/Button.svelte @@ -24,7 +24,7 @@ export let labelParams: Record = {} export let kind: ButtonKind = 'secondary' export let size: ButtonSize = 'medium' - export let shape: 'circle' | undefined = undefined + export let shape: 'circle' | 'round' | undefined = undefined export let icon: Asset | AnySvelteComponent | undefined = undefined export let justify: 'left' | 'center' = 'center' export let disabled: boolean = false @@ -56,7 +56,8 @@ bind:this={input} class="button {kind} {size} jf-{justify}" class:only-icon={iconOnly} - class:border-radius-1={shape !== 'circle'} + class:border-radius-1={shape !== 'circle' && shape !== 'round'} + class:border-radius-2={shape === 'round'} class:border-radius-4={shape === 'circle'} class:highlight disabled={disabled || loading} diff --git a/plugins/recruit-resources/src/components/Candidates.svelte b/plugins/recruit-resources/src/components/Candidates.svelte index 20a3926e0f..b4df0a0208 100644 --- a/plugins/recruit-resources/src/components/Candidates.svelte +++ b/plugins/recruit-resources/src/components/Candidates.svelte @@ -84,7 +84,7 @@ updateCategory(evt.detail)} /> diff --git a/plugins/recruit-resources/src/components/SkillsView.svelte b/plugins/recruit-resources/src/components/SkillsView.svelte index 3190f49cbf..947c5e0404 100644 --- a/plugins/recruit-resources/src/components/SkillsView.svelte +++ b/plugins/recruit-resources/src/components/SkillsView.svelte @@ -1,7 +1,25 @@ - - + diff --git a/plugins/tags-assets/lang/en.json b/plugins/tags-assets/lang/en.json index a3f8c0fbfe..9e93e39e1c 100644 --- a/plugins/tags-assets/lang/en.json +++ b/plugins/tags-assets/lang/en.json @@ -30,6 +30,9 @@ "CategoryTagsLabel": "Category Items", "OtherCategoryLabel": "Other", "AllCategories": "All Categories", - "DefaultLabel": "Default category" + "DefaultLabel": "Default category", + "SelectAll": "Select all", + "SelectNone": "Select none", + "ApplyTags": "Apply" } } \ No newline at end of file diff --git a/plugins/tags-assets/lang/ru.json b/plugins/tags-assets/lang/ru.json index 09f46c350e..de7fe0b29d 100644 --- a/plugins/tags-assets/lang/ru.json +++ b/plugins/tags-assets/lang/ru.json @@ -29,6 +29,9 @@ "CategoryTargetClass": "Категория", "CategoryTagsLabel": "Теги категории", "OtherCategoryLabel": "Другое", - "AllCategories": "Все категории" + "AllCategories": "Все категории", + "SelectAll": "Выбрать все", + "SelectNone": "Выбрать ничего", + "ApplyTags": "Применить" } } \ No newline at end of file diff --git a/plugins/tags-resources/src/components/CategoryBar.svelte b/plugins/tags-resources/src/components/CategoryBar.svelte index f3e14af60e..04b406358d 100644 --- a/plugins/tags-resources/src/components/CategoryBar.svelte +++ b/plugins/tags-resources/src/components/CategoryBar.svelte @@ -16,27 +16,30 @@ import { Class, Doc, Ref, SortingOrder } from '@anticrm/core' import { createQuery } from '@anticrm/presentation' import { TagCategory, TagElement } from '@anticrm/tags' - import { getPlatformColorForText, Button } from '@anticrm/ui' + import { Button, getPlatformColorForText, showPopup } from '@anticrm/ui' import { createEventDispatcher } from 'svelte' import tags from '../plugin' import { getTagStyle } from '../utils' + import TagsCategoryPopup from './TagsCategoryPopup.svelte' export let targetClass: Ref> export let category: Ref | undefined = undefined + export let selected: Ref[] = [] export let gap: 'small' | 'big' = 'small' - + export let mode: 'item' | 'category' = 'category' + let categories: TagCategory[] = [] let visibleCategories: TagCategory[] = [] - + const stepStyle = gap === 'small' ? 'gap-1' : 'gap-2' - + const dispatch = createEventDispatcher() - + let elements: TagElement[] = [] - + let categoryCounts = new Map, TagElement[]>() let categoryKeys: Ref[] = [] - + const elementsQuery = createQuery() $: elementsQuery.query( tags.class.TagElement, @@ -55,6 +58,9 @@ const counts = new Map, TagElement[]>() for (const e of elements) { if (e.category !== undefined) { + if (selected.includes(e._id) && category === undefined) { + category = e.category + } const els = counts.get(e.category) ?? [] els.push(e) counts.set(e.category, els) @@ -82,7 +88,38 @@ } else { category = item._id } - dispatch('change', { category, elements: category !== undefined ? categoryCounts.get(category) ?? [] : [] }) + selected = (category !== undefined ? categoryCounts.get(category) ?? [] : []).map(it => it._id) + dispatch('change', { category, elements: selected }) + } + + function selectTag (evt: Event, category: TagCategory): void { + showPopup( + TagsCategoryPopup, + { + category, + targetClass, + selected, + keyLabel: category.label, + hideAdd: true + }, + evt.target as HTMLElement, + (result) => { + if (result !== undefined) { + selected = result.map((it: TagElement) => it._id) + dispatch('change', { category: category._id, elements: result }) + } + } + ) + } + const visibleCategoriesRef: HTMLElement[] = [] + + $: visibleCategoriesRef.length = visibleCategories.length + + $: if (category !== undefined && visibleCategories.length > 0 && visibleCategoriesRef.length > 0) { + const idx = visibleCategories.findIndex(it => it._id === category) + if (idx !== -1) { + visibleCategoriesRef[idx]?.scrollIntoView({ block: 'nearest' }) + } } @@ -101,16 +138,25 @@
- {#each visibleCategories as item (item._id)} -
{ - if (item._id !== category) selectItem(item) + on:click={(evt) => { + if (mode === 'category') { + selectItem(item) + } else { + selectTag(evt, item) + } }} > - {item.label} ({categoryCounts.get(item._id)?.length ?? ''}) + {item.label} + {#if item._id === category && mode === 'item'} + ({selected.length}/{categoryCounts.get(item._id)?.length ?? ''}) + {:else} + ({categoryCounts.get(item._id)?.length ?? ''}) + {/if}
{/each}
diff --git a/plugins/tags-resources/src/components/TagElementCountPresenter.svelte b/plugins/tags-resources/src/components/TagElementCountPresenter.svelte new file mode 100644 index 0000000000..cdefd29b7d --- /dev/null +++ b/plugins/tags-resources/src/components/TagElementCountPresenter.svelte @@ -0,0 +1,31 @@ + + + +{#if (tagElements?.get(value._id)?.count ?? 0) > 0} +
onTag?.(value)}> + +  {tagElements?.get(value._id)?.count ?? 0} +
+{/if} diff --git a/plugins/tags-resources/src/components/TagsCategoryPopup.svelte b/plugins/tags-resources/src/components/TagsCategoryPopup.svelte new file mode 100644 index 0000000000..a736d299c9 --- /dev/null +++ b/plugins/tags-resources/src/components/TagsCategoryPopup.svelte @@ -0,0 +1,162 @@ + + + +
+
+
+
+ +
+
+
{ + search = '' + searchElement.focus() + }}> + {#if search !== ''}
{/if} +
+
+
+
+
+
+ {#if elements.length > 0} +
+ +
+ {/if} + {#if elements.length === 0} +
+
+ {/if} +
+
+
+
+
+ + diff --git a/plugins/tags-resources/src/components/TagsEditor.svelte b/plugins/tags-resources/src/components/TagsEditor.svelte index e9ea0d069f..6ba29103ec 100644 --- a/plugins/tags-resources/src/components/TagsEditor.svelte +++ b/plugins/tags-resources/src/components/TagsEditor.svelte @@ -14,15 +14,14 @@ -->
@@ -97,6 +123,14 @@ } ] : []), + { + key: '', + presenter: tags.component.TagElementCountPresenter, + label: item, + props: { tagElements, label: item, onTag }, + sortingKey: '@tagCount', + sortingFunction: countSorting + }, 'description', 'modifiedOn' ]} diff --git a/plugins/tags-resources/src/index.ts b/plugins/tags-resources/src/index.ts index a823855f8b..6a081699d2 100644 --- a/plugins/tags-resources/src/index.ts +++ b/plugins/tags-resources/src/index.ts @@ -25,6 +25,7 @@ import TagsEditor from './components/TagsEditor.svelte' import TagsItemPresenter from './components/TagsItemPresenter.svelte' import TagsPresenter from './components/TagsPresenter.svelte' import TagsView from './components/TagsView.svelte' +import TagElementCountPresenter from './components/TagElementCountPresenter.svelte' export default async (): Promise => ({ component: { @@ -37,7 +38,8 @@ export default async (): Promise => ({ TagsDropdownEditor, TagsItemPresenter, CategoryPresenter, - TagsCategoryBar + TagsCategoryBar, + TagElementCountPresenter }, actionImpl: { Open: (value: TagElement, evt: MouseEvent) => { diff --git a/plugins/tags-resources/src/plugin.ts b/plugins/tags-resources/src/plugin.ts index 086b447d01..ae3bf2d143 100644 --- a/plugins/tags-resources/src/plugin.ts +++ b/plugins/tags-resources/src/plugin.ts @@ -18,7 +18,8 @@ import { AnyComponent } from '@anticrm/ui' export default mergeIds(tagsId, tags, { component: { TagElementPresenter: '' as AnyComponent, - CategoryPresenter: '' as AnyComponent + CategoryPresenter: '' as AnyComponent, + TagElementCountPresenter: '' as AnyComponent }, string: { NoTags: '' as IntlString, @@ -40,6 +41,9 @@ export default mergeIds(tagsId, tags, { TagName: '' as IntlString, SaveLabel: '' as IntlString, CategoryLabel: '' as IntlString, - AllCategories: '' as IntlString + AllCategories: '' as IntlString, + SelectAll: '' as IntlString, + SelectNone: '' as IntlString, + ApplyTags: '' as IntlString } })