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]
}