mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Surface dropdown choices in advanced sidebar filter (#7101)
* Mark scope as system field * Surface choices in advanced sidebar detail
This commit is contained in:
61
app/src/components/v-select/select-list-item-group.vue
Normal file
61
app/src/components/v-select/select-list-item-group.vue
Normal 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>
|
||||
54
app/src/components/v-select/select-list-item.vue
Normal file
54
app/src/components/v-select/select-list-item.vue
Normal 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>
|
||||
8
app/src/components/v-select/types.ts
Normal file
8
app/src/components/v-select/types.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export type Option = {
|
||||
value: string | number | null;
|
||||
icon?: string;
|
||||
text?: string;
|
||||
disabled?: boolean;
|
||||
children?: Option[];
|
||||
divider?: boolean;
|
||||
};
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -8,4 +8,5 @@ export default defineInterface({
|
||||
component: InterfaceSystemScope,
|
||||
types: ['string'],
|
||||
options: [],
|
||||
system: true,
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ import { i18n } from '@/lang';
|
||||
type Option = {
|
||||
text: string;
|
||||
value: string | number | boolean;
|
||||
children?: Option[];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user