EmptyValue process function (#10756)

* EmptyValue process function

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>

* Fix formatting

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>

---------

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2026-04-13 11:50:09 +07:00
committed by GitHub
parent d80008d9d3
commit 844feae395
24 changed files with 74 additions and 34 deletions
+12
View File
@@ -461,6 +461,18 @@ export function defineFunctions (builder: Builder): void {
process.function.RoleContext
)
builder.createDoc(
process.class.ProcessFunction,
core.space.Model,
{
of: core.class.TypeAny,
category: 'attribute',
label: process.string.EmptyValue,
type: 'context'
},
process.function.EmptyValue
)
builder.createDoc(
process.class.ProcessFunction,
core.space.Model,
+4
View File
@@ -343,6 +343,10 @@ export function createModel (builder: Builder): void {
func: serverProcess.transform.RemoveLast
})
builder.mixin(process.function.EmptyValue, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
func: serverProcess.transform.EmptyValue
})
builder.mixin(process.function.EmptyArray, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, {
func: serverProcess.transform.EmptyArray
})
@@ -13,9 +13,10 @@
// limitations under the License.
-->
<script lang="ts">
import type { Class, Doc, DocumentQuery, FindOptions, Ref, WithLookup } from '@hcengineering/core'
import { type Card } from '@hcengineering/card'
import type { Class, Doc, DocumentQuery, FindOptions, Ref, WithLookup } from '@hcengineering/core'
import { ActionContext, createQuery, getClient } from '@hcengineering/presentation'
import { Scroller } from '@hcengineering/ui'
import { BuildModelKey, ViewOptions } from '@hcengineering/view'
import { ListSelectionProvider, SelectDirection, buildConfigLookup, focusStore } from '@hcengineering/view-resources'
import CardGridItem from './CardGridItem.svelte'
@@ -71,20 +72,22 @@
<ActionContext context={{ mode: 'browser' }} />
<div class="grid-container">
{#each objects as object, i}
{@const selected = selection === i}
<div class="grid-cell">
<CardGridItem
{object}
{selected}
on:obj-focus={(evt) => {
listProvider.updateFocus(evt.detail)
}}
/>
</div>
{/each}
</div>
<Scroller fade={{ multipler: { top: 3, bottom: 2.5 } }} padding={'0 1rem'} checkForHeaders>
<div class="grid-container">
{#each objects as object, i}
{@const selected = selection === i}
<div class="grid-cell">
<CardGridItem
{object}
{selected}
on:obj-focus={(evt) => {
listProvider.updateFocus(evt.detail)
}}
/>
</div>
{/each}
</div>
</Scroller>
<style lang="scss">
.grid-container {
@@ -31,7 +31,7 @@
<span class="flex-presenter">
<span class="mr-1"><Icon {icon} size="small" /></span>
<Label label={communication.string.Unset} />
<span class="lower"><Label label={model.label} /></span>
<span class="lower ml-1"><Label label={model.label} /></span>
</span>
{:else}
<ActivityAttributeValue {model} {icon} values={value}>
+2 -1
View File
@@ -163,7 +163,8 @@
"YearFromDate": "Rok z data",
"MonthFromDate": "Měsíc z data",
"DayFromDate": "Den z data",
"DateDifference": "Rozdíl dat"
"DateDifference": "Rozdíl dat",
"EmptyValue": "Prázdná hodnota"
},
"error": {
"MethodNotFound": "Metoda nenalezena: {methodId}",
+2 -1
View File
@@ -163,7 +163,8 @@
"YearFromDate": "Jahr aus Datum",
"MonthFromDate": "Monat aus Datum",
"DayFromDate": "Tag aus Datum",
"DateDifference": "Datumsdifferenz"
"DateDifference": "Datumsdifferenz",
"EmptyValue": "Leerer Wert"
},
"error": {
"MethodNotFound": "Methode nicht gefunden: {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "Year from date",
"MonthFromDate": "Month from date",
"DayFromDate": "Day from date",
"DateDifference": "Date difference"
"DateDifference": "Date difference",
"EmptyValue": "Empty value"
},
"error": {
"MethodNotFound": "Method not found: {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "Año desde fecha",
"MonthFromDate": "Mes desde fecha",
"DayFromDate": "Día desde fecha",
"DateDifference": "Diferencia de fechas"
"DateDifference": "Diferencia de fechas",
"EmptyValue": "Valor vacío"
},
"error": {
"MethodNotFound": "Método no encontrado: {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "Année depuis date",
"MonthFromDate": "Mois depuis date",
"DayFromDate": "Jour depuis date",
"DateDifference": "Différence de dates"
"DateDifference": "Différence de dates",
"EmptyValue": "Valeur vide"
},
"error": {
"MethodNotFound": "Méthode introuvable : {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "Anno da data",
"MonthFromDate": "Mese da data",
"DayFromDate": "Giorno da data",
"DateDifference": "Differenza di date"
"DateDifference": "Differenza di date",
"EmptyValue": "Valore vuoto"
},
"error": {
"MethodNotFound": "Metodo non trovato: {methodId}",
+2 -1
View File
@@ -167,7 +167,8 @@
"YearFromDate": "日付から年",
"MonthFromDate": "日付から月",
"DayFromDate": "日付から日",
"DateDifference": "日付の差"
"DateDifference": "日付の差",
"EmptyValue": "空の値"
},
"error": {
"MethodNotFound": "メソッドが見つかりません: {methodId}",
+2 -1
View File
@@ -156,7 +156,8 @@
"YearFromDate": "Ano de data",
"MonthFromDate": "Mês de data",
"DayFromDate": "Dia de data",
"DateDifference": "Diferença de datas"
"DateDifference": "Diferença de datas",
"EmptyValue": "Valor vazio"
},
"error": {
"MethodNotFound": "Método não encontrado: {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "Ano de data",
"MonthFromDate": "Mês de data",
"DayFromDate": "Dia de data",
"DateDifference": "Diferença de datas"
"DateDifference": "Diferença de datas",
"EmptyValue": "Valor vazio"
},
"error": {
"MethodNotFound": "Método não encontrado: {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "Год из даты",
"MonthFromDate": "Месяц из даты",
"DayFromDate": "День из даты",
"DateDifference": "Разница дат"
"DateDifference": "Разница дат",
"EmptyValue": "Пустое значение"
},
"error": {
"MethodNotFound": "Метод не найден: {methodId}",
+2 -1
View File
@@ -163,7 +163,8 @@
"YearFromDate": "Tarihten yıl",
"MonthFromDate": "Tarihten ay",
"DayFromDate": "Tarihten gün",
"DateDifference": "Tarih farkı"
"DateDifference": "Tarih farkı",
"EmptyValue": "Boş değer"
},
"error": {
"MethodNotFound": "Metod bulunamadı: {methodId}",
+2 -1
View File
@@ -168,7 +168,8 @@
"YearFromDate": "日期到年份",
"MonthFromDate": "日期到月份",
"DayFromDate": "日期到天",
"DateDifference": "日期差异"
"DateDifference": "日期差异",
"EmptyValue": "空值"
},
"error": {
"MethodNotFound": "找不到方法:{methodId}",
+1
View File
@@ -233,6 +233,7 @@ export default mergeIds(processId, process, {
For: '' as IntlString,
Attribute: '' as IntlString,
Context: '' as IntlString,
EmptyValue: '' as IntlString,
EmptyArray: '' as IntlString,
ExecutionInitiator: '' as IntlString,
ExecutionStarted: '' as IntlString,
+1 -1
View File
@@ -331,7 +331,7 @@ function getContextFunctions (
break
}
default: {
if (hierarchy.isDerived(func.of, target)) {
if (hierarchy.isDerived(func.of, target) || func.of === core.class.TypeAny) {
matched.push(func._id)
}
}
+1
View File
@@ -373,6 +373,7 @@ export default plugin(processId, {
ExecutionStarted: '' as Ref<ProcessFunction>,
ExecutionEmployeeInitiator: '' as Ref<ProcessFunction>,
ExecutionInitiator: '' as Ref<ProcessFunction>,
EmptyValue: '' as Ref<ProcessFunction>,
EmptyArray: '' as Ref<ProcessFunction>,
CurrentDate: '' as Ref<ProcessFunction>,
StringFromNumber: '' as Ref<ProcessFunction>,
@@ -17,14 +17,14 @@
import { getEmbeddedLabel } from '@hcengineering/platform'
import { LabelAndProps, LinkWrapper, tooltip } from '@hcengineering/ui'
export let value: string | string[] | undefined
export let value: string | string[] | null | undefined
export let accent: boolean = false
export let oneLine: boolean = false
$: tooltipParams = getTooltip(value)
function getTooltip (value: string | string[] | undefined): LabelAndProps | undefined {
if (value === undefined) return
function getTooltip (value: string | string[] | null | undefined): LabelAndProps | undefined {
if (value == null) return
let str = ''
if (Array.isArray(value)) {
str = value.reduce((acc, curr, i) => (acc += i === 0 ? curr : ` ${curr}`), '')
@@ -94,6 +94,7 @@ import {
DayFromDate,
Divide,
EmptyArray,
EmptyValue,
ExecutionInitiator,
ExecutionStarted,
Filter,
@@ -680,6 +681,7 @@ export default async () => ({
RemoveFirst,
RemoveLast,
EmptyArray,
EmptyValue,
ExecutionInitiator,
ExecutionStarted,
FirstMatchValue,
@@ -463,6 +463,10 @@ export async function CurrentDate (): Promise<Timestamp> {
return Date.now()
}
export function EmptyValue (): null {
return null
}
export function EmptyArray (): any[] {
return []
}
@@ -247,12 +247,12 @@ async function getFunctionValue (
const funcImpl = control.client.getHierarchy().as(transform, serverProcess.mixin.FuncImpl)
const f = await getResource(funcImpl.func)
const val = await f(res, {}, control, execution)
if (val == null) {
if (val == null && context.func !== process.function.EmptyValue) {
throw processError(process.error.EmptyFunctionResult, {}, { func: func.label })
}
return val
}
if (res == null) {
if (res == null && context.func !== process.function.EmptyValue) {
throw processError(process.error.EmptyFunctionResult, {}, { func: func.label })
}
return res
+1
View File
@@ -109,6 +109,7 @@ export default plugin(serverProcessId, {
RemoveLast: '' as Resource<TransformFunc>,
CurrentUser: '' as Resource<TransformFunc>,
CurrentDate: '' as Resource<TransformFunc>,
EmptyValue: '' as Resource<TransformFunc>,
EmptyArray: '' as Resource<TransformFunc>,
Filter: '' as Resource<TransformFunc>,
FirstMatchValue: '' as Resource<TransformFunc>,