Surface dropdown choices in advanced sidebar filter (#7101)

* Mark scope as system field

* Surface choices in advanced sidebar detail
This commit is contained in:
Rijk van Zanten
2021-07-30 18:16:34 +02:00
committed by GitHub
parent ed1eb6e95d
commit 673818b04e
8 changed files with 183 additions and 30 deletions

View File

@@ -0,0 +1,61 @@
<template>
<v-list-group>
<template #activator>
<select-list-item
:model-value="modelValue"
:item="item"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
/>
</template>
<template v-for="(item, index) in item.children" :key="index">
<select-list-item-group
v-if="item.children"
:item="item"
:model-value="modelValue"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
/>
<select-list-item
v-else
:model-value="modelValue"
:item="item"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
/>
</template>
</v-list-group>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { Option } from './types';
import SelectListItem from './select-list-item.vue';
export default defineComponent({
name: 'SelectListItemGroup',
components: { SelectListItem },
props: {
item: {
type: Object as PropType<Option>,
required: true,
},
modelValue: {
type: [String, Number, Array] as PropType<string | number | (string | number)[]>,
default: null,
},
multiple: {
type: Boolean,
required: true,
},
allowOther: {
type: Boolean,
required: true,
},
},
});
</script>

View File

@@ -0,0 +1,54 @@
<template>
<v-divider v-if="item.divider === true" />
<v-list-item
v-else
:active="multiple ? (modelValue || []).includes(item.value) : modelValue === item.value"
:disabled="item.disabled"
clickable
@click="multiple ? null : $emit('update:modelValue', item.value)"
>
<v-list-item-icon v-if="multiple === false && allowOther === false && item.icon">
<v-icon :name="item.icon" />
</v-list-item-icon>
<v-list-item-content>
<span v-if="multiple === false" class="item-text">{{ item.text }}</span>
<v-checkbox
v-else
:model-value="modelValue || []"
:label="item.text"
:value="item.value"
:disabled="item.disabled"
@update:model-value="$emit('update:modelValue', $event.length > 0 ? $event : null)"
/>
</v-list-item-content>
</v-list-item>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { Option } from './types';
export default defineComponent({
name: 'SelectListItem',
props: {
item: {
type: Object as PropType<Option>,
required: true,
},
modelValue: {
type: [String, Number, Array] as PropType<string | number | (string | number)[]>,
default: null,
},
multiple: {
type: Boolean,
required: true,
},
allowOther: {
type: Boolean,
required: true,
},
},
emits: ['update:modelValue'],
});
</script>

View File

@@ -0,0 +1,8 @@
export type Option = {
value: string | number | null;
icon?: string;
text?: string;
disabled?: boolean;
children?: Option[];
divider?: boolean;
};

View File

@@ -44,30 +44,22 @@
</template>
<template v-for="(item, index) in internalItems" :key="index">
<v-divider v-if="item.divider === true" />
<v-list-item
<select-list-item-group
v-if="item.children"
:item="item"
:model-value="modelValue"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
/>
<select-list-item
v-else
:active="multiple ? (modelValue || []).includes(item.value) : modelValue === item.value"
:disabled="item.disabled"
clickable
@click="multiple ? null : $emit('update:modelValue', item.value)"
>
<v-list-item-icon v-if="multiple === false && allowOther === false && itemIcon !== null && item.icon">
<v-icon :name="item.icon" />
</v-list-item-icon>
<v-list-item-content>
<span v-if="multiple === false" class="item-text">{{ item.text }}</span>
<v-checkbox
v-else
:model-value="modelValue || []"
:label="item.text"
:value="item.value"
:disabled="item.disabled"
@update:model-value="$emit('update:modelValue', $event.length > 0 ? $event : null)"
/>
</v-list-item-content>
</v-list-item>
:model-value="modelValue"
:item="item"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
/>
</template>
<v-list-item v-if="allowOther && multiple === false" :active="usesOtherValue" @click.stop>
@@ -124,11 +116,15 @@ import { useI18n } from 'vue-i18n';
import { defineComponent, PropType, computed, toRefs, Ref } from 'vue';
import { useCustomSelection, useCustomSelectionMultiple } from '@/composables/use-custom-selection';
import { get } from 'lodash';
import SelectListItemGroup from './select-list-item-group.vue';
import SelectListItem from './select-list-item.vue';
import { Option } from './types';
type ItemsRaw = (string | any)[];
type InputValue = string[] | string;
export default defineComponent({
components: { SelectListItemGroup, SelectListItem },
props: {
items: {
type: Array as PropType<ItemsRaw>,
@@ -150,6 +146,10 @@ export default defineComponent({
type: String,
default: 'disabled',
},
itemChildren: {
type: String,
default: 'children',
},
modelValue: {
type: [Array, String, Number, Boolean] as PropType<InputValue>,
default: null,
@@ -211,7 +211,7 @@ export default defineComponent({
function useItems() {
const internalItems = computed(() => {
const items = props.items.map((item) => {
const parseItem = (item: Record<string, any>): Option => {
if (typeof item === 'string') {
return {
text: item,
@@ -219,15 +219,20 @@ export default defineComponent({
};
}
if (item.divider === true) return { divider: true };
if (item.divider === true) return { value: null, divider: true };
const children = get(item, props.itemChildren) ? get(item, props.itemChildren).map(parseItem) : null;
return {
text: get(item, props.itemText),
value: get(item, props.itemValue),
icon: get(item, props.itemIcon),
disabled: get(item, props.itemDisabled),
children,
};
});
};
const items = props.items.map(parseItem);
return items;
});

View File

@@ -8,4 +8,5 @@ export default defineInterface({
component: InterfaceSystemScope,
types: ['string'],
options: [],
system: true,
});

View File

@@ -26,6 +26,7 @@ import { i18n } from '@/lang';
type Option = {
text: string;
value: string | number | boolean;
children?: Option[];
};
export default defineComponent({

View File

@@ -35,7 +35,7 @@
/>
</div>
<div class="field">
<filter-input v-model="value" :type="field.type" :operator="activeOperator" :disabled="disabled" />
<filter-input v-model="value" :field="field" :type="field.type" :operator="activeOperator" :disabled="disabled" />
</div>
</div>
</template>

View File

@@ -7,6 +7,8 @@
:type="type"
:value="csvValue[0]"
:placeholder="t('lower_limit')"
:allow-other="true"
:choices="choices"
autofocus
@input="setCSV(0, $event)"
/>
@@ -17,6 +19,8 @@
:type="type"
:value="csvValue[1]"
:placeholder="t('upper_limit')"
:allow-other="true"
:choices="choices"
autofocus
@input="setCSV(1, $event)"
/>
@@ -30,6 +34,8 @@
:value="val"
:placeholder="t('enter_a_value')"
:disabled="disabled"
:allow-other="true"
:choices="choices"
autofocus
@input="setCSV(index, $event)"
/>
@@ -50,6 +56,8 @@
:value="internalValue"
:placeholder="t('enter_a_value')"
:disabled="disabled"
:choices="choices"
:allow-other="true"
autofocus
@input="internalValue = $event"
/>
@@ -60,7 +68,7 @@
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, PropType, computed } from 'vue';
import { FilterOperator, Type } from '@directus/shared/types';
import { FilterOperator, Type, Field } from '@directus/shared/types';
import { getDefaultInterfaceForType } from '@/utils/get-default-interface-for-type';
export default defineComponent({
@@ -81,6 +89,10 @@ export default defineComponent({
type: Boolean,
default: false,
},
field: {
type: Object as PropType<Field>,
default: null,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
@@ -104,9 +116,20 @@ export default defineComponent({
},
});
const interfaceComponent = computed(() => `interface-${getDefaultInterfaceForType(props.type)}`);
const choices = computed(() => {
if (!props.field) return null;
return props.field?.meta?.options?.choices || null;
});
return { t, internalValue, csvValue, setCSV, removeCSV, addCSV, interfaceComponent };
const interfaceComponent = computed(() => {
if (choices.value) {
return 'interface-select-dropdown';
}
return `interface-${getDefaultInterfaceForType(props.type)}`;
});
return { t, internalValue, csvValue, setCSV, removeCSV, addCSV, interfaceComponent, choices };
function setCSV(index: number, value: string) {
const newValue = Object.assign([], csvValue.value, { [index]: value });