AddTag action (#9813)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-09-09 16:37:34 +07:00
committed by GitHub
parent 8aca4d07f8
commit cb3ef3401a
23 changed files with 336 additions and 15 deletions
@@ -0,0 +1,49 @@
<!--
// Copyright © 2025 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 { Tag } from '@hcengineering/card'
import { Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { MethodParams, Step } from '@hcengineering/process'
import { Label } from '@hcengineering/ui'
import plugin from '../../plugin'
export let step: Step<Tag>
export let params: MethodParams<Tag>
const client = getClient()
$: method = client.getModel().findAllSync(plugin.class.Method, { _id: step.methodId })[0]
$: tag = params._id as Ref<Tag> | undefined
$: selected = tag && client.getModel().findObject(tag)
</script>
<div class="flex-row-center flex-gap-1">
<Label label={method.label} />:
<div class="title">
{#if selected}
<Label label={selected.label} />
{/if}
</div>
</div>
<style lang="scss">
.title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: var(--theme-caption-color);
}
</style>
@@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import core, { AnyAttribute, Association, Doc, generateId, Ref, RefTo, Relation } from '@hcengineering/core'
import core, { AnyAttribute, Association, generateId, Ref, Relation } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Process, Step } from '@hcengineering/process'
import { Label, tooltip } from '@hcengineering/ui'
@@ -0,0 +1,132 @@
<!--
// Copyright © 2025 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 cardPlugin, { Tag } from '@hcengineering/card'
import { Process, Step } from '@hcengineering/process'
import ParamsEditor from './ParamsEditor.svelte'
import { Ref } from '@hcengineering/core'
import presentation, { getClient } from '@hcengineering/presentation'
import { Button, eventToHTMLElement, Label, SelectPopup, showPopup, tooltip } from '@hcengineering/ui'
import TagSelector from './TagSelector.svelte'
import { createEventDispatcher } from 'svelte'
export let process: Process
export let step: Step<Tag>
const params = step.params
let _id = params._id as Ref<Tag>
let props = params.props
const client = getClient()
const dispatch = createEventDispatcher()
let keys = Object.keys(props || {})
function changeTag (e: CustomEvent<{ tag: Ref<Tag> }>): void {
if (e.detail !== undefined) {
_id = e.detail.tag
keys = []
params._id = _id
props = {}
params.props = props
step.params = params
dispatch('change', step)
}
}
function removeParam (e: CustomEvent<{ key: string }>): void {
if (e.detail !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete props[e.detail.key]
keys = keys.filter((p) => p !== e.detail.key)
params.props = props
step.params = params
dispatch('change', step)
}
}
$: allAttrs = _id !== undefined ? Array.from(client.getHierarchy().getOwnAttributes(_id).values()) : []
$: possibleAttrs = allAttrs.filter((attr) => !keys.includes(attr.name))
function onAdd (e: MouseEvent): void {
showPopup(
SelectPopup,
{
value: possibleAttrs.map((p) => {
return { id: p.name, label: p.label }
})
},
eventToHTMLElement(e),
(res) => {
if (res != null) {
addKey(res)
}
}
)
}
function addKey (key: string): void {
keys = [...keys, key]
}
function change (e: CustomEvent<any>): void {
if (e.detail == null) return
props = e.detail
params.props = props
step.params = params
dispatch('change', step)
}
</script>
<div class="grid">
<span
class="labelOnPanel"
use:tooltip={{
props: { label: cardPlugin.string.Tag }
}}
>
<Label label={cardPlugin.string.Tag} />
</span>
<TagSelector {process} tag={_id} on:change={changeTag} />
</div>
<ParamsEditor
{process}
_class={_id}
allowRemove
bind:params={props}
{keys}
on:change={change}
on:remove={removeParam}
/>
{#if possibleAttrs.length > 0}
<div class="flex-center">
<Button label={presentation.string.Add} width={'100%'} kind={'link-bordered'} size={'large'} on:click={onAdd} />
</div>
{/if}
<style lang="scss">
.grid {
display: grid;
grid-template-columns: 1fr 1.5fr;
grid-auto-rows: minmax(2rem, max-content);
justify-content: start;
width: calc(100% - 4rem);
align-items: center;
margin: 0.25rem 2rem 0.5rems;
row-gap: 0.5rem;
column-gap: 1rem;
height: min-content;
}
</style>
@@ -0,0 +1,78 @@
<!--
// Copyright © 2025 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 cardPlugin, { Tag } from '@hcengineering/card'
import { Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Process } from '@hcengineering/process'
import { Button, eventToHTMLElement, Label, SelectPopup, SelectPopupValueType, showPopup } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
export let process: Process
export let tag: Ref<Tag> | undefined = undefined
const client = getClient()
const hierarchy = client.getHierarchy()
const dispatch = createEventDispatcher()
function open (e: MouseEvent): void {
const res: Tag[] = []
const ancestors = hierarchy.getAncestors(process.masterTag)
const tags = client.getModel().findAllSync(cardPlugin.class.Tag, {})
for (const p of tags) {
try {
const base = hierarchy.getBaseClass(p._id)
if (process.masterTag === base || ancestors.includes(base)) {
res.push(p)
}
} catch (err) {
console.log('error', err, p._id)
}
}
const items: SelectPopupValueType[] = []
res.forEach((cl) => {
if (cl._class !== cardPlugin.class.Tag) return
items.push({
id: cl._id,
label: cl.label,
isSelected: tag !== undefined && tag === cl._id
})
})
showPopup(
SelectPopup,
{
value: items
},
eventToHTMLElement(e),
(res) => {
if (res !== undefined) {
tag = res
dispatch('change', {
tag
})
}
}
)
}
$: selected = tag !== undefined ? client.getModel().findObject(tag) : undefined
</script>
<Button on:click={open} width={'100%'}>
<div slot="content">
<Label label={selected?.label ?? cardPlugin.string.Tag} />
</div>
</Button>