mirror of
https://github.com/directus/directus.git
synced 2026-01-22 17:57:59 -05:00
Add new translations interface (#7727)
* added v-select and button to start sidebyside view * v-chip next to field name on translations * v-chip color changed * add baisc logic * finish inner workings of translation interface * finish design * clean up code * remove unused prop * small tweaks * finish translation interface * fix lang icon * tweak styling * Use v-model over separate bind+event * Tweak margin definition * Add class to field-name to prevent span confusion * Rename classes to match var names * Add limit -1, remove commented code * Tweak toggle tooltip wording * Add hover state to v-icons * Use self-closing elements * Remove unused imports * Rename newVal->sideBySideEnabled * Use filter + length instead of reducer * Fix param typo * Move dividers into main translations component * Base initial language on fetched languages array * Move styling to language-select, simplify component * Don't rely on deep styling * Tweak interactive state of chip * Use existing form-grid for side-by-side layoutin * Only fetch preview values when we dont have them yet * Improve stability of edited status * Fix hover state of v-icon Co-authored-by: Nitwel <nitwel@arcor.de> Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
@@ -104,7 +104,7 @@ body {
|
||||
border: var(--border-width) solid var(--v-chip-background-color);
|
||||
border-radius: 16px;
|
||||
|
||||
&:hover {
|
||||
&.clickable:hover {
|
||||
color: var(--v-chip-color-hover);
|
||||
background-color: var(--v-chip-background-color-hover);
|
||||
border-color: var(--v-chip-background-color-hover);
|
||||
@@ -120,7 +120,7 @@ body {
|
||||
background-color: var(--v-chip-background-color);
|
||||
border-color: var(--v-chip-background-color);
|
||||
|
||||
&:hover {
|
||||
&.clickable:hover {
|
||||
color: var(--v-chip-color);
|
||||
background-color: var(--v-chip-background-color);
|
||||
border-color: var(--v-chip-background-color);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
class="v-field-select"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<v-chip v-tooltip="element.field" class="field draggable" @click="removeField(element.field)">
|
||||
<v-chip v-tooltip="element.field" clickable class="field draggable" @click="removeField(element.field)">
|
||||
{{ element.name }}
|
||||
</v-chip>
|
||||
</template>
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
:value="field.field"
|
||||
@update:model-value="$emit('toggle-batch', field)"
|
||||
/>
|
||||
<span v-tooltip="edited ? t('edited') : null" @click="toggle">
|
||||
<span v-tooltip="edited ? t('edited') : null" class="field-name" @click="toggle">
|
||||
{{ field.name }}
|
||||
<v-icon v-if="field.meta?.required === true" class="required" sup name="star" />
|
||||
<v-icon v-if="!disabled" class="ctx-arrow" :class="{ active }" name="arrow_drop_down" />
|
||||
</span>
|
||||
<v-chip v-if="badge" x-small>{{ badge }}</v-chip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -53,6 +54,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
badge: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ['toggle-batch'],
|
||||
setup() {
|
||||
@@ -79,6 +84,11 @@ export default defineComponent({
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.v-chip {
|
||||
margin: 0;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.required {
|
||||
--v-icon-color: var(--primary);
|
||||
|
||||
@@ -88,7 +98,7 @@ export default defineComponent({
|
||||
.ctx-arrow {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -20px;
|
||||
right: -24px;
|
||||
color: var(--foreground-subdued);
|
||||
opacity: 0;
|
||||
transition: opacity var(--fast) var(--transition);
|
||||
@@ -118,7 +128,7 @@ export default defineComponent({
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
> span {
|
||||
.field-name {
|
||||
margin-left: -16px;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
:batch-active="batchActive"
|
||||
:edited="isEdited"
|
||||
:has-error="!!validationError"
|
||||
:badge="badge"
|
||||
@toggle-batch="$emit('toggle-batch', $event)"
|
||||
/>
|
||||
</template>
|
||||
@@ -111,6 +112,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
badge: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ['toggle-batch', 'unset', 'update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
:primary-key="primaryKey"
|
||||
:loading="loading"
|
||||
:validation-error="validationErrors.find((err) => err.field === field.field)"
|
||||
:badge="badge"
|
||||
@update:model-value="setValue(field, $event)"
|
||||
@unset="unsetValue(field)"
|
||||
@toggle-batch="toggleBatchField(field)"
|
||||
@@ -124,6 +125,10 @@ export default defineComponent({
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
badge: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
<template>
|
||||
<div
|
||||
class="v-progress-linear"
|
||||
:class="{
|
||||
absolute,
|
||||
bottom,
|
||||
fixed,
|
||||
indeterminate,
|
||||
rounded,
|
||||
top,
|
||||
}"
|
||||
:class="[
|
||||
{
|
||||
absolute,
|
||||
bottom,
|
||||
fixed,
|
||||
indeterminate,
|
||||
rounded,
|
||||
top,
|
||||
colorful,
|
||||
},
|
||||
color,
|
||||
]"
|
||||
@animationiteration="$emit('animationiteration')"
|
||||
>
|
||||
<div
|
||||
@@ -22,7 +26,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -54,8 +58,21 @@ export default defineComponent({
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
colorful: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['animationiteration'],
|
||||
setup(props) {
|
||||
const color = computed(() => {
|
||||
if (props.value <= 33) return 'danger';
|
||||
if (props.value <= 66) return 'warning';
|
||||
return 'success';
|
||||
});
|
||||
|
||||
return { color };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -116,6 +133,20 @@ body {
|
||||
&.top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.colorful {
|
||||
&.danger .inner {
|
||||
background-color: var(--danger);
|
||||
}
|
||||
|
||||
&.warning .inner {
|
||||
background-color: var(--warning);
|
||||
}
|
||||
|
||||
&.success .inner {
|
||||
background-color: var(--success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes indeterminate {
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
@click="toggle"
|
||||
>
|
||||
<template v-if="$slots.prepend" #prepend><slot name="prepend" /></template>
|
||||
<template #append><v-icon name="expand_more" :class="{ active }" /></template>
|
||||
<template #append>
|
||||
<v-icon name="expand_more" :class="{ active }" />
|
||||
<slot name="append" />
|
||||
</template>
|
||||
</v-input>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
:disabled="disabled"
|
||||
small
|
||||
label
|
||||
clickable
|
||||
@click="toggleTag(preset)"
|
||||
>
|
||||
{{ preset }}
|
||||
@@ -32,6 +33,7 @@
|
||||
class="tag"
|
||||
small
|
||||
label
|
||||
clickable
|
||||
@click="removeTag(val)"
|
||||
>
|
||||
{{ val }}
|
||||
|
||||
153
app/src/interfaces/translations/language-select.vue
Normal file
153
app/src/interfaces/translations/language-select.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<v-menu attached class="language-select" :class="{ secondary }">
|
||||
<template #activator="{ toggle, active }">
|
||||
<button class="toggle" @click="toggle">
|
||||
<v-icon class="translate" name="translate" />
|
||||
<span class="display-value">{{ displayValue }}</span>
|
||||
<v-icon name="expand_more" :class="{ active }" />
|
||||
<span class="append-slot"><slot name="append" /></span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<v-list>
|
||||
<v-list-item v-for="(item, index) in items" :key="index" @click="$emit('update:modelValue', item.value)">
|
||||
<div class="start">
|
||||
<div class="dot" :class="{ show: item.edited }"></div>
|
||||
{{ item.text }}
|
||||
</div>
|
||||
<div class="end">
|
||||
<v-progress-linear
|
||||
v-tooltip="`${Math.round((item.current / item.max) * 100)}%`"
|
||||
:value="item.progress"
|
||||
colorful
|
||||
/>
|
||||
</div>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
items: {
|
||||
type: Array as PropType<Record<string, any>[]>,
|
||||
default: () => [],
|
||||
},
|
||||
secondary: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props) {
|
||||
const displayValue = computed(() => {
|
||||
const item = props.items.find((item) => item.value === props.modelValue);
|
||||
return item?.text ?? props.modelValue;
|
||||
});
|
||||
|
||||
return { displayValue };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.toggle {
|
||||
--v-icon-color: var(--primary);
|
||||
--v-icon-color-hover: var(--primary-150);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: var(--input-height);
|
||||
padding: var(--input-padding);
|
||||
color: var(--primary);
|
||||
text-align: left;
|
||||
background-color: var(--primary-alt);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
.display-value {
|
||||
flex-grow: 1;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.append-slot:not(:empty) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.v-input .input {
|
||||
color: var(--primary);
|
||||
background-color: var(--primary-alt);
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.v-icon {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
.toggle {
|
||||
--v-icon-color: var(--blue);
|
||||
--v-icon-color-hover: var(--blue-150);
|
||||
|
||||
color: var(--blue);
|
||||
background-color: var(--blue-alt);
|
||||
}
|
||||
}
|
||||
|
||||
.v-list {
|
||||
.v-list-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
|
||||
.start {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.end {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: var(--foreground-subdued);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--background-normal);
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 100%;
|
||||
|
||||
&.show::before {
|
||||
display: block;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: var(--foreground-subdued);
|
||||
border-radius: 2px;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
.v-progress-linear {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,29 +4,8 @@
|
||||
</v-notice>
|
||||
<div v-else class="form-grid">
|
||||
<div class="field half">
|
||||
<p class="type-label">{{ t('language_display_template') }}</p>
|
||||
<v-field-template
|
||||
v-model="languageTemplate"
|
||||
:collection="languageCollection"
|
||||
:depth="2"
|
||||
:placeholder="
|
||||
languageCollectionInfo && languageCollectionInfo.meta && languageCollectionInfo.meta.display_template
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field half">
|
||||
<p class="type-label">{{ t('translations_display_template') }}</p>
|
||||
<v-field-template
|
||||
v-model="translationsTemplate"
|
||||
:collection="translationsCollection"
|
||||
:depth="2"
|
||||
:placeholder="
|
||||
translationsCollectionInfo &&
|
||||
translationsCollectionInfo.meta &&
|
||||
translationsCollectionInfo.meta.display_template
|
||||
"
|
||||
/>
|
||||
<p class="type-label">{{ t('interfaces.translations.language_field') }}</p>
|
||||
<v-select v-model="languageField" :items="languageCollectionFields" item-text="name" item-value="field" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -36,7 +15,7 @@ import { useI18n } from 'vue-i18n';
|
||||
import { Relation } from '@/types';
|
||||
import { Field } from '@directus/shared/types';
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
import { useCollectionsStore } from '@/stores/';
|
||||
import { useFieldsStore } from '@/stores/';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -61,28 +40,16 @@ export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const { t } = useI18n();
|
||||
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const translationsTemplate = computed({
|
||||
const languageField = computed({
|
||||
get() {
|
||||
return props.value?.translationsTemplate;
|
||||
return props.value?.languageField;
|
||||
},
|
||||
set(newTemplate: string) {
|
||||
emit('input', {
|
||||
...(props.value || {}),
|
||||
translationsTemplate: newTemplate,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const languageTemplate = computed({
|
||||
get() {
|
||||
return props.value?.languageTemplate;
|
||||
},
|
||||
set(newTemplate: string) {
|
||||
emit('input', {
|
||||
...(props.value || {}),
|
||||
languageTemplate: newTemplate,
|
||||
languageField: newTemplate,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -109,27 +76,18 @@ export default defineComponent({
|
||||
);
|
||||
});
|
||||
|
||||
const translationsCollection = computed(() => translationsRelation.value?.collection ?? null);
|
||||
const languageCollection = computed(() => languageRelation.value?.related_collection ?? null);
|
||||
|
||||
const translationsCollectionInfo = computed(() => {
|
||||
if (!translationsCollection.value) return null;
|
||||
return collectionsStore.getCollection(translationsCollection.value);
|
||||
});
|
||||
|
||||
const languageCollectionInfo = computed(() => {
|
||||
if (!languageCollection.value) return null;
|
||||
return collectionsStore.getCollection(languageCollection.value);
|
||||
const languageCollectionFields = computed(() => {
|
||||
if (!languageCollection.value) return [];
|
||||
return fieldsStore.getFieldsForCollection(languageCollection.value);
|
||||
});
|
||||
|
||||
return {
|
||||
t,
|
||||
languageTemplate,
|
||||
translationsTemplate,
|
||||
translationsCollection,
|
||||
translationsCollectionInfo,
|
||||
languageField,
|
||||
languageCollection,
|
||||
languageCollectionInfo,
|
||||
languageCollectionFields,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,54 +1,66 @@
|
||||
<template>
|
||||
<div v-if="languagesLoading || previewLoading">
|
||||
<v-skeleton-loader v-for="n in 5" :key="n" />
|
||||
</div>
|
||||
|
||||
<v-list v-else class="translations">
|
||||
<v-list-item
|
||||
v-for="(languageItem, i) in languages"
|
||||
:key="languageItem[languagesPrimaryKeyField]"
|
||||
clickable
|
||||
class="language-row"
|
||||
block
|
||||
@click="startEditing(languageItem[languagesPrimaryKeyField])"
|
||||
>
|
||||
<v-icon class="translate" name="translate" left />
|
||||
<render-template :template="internalLanguageTemplate" :collection="languagesCollection" :item="languageItem" />
|
||||
<render-template
|
||||
class="preview"
|
||||
:template="internalTranslationsTemplate"
|
||||
:collection="translationsCollection"
|
||||
:item="previewItems[i]"
|
||||
<div class="translations" :class="{ split: splitViewEnabled }">
|
||||
<div class="primary" :class="splitViewEnabled ? 'half' : 'full'">
|
||||
<language-select v-model="firstLang" :items="languageOptions">
|
||||
<template #append>
|
||||
<v-icon
|
||||
v-if="splitViewAvailable && !splitViewEnabled"
|
||||
v-tooltip="t('interfaces.translations.toggle_split_view')"
|
||||
name="flip"
|
||||
clickable
|
||||
@click.stop="splitView = true"
|
||||
/>
|
||||
</template>
|
||||
</language-select>
|
||||
<v-form
|
||||
:loading="valuesLoading"
|
||||
:fields="fields"
|
||||
:model-value="firstItem"
|
||||
:initial-values="firstItemInitial"
|
||||
:badge="languageOptions.find((lang) => lang.value === firstLang)?.text"
|
||||
@update:modelValue="updateValue($event, firstLang)"
|
||||
/>
|
||||
<div class="spacer" />
|
||||
</v-list-item>
|
||||
|
||||
<drawer-item
|
||||
v-if="editing"
|
||||
active
|
||||
:collection="translationsCollection"
|
||||
:primary-key="editing"
|
||||
:edits="edits"
|
||||
:circular-field="translationsRelation.field"
|
||||
@input="stageEdits"
|
||||
@update:active="cancelEdit"
|
||||
/>
|
||||
</v-list>
|
||||
<v-divider />
|
||||
</div>
|
||||
<div v-if="splitViewEnabled" class="secondary" :class="splitViewEnabled ? 'half' : 'full'">
|
||||
<language-select v-model="secondLang" :items="languageOptions" secondary>
|
||||
<template #append>
|
||||
<v-icon
|
||||
v-tooltip="t('interfaces.translations.toggle_split_view')"
|
||||
name="close"
|
||||
clickable
|
||||
@click.stop="splitView = !splitView"
|
||||
/>
|
||||
</template>
|
||||
</language-select>
|
||||
<v-form
|
||||
:loading="valuesLoading"
|
||||
:initial-values="secondItemInitial"
|
||||
:fields="fields"
|
||||
:badge="languageOptions.find((lang) => lang.value === secondLang)?.text"
|
||||
:model-value="secondItem"
|
||||
@update:modelValue="updateValue($event, secondLang)"
|
||||
/>
|
||||
<v-divider />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed, ref, watch } from 'vue';
|
||||
import { useRelationsStore, useFieldsStore } from '@/stores/';
|
||||
import LanguageSelect from './language-select.vue';
|
||||
import { computed, defineComponent, PropType, Ref, ref, toRefs, watch, unref } from 'vue';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import { useFieldsStore, useRelationsStore } from '@/stores/';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import api from '@/api';
|
||||
import { Relation } from '@/types';
|
||||
import { getFieldsFromTemplate } from '@/utils/get-fields-from-template';
|
||||
import DrawerItem from '@/views/private/components/drawer-item/drawer-item.vue';
|
||||
import { useCollection } from '@/composables/use-collection';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { isPlainObject } from 'lodash';
|
||||
import { cloneDeep, isEqual, assign } from 'lodash';
|
||||
import { notEmpty } from '@/utils/is-empty';
|
||||
import { useWindowSize } from '@/composables/use-window-size';
|
||||
|
||||
export default defineComponent({
|
||||
components: { DrawerItem },
|
||||
components: { LanguageSelect },
|
||||
props: {
|
||||
collection: {
|
||||
type: String,
|
||||
@@ -62,23 +74,37 @@ export default defineComponent({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
languageTemplate: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
translationsTemplate: {
|
||||
languageField: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
value: {
|
||||
type: Array as PropType<(string | number | Record<string, any>)[]>,
|
||||
default: () => [],
|
||||
type: Array as PropType<(string | number | Record<string, any>)[] | null>,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: ['input'],
|
||||
setup(props, { emit }) {
|
||||
const { collection } = toRefs(props);
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const { width } = useWindowSize();
|
||||
|
||||
const splitView = ref(false);
|
||||
const firstLang = ref<string | number>();
|
||||
const secondLang = ref<string | number>();
|
||||
|
||||
const { info: collectionInfo } = useCollection(collection);
|
||||
|
||||
watch(splitView, (splitViewEnabled) => {
|
||||
const lang = languageOptions.value;
|
||||
|
||||
if (splitViewEnabled && secondLang.value === firstLang.value) {
|
||||
secondLang.value = lang[0].value === firstLang.value ? lang[1].value : lang[0].value;
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
relationsForField,
|
||||
@@ -91,29 +117,57 @@ export default defineComponent({
|
||||
translationsLanguageField,
|
||||
} = useRelations();
|
||||
|
||||
const { languages, loading: languagesLoading, template: internalLanguageTemplate } = useLanguages();
|
||||
const { startEditing, editing, edits, stageEdits, cancelEdit } = useEdits();
|
||||
const { previewItems, template: internalTranslationsTemplate, loading: previewLoading } = usePreview();
|
||||
const { languageOptions, loading: languagesLoading } = useLanguages();
|
||||
const {
|
||||
items,
|
||||
firstItem,
|
||||
loading: valuesLoading,
|
||||
updateValue,
|
||||
secondItem,
|
||||
firstItemInitial,
|
||||
secondItemInitial,
|
||||
} = useEdits();
|
||||
|
||||
const fields = computed(() => {
|
||||
if (translationsCollection.value === null) return [];
|
||||
return fieldsStore.getFieldsForCollection(translationsCollection.value);
|
||||
});
|
||||
|
||||
const splitViewAvailable = computed(() => {
|
||||
return width.value > 960;
|
||||
});
|
||||
|
||||
const splitViewEnabled = computed(() => {
|
||||
return splitViewAvailable.value && splitView.value;
|
||||
});
|
||||
|
||||
return {
|
||||
collectionInfo,
|
||||
splitView,
|
||||
firstLang,
|
||||
secondLang,
|
||||
t,
|
||||
languageOptions,
|
||||
fields,
|
||||
relationsForField,
|
||||
translationsRelation,
|
||||
translationsCollection,
|
||||
translationsPrimaryKeyField,
|
||||
languagesRelation,
|
||||
languages,
|
||||
internalLanguageTemplate,
|
||||
internalTranslationsTemplate,
|
||||
languagesCollection,
|
||||
languagesPrimaryKeyField,
|
||||
languagesLoading,
|
||||
startEditing,
|
||||
translationsLanguageField,
|
||||
editing,
|
||||
stageEdits,
|
||||
cancelEdit,
|
||||
edits,
|
||||
previewItems,
|
||||
previewLoading,
|
||||
items,
|
||||
firstItem,
|
||||
secondItem,
|
||||
updateValue,
|
||||
relationsStore,
|
||||
firstItemInitial,
|
||||
secondItemInitial,
|
||||
splitViewAvailable,
|
||||
splitViewEnabled,
|
||||
languagesLoading,
|
||||
valuesLoading,
|
||||
};
|
||||
|
||||
function useRelations() {
|
||||
@@ -174,40 +228,84 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function useLanguages() {
|
||||
const languages = ref<Record<string, any>[]>();
|
||||
const languages = ref<Record<string, any>[]>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref<any>(null);
|
||||
|
||||
const { info: languagesCollectionInfo } = useCollection(languagesCollection);
|
||||
|
||||
const template = computed(() => {
|
||||
if (!languagesPrimaryKeyField.value) return '';
|
||||
|
||||
return (
|
||||
props.languageTemplate ||
|
||||
languagesCollectionInfo.value?.meta?.display_template ||
|
||||
`{{ ${languagesPrimaryKeyField.value} }}`
|
||||
);
|
||||
});
|
||||
|
||||
watch(languagesCollection, fetchLanguages, { immediate: true });
|
||||
|
||||
return { languages, loading, error, template };
|
||||
const languageOptions = computed(() => {
|
||||
const langField = translationsLanguageField.value;
|
||||
|
||||
if (langField === null) return [];
|
||||
|
||||
const writableFields = fields.value.filter(
|
||||
(field) => field.type !== 'alias' && field.meta?.hidden === false && field.meta.readonly === false
|
||||
);
|
||||
|
||||
const totalFields = writableFields.length;
|
||||
|
||||
return languages.value.map((language) => {
|
||||
if (languagesPrimaryKeyField.value === null) return language;
|
||||
|
||||
const langCode = language[languagesPrimaryKeyField.value];
|
||||
|
||||
const initialValue = items.value.find((item) => item[langField] === langCode) ?? {};
|
||||
|
||||
const edits = props.value?.find((val) => typeof val === 'object' && val[langField] === langCode) as
|
||||
| Record<string, any>
|
||||
| undefined;
|
||||
|
||||
const item = { ...initialValue, ...(edits ?? {}) };
|
||||
|
||||
const filledFields = writableFields.filter((field) => {
|
||||
return field.field in item && notEmpty(item[field.field]);
|
||||
}).length;
|
||||
|
||||
return {
|
||||
text: language[props.languageField ?? languagesPrimaryKeyField.value],
|
||||
value: langCode,
|
||||
edited: edits !== undefined,
|
||||
progress: (filledFields / totalFields) * 100,
|
||||
max: totalFields,
|
||||
current: filledFields,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return { languageOptions, loading, error };
|
||||
|
||||
async function fetchLanguages() {
|
||||
if (!languagesCollection.value || !languagesPrimaryKeyField.value) return;
|
||||
|
||||
const fields = getFieldsFromTemplate(template.value);
|
||||
const fields = new Set<string>();
|
||||
|
||||
if (fields.includes(languagesPrimaryKeyField.value) === false) {
|
||||
fields.push(languagesPrimaryKeyField.value);
|
||||
if (props.languageField !== null) {
|
||||
fields.add(props.languageField);
|
||||
}
|
||||
|
||||
fields.add(languagesPrimaryKeyField.value);
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/items/${languagesCollection.value}`, { params: { fields, limit: -1 } });
|
||||
const response = await api.get(`/items/${languagesCollection.value}`, {
|
||||
params: {
|
||||
fields: Array.from(fields),
|
||||
limit: -1,
|
||||
sort: props.languageField ?? languagesPrimaryKeyField.value,
|
||||
},
|
||||
});
|
||||
|
||||
languages.value = response.data.data;
|
||||
|
||||
if (!firstLang.value) {
|
||||
firstLang.value = response.data.data?.[0]?.[languagesPrimaryKeyField.value];
|
||||
}
|
||||
|
||||
if (!secondLang.value) {
|
||||
secondLang.value = response.data.data?.[1]?.[languagesPrimaryKeyField.value];
|
||||
}
|
||||
} catch (err: any) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
@@ -217,234 +315,174 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function useEdits() {
|
||||
const keyMap = ref<Record<string, string | number>[]>();
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref<any>(null);
|
||||
|
||||
const editing = ref<boolean | string | number>(false);
|
||||
const edits = ref<Record<string, any>>();
|
||||
|
||||
const existingPrimaryKeys = computed(() => {
|
||||
const pkField = translationsPrimaryKeyField.value;
|
||||
if (!pkField) return [];
|
||||
return (props.value || [])
|
||||
.map((value) => {
|
||||
if (typeof value === 'string' || typeof value === 'number') return value;
|
||||
return value[pkField];
|
||||
})
|
||||
.filter((key) => key);
|
||||
});
|
||||
|
||||
watch(() => props.value, fetchKeyMap, { immediate: true });
|
||||
|
||||
return { startEditing, editing, edits, stageEdits, cancelEdit };
|
||||
|
||||
function startEditing(language: string | number) {
|
||||
if (!translationsLanguageField.value || !translationsPrimaryKeyField.value) return;
|
||||
|
||||
edits.value = {
|
||||
[translationsLanguageField.value]: language,
|
||||
};
|
||||
|
||||
const existingEdits = (props.value || []).find((val) => {
|
||||
if (typeof val === 'string' || typeof val === 'number') return false;
|
||||
return val[translationsLanguageField.value!] === language;
|
||||
});
|
||||
|
||||
if (existingEdits) {
|
||||
edits.value = {
|
||||
...edits.value,
|
||||
...(existingEdits as Record<string, any>),
|
||||
};
|
||||
}
|
||||
|
||||
const primaryKey =
|
||||
keyMap.value?.find((record) => record[translationsLanguageField.value!] === language)?.[
|
||||
translationsPrimaryKeyField.value
|
||||
] || '+';
|
||||
|
||||
if (primaryKey !== '+') {
|
||||
edits.value = {
|
||||
...edits.value,
|
||||
[translationsPrimaryKeyField.value]: primaryKey,
|
||||
};
|
||||
}
|
||||
|
||||
editing.value = primaryKey;
|
||||
}
|
||||
|
||||
async function fetchKeyMap() {
|
||||
if (!props.value) return;
|
||||
if (keyMap.value) return;
|
||||
if (!existingPrimaryKeys.value?.length) return;
|
||||
const pkField = translationsPrimaryKeyField.value;
|
||||
if (!pkField) return;
|
||||
|
||||
const collection = translationsRelation.value?.collection;
|
||||
|
||||
if (!collection) return;
|
||||
|
||||
const fields = [pkField, translationsLanguageField.value];
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/items/${collection}`, {
|
||||
params: {
|
||||
fields,
|
||||
filter: {
|
||||
[pkField]: {
|
||||
_in: existingPrimaryKeys.value,
|
||||
},
|
||||
},
|
||||
limit: -1,
|
||||
},
|
||||
});
|
||||
|
||||
keyMap.value = response.data.data;
|
||||
} catch (err: any) {
|
||||
error.value = err;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function stageEdits(edits: any) {
|
||||
if (!translationsLanguageField.value) return;
|
||||
const pkField = translationsPrimaryKeyField.value;
|
||||
if (!pkField) return;
|
||||
|
||||
const editedLanguage = edits[translationsLanguageField.value];
|
||||
|
||||
const languageAlreadyEdited = !!(props.value || []).find((val) => {
|
||||
if (typeof val === 'string' || typeof val === 'number') return false;
|
||||
return val[translationsLanguageField.value!] === editedLanguage;
|
||||
});
|
||||
|
||||
if (languageAlreadyEdited === true) {
|
||||
emit(
|
||||
'input',
|
||||
props.value.map((val) => {
|
||||
if (typeof val === 'string' || typeof val === 'number') return val;
|
||||
|
||||
if (val[translationsLanguageField.value!] === editedLanguage) {
|
||||
return edits;
|
||||
}
|
||||
|
||||
return val;
|
||||
})
|
||||
);
|
||||
} else {
|
||||
if (editing.value === '+') {
|
||||
emit('input', [...(props.value || []), edits]);
|
||||
} else {
|
||||
emit(
|
||||
'input',
|
||||
props.value.map((val) => {
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
if (val === editing.value) return edits;
|
||||
} else {
|
||||
if (val[pkField] === editing.value) return edits;
|
||||
}
|
||||
|
||||
return val;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
editing.value = false;
|
||||
}
|
||||
|
||||
function cancelEdit() {
|
||||
edits.value = {};
|
||||
editing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function usePreview() {
|
||||
const items = ref<Record<string, any>[]>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const previewItems = ref<Record<string, any>[]>([]);
|
||||
|
||||
const { info: translationsCollectionInfo } = useCollection(translationsCollection);
|
||||
const firstItem = computed(() => getEditedValue(firstLang));
|
||||
const secondItem = computed(() => getEditedValue(secondLang));
|
||||
|
||||
const template = computed(() => {
|
||||
if (!translationsPrimaryKeyField.value) return '';
|
||||
const firstItemInitial = computed<Record<string, any>>(() => getExistingValue(firstLang));
|
||||
const secondItemInitial = computed<Record<string, any>>(() => getExistingValue(secondLang));
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newVal, oldVal) => {
|
||||
if (
|
||||
newVal &&
|
||||
newVal !== oldVal &&
|
||||
newVal?.every((item) => typeof item === 'string' || typeof item === 'number')
|
||||
) {
|
||||
loadItems();
|
||||
}
|
||||
|
||||
if (newVal === null || newVal.length === 0) {
|
||||
items.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { items, firstItem, updateValue, secondItem, firstItemInitial, secondItemInitial, loading, error };
|
||||
|
||||
function getExistingValue(langRef: string | number | undefined | Ref<string | number | undefined>) {
|
||||
const lang = unref(langRef);
|
||||
|
||||
const langField = translationsLanguageField.value;
|
||||
if (langField === null) return {};
|
||||
|
||||
return (items.value.find((item) => item[langField] === lang) as Record<string, any>) ?? {};
|
||||
}
|
||||
|
||||
function getEditedValue(langRef: string | number | undefined | Ref<string | number | undefined>) {
|
||||
const lang = unref(langRef);
|
||||
|
||||
const langField = translationsLanguageField.value;
|
||||
if (langField === null) return {};
|
||||
|
||||
return (
|
||||
props.translationsTemplate ||
|
||||
translationsCollectionInfo.value?.meta?.display_template ||
|
||||
`{{ ${translationsPrimaryKeyField.value} }}`
|
||||
(props.value?.find((item) => typeof item === 'object' && item[langField] === lang) as Record<string, any>) ??
|
||||
{}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
watch(() => props.value, fetchPreviews, { immediate: true });
|
||||
watch(languages, fetchPreviews, { immediate: true });
|
||||
async function loadItems() {
|
||||
const pkField = translationsPrimaryKeyField.value;
|
||||
|
||||
return { loading, error, previewItems, fetchPreviews, template };
|
||||
|
||||
async function fetchPreviews() {
|
||||
if (!translationsRelation.value || !languagesRelation.value || !languages.value) return;
|
||||
|
||||
if (props.primaryKey === '+') return;
|
||||
if (pkField === null || !props.value || props.value.length === 0) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const fields = getFieldsFromTemplate(template.value);
|
||||
|
||||
if (fields.includes(languagesRelation.value.field) === false) {
|
||||
fields.push(languagesRelation.value.field);
|
||||
}
|
||||
|
||||
const existing = await api.get(`/items/${translationsCollection.value}`, {
|
||||
const response = await api.get(`/items/${translationsCollection.value}`, {
|
||||
params: {
|
||||
fields,
|
||||
fields: '*',
|
||||
limit: -1,
|
||||
filter: {
|
||||
[translationsRelation.value.field]: {
|
||||
_eq: props.primaryKey,
|
||||
[pkField]: {
|
||||
_in: props.value,
|
||||
},
|
||||
},
|
||||
limit: -1,
|
||||
},
|
||||
});
|
||||
|
||||
previewItems.value = languages.value.map((language) => {
|
||||
const pkField = languagesPrimaryKeyField.value;
|
||||
if (!pkField) return;
|
||||
|
||||
const existingEdit =
|
||||
props.value && Array.isArray(props.value)
|
||||
? (props.value.find(
|
||||
(edit) =>
|
||||
isPlainObject(edit) &&
|
||||
(edit as Record<string, any>)[languagesRelation.value!.field] === language[pkField]
|
||||
) as Record<string, any>)
|
||||
: {};
|
||||
|
||||
return {
|
||||
...(existing.data.data?.find(
|
||||
(item: Record<string, any>) => item[languagesRelation.value!.field] === language[pkField]
|
||||
) ?? {}),
|
||||
...existingEdit,
|
||||
};
|
||||
});
|
||||
} catch (err: any) {
|
||||
items.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
previewItems.value = [];
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function updateValue(edits: Record<string, any>, lang: string) {
|
||||
const pkField = translationsPrimaryKeyField.value;
|
||||
const langField = translationsLanguageField.value;
|
||||
|
||||
const existing = getExistingValue(lang);
|
||||
|
||||
const values = assign({}, existing, edits);
|
||||
|
||||
if (pkField === null || langField === null) return;
|
||||
|
||||
let copyValue = cloneDeep(props.value ?? []);
|
||||
|
||||
if (pkField in values === false) {
|
||||
const newIndex = copyValue.findIndex((item) => typeof item === 'object' && item[langField] === lang);
|
||||
|
||||
if (newIndex !== -1) {
|
||||
if (Object.keys(values).length === 1 && langField in values) {
|
||||
copyValue.splice(newIndex, 1);
|
||||
} else {
|
||||
copyValue[newIndex] = values;
|
||||
}
|
||||
} else {
|
||||
copyValue.push({
|
||||
...values,
|
||||
[langField]: lang,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const initialValues = items.value.find((item) => item[langField] === lang);
|
||||
|
||||
copyValue = copyValue.map((item) => {
|
||||
if (typeof item === 'number' || typeof item === 'string') {
|
||||
if (values[pkField] === item) {
|
||||
return values;
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
} else {
|
||||
if (values[pkField] === item[pkField]) {
|
||||
if (isEqual(initialValues, { ...initialValues, ...values })) {
|
||||
return values[pkField];
|
||||
} else {
|
||||
return values;
|
||||
}
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
emit('input', copyValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.preview {
|
||||
color: var(--foreground-subdued);
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/mixins/form-grid';
|
||||
|
||||
.translations {
|
||||
@include form-grid;
|
||||
|
||||
.v-form {
|
||||
--form-vertical-gap: 32px;
|
||||
--v-chip-color: var(--primary);
|
||||
--v-chip-background-color: var(--primary-alt);
|
||||
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.v-divider {
|
||||
margin-top: var(--form-vertical-gap);
|
||||
}
|
||||
|
||||
.primary {
|
||||
--v-divider-color: var(--primary-50);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
--v-divider-color: var(--blue-50);
|
||||
|
||||
.v-form {
|
||||
--primary: var(--blue);
|
||||
--v-chip-color: var(--blue);
|
||||
--v-chip-background-color: var(--blue-alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1288,6 +1288,8 @@ interfaces:
|
||||
translations:
|
||||
display_template: Display Template
|
||||
no_collection: No Collection
|
||||
toggle_split_view: Toggle Split View
|
||||
language_field: Language Field
|
||||
list-o2m-tree-view:
|
||||
description: Tree view for nested recursive one-to-many items
|
||||
recursive_only: The tree view interface only works for recursive relationships.
|
||||
|
||||
261
changelog.md
261
changelog.md
@@ -6,118 +6,205 @@ _Changes marked with a :warning: contain potential breaking changes depending on
|
||||
|
||||
### ⚠️ Potential Breaking Changes
|
||||
|
||||
- Custom API endpoints no longer use a `/custom` prefix. Please update your usage of custom endpoints to drop the `/custom` prefix See [#7695](https://github.com/directus/directus/pull/7695)
|
||||
- Layouts use a revised setup that relies on props instead of `useState`. See [#7489](https://github.com/directus/directus/pull/7489)
|
||||
- Custom API endpoints no longer use a `/custom` prefix. Please update your usage of custom endpoints to drop the
|
||||
`/custom` prefix See [#7695](https://github.com/directus/directus/pull/7695)
|
||||
- Layouts use a revised setup that relies on props instead of `useState`. See
|
||||
[#7489](https://github.com/directus/directus/pull/7489)
|
||||
|
||||
### :sparkles: New Features
|
||||
|
||||
- **API**
|
||||
- [#7789](https://github.com/directus/directus/pull/7789) Add environment variable to force-exclude tables from Directus ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7777](https://github.com/directus/directus/pull/7777) Expose logger through ExtensionContext ([@Moeriki](https://github.com/Moeriki))
|
||||
- [#7759](https://github.com/directus/directus/pull/7759) Show a warning if PostGIS is missing ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7789](https://github.com/directus/directus/pull/7789) Add environment variable to force-exclude tables from
|
||||
Directus ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7777](https://github.com/directus/directus/pull/7777) Expose logger through ExtensionContext
|
||||
([@Moeriki](https://github.com/Moeriki))
|
||||
- [#7759](https://github.com/directus/directus/pull/7759) Show a warning if PostGIS is missing
|
||||
([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- **App**
|
||||
- [#7605](https://github.com/directus/directus/pull/7605) Add search result highlighting to tree-view interface ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7605](https://github.com/directus/directus/pull/7605) Add search result highlighting to tree-view interface
|
||||
([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
|
||||
### :rocket: Improvements
|
||||
|
||||
- **App**
|
||||
- [#7749](https://github.com/directus/directus/pull/7749) Disable attribute inheritance for all layout components ([@nickrum](https://github.com/nickrum))
|
||||
- [#7738](https://github.com/directus/directus/pull/7738) Warn the user when a collapsed group field had an error ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7687](https://github.com/directus/directus/pull/7687) Resolve editor/type warnings ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7668](https://github.com/directus/directus/pull/7668) Replace system provide with composables ([@nickrum](https://github.com/nickrum))
|
||||
- [#7650](https://github.com/directus/directus/pull/7650) Allow to select system collections in m2a ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7583](https://github.com/directus/directus/pull/7583) Display private images in WYSIWYG editor ([@jaycammarano](https://github.com/jaycammarano))
|
||||
- [#7578](https://github.com/directus/directus/pull/7578) Add `search this area` button to map layout. ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7563](https://github.com/directus/directus/pull/7563) Move basemap input higher in sidebar options. Keep map interactive under v-info ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7535](https://github.com/directus/directus/pull/7535) Allow using regular input interface on TEXT type fields ([@alexkharech](https://github.com/alexkharech))
|
||||
- [#7749](https://github.com/directus/directus/pull/7749) Disable attribute inheritance for all layout components
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- [#7738](https://github.com/directus/directus/pull/7738) Warn the user when a collapsed group field had an error
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7687](https://github.com/directus/directus/pull/7687) Resolve editor/type warnings
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7668](https://github.com/directus/directus/pull/7668) Replace system provide with composables
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- [#7650](https://github.com/directus/directus/pull/7650) Allow to select system collections in m2a
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7583](https://github.com/directus/directus/pull/7583) Display private images in WYSIWYG editor
|
||||
([@jaycammarano](https://github.com/jaycammarano))
|
||||
- [#7578](https://github.com/directus/directus/pull/7578) Add `search this area` button to map layout.
|
||||
([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7563](https://github.com/directus/directus/pull/7563) Move basemap input higher in sidebar options. Keep map
|
||||
interactive under v-info ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7535](https://github.com/directus/directus/pull/7535) Allow using regular input interface on TEXT type fields
|
||||
([@alexkharech](https://github.com/alexkharech))
|
||||
- **Extensions**
|
||||
- [#7714](https://github.com/directus/directus/pull/7714) Improve API extension context types ([@nickrum](https://github.com/nickrum))
|
||||
- :warning: [#7695](https://github.com/directus/directus/pull/7695) Remove /custom subpath for endpoints and add a way to customize the endpoint subpath ([@nickrum](https://github.com/nickrum))
|
||||
- [#7668](https://github.com/directus/directus/pull/7668) Replace system provide with composables ([@nickrum](https://github.com/nickrum))
|
||||
- [#7629](https://github.com/directus/directus/pull/7629) Share vue-router between App and extensions ([@nickrum](https://github.com/nickrum))
|
||||
- [#7627](https://github.com/directus/directus/pull/7627) Allow json imports and replace NODE_ENV env var when building extensions ([@nickrum](https://github.com/nickrum))
|
||||
- [#7714](https://github.com/directus/directus/pull/7714) Improve API extension context types
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- :warning: [#7695](https://github.com/directus/directus/pull/7695) Remove /custom subpath for endpoints and add a way
|
||||
to customize the endpoint subpath ([@nickrum](https://github.com/nickrum))
|
||||
- [#7668](https://github.com/directus/directus/pull/7668) Replace system provide with composables
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- [#7629](https://github.com/directus/directus/pull/7629) Share vue-router between App and extensions
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- [#7627](https://github.com/directus/directus/pull/7627) Allow json imports and replace NODE_ENV env var when
|
||||
building extensions ([@nickrum](https://github.com/nickrum))
|
||||
- **API**
|
||||
- [#7711](https://github.com/directus/directus/pull/7711) Remove permission.limit ([@Nitwel](https://github.com/Nitwel))
|
||||
- :warning: [#7695](https://github.com/directus/directus/pull/7695) Remove /custom subpath for endpoints and add a way to customize the endpoint subpath ([@nickrum](https://github.com/nickrum))
|
||||
- [#7604](https://github.com/directus/directus/pull/7604) Log localhost url on startup so it's clickable in terminals ([@zebapy](https://github.com/zebapy))
|
||||
- [#7711](https://github.com/directus/directus/pull/7711) Remove permission.limit
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- :warning: [#7695](https://github.com/directus/directus/pull/7695) Remove /custom subpath for endpoints and add a way
|
||||
to customize the endpoint subpath ([@nickrum](https://github.com/nickrum))
|
||||
- [#7604](https://github.com/directus/directus/pull/7604) Log localhost url on startup so it's clickable in terminals
|
||||
([@zebapy](https://github.com/zebapy))
|
||||
|
||||
### :bug: Bug Fixes
|
||||
|
||||
- **App**
|
||||
- [#7780](https://github.com/directus/directus/pull/7780) Use OpenMapTiles font instead of ArcGIS ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7778](https://github.com/directus/directus/pull/7778) Fixes bug when trying to edit geometry in code interface. ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7768](https://github.com/directus/directus/pull/7768) Fix hash link in docs module ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7763](https://github.com/directus/directus/pull/7763) Fix branch emitter logic from grand-to-child ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7760](https://github.com/directus/directus/pull/7760) Fix 'Inactive' to 'Invited' translations on user status ([@joselcvarela](https://github.com/joselcvarela))
|
||||
- [#7756](https://github.com/directus/directus/pull/7756) fix WYSIWYG field focus event ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7716](https://github.com/directus/directus/pull/7716) Fix input-code component lint style ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7712](https://github.com/directus/directus/pull/7712) Prevent generated columns edition ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7703](https://github.com/directus/directus/pull/7703) Fix alignment of collection nav grouping ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7698](https://github.com/directus/directus/pull/7698) Add permission prop check ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7697](https://github.com/directus/directus/pull/7697) Add upload event for file imports ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7684](https://github.com/directus/directus/pull/7684) Add missing translations ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7683](https://github.com/directus/directus/pull/7683) Move related values link to icon ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7682](https://github.com/directus/directus/pull/7682) Fix firefox being buggy with numbers as value inputs ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7669](https://github.com/directus/directus/pull/7669) Add missing translations ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7666](https://github.com/directus/directus/pull/7666) Fix items not getting matched properly ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7635](https://github.com/directus/directus/pull/7635) Prevent collection from crashing on unknown layout ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7632](https://github.com/directus/directus/pull/7632) Assign edits instead of merge ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7631](https://github.com/directus/directus/pull/7631) Fix o2m flashing / reloading when typing ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7780](https://github.com/directus/directus/pull/7780) Use OpenMapTiles font instead of ArcGIS
|
||||
([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7778](https://github.com/directus/directus/pull/7778) Fixes bug when trying to edit geometry in code interface.
|
||||
([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7768](https://github.com/directus/directus/pull/7768) Fix hash link in docs module
|
||||
([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7763](https://github.com/directus/directus/pull/7763) Fix branch emitter logic from grand-to-child
|
||||
([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7760](https://github.com/directus/directus/pull/7760) Fix 'Inactive' to 'Invited' translations on user status
|
||||
([@joselcvarela](https://github.com/joselcvarela))
|
||||
- [#7756](https://github.com/directus/directus/pull/7756) fix WYSIWYG field focus event
|
||||
([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7716](https://github.com/directus/directus/pull/7716) Fix input-code component lint style
|
||||
([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7712](https://github.com/directus/directus/pull/7712) Prevent generated columns edition
|
||||
([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7703](https://github.com/directus/directus/pull/7703) Fix alignment of collection nav grouping
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7698](https://github.com/directus/directus/pull/7698) Add permission prop check
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7697](https://github.com/directus/directus/pull/7697) Add upload event for file imports
|
||||
([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7684](https://github.com/directus/directus/pull/7684) Add missing translations
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7683](https://github.com/directus/directus/pull/7683) Move related values link to icon
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7682](https://github.com/directus/directus/pull/7682) Fix firefox being buggy with numbers as value inputs
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7669](https://github.com/directus/directus/pull/7669) Add missing translations
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7666](https://github.com/directus/directus/pull/7666) Fix items not getting matched properly
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7635](https://github.com/directus/directus/pull/7635) Prevent collection from crashing on unknown layout
|
||||
([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7632](https://github.com/directus/directus/pull/7632) Assign edits instead of merge
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7631](https://github.com/directus/directus/pull/7631) Fix o2m flashing / reloading when typing
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7628](https://github.com/directus/directus/pull/7628) Truely unref item ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7602](https://github.com/directus/directus/pull/7602) Add mapbox-key to map interface initialization ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7599](https://github.com/directus/directus/pull/7599) Check if perms have edits ([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7562](https://github.com/directus/directus/pull/7562) Fix calendar layout not opening detail pages for system collections ([@azrikahar](https://github.com/azrikahar))
|
||||
- :warning: [#7489](https://github.com/directus/directus/pull/7489) Rework layout extension component management ([@nickrum](https://github.com/nickrum))
|
||||
- [#7602](https://github.com/directus/directus/pull/7602) Add mapbox-key to map interface initialization
|
||||
([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7599](https://github.com/directus/directus/pull/7599) Check if perms have edits
|
||||
([@Nitwel](https://github.com/Nitwel))
|
||||
- [#7562](https://github.com/directus/directus/pull/7562) Fix calendar layout not opening detail pages for system
|
||||
collections ([@azrikahar](https://github.com/azrikahar))
|
||||
- :warning: [#7489](https://github.com/directus/directus/pull/7489) Rework layout extension component management
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- Update WYSIWYG styling ([@benhaynes](https://github.com/benhaynes))
|
||||
- **Extensions**
|
||||
- [#7624](https://github.com/directus/directus/pull/7624) Enable browser module resolution when building app extensions ([@nickrum](https://github.com/nickrum))
|
||||
- [#7624](https://github.com/directus/directus/pull/7624) Enable browser module resolution when building app
|
||||
extensions ([@nickrum](https://github.com/nickrum))
|
||||
- **API**
|
||||
- [#7581](https://github.com/directus/directus/pull/7581) Fix uploaded_by not always setting user ([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7568](https://github.com/directus/directus/pull/7568) fix(api): merge original user object into payload from auth hook ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7561](https://github.com/directus/directus/pull/7561) Handle difference between `pg` and `postgres` as db client in geometry helper ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7553](https://github.com/directus/directus/pull/7553) Fix asset transformation `withEnlargement` type ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7581](https://github.com/directus/directus/pull/7581) Fix uploaded_by not always setting user
|
||||
([@rijkvanzanten](https://github.com/rijkvanzanten))
|
||||
- [#7568](https://github.com/directus/directus/pull/7568) fix(api): merge original user object into payload from auth
|
||||
hook ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7561](https://github.com/directus/directus/pull/7561) Handle difference between `pg` and `postgres` as db client
|
||||
in geometry helper ([@Oreilles](https://github.com/Oreilles))
|
||||
- [#7553](https://github.com/directus/directus/pull/7553) Fix asset transformation `withEnlargement` type
|
||||
([@azrikahar](https://github.com/azrikahar))
|
||||
|
||||
### :memo: Documentation
|
||||
|
||||
- [#7771](https://github.com/directus/directus/pull/7771) tiny rewrite of operator descriptions in docs/reference/filter-rules ([@definiteIymaybe](https://github.com/definiteIymaybe))
|
||||
- [#7757](https://github.com/directus/directus/pull/7757) Document usage of custom reset URL in request password in the SDK ([@joselcvarela](https://github.com/joselcvarela))
|
||||
- [#7750](https://github.com/directus/directus/pull/7750) Update layout docs to new layouts system ([@nickrum](https://github.com/nickrum))
|
||||
- [#7648](https://github.com/directus/directus/pull/7648) Update mentions of Vue 2 to Vue 3 in codebase-overview.md ([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7586](https://github.com/directus/directus/pull/7586) Add installation guide for plesk/shared hosting ([@Tummerhore](https://github.com/Tummerhore))
|
||||
- [#7771](https://github.com/directus/directus/pull/7771) tiny rewrite of operator descriptions in
|
||||
docs/reference/filter-rules ([@definiteIymaybe](https://github.com/definiteIymaybe))
|
||||
- [#7757](https://github.com/directus/directus/pull/7757) Document usage of custom reset URL in request password in the
|
||||
SDK ([@joselcvarela](https://github.com/joselcvarela))
|
||||
- [#7750](https://github.com/directus/directus/pull/7750) Update layout docs to new layouts system
|
||||
([@nickrum](https://github.com/nickrum))
|
||||
- [#7648](https://github.com/directus/directus/pull/7648) Update mentions of Vue 2 to Vue 3 in codebase-overview.md
|
||||
([@azrikahar](https://github.com/azrikahar))
|
||||
- [#7586](https://github.com/directus/directus/pull/7586) Add installation guide for plesk/shared hosting
|
||||
([@Tummerhore](https://github.com/Tummerhore))
|
||||
|
||||
### :package: Dependency Updates
|
||||
|
||||
- [#7786](https://github.com/directus/directus/pull/7786) Update dependency npm to v7.22.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7785](https://github.com/directus/directus/pull/7785) Update vue monorepo to v3.2.8 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7770](https://github.com/directus/directus/pull/7770) Update dependency sass to v1.39.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7769](https://github.com/directus/directus/pull/7769) Update dependency knex-schema-inspector to v1.6.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7766](https://github.com/directus/directus/pull/7766) Update vue monorepo to v3.2.7 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7752](https://github.com/directus/directus/pull/7752) Update dependency vite to v2.5.3 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7742](https://github.com/directus/directus/pull/7742) Update dependency @types/sharp to v0.28.6 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7728](https://github.com/directus/directus/pull/7728) Update gatsby monorepo to v3.13.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7718](https://github.com/directus/directus/pull/7718) Update dependency knex-schema-inspector to v1.5.15 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7715](https://github.com/directus/directus/pull/7715) Update dependency vite to v2.5.2 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7708](https://github.com/directus/directus/pull/7708) Update dependency knex-schema-inspector to v1.5.14 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7705](https://github.com/directus/directus/pull/7705) Update dependency eslint-plugin-prettier to v4 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7704](https://github.com/directus/directus/pull/7704) Update typescript-eslint monorepo to v4.30.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7690](https://github.com/directus/directus/pull/7690) Update dependency micromark to v3 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7672](https://github.com/directus/directus/pull/7672) Update dependency sass to v1.38.2 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7656](https://github.com/directus/directus/pull/7656) update jest monorepo to v27.1.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7655](https://github.com/directus/directus/pull/7655) update dependency @types/markdown-it to v12.2.1 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7646](https://github.com/directus/directus/pull/7646) update dependency tinymce to v5.9.1 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7643](https://github.com/directus/directus/pull/7643) update dependency eslint-plugin-vue to v7.17.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7638](https://github.com/directus/directus/pull/7638) update dependency typescript to v4.4.2 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7614](https://github.com/directus/directus/pull/7614) update dependency tinymce to v5.9.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7606](https://github.com/directus/directus/pull/7606) pin dependencies ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7595](https://github.com/directus/directus/pull/7595) update dependency nock to v13.1.3 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7582](https://github.com/directus/directus/pull/7582) pin dependency @types/supertest to 2.0.11 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7580](https://github.com/directus/directus/pull/7580) update dependency @vitejs/plugin-vue to v1.6.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7579](https://github.com/directus/directus/pull/7579) update vue monorepo to v3.2.6 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7576](https://github.com/directus/directus/pull/7576) update vue monorepo to v3.2.5 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7571](https://github.com/directus/directus/pull/7571) update dependency @vitejs/plugin-vue to v1.5.0 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7570](https://github.com/directus/directus/pull/7570) update dependency vite to v2.5.1 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7558](https://github.com/directus/directus/pull/7558) update dependency sass to v1.38.1 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7556](https://github.com/directus/directus/pull/7556) update dependency @types/marked to v2.0.5 ([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7786](https://github.com/directus/directus/pull/7786) Update dependency npm to v7.22.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7785](https://github.com/directus/directus/pull/7785) Update vue monorepo to v3.2.8
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7770](https://github.com/directus/directus/pull/7770) Update dependency sass to v1.39.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7769](https://github.com/directus/directus/pull/7769) Update dependency knex-schema-inspector to v1.6.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7766](https://github.com/directus/directus/pull/7766) Update vue monorepo to v3.2.7
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7752](https://github.com/directus/directus/pull/7752) Update dependency vite to v2.5.3
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7742](https://github.com/directus/directus/pull/7742) Update dependency @types/sharp to v0.28.6
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7728](https://github.com/directus/directus/pull/7728) Update gatsby monorepo to v3.13.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7718](https://github.com/directus/directus/pull/7718) Update dependency knex-schema-inspector to v1.5.15
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7715](https://github.com/directus/directus/pull/7715) Update dependency vite to v2.5.2
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7708](https://github.com/directus/directus/pull/7708) Update dependency knex-schema-inspector to v1.5.14
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7705](https://github.com/directus/directus/pull/7705) Update dependency eslint-plugin-prettier to v4
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7704](https://github.com/directus/directus/pull/7704) Update typescript-eslint monorepo to v4.30.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7690](https://github.com/directus/directus/pull/7690) Update dependency micromark to v3
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7672](https://github.com/directus/directus/pull/7672) Update dependency sass to v1.38.2
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7656](https://github.com/directus/directus/pull/7656) update jest monorepo to v27.1.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7655](https://github.com/directus/directus/pull/7655) update dependency @types/markdown-it to v12.2.1
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7646](https://github.com/directus/directus/pull/7646) update dependency tinymce to v5.9.1
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7643](https://github.com/directus/directus/pull/7643) update dependency eslint-plugin-vue to v7.17.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7638](https://github.com/directus/directus/pull/7638) update dependency typescript to v4.4.2
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7614](https://github.com/directus/directus/pull/7614) update dependency tinymce to v5.9.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7606](https://github.com/directus/directus/pull/7606) pin dependencies
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7595](https://github.com/directus/directus/pull/7595) update dependency nock to v13.1.3
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7582](https://github.com/directus/directus/pull/7582) pin dependency @types/supertest to 2.0.11
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7580](https://github.com/directus/directus/pull/7580) update dependency @vitejs/plugin-vue to v1.6.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7579](https://github.com/directus/directus/pull/7579) update vue monorepo to v3.2.6
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7576](https://github.com/directus/directus/pull/7576) update vue monorepo to v3.2.5
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7571](https://github.com/directus/directus/pull/7571) update dependency @vitejs/plugin-vue to v1.5.0
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7570](https://github.com/directus/directus/pull/7570) update dependency vite to v2.5.1
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7558](https://github.com/directus/directus/pull/7558) update dependency sass to v1.38.1
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
- [#7556](https://github.com/directus/directus/pull/7556) update dependency @types/marked to v2.0.5
|
||||
([@renovate[bot]](https://github.com/apps/renovate))
|
||||
|
||||
## v9.0.0-rc.91 (August 23, 2021)
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@ export default {};
|
||||
#### Accessing the API from within your extension
|
||||
|
||||
The Directus App's Vue app instance provides a field called `api`, which can be injected into Vue components using
|
||||
[Vue's inject framework](https://v3.vuejs.org/guide/component-provide-inject.html). This `api` field contains a
|
||||
property called `api`, which is an authenticated Axios instance. Here's an example of how to use it:
|
||||
[Vue's inject framework](https://v3.vuejs.org/guide/component-provide-inject.html). This `api` field contains a property
|
||||
called `api`, which is an authenticated Axios instance. Here's an example of how to use it:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user