multi selectable card setting

This commit is contained in:
oguzhankoral
2025-06-25 18:46:41 +03:00
parent 57ef9685b6
commit d5df4180ff
5 changed files with 156 additions and 6 deletions
+6 -2
View File
@@ -1,7 +1,6 @@
<template>
<!-- ONLY FOR TEST FOR NOW-->
<form class="flex flex-col space-y-4 form-json-form">
<span>Settings</span>
<FormJsonForm :schema="jsonSchema" @change="onParamsFormChange"></FormJsonForm>
</form>
</template>
@@ -21,6 +20,11 @@ const jsonSchema = {
type: 'string',
title: 'Favorite Color',
enum: ['red', 'green', 'blue']
},
multiSelect: {
type: 'array',
title: 'Multi Favorite Chars',
enum: ['a', 'b', 'c', 'd']
}
}
}
@@ -28,6 +32,6 @@ const jsonSchema = {
const paramsFormState = ref<JsonFormsChangeEvent>()
const onParamsFormChange = (e: JsonFormsChangeEvent) => {
paramsFormState.value = e
console.log(JSON.stringify(e))
console.log(e)
}
</script>
@@ -0,0 +1,111 @@
<template>
<div>
<div class="text-foreground-2 text-body-2xs mb-1 pl-1">
{{ control.label }}
</div>
<FormSelectMulti
:model-value="modelValue"
:name="fieldName"
:rules="multiValidator"
:label="control.label"
:items="control.options"
clearable
:search="true"
:search-placeholder="'Search'"
:filter-predicate="searchFilterPredicate"
:help="control.description"
:allow-unset="false"
by="value"
button-style="tinted"
:validate-on-value-update="validateOnValueUpdate"
mount-menu-on-body
@update:model-value="handleChange"
>
<template #nothing-selected>
{{
appliedOptions['placeholder']
? appliedOptions['placeholder']
: 'Select values'
}}
</template>
<template #something-selected="{ value }">
<div ref="elementToWatchForChanges" class="flex items-center space-x-0.5">
<div ref="itemContainer" class="flex flex-wrap overflow-hidden space-x-0.5">
<div v-for="(item, i) in value" :key="item.value" class="text-foreground">
{{ item.label + (i < value.length - 1 ? ', ' : '') }}
</div>
</div>
<div v-if="hiddenSelectedItemCount > 0" class="text-foreground-2 normal">
+{{ hiddenSelectedItemCount }}
</div>
</div>
</template>
<template #option="{ item }">
<div class="flex items-center text-foreground-2 text-body-2xs">
<span class="truncate">{{ item.label }}</span>
</div>
</template>
</FormSelectMulti>
</div>
</template>
<script setup lang="ts">
import type { ControlElement } from '@jsonforms/core'
import { rendererProps, useJsonFormsEnumControl } from '@jsonforms/vue'
import type { Nullable } from '@speckle/shared'
import { useFormSelectChildInternals } from '@speckle/ui-components'
import type { GenericValidateFunction } from 'vee-validate'
import { useJsonRendererBaseSetup } from '~/lib/form/composables/jsonRenderers'
type OptionType = { value: string; label: string }
type ValueType = OptionType | OptionType[] | undefined
const emit = defineEmits<(e: 'update:modelValue', v: ValueType) => void>()
const props = defineProps({
...rendererProps<ControlElement>(),
// TODO: Doesn't appear that jsonforms properly supports multiple selection
multiple: {
type: Boolean,
default: true
},
controlOverrides: {
type: Object as PropType<Nullable<ReturnType<typeof useJsonFormsEnumControl>>>,
default: null
}
})
const searchFilterPredicate = (item: OptionType, search: string) =>
item.label.toLocaleLowerCase().includes(search.toLocaleLowerCase())
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
const { hiddenSelectedItemCount, isArrayValue } =
useFormSelectChildInternals<OptionType>({
props: toRefs(props),
emit,
dynamicVisibility: { elementToWatchForChanges, itemContainer }
})
/* eslint-disable @typescript-eslint/no-explicit-any */
const multiValidator: GenericValidateFunction<any> = () => true // ignoring validation for multi enum since it is custom and jsonforms does not support it properly
const { handleChange, control, appliedOptions, fieldName, validateOnValueUpdate } =
useJsonRendererBaseSetup(props.controlOverrides || useJsonFormsEnumControl(props), {
onChangeValueConverter: (newVal: ValueType) => {
if (props.multiple && isArrayValue(newVal)) {
return newVal.map((v) => v.value)
} else if (newVal && !props.multiple && !isArrayValue(newVal)) {
return newVal.value
} else {
return undefined
}
}
})
const modelValue = computed(() => {
const val = control.value.data as OptionType[]
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return control.value.options.filter((o) => val?.includes(o.value))
})
</script>
@@ -7,8 +7,8 @@
:label="control.label"
:placeholder="appliedOptions['placeholder']"
:help="control.description"
color="foundation"
show-label
size="lg"
:validate-on-value-update="validateOnValueUpdate"
@update:model-value="handleChange"
/>
+22 -2
View File
@@ -1,6 +1,7 @@
import type { JsonFormsRendererRegistryEntry } from '@jsonforms/core'
import {
and,
hasType,
isBooleanControl,
isDateControl,
isDateTimeControl,
@@ -11,12 +12,15 @@ import {
isOneOfEnumControl,
isStringControl,
isTimeControl,
rankWith
rankWith,
schemaMatches,
uiTypeIs
} from '@jsonforms/core'
import { vanillaRenderers } from '@jsonforms/vue-vanilla'
import BooleanControlRenderer from '~/components/form/json/BooleanControlRenderer.vue'
import DateControlRenderer from '~/components/form/json/DateControlRenderer.vue'
import DateTimeControlRenderer from '~/components/form/json/DateTimeControlRenderer.vue'
import MultiEnumControlRenderer from '~/components/form/json/MultiEnumControlRenderer.vue'
import EnumControlRenderer from '~/components/form/json/EnumControlRenderer.vue'
import EnumOneOfControlRenderer from '~/components/form/json/EnumOneOfControlRenderer.vue'
import IntegerControlRenderer from '~/components/form/json/IntegerControlRenderer.vue'
@@ -75,6 +79,21 @@ export const timeControlRenderer: JsonFormsRendererRegistryEntry = {
tester: rankWith(4, isTimeControl)
}
export const multiEnumControlRenderer: JsonFormsRendererRegistryEntry = {
renderer: MultiEnumControlRenderer as unknown,
tester: rankWith(
6,
and(
uiTypeIs('Control'),
and(
schemaMatches(
(schema) => hasType(schema, 'array') && !Array.isArray(schema.items)
)
)
)
)
}
export const renderers: JsonFormsRendererRegistryEntry[] = markRaw([
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
...vanillaRenderers,
@@ -87,5 +106,6 @@ export const renderers: JsonFormsRendererRegistryEntry[] = markRaw([
numberControlRenderer,
dateControlRenderer,
dateTimeControlRenderer,
timeControlRenderer
timeControlRenderer,
multiEnumControlRenderer
])
+16 -1
View File
@@ -1,7 +1,10 @@
<template>
<div class="flex flex-col space-y-2">
<div class="px-2 mt-2">
<FormButton to="/" size="sm" :icon-left="ArrowLeftIcon">Home</FormButton>
<FormButton to="/" size="sm" :icon-left="ArrowLeftIcon" class="my-2">
Home
</FormButton>
<hr />
<p class="h5">Document info</p>
<p class="text-sm text-foreground-2 py-2">
Current document info. This should change on document swaps, closure, opening,
@@ -11,6 +14,7 @@
<pre>{{ documentInfo }}</pre>
</div>
</div>
<hr />
<div class="px-2">
<p class="h5">Send Filters</p>
<p class="text-sm text-foreground-2 space-x-2">Available send filters:</p>
@@ -32,6 +36,7 @@
<pre>{{ sendFilters }}</pre>
</div>
</div>
<hr />
<div class="px-2">
<p class="h5 mb-4">Chromium 65 Scrollable Dialogs Test</p>
<FormButton @click="showBigDialog = !showBigDialog">Show Big Dialog</FormButton>
@@ -41,6 +46,14 @@
</div>
</CommonDialog>
</div>
<hr />
<div class="px-2">
<p class="h5">Settings</p>
<div class="border rounded-lg p-1">
<ConfigDialog></ConfigDialog>
</div>
</div>
<hr />
<div class="px-2">
<p class="h5">Selection info</p>
<p class="text-sm text-foreground-2 py-2">
@@ -56,6 +69,7 @@
<pre>{{ selectionInfo }}</pre>
</div>
</div>
<hr />
<div class="px-2">
<p class="h5">Document State</p>
<p class="text-sm text-foreground-2 py-2">
@@ -70,6 +84,7 @@
<pre>{{ projectModelGroups }}</pre>
</div>
</div>
<hr />
<div class="px-2">
<p class="h5">Binding tests</p>
<p class="text-sm text-foreground-2 py-2">