ValueIsSet criteria mode (#9815)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-09-09 22:52:16 +07:00
committed by GitHub
parent cb3ef3401a
commit eb9150541b
20 changed files with 96 additions and 72 deletions
@@ -28,7 +28,7 @@
showPopup
} from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { buildResult, Mode, Modes, parseValue } from '../../query'
import { buildResult, Mode, parseValue } from '../../query'
import ContextSelectorPopup from '../attributeEditors/ContextSelectorPopup.svelte'
import ContextValue from '../attributeEditors/ContextValue.svelte'
@@ -78,7 +78,7 @@
$: presenterClass = getAttributePresenterClass(hierarchy, attribute.type)
function changeResult () {
if (val === undefined || val === '') {
if (!selectedMode.withoutEditor && (val === undefined || val === '')) {
dispatch('change', null)
return
}
@@ -103,60 +103,61 @@
changeResult()
}}
/>
<div class="text-input" class:context={contextValue}>
{#if contextValue}
<ContextValue
{process}
masterTag={process.masterTag}
{contextValue}
{context}
{attribute}
category={presenterClass.category}
attrClass={presenterClass.attrClass}
on:update={(e) => {
onSelect(e.detail)
}}
/>
{:else}
<div class="w-full">
{#if baseEditor}
<Component
is={baseEditor}
props={{
label: attribute?.label,
placeholder: attribute?.label,
kind: 'ghost',
size: 'large',
width: '100%',
justify: 'left',
readonly,
type: attribute?.type,
value: val,
onChange,
focus
}}
/>
{/if}
{#if !selectedMode.withoutEditor}
<div class="text-input" class:context={contextValue}>
{#if contextValue}
<ContextValue
{process}
masterTag={process.masterTag}
{contextValue}
{context}
{attribute}
category={presenterClass.category}
attrClass={presenterClass.attrClass}
on:update={(e) => {
onSelect(e.detail)
}}
/>
{:else}
<div class="w-full">
{#if baseEditor}
<Component
is={baseEditor}
props={{
label: attribute?.label,
placeholder: attribute?.label,
kind: 'ghost',
size: 'large',
width: '100%',
justify: 'left',
readonly,
type: attribute?.type,
value: val,
onChange,
focus
}}
/>
{/if}
</div>
{/if}
<div class="button flex-row-center">
<Button
icon={IconAdd}
kind="ghost"
on:click={(e) => {
selectContext(e)
}}
/>
<Button
icon={IconClose}
kind="ghost"
on:click={() => {
dispatch('delete')
}}
/>
</div>
{/if}
<div class="button flex-row-center">
<Button
icon={IconAdd}
kind="ghost"
on:click={(e) => {
selectContext(e)
}}
/>
<Button
icon={IconClose}
kind="ghost"
on:click={() => {
dispatch('delete')
}}
/>
</div>
</div>
{/if}
</div>
<style lang="scss">
@@ -27,7 +27,7 @@
export let attribute: AnyAttribute
export let baseEditor: AnyComponent | undefined
const modes: Mode[] = [Modes.Equal, Modes.NotEqual]
const modes: Mode[] = [Modes.Equal, Modes.NotEqual, Modes.Exists]
</script>
<BaseCriteria {readonly} {value} {process} {context} {attribute} {baseEditor} {modes} on:change on:delete />
@@ -27,7 +27,7 @@
export let attribute: AnyAttribute
export let baseEditor: AnyComponent | undefined
const modes: Mode[] = [Modes.Equal, Modes.GT, Modes.LT]
const modes: Mode[] = [Modes.Equal, Modes.GT, Modes.LT, Modes.Exists]
</script>
<BaseCriteria {readonly} {value} {process} {context} {attribute} {baseEditor} {modes} on:change on:delete />
@@ -27,7 +27,7 @@
export let attribute: AnyAttribute
export let baseEditor: AnyComponent | undefined
const modes: Mode[] = [Modes.Equal, Modes.NotEqual]
const modes: Mode[] = [Modes.Equal, Modes.NotEqual, Modes.Exists]
</script>
<BaseCriteria {readonly} {value} {process} {context} {attribute} {baseEditor} {modes} on:change on:delete />
@@ -27,7 +27,7 @@
export let attribute: AnyAttribute
export let baseEditor: AnyComponent | undefined
const modes: Mode[] = [Modes.Equal, Modes.GT, Modes.LT]
const modes: Mode[] = [Modes.Equal, Modes.GT, Modes.LT, Modes.Exists]
</script>
<BaseCriteria {readonly} {value} {process} {context} {attribute} {baseEditor} {modes} on:change on:delete />
@@ -27,7 +27,7 @@
export let attribute: AnyAttribute
export let baseEditor: AnyComponent | undefined
const modes: Mode[] = [Modes.Equal, Modes.NotEqual]
const modes: Mode[] = [Modes.Equal, Modes.NotEqual, Modes.Exists]
</script>
<BaseCriteria {readonly} {value} {process} {context} {attribute} {baseEditor} {modes} on:change on:delete />
@@ -27,7 +27,7 @@
export let attribute: AnyAttribute
export let baseEditor: AnyComponent | undefined
const modes: Mode[] = [Modes.Equal, Modes.StringContains]
const modes: Mode[] = [Modes.Equal, Modes.StringContains, Modes.Exists]
</script>
<BaseCriteria {readonly} {value} {process} {context} {attribute} {baseEditor} {modes} on:change on:delete />
@@ -64,7 +64,7 @@
<Label label={attribute.label} />
</div>
<Label label={selectedMode.label} />
{#if presenter}
{#if presenter && !selectedMode.withoutEditor}
{#if contextValue && context}
<ContextValuePresenter {contextValue} {context} {process} />
{:else}
+13 -1
View File
@@ -20,6 +20,7 @@ export interface Mode {
label: IntlString
query: QuerySelector<any> | string
parse?: (value: any) => any
withoutEditor?: boolean
}
const Equal: Mode = {
@@ -46,6 +47,13 @@ const LT: Mode = {
query: { $lt: '$val' as any }
}
const Exists: Mode = {
id: 'exists',
label: view.string.ValueIsSet,
query: { $exists: true },
withoutEditor: true
}
const StringContains: Mode = {
id: 'contains',
label: view.string.Contains,
@@ -105,6 +113,7 @@ const ArraySizeLte: Mode = {
}
export const Modes: Record<string, Mode> = {
Exists,
Equal,
NotEqual,
StringContains,
@@ -133,7 +142,10 @@ export function parseValue (modes: Mode[], value: any): [any, Mode] {
while (typeof obj1 === 'object' && typeof obj2 === 'object' && Object.keys(obj1)[0] === Object.keys(obj2)[0]) {
const key1 = Object.keys(obj1)[0]
const key2 = Object.keys(obj2)[0]
if (typeof obj1[key1] === 'string' && (typeof obj2[key2] !== 'object' || Array.isArray(obj2[key2]))) {
if (
['string', 'boolean'].includes(typeof obj1[key1]) &&
(typeof obj2[key2] !== 'object' || Array.isArray(obj2[key2]))
) {
if (mode.parse !== undefined) {
return [mode.parse(obj2[key2]), mode]
}
+2 -1
View File
@@ -147,6 +147,7 @@
"Icon": "Ikona",
"Color": "Barva",
"AutomationOnly": "Pouze automatizace",
"CopyLink": "Kopírovat odkaz"
"CopyLink": "Kopírovat odkaz",
"ValueIsSet": "Hodnota je nastavena"
}
}
+2 -1
View File
@@ -147,6 +147,7 @@
"Icon": "Symbol",
"Color": "Farbe",
"AutomationOnly": "Nur Automatisierung",
"CopyLink": "Link kopieren"
"CopyLink": "Link kopieren",
"ValueIsSet": "Wert ist gesetzt"
}
}
+2 -1
View File
@@ -147,6 +147,7 @@
"Icon": "Icon",
"Color": "Color",
"AutomationOnly": "Automation only",
"CopyLink": "Copy link"
"CopyLink": "Copy link",
"ValueIsSet": "Value is set"
}
}
+2 -1
View File
@@ -142,6 +142,7 @@
"Icon": "Icono",
"Color": "Color",
"AutomationOnly": "Solo automatización",
"CopyLink": "Copiar enlace"
"CopyLink": "Copiar enlace",
"ValueIsSet": "El valor está establecido"
}
}
+2 -1
View File
@@ -142,6 +142,7 @@
"Icon": "Icône",
"Color": "Couleur",
"AutomationOnly": "Automatisation uniquement",
"CopyLink": "Copier le lien"
"CopyLink": "Copier le lien",
"ValueIsSet": "La valeur est définie"
}
}
+2 -1
View File
@@ -142,6 +142,7 @@
"Icon": "Icona",
"Color": "Colore",
"AutomationOnly": "Solo automazione",
"CopyLink": "Copia link"
"CopyLink": "Copia link",
"ValueIsSet": "Il valore è impostato"
}
}
+2 -1
View File
@@ -142,6 +142,7 @@
"Icon": "アイコン",
"Color": "色",
"AutomationOnly": "自動化のみ",
"CopyLink": "リンクをコピー"
"CopyLink": "リンクをコピー",
"ValueIsSet": "値が設定されています"
}
}
+2 -1
View File
@@ -142,6 +142,7 @@
"Icon": "Ícone",
"Color": "Cor",
"AutomationOnly": "Apenas automação",
"CopyLink": "Copiar link"
"CopyLink": "Copiar link",
"ValueIsSet": "O valor está definido"
}
}
+2 -1
View File
@@ -144,6 +144,7 @@
"Icon": "Иконка",
"Color": "Цвет",
"AutomationOnly": "Только автоматизация",
"CopyLink": "Скопировать ссылку"
"CopyLink": "Скопировать ссылку",
"ValueIsSet": "Значение установлено"
}
}
+2 -1
View File
@@ -147,6 +147,7 @@
"Icon": "图标",
"Color": "颜色",
"AutomationOnly": "仅限自动化",
"CopyLink": "复制链接"
"CopyLink": "复制链接",
"ValueIsSet": "值已设置"
}
}
+1
View File
@@ -38,6 +38,7 @@ export default mergeIds(viewId, view, {
string: {
Contains: '' as IntlString,
NotContains: '' as IntlString,
ValueIsSet: '' as IntlString,
LabelYes: '' as IntlString,
LabelNo: '' as IntlString,
ChooseAColor: '' as IntlString,