Tags action (#2997)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-04-17 12:41:08 +06:00 committed by GitHub
parent e4a53a1b37
commit 26b2a634fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 148 additions and 6 deletions

View File

@ -27,8 +27,7 @@ export default mergeIds(tagsId, tags, {
Tags: '' as AnyComponent, Tags: '' as AnyComponent,
TagReferencePresenter: '' as AnyComponent, TagReferencePresenter: '' as AnyComponent,
TagsItemPresenter: '' as AnyComponent, TagsItemPresenter: '' as AnyComponent,
TagsFilter: '' as AnyComponent, TagsFilter: '' as AnyComponent
TagsEditorPopup: '' as AnyComponent
}, },
string: { string: {
TagElementLabel: '' as IntlString, TagElementLabel: '' as IntlString,

View File

@ -1509,6 +1509,58 @@ export function createModel (builder: Builder): void {
tracker.action.SetSprint tracker.action.SetSprint
) )
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: tags.component.TagsEditorPopup,
element: 'top',
fillProps: {
_object: 'object'
}
},
label: tracker.string.Labels,
icon: tags.icon.Tags,
keyBinding: ['keyL'],
input: 'focus',
category: tracker.category.Tracker,
target: tracker.class.Issue,
context: {
mode: ['context', 'browser'],
application: tracker.app.Tracker,
group: 'edit'
}
},
tracker.action.SetLabels
)
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: tags.component.ObjectsTagsEditorPopup,
element: 'top',
fillProps: {
_objects: 'objects'
}
},
label: tracker.string.Labels,
icon: tags.icon.Tags,
keyBinding: ['keyL'],
input: 'selection',
category: tracker.category.Tracker,
target: tracker.class.Issue,
context: {
mode: ['context', 'browser'],
application: tracker.app.Tracker,
group: 'edit'
}
},
tracker.action.SetLabels
)
createAction( createAction(
builder, builder,
{ {

View File

@ -0,0 +1,72 @@
<!--
// 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.
-->
<script lang="ts">
import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import tags, { TagElement, TagReference } from '@hcengineering/tags'
import TagsPopup from './TagsPopup.svelte'
export let objects: Doc[]
let selected: Ref<TagElement>[] = []
let tagRefs: TagReference[] = []
const query = createQuery()
$: query.query(tags.class.TagReference, { attachedTo: { $in: objects.map((p) => p._id) } }, (result) => {
tagRefs = result
const res: Record<Ref<TagElement>, TagReference> = {}
for (const value of result) {
const arr = (res as any)[value.tag] ?? []
arr.push(value)
;(res as any)[value.tag] = arr
}
const sel: Ref<TagElement>[] = []
for (const value in res) {
if ((res as any)[value].length === objects.length) {
sel.push(value as Ref<TagElement>)
}
}
selected = sel
})
const client = getClient()
async function addRef ({ title, color, _id: tag }: TagElement): Promise<void> {
await Promise.all(
objects.map(async (object) => {
if (tagRefs.findIndex((p) => p.attachedTo === object._id) !== -1) return
await client.addCollection(tags.class.TagReference, object.space, object._id, object._class, 'labels', {
title,
color,
tag
})
})
)
}
async function removeTag (tag: TagElement): Promise<void> {
await Promise.all(
objects.map(async (object) => {
const tagRef = await client.findOne(tags.class.TagReference, { attachedTo: object._id, tag: tag._id })
if (tagRef) await client.remove(tagRef)
})
)
}
async function onUpdate (event: CustomEvent<{ action: string; tag: TagElement }>) {
const result = event.detail
if (result === undefined) return
if (result.action === 'add') addRef(result.tag)
else if (result.action === 'remove') removeTag(result.tag)
}
</script>
<TagsPopup targetClass={objects[0]._class} {selected} on:update={onUpdate} />

View File

@ -1,3 +1,17 @@
<!--
// 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.
-->
<script lang="ts"> <script lang="ts">
import { Doc, Ref } from '@hcengineering/core' import { Doc, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation' import { createQuery, getClient } from '@hcengineering/presentation'
@ -20,7 +34,7 @@
}) })
} }
async function removeTag (tag: TagElement): Promise<void> { async function removeTag (tag: TagElement): Promise<void> {
const tagRef = await client.findOne(tags.class.TagReference, { tag: tag._id }) const tagRef = await client.findOne(tags.class.TagReference, { tag: tag._id, attachedTo: object._id })
if (tagRef) await client.remove(tagRef) if (tagRef) await client.remove(tagRef)
} }
async function onUpdate (event: CustomEvent<{ action: string; tag: TagElement }>) { async function onUpdate (event: CustomEvent<{ action: string; tag: TagElement }>) {

View File

@ -31,6 +31,7 @@ import TagsAttributeEditor from './components/TagsAttributeEditor.svelte'
import TagsEditorPopup from './components/TagsEditorPopup.svelte' import TagsEditorPopup from './components/TagsEditorPopup.svelte'
import LabelsPresenter from './components/LabelsPresenter.svelte' import LabelsPresenter from './components/LabelsPresenter.svelte'
import CreateTagElement from './components/CreateTagElement.svelte' import CreateTagElement from './components/CreateTagElement.svelte'
import ObjectsTagsEditorPopup from './components/ObjectsTagsEditorPopup.svelte'
import { ObjQueryType } from '@hcengineering/core' import { ObjQueryType } from '@hcengineering/core'
import { getRefs } from './utils' import { getRefs } from './utils'
import { Filter } from '@hcengineering/view' import { Filter } from '@hcengineering/view'
@ -67,7 +68,8 @@ export default async (): Promise<Resources> => ({
TagElementCountPresenter, TagElementCountPresenter,
TagsAttributeEditor, TagsAttributeEditor,
TagsEditorPopup, TagsEditorPopup,
LabelsPresenter LabelsPresenter,
ObjectsTagsEditorPopup
}, },
actionImpl: { actionImpl: {
Open: (value: TagElement, evt: MouseEvent) => { Open: (value: TagElement, evt: MouseEvent) => {

View File

@ -107,7 +107,9 @@ const tagsPlugin = plugin(tagsId, {
TagsAttributeEditor: '' as AnyComponent, TagsAttributeEditor: '' as AnyComponent,
TagsPresenter: '' as AnyComponent, TagsPresenter: '' as AnyComponent,
LabelsPresenter: '' as AnyComponent, LabelsPresenter: '' as AnyComponent,
TagElementPresenter: '' as AnyComponent TagElementPresenter: '' as AnyComponent,
TagsEditorPopup: '' as AnyComponent,
ObjectsTagsEditorPopup: '' as AnyComponent
}, },
category: { category: {
NoCategory: '' as Ref<TagCategory> NoCategory: '' as Ref<TagCategory>

View File

@ -504,7 +504,8 @@ export default plugin(trackerId, {
NewSubIssue: '' as Ref<Action>, NewSubIssue: '' as Ref<Action>,
EditWorkflowStatuses: '' as Ref<Action>, EditWorkflowStatuses: '' as Ref<Action>,
EditProject: '' as Ref<Action>, EditProject: '' as Ref<Action>,
SetSprint: '' as Ref<Action> SetSprint: '' as Ref<Action>,
SetLabels: '' as Ref<Action>
}, },
project: { project: {
DefaultProject: '' as Ref<Project> DefaultProject: '' as Ref<Project>