Fix a few dev mode warnings (#18249)

* `list-m2m` make layout optional

* Silence a few more warnings

* Properly type v-button `to`

* Make search prop in field-detail-simple not required

* Set undefined as default value for text in v-text-overflow

* Change initial search value to undefined in field-detail

* Rework field-detail and field-detail-simple to use script setup

* One less ctrl+z

* Remove unnecessary new line

---------

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Hannes Küttner
2023-04-19 19:13:20 +02:00
committed by GitHub
parent e189118557
commit 7bf87f3df3
7 changed files with 200 additions and 210 deletions

View File

@@ -40,7 +40,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { RouteLocation, useRoute, useLink } from 'vue-router';
import { RouteLocationRaw, useRoute, useLink } from 'vue-router';
import { useSizeClass, useGroupable } from '@directus/composables';
import { isEqual, isNil } from 'lodash';
@@ -64,7 +64,7 @@ interface Props {
/** Show a circular progress bar */
loading?: boolean;
/** To what internal link the button should direct */
to?: string | RouteLocation;
to?: RouteLocationRaw;
/** To what external link the button should direct */
href?: string;
/** Renders the button highlighted */

View File

@@ -11,7 +11,7 @@ import { useElementSize } from '@directus/composables';
interface Props {
/** The text that should be displayed */
text: string | number | boolean | Record<string, any> | Array<any>;
text?: string | number | boolean | Record<string, any> | Array<any>;
/** What parts of the text should be highlighted */
highlight?: string;
/** The placement of the tooltip */
@@ -19,6 +19,7 @@ interface Props {
}
withDefaults(defineProps<Props>(), {
text: undefined,
highlight: undefined,
placement: 'top',
});

View File

@@ -12,7 +12,7 @@ import { useCollection } from '@directus/composables';
import { getEndpoint } from '@directus/utils';
import { AxiosResponse } from 'axios';
import { mergeWith } from 'lodash';
import { computed, ComputedRef, Ref, ref, unref, watch } from 'vue';
import { computed, ComputedRef, isRef, Ref, ref, unref, watch } from 'vue';
import { usePermissions } from './use-permissions';
import { Field, Query, Relation } from '@directus/types';
import { getDefaultValuesFromFields } from '@/utils/get-default-values-from-fields';
@@ -79,7 +79,7 @@ export function useItem(
const defaultValues = getDefaultValuesFromFields(fieldsWithPermissions);
watch([collection, primaryKey, query], refresh, { immediate: true });
watch([collection, primaryKey, ...(isRef(query) ? [query] : [])], refresh, { immediate: true });
return {
edits,

View File

@@ -225,7 +225,7 @@ const props = withDefaults(
collection: string;
field: string;
width: string;
layout: LAYOUTS;
layout?: LAYOUTS;
tableSpacing?: 'compact' | 'cozy' | 'comfortable';
fields?: Array<string>;
template?: string | null;

View File

@@ -222,7 +222,7 @@ const props = withDefaults(
collection: string;
field: string;
width: string;
layout: LAYOUTS;
layout?: LAYOUTS;
tableSpacing?: 'compact' | 'cozy' | 'comfortable';
fields?: Array<string>;
template?: string | null;

View File

@@ -38,142 +38,138 @@
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed, toRefs, watch } from 'vue';
<script lang="ts" setup>
import { computed, toRefs, watch } from 'vue';
import { Collection } from '@directus/types';
import { useI18n } from 'vue-i18n';
import { orderBy } from 'lodash';
import { useFieldDetailStore, syncFieldDetailStoreProperty } from '../store/';
import { syncRefProperty } from '@/utils/sync-ref-property';
import { syncFieldDetailStoreProperty, useFieldDetailStore } from '../store/';
import FieldConfiguration from './field-configuration.vue';
import { useExtensions } from '@/extensions';
export default defineComponent({
components: { FieldConfiguration },
props: {
collection: {
type: Object as PropType<Collection>,
required: true,
},
search: {
type: String,
required: true,
},
},
emits: ['save', 'toggleAdvanced'],
setup(props) {
const { collection, search } = toRefs(props);
const props = withDefaults(
defineProps<{
collection: Collection;
search: string | null;
}>(),
{
search: null,
}
);
const { t } = useI18n();
defineEmits<{
(e: 'save'): void;
(e: 'toggleAdvanced'): void;
}>();
const fieldDetail = useFieldDetailStore();
watch(collection, () => fieldDetail.update({ collection: collection.value.collection }), { immediate: true });
const { collection, search } = toRefs(props);
const { interfaces } = useExtensions();
const { t } = useI18n();
const interfacesSorted = computed(() => {
return orderBy(
interfaces.value.filter((inter) => !inter.system),
['order']
);
});
const fieldDetail = useFieldDetailStore();
watch(collection, () => fieldDetail.update({ collection: collection.value.collection }), { immediate: true });
const groups = computed(() => {
const groupsWithInterfaces = [
{
key: 'standard',
name: t('interface_group_text_and_numbers'),
interfaces: filterInterfacesByGroup('standard'),
},
{
key: 'selection',
name: t('interface_group_selection'),
interfaces: filterInterfacesByGroup('selection'),
},
{
key: 'relational',
name: t('interface_group_relational'),
interfaces: filterInterfacesByGroup('relational'),
},
{
key: 'presentation',
name: t('interface_group_presentation'),
interfaces: filterInterfacesByGroup('presentation'),
},
{
key: 'group',
name: t('interface_group_groups'),
interfaces: filterInterfacesByGroup('group'),
},
{
key: 'other',
name: t('interface_group_other'),
interfaces: filterInterfacesByGroup('other'),
},
];
const { interfaces } = useExtensions();
if (!search.value) return groupsWithInterfaces;
return groupsWithInterfaces.filter((group) => group.interfaces.length > 0);
function filterInterfacesByGroup(group: string) {
const filteredInterfaces = interfacesSorted.value.filter((inter) => (inter.group ?? 'other') === group);
if (!search.value) return filteredInterfaces;
const searchValue = search.value!.toLowerCase();
return filteredInterfaces.filter(
(inter) => inter.id.toLowerCase().includes(searchValue) || inter.name.toLowerCase().includes(searchValue)
);
}
});
const chosenInterface = syncFieldDetailStoreProperty('field.meta.interface');
const configRow = computed(() => {
if (!chosenInterface.value) return null;
let indexInGroup: number | null = null;
groups.value.forEach((group) => {
const index = group.interfaces.findIndex((inter) => inter.id === chosenInterface.value);
if (index !== -1) indexInGroup = index;
});
if (indexInGroup === null) return null;
const windowWidth = window.innerWidth;
let columns = 1;
if (windowWidth > 400) {
columns = 2;
}
if (windowWidth > 600) {
columns = 3;
}
if (windowWidth > 840) {
columns = 4;
}
return Math.ceil((indexInGroup + 1) / columns) + 1;
});
return { t, interfaces, groups, isSVG, syncRefProperty, chosenInterface, configRow, toggleInterface };
function isSVG(path: string) {
return path.startsWith('<svg');
}
function toggleInterface(id: string) {
if (chosenInterface.value === id) {
chosenInterface.value = null;
} else {
chosenInterface.value = id;
}
}
},
const interfacesSorted = computed(() => {
return orderBy(
interfaces.value.filter((inter) => !inter.system),
['order']
);
});
const groups = computed(() => {
const groupsWithInterfaces = [
{
key: 'standard',
name: t('interface_group_text_and_numbers'),
interfaces: filterInterfacesByGroup('standard'),
},
{
key: 'selection',
name: t('interface_group_selection'),
interfaces: filterInterfacesByGroup('selection'),
},
{
key: 'relational',
name: t('interface_group_relational'),
interfaces: filterInterfacesByGroup('relational'),
},
{
key: 'presentation',
name: t('interface_group_presentation'),
interfaces: filterInterfacesByGroup('presentation'),
},
{
key: 'group',
name: t('interface_group_groups'),
interfaces: filterInterfacesByGroup('group'),
},
{
key: 'other',
name: t('interface_group_other'),
interfaces: filterInterfacesByGroup('other'),
},
];
if (!search.value) return groupsWithInterfaces;
return groupsWithInterfaces.filter((group) => group.interfaces.length > 0);
function filterInterfacesByGroup(group: string) {
const filteredInterfaces = interfacesSorted.value.filter((inter) => (inter.group ?? 'other') === group);
if (!search.value) return filteredInterfaces;
const searchValue = search.value!.toLowerCase();
return filteredInterfaces.filter(
(inter) => inter.id.toLowerCase().includes(searchValue) || inter.name.toLowerCase().includes(searchValue)
);
}
});
const chosenInterface = syncFieldDetailStoreProperty('field.meta.interface');
const configRow = computed(() => {
if (!chosenInterface.value) return null;
let indexInGroup: number | null = null;
groups.value.forEach((group) => {
const index = group.interfaces.findIndex((inter) => inter.id === chosenInterface.value);
if (index !== -1) indexInGroup = index;
});
if (indexInGroup === null) return null;
const windowWidth = window.innerWidth;
let columns = 1;
if (windowWidth > 400) {
columns = 2;
}
if (windowWidth > 600) {
columns = 3;
}
if (windowWidth > 840) {
columns = 4;
}
return Math.ceil((indexInGroup + 1) / columns) + 1;
});
function isSVG(path: string) {
return path.startsWith('<svg');
}
function toggleInterface(id: string) {
if (chosenInterface.value === id) {
chosenInterface.value = null;
} else {
chosenInterface.value = id;
}
}
</script>
<style scoped lang="scss">

View File

@@ -38,8 +38,8 @@
</v-drawer>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, computed, toRefs, watch } from 'vue';
<script lang="ts" setup>
import { ref, computed, toRefs, watch } from 'vue';
import { LocalType } from '@directus/types';
import { useFieldDetailStore } from './store/';
import FieldDetailSimple from './field-detail-simple/field-detail-simple.vue';
@@ -55,88 +55,81 @@ import { useDialogRoute } from '@/composables/use-dialog-route';
import { storeToRefs } from 'pinia';
import { unexpectedError } from '@/utils/unexpected-error';
export default defineComponent({
name: 'FieldDetail',
components: { FieldDetailSimple, FieldDetailAdvanced, FieldDetailAdvancedTabs, FieldDetailAdvancedActions },
props: {
collection: {
type: String,
required: true,
},
field: {
type: String,
required: true,
},
type: {
type: String as PropType<LocalType>,
default: null,
},
const props = withDefaults(
defineProps<{
collection: string;
field: string;
type: LocalType | null;
}>(),
{
type: null,
}
);
const { collection, field, type } = toRefs(props);
const search = ref<string | null>(null);
const isOpen = useDialogRoute();
const fieldDetail = useFieldDetailStore();
const { editing } = storeToRefs(fieldDetail);
watch(
[field, type],
() => {
if (!type.value) return;
fieldDetail.startEditing(collection.value, field.value, type.value);
},
setup(props) {
const { collection } = toRefs(props);
{ immediate: true }
);
const search = ref<string | null>(null);
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
const router = useRouter();
const { t } = useI18n();
const isOpen = useDialogRoute();
const fieldDetail = useFieldDetailStore();
const { editing } = storeToRefs(fieldDetail);
watch(
() => props.field,
() => fieldDetail.startEditing(props.collection, props.field, props.type),
{ immediate: true }
);
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
const router = useRouter();
const { t } = useI18n();
const collectionInfo = computed(() => {
return collectionsStore.getCollection(collection.value);
});
const simple = ref(props.type === null);
const title = computed(() => {
const existingField = fieldsStore.getField(props.collection, props.field);
const fieldName = existingField?.name || formatTitle(fieldDetail.field.name || '');
if (props.field === '+' && fieldName === '') {
return t('creating_new_field', { collection: collectionInfo.value?.name });
} else {
return t('field_in_collection', { field: fieldName, collection: collectionInfo.value?.name });
}
});
const currentTab = ref(['schema']);
const showAdvanced = computed(() => {
return editing.value !== '+' || !simple.value;
});
return { search, simple, cancel, collectionInfo, t, title, save, isOpen, currentTab, showAdvanced };
async function cancel() {
await router.push(`/settings/data-model/${props.collection}`);
fieldDetail.$reset();
}
async function save() {
try {
await fieldDetail.save();
} catch (err: any) {
unexpectedError(err);
return;
}
router.push(`/settings/data-model/${props.collection}`);
fieldDetail.$reset();
}
},
const collectionInfo = computed(() => {
return collectionsStore.getCollection(collection.value);
});
const simple = ref(props.type === null);
const title = computed(() => {
const existingField = fieldsStore.getField(props.collection, props.field);
const fieldName = existingField?.name || formatTitle(fieldDetail.field.name || '');
if (props.field === '+' && fieldName === '') {
return t('creating_new_field', { collection: collectionInfo.value?.name });
} else {
return t('field_in_collection', { field: fieldName, collection: collectionInfo.value?.name });
}
});
const currentTab = ref(['schema']);
const showAdvanced = computed(() => {
return editing.value !== '+' || !simple.value;
});
async function cancel() {
await router.push(`/settings/data-model/${props.collection}`);
fieldDetail.$reset();
}
async function save() {
try {
await fieldDetail.save();
} catch (err: any) {
unexpectedError(err);
return;
}
router.push(`/settings/data-model/${props.collection}`);
fieldDetail.$reset();
}
</script>
<style lang="scss" scoped>