mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-10 11:47:42 +02:00
Add FirstMatchValue and Filter functions (#10042)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -136,10 +136,31 @@
|
||||
}
|
||||
|
||||
function onFunctionSelect (e: Ref<ProcessFunction>): void {
|
||||
// if editor is undefined
|
||||
const func = client.getModel().findAllSync(plugin.class.ProcessFunction, { _id: e })[0]
|
||||
onFunction(e, {}, addFunction)
|
||||
}
|
||||
|
||||
function addFunction (func: Func): void {
|
||||
const arr = contextValue.functions ?? []
|
||||
arr.push(func)
|
||||
contextValue.functions = arr
|
||||
onChange(contextValue)
|
||||
}
|
||||
|
||||
function onSourceFunctionSelect (e: Ref<ProcessFunction>): void {
|
||||
onFunction(e, contextValue.sourceFunction?.props ?? {}, (res) => (contextValue.sourceFunction = res))
|
||||
}
|
||||
|
||||
function onSourceFunctionChange (e: Func): void {
|
||||
contextValue.sourceFunction = e
|
||||
onChange(contextValue)
|
||||
}
|
||||
|
||||
function onFunction (_func: Ref<ProcessFunction>, props: Record<string, any>, cb: (res: Func) => void) {
|
||||
const func = client.getModel().findAllSync(plugin.class.ProcessFunction, { _id: _func })[0]
|
||||
if (func.editor === undefined) {
|
||||
addFunction(e, {})
|
||||
const res: Func = { func: _func, props: {} }
|
||||
cb(res)
|
||||
onChange(contextValue)
|
||||
closeTooltip()
|
||||
} else {
|
||||
showPopup(
|
||||
@@ -149,46 +170,49 @@
|
||||
process,
|
||||
masterTag: process.masterTag,
|
||||
context,
|
||||
attribute
|
||||
attribute,
|
||||
props
|
||||
},
|
||||
elements[functionButtonIndex],
|
||||
elements[0],
|
||||
(res) => {
|
||||
if (res != null) {
|
||||
addFunction(e, res)
|
||||
const result: Func = { func: _func, props: res }
|
||||
cb(result)
|
||||
onChange(contextValue)
|
||||
closeTooltip()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function addFunction (func: Ref<ProcessFunction>, props: Record<string, any>): void {
|
||||
const arr = contextValue.functions ?? []
|
||||
arr.push({
|
||||
func,
|
||||
props
|
||||
})
|
||||
contextValue.functions = arr
|
||||
onChange(contextValue)
|
||||
}
|
||||
|
||||
function onSourceFunctionSelect (e: Ref<ProcessFunction>): void {
|
||||
contextValue.sourceFunction = { func: e, props: {} }
|
||||
onChange(contextValue)
|
||||
}
|
||||
|
||||
function onFunctionChange (e: Ref<ProcessFunction>, i: number): void {
|
||||
if (contextValue.functions === undefined) return
|
||||
contextValue.functions[i].func = e
|
||||
contextValue.functions = contextValue.functions
|
||||
onChange(contextValue)
|
||||
onFunction(e, contextValue.functions?.[i]?.props ?? {}, (res) => {
|
||||
if (contextValue.functions === undefined) {
|
||||
contextValue.functions = []
|
||||
}
|
||||
contextValue.functions[i] = res
|
||||
contextValue.functions = contextValue.functions
|
||||
})
|
||||
}
|
||||
|
||||
function getFunctionChange (i: number): (e: Ref<ProcessFunction>) => void {
|
||||
function getFunctionSelect (i: number): (e: Ref<ProcessFunction>) => void {
|
||||
return (e: Ref<ProcessFunction>) => {
|
||||
onFunctionChange(e, i)
|
||||
}
|
||||
}
|
||||
|
||||
function getFunctionChange (i: number): (e: Func) => void {
|
||||
return (e: Func) => {
|
||||
if (contextValue.functions === undefined) {
|
||||
contextValue.functions = []
|
||||
}
|
||||
contextValue.functions[i] = e
|
||||
contextValue.functions = contextValue.functions
|
||||
onChange(contextValue)
|
||||
}
|
||||
}
|
||||
|
||||
function onFunctionRemove (pos: number): void {
|
||||
const arr = contextValue.functions ?? []
|
||||
if (pos !== -1) {
|
||||
@@ -256,9 +280,15 @@
|
||||
}}
|
||||
label={sourceFunc.label}
|
||||
props={{
|
||||
attribute,
|
||||
process,
|
||||
context,
|
||||
func: contextValue.sourceFunction,
|
||||
availableFunctions: reduceFuncs,
|
||||
onSelect: onSourceFunctionSelect
|
||||
onSelect: onSourceFunctionSelect,
|
||||
onChange: onSourceFunctionChange
|
||||
}}
|
||||
component={plugin.component.FunctionSubmenu}
|
||||
options={{ component: plugin.component.FunctionSelector }}
|
||||
withHover
|
||||
/>
|
||||
@@ -279,9 +309,15 @@
|
||||
}}
|
||||
label={getFunction(f.func)?.label}
|
||||
props={{
|
||||
func: f,
|
||||
attribute,
|
||||
process,
|
||||
context,
|
||||
availableFunctions: reduceFuncs,
|
||||
onSelect: getFunctionChange(i)
|
||||
onSelect: getFunctionSelect(i),
|
||||
onChange: getFunctionChange(i)
|
||||
}}
|
||||
component={plugin.component.FunctionSubmenu}
|
||||
options={{ component: plugin.component.FunctionSelector }}
|
||||
withHover
|
||||
/>
|
||||
|
||||
@@ -14,13 +14,14 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
||||
import { Process } from '@hcengineering/process'
|
||||
import { Component, Label, tooltip } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getCirteriaEditor, getContext } from '../../utils'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let readonly: boolean
|
||||
export let process: Process
|
||||
export let key: string
|
||||
@@ -30,7 +31,7 @@
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
$: attribute = hierarchy.getAttribute(process.masterTag, key)
|
||||
$: attribute = hierarchy.getAttribute(_class, key)
|
||||
$: presenterClass = getAttributePresenterClass(hierarchy, attribute.type)
|
||||
|
||||
$: updateCriteria = getCirteriaEditor(presenterClass.attrClass, presenterClass.category)
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
$: presenterClass = getAttributePresenterClass(hierarchy, attribute.type)
|
||||
|
||||
$: baseEditor = findAttributeEditor(client, process.masterTag, attribute.name)
|
||||
$: baseEditor = findAttributeEditor(client, attribute.attributeOf, attribute.name)
|
||||
</script>
|
||||
|
||||
<div class="text-input" class:context={contextValue}>
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { Process } from '@hcengineering/process'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import AttributeCriteria from './AttributeCriteria.svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let readonly: boolean
|
||||
export let process: Process
|
||||
export let keys: string[]
|
||||
@@ -41,6 +42,7 @@
|
||||
<div class="editor-grid">
|
||||
{#each keys as key}
|
||||
<AttributeCriteria
|
||||
{_class}
|
||||
{process}
|
||||
{key}
|
||||
{params}
|
||||
|
||||
@@ -93,7 +93,15 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<CriteriasEditor {keys} {readonly} {process} {params} on:remove={remove} on:change={change} />
|
||||
<CriteriasEditor
|
||||
_class={process.masterTag}
|
||||
{keys}
|
||||
{readonly}
|
||||
{process}
|
||||
{params}
|
||||
on:remove={remove}
|
||||
on:change={change}
|
||||
/>
|
||||
{#if possibleAttrs.length > 0}
|
||||
<div class="flex-center mt-4">
|
||||
<Button label={presentation.string.Add} width={'100%'} kind={'link-bordered'} size={'large'} on:click={onAdd} />
|
||||
|
||||
@@ -90,7 +90,15 @@
|
||||
</script>
|
||||
|
||||
{#if key !== undefined}
|
||||
<CriteriasEditor keys={[key]} {readonly} {process} {params} on:remove={remove} on:change={change} />
|
||||
<CriteriasEditor
|
||||
keys={[key]}
|
||||
_class={process.masterTag}
|
||||
{readonly}
|
||||
{process}
|
||||
{params}
|
||||
on:remove={remove}
|
||||
on:change={change}
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex-center mt-4">
|
||||
<Button label={view.string.Select} width={'100%'} kind={'link-bordered'} size={'large'} on:click={onSelect} />
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<!--
|
||||
// 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 { AnyAttribute, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Context, Func, Process, ProcessFunction } from '@hcengineering/process'
|
||||
import { ButtonIcon, eventToHTMLElement, IconSettings, Label, showPopup } from '@hcengineering/ui'
|
||||
import plugin from '../../plugin'
|
||||
|
||||
export let attribute: AnyAttribute
|
||||
export let process: Process
|
||||
export let context: Context
|
||||
export let func: Func
|
||||
export let onChange: (func: Func) => void
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function getFunction (_id: Ref<ProcessFunction>): ProcessFunction {
|
||||
return client.getModel().findAllSync(plugin.class.ProcessFunction, { _id })[0]
|
||||
}
|
||||
|
||||
function onConfigure (e: MouseEvent): void {
|
||||
const f = getFunction(func.func)
|
||||
if (f.editor === undefined) return
|
||||
showPopup(
|
||||
f.editor,
|
||||
{
|
||||
func: f,
|
||||
masterTag: process.masterTag,
|
||||
process,
|
||||
context,
|
||||
attribute,
|
||||
props: func.props
|
||||
},
|
||||
eventToHTMLElement(e),
|
||||
(res) => {
|
||||
if (res != null) {
|
||||
func.props = res
|
||||
onChange(func)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center flex-gap-2 pr-1 flex-shrink">
|
||||
<span class="overflow-label">
|
||||
<Label label={getFunction(func.func).label} />
|
||||
</span>
|
||||
{#if getFunction(func.func).editor}
|
||||
<ButtonIcon icon={IconSettings} size="small" kind="tertiary" on:click={onConfigure} />
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,112 @@
|
||||
<!--
|
||||
// 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 core, { AnyAttribute } from '@hcengineering/core'
|
||||
import presentation, { Card, getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
||||
import { Process, ProcessFunction } from '@hcengineering/process'
|
||||
import { Button, SelectPopup, eventToHTMLElement, showPopup } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getCirteriaEditor } from '../../utils'
|
||||
import CriteriasEditor from '../criterias/CriteriasEditor.svelte'
|
||||
|
||||
export let func: ProcessFunction
|
||||
export let process: Process
|
||||
export let attribute: AnyAttribute
|
||||
export let props: Record<string, any> = {}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
const presenterClass = getAttributePresenterClass(hierarchy, attribute.type)
|
||||
|
||||
function save (): void {
|
||||
dispatch('close', {
|
||||
...props,
|
||||
_class: presenterClass.attrClass
|
||||
})
|
||||
}
|
||||
|
||||
function getKeys (): AnyAttribute[] {
|
||||
const attributes = hierarchy.getAllAttributes(presenterClass.attrClass, core.class.Doc)
|
||||
const res: AnyAttribute[] = []
|
||||
for (const [key, attr] of attributes) {
|
||||
if (key === '_id' || key === '_class') continue
|
||||
if (attr.hidden === true) continue
|
||||
const presenterClass = getAttributePresenterClass(hierarchy, attr.type)
|
||||
const updateCriteria = getCirteriaEditor(presenterClass.attrClass, presenterClass.category)
|
||||
const editor = updateCriteria?.editor
|
||||
if (editor == null) continue
|
||||
res.push(attr)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
let keys = Object.keys(props).filter((p) => p !== '_class')
|
||||
|
||||
const allAttrs = getKeys()
|
||||
$: 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 remove (e: CustomEvent<any>): void {
|
||||
if (e.detail !== undefined) {
|
||||
const key = e.detail.key
|
||||
keys = keys.filter((k) => k !== key)
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete (props as any)[key]
|
||||
}
|
||||
}
|
||||
|
||||
function change (e: CustomEvent<any>): void {
|
||||
if (e.detail == null) return
|
||||
props = e.detail
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card on:close width={'x-small'} label={func.label} canSave okAction={save} okLabel={presentation.string.Save}>
|
||||
<CriteriasEditor
|
||||
_class={presenterClass.attrClass}
|
||||
readonly={false}
|
||||
{keys}
|
||||
{process}
|
||||
params={props}
|
||||
on:remove={remove}
|
||||
on:change={change}
|
||||
/>
|
||||
{#if possibleAttrs.length > 0}
|
||||
<div class="flex-center mt-4">
|
||||
<Button label={presentation.string.Add} width={'100%'} kind={'link-bordered'} size={'large'} on:click={onAdd} />
|
||||
</div>
|
||||
{/if}
|
||||
</Card>
|
||||
Reference in New Issue
Block a user