mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Fix perf regression in translations (#5041)
* Only translate extensions on language load * Make table-row functional
This commit is contained in:
@@ -70,7 +70,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const interfaces = getInterfaces();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const interfaceExists = computed(() => {
|
||||
return !!interfaces.value.find((inter) => inter.id === props.field?.meta?.interface || 'text-input');
|
||||
|
||||
@@ -175,8 +175,6 @@ export default defineComponent({
|
||||
} else {
|
||||
return 'grid';
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return { formFields: formFieldsParsed, gridClass, isDisabled };
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
<template>
|
||||
<template functional>
|
||||
<tr
|
||||
class="table-row"
|
||||
:class="{ subdued, clickable: hasClickListener }"
|
||||
@click="$emit('click', $event)"
|
||||
:class="{ subdued: props.subdued, clickable: props.hasClickListener }"
|
||||
@click="listeners.click"
|
||||
:style="{
|
||||
'--table-row-height': height + 2 + 'px',
|
||||
'--table-row-height': props.height + 2 + 'px',
|
||||
'--table-row-line-height': 1,
|
||||
}"
|
||||
>
|
||||
<td v-if="showManualSort" class="manual cell" @click.stop>
|
||||
<v-icon name="drag_handle" class="drag-handle" :class="{ 'sorted-manually': sortedManually }" />
|
||||
<td v-if="props.showManualSort" class="manual cell" @click.stop>
|
||||
<v-icon name="drag_handle" class="drag-handle" :class="{ 'sorted-manually': props.sortedManually }" />
|
||||
</td>
|
||||
<td v-if="showSelect" class="select cell" @click.stop>
|
||||
<v-checkbox :inputValue="isSelected" @change="toggleSelect" />
|
||||
|
||||
<td v-if="props.showSelect" class="select cell" @click.stop>
|
||||
<v-checkbox :inputValue="props.isSelected" @change="listeners['item-selected']" />
|
||||
</td>
|
||||
<td class="cell" :class="getClassesForCell(header)" v-for="header in headers" :key="header.value">
|
||||
<slot :name="`item.${header.value}`" :item="item">
|
||||
<v-text-overflow v-if="get(item, header.value)" :text="get(item, header.value)" />
|
||||
|
||||
<td class="cell" :class="`align-${header.align}`" v-for="header in props.headers" :key="header.value">
|
||||
<slot :name="`item.${header.value}`" :item="props.item">
|
||||
<v-text-overflow v-if="props.item[header.value]" :text="props.item[header.value]" />
|
||||
<value-null v-else />
|
||||
</slot>
|
||||
</td>
|
||||
@@ -30,7 +32,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from '@vue/composition-api';
|
||||
import { get } from 'lodash';
|
||||
import { Header } from '../types';
|
||||
|
||||
export default defineComponent({
|
||||
@@ -72,26 +73,6 @@ export default defineComponent({
|
||||
default: 48,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
return { getClassesForCell, toggleSelect, get };
|
||||
|
||||
function getClassesForCell(header: Header) {
|
||||
const classes: string[] = [];
|
||||
|
||||
if (header.align) {
|
||||
classes.push(`align-${header.align}`);
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
function toggleSelect() {
|
||||
emit('item-selected', {
|
||||
item: props.item,
|
||||
value: !props.isSelected,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -62,7 +62,12 @@
|
||||
:has-click-listener="!disabled && hasRowClick"
|
||||
:height="rowHeight"
|
||||
@click="hasRowClick ? $emit('click:row', item) : null"
|
||||
@item-selected="onItemSelected"
|
||||
@item-selected="
|
||||
onItemSelected({
|
||||
item: item,
|
||||
value: !getSelectedState(item),
|
||||
})
|
||||
"
|
||||
>
|
||||
<template v-for="header in _headers" #[`item.${header.value}`]>
|
||||
<slot :item="item" :name="`item.${header.value}`" />
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Field } from '@/types';
|
||||
import { clone } from 'lodash';
|
||||
|
||||
export default function useFormFields(fields: Ref<Field[]>) {
|
||||
const interfaces = getInterfaces();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const formFields = computed(() => {
|
||||
let formFields = clone(fields.value);
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { translateReactive } from '@/utils/translate-object-values';
|
||||
import { ref, Ref } from '@vue/composition-api';
|
||||
import { DisplayConfig } from './types';
|
||||
|
||||
let displaysRaw: Ref<DisplayConfig[]>;
|
||||
let displays: Ref<DisplayConfig[]>;
|
||||
|
||||
export function getDisplays() {
|
||||
if (!displaysRaw) {
|
||||
displaysRaw = ref([]);
|
||||
}
|
||||
|
||||
if (!displays) {
|
||||
displays = ref([]);
|
||||
}
|
||||
|
||||
return translateReactive(displays);
|
||||
return { displays, displaysRaw };
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import api from '@/api';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import asyncPool from 'tiny-async-pool';
|
||||
|
||||
const displays = getDisplays();
|
||||
const { displaysRaw } = getDisplays();
|
||||
|
||||
export async function registerDisplays() {
|
||||
const context = require.context('.', true, /^.*index\.ts$/);
|
||||
@@ -34,9 +34,9 @@ export async function registerDisplays() {
|
||||
console.warn(`Couldn't load custom displays`);
|
||||
}
|
||||
|
||||
displays.value = modules;
|
||||
displaysRaw.value = modules;
|
||||
|
||||
displays.value.forEach((display) => {
|
||||
displaysRaw.value.forEach((display) => {
|
||||
if (typeof display.handler !== 'function') {
|
||||
registerComponent('display-' + display.id, display.handler as Component);
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ export async function hydrate(stores = useStores()) {
|
||||
await userStore.hydrate();
|
||||
|
||||
if (userStore.state.currentUser?.role) {
|
||||
await setLanguage((userStore.state.currentUser?.language as Language) || 'en-US');
|
||||
await Promise.all(stores.filter(({ id }) => id !== 'userStore').map((store) => store.hydrate?.()));
|
||||
await registerModules();
|
||||
await Promise.all(stores.filter(({ id }) => id !== 'userStore').map((store) => store.hydrate?.()));
|
||||
await setLanguage((userStore.state.currentUser?.language as Language) || 'en-US');
|
||||
}
|
||||
} catch (error) {
|
||||
appStore.state.error = error;
|
||||
|
||||
@@ -42,7 +42,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { parent }) {
|
||||
const interfaces = getInterfaces();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const values = inject('values', ref<Record<string, any>>({}));
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const interfaces = getInterfaces();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const values = inject('values', ref<Record<string, any>>({}));
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { translateReactive } from '@/utils/translate-object-values';
|
||||
import { ref, Ref } from '@vue/composition-api';
|
||||
import { InterfaceConfig } from './types';
|
||||
|
||||
let interfacesRaw: Ref<InterfaceConfig[]>;
|
||||
let interfaces: Ref<InterfaceConfig[]>;
|
||||
|
||||
export function getInterfaces() {
|
||||
if (!interfacesRaw) {
|
||||
interfacesRaw = ref([]);
|
||||
}
|
||||
|
||||
if (!interfaces) {
|
||||
interfaces = ref([]);
|
||||
}
|
||||
|
||||
return translateReactive(interfaces);
|
||||
return { interfaces, interfacesRaw };
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import api from '@/api';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import asyncPool from 'tiny-async-pool';
|
||||
|
||||
const interfaces = getInterfaces();
|
||||
const { interfacesRaw } = getInterfaces();
|
||||
|
||||
export async function registerInterfaces() {
|
||||
const context = require.context('.', true, /^.*index\.ts$/);
|
||||
@@ -34,9 +34,9 @@ export async function registerInterfaces() {
|
||||
console.warn(`Couldn't load custom interfaces`);
|
||||
}
|
||||
|
||||
interfaces.value = modules;
|
||||
interfacesRaw.value = modules;
|
||||
|
||||
interfaces.value.forEach((inter) => {
|
||||
interfacesRaw.value.forEach((inter) => {
|
||||
registerComponent('interface-' + inter.id, inter.component);
|
||||
|
||||
if (typeof inter.options !== 'function' && Array.isArray(inter.options) === false) {
|
||||
|
||||
@@ -7,8 +7,19 @@ import availableLanguages from './available-languages.yaml';
|
||||
import enUSBase from './translations/en-US.yaml';
|
||||
import dateFormats from './date-formats.yaml';
|
||||
|
||||
import { getModules } from '@/modules';
|
||||
import { getLayouts } from '@/layouts';
|
||||
import { getInterfaces } from '@/interfaces';
|
||||
import { getDisplays } from '@/displays';
|
||||
import { translate } from '@/utils/translate-object-values';
|
||||
|
||||
Vue.use(VueI18n);
|
||||
|
||||
const { modules, modulesRaw } = getModules();
|
||||
const { layouts, layoutsRaw } = getLayouts();
|
||||
const { interfaces, interfacesRaw } = getInterfaces();
|
||||
const { displays, displaysRaw } = getDisplays();
|
||||
|
||||
export const i18n = new VueI18n({
|
||||
locale: 'en-US',
|
||||
fallbackLocale: 'en-US',
|
||||
@@ -41,6 +52,11 @@ export async function setLanguage(lang: Language): Promise<boolean> {
|
||||
i18n.locale = lang;
|
||||
(document.querySelector('html') as HTMLElement).setAttribute('lang', lang);
|
||||
|
||||
modules.value = translate(modulesRaw.value);
|
||||
layouts.value = translate(layoutsRaw.value);
|
||||
interfaces.value = translate(interfacesRaw.value);
|
||||
displays.value = translate(displaysRaw.value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { translateReactive } from '@/utils/translate-object-values';
|
||||
import { ref, Ref } from '@vue/composition-api';
|
||||
import { LayoutConfig } from './types';
|
||||
|
||||
let layoutsRaw: Ref<LayoutConfig[]>;
|
||||
let layouts: Ref<LayoutConfig[]>;
|
||||
|
||||
export function getLayouts() {
|
||||
if (!layoutsRaw) {
|
||||
layoutsRaw = ref([]);
|
||||
}
|
||||
|
||||
if (!layouts) {
|
||||
layouts = ref([]);
|
||||
}
|
||||
|
||||
return translateReactive(layouts);
|
||||
return { layouts, layoutsRaw };
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import api from '@/api';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import asyncPool from 'tiny-async-pool';
|
||||
|
||||
const layouts = getLayouts();
|
||||
const { layoutsRaw } = getLayouts();
|
||||
|
||||
export async function registerLayouts() {
|
||||
const context = require.context('.', true, /^.*index\.ts$/);
|
||||
@@ -33,9 +33,9 @@ export async function registerLayouts() {
|
||||
console.warn(`Couldn't load custom layouts`);
|
||||
}
|
||||
|
||||
layouts.value = modules;
|
||||
layoutsRaw.value = modules;
|
||||
|
||||
layouts.value.forEach((layout) => {
|
||||
layoutsRaw.value.forEach((layout) => {
|
||||
registerComponent('layout-' + layout.id, layout.component);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { translateReactive } from '@/utils/translate-object-values';
|
||||
import { ref, Ref } from '@vue/composition-api';
|
||||
import { ModuleConfig } from './types';
|
||||
|
||||
let modulesRaw: Ref<ModuleConfig[]>;
|
||||
let modules: Ref<ModuleConfig[]>;
|
||||
|
||||
export function getModules() {
|
||||
if (!modulesRaw) {
|
||||
modulesRaw = ref([]);
|
||||
}
|
||||
|
||||
if (!modules) {
|
||||
modules = ref([]);
|
||||
}
|
||||
|
||||
return translateReactive(modules);
|
||||
return { modules, modulesRaw };
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import api from '@/api';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import asyncPool from 'tiny-async-pool';
|
||||
|
||||
const modules = getModules();
|
||||
const { modulesRaw } = getModules();
|
||||
|
||||
let queuedModules: any = [];
|
||||
|
||||
export async function loadModules() {
|
||||
@@ -65,7 +66,7 @@ export async function register() {
|
||||
|
||||
replaceRoutes((routes) => insertBeforeProjectWildcard(routes, moduleRoutes));
|
||||
|
||||
modules.value = registeredModules;
|
||||
modulesRaw.value = registeredModules;
|
||||
|
||||
function insertBeforeProjectWildcard(currentRoutes: RouteConfig[], routesToBeAdded: RouteConfig[]) {
|
||||
// Find the index of the /* route, so we can insert the module routes right above that
|
||||
@@ -76,5 +77,5 @@ export async function register() {
|
||||
|
||||
export function unregister() {
|
||||
replaceRoutes((routes) => routes);
|
||||
modules.value = [];
|
||||
modulesRaw.value = [];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<v-fancy-select class="select" :items="selectItems" v-model="fieldData.meta.display" />
|
||||
|
||||
<v-notice class="not-found" type="danger" v-if="fieldData.meta.display && !selectedDisplay">
|
||||
@@ -56,8 +55,8 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const displays = getDisplays();
|
||||
const interfaces = getInterfaces();
|
||||
const { displays } = getDisplays();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const selectedInterface = computed(() => {
|
||||
return interfaces.value.find((inter) => inter.id === state.fieldData.meta.interface);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<v-fancy-select class="select" :items="selectItems" v-model="fieldData.meta.interface" />
|
||||
|
||||
<v-notice class="not-found" type="danger" v-if="fieldData.meta.interface && !selectedInterface">
|
||||
@@ -54,7 +53,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const interfaces = getInterfaces();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const selectItems = computed(() => {
|
||||
const type: string = state.fieldData?.type || 'alias';
|
||||
|
||||
@@ -32,8 +32,8 @@ let generationInfo: ComputedRef<GenerationInfo[]>;
|
||||
export { state, availableInterfaces, availableDisplays, generationInfo, initLocalStore, clearLocalStore };
|
||||
|
||||
function initLocalStore(collection: string, field: string, type: typeof localTypes[number]) {
|
||||
const interfaces = getInterfaces();
|
||||
const displays = getDisplays();
|
||||
const { interfaces } = getInterfaces();
|
||||
const { displays } = getDisplays();
|
||||
|
||||
state = reactive<any>({
|
||||
fieldData: {
|
||||
|
||||
@@ -218,7 +218,7 @@ export default defineComponent({
|
||||
const relationsStore = useRelationsStore();
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
const interfaces = getInterfaces();
|
||||
const { interfaces } = getInterfaces();
|
||||
|
||||
const editActive = ref(false);
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ type Preset = {
|
||||
export default defineComponent({
|
||||
components: { SettingsNavigation, ValueNull, PresetsInfoSidebarDetail },
|
||||
setup() {
|
||||
const layouts = getLayouts();
|
||||
const { layouts } = getLayouts();
|
||||
const collectionsStore = useCollectionsStore();
|
||||
|
||||
const selection = ref<Preset[]>([]);
|
||||
|
||||
@@ -155,7 +155,7 @@ export default defineComponent({
|
||||
setup(props) {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const presetsStore = usePresetsStore();
|
||||
const layouts = getLayouts();
|
||||
const { layouts } = getLayouts();
|
||||
const { backLink } = useLinks();
|
||||
|
||||
const isNew = computed(() => props.id === '+');
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getDisplays } from '@/displays';
|
||||
|
||||
export default function adjustFieldsForDisplays(fields: readonly string[], parentCollection: string) {
|
||||
const fieldsStore = useFieldsStore();
|
||||
const displays = getDisplays();
|
||||
const { displays } = getDisplays();
|
||||
|
||||
const adjustedFields: string[] = fields
|
||||
.map((fieldKey) => {
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
import { computed, Ref } from '@vue/composition-api';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import i18n from '@/lang';
|
||||
|
||||
export function translateReactive<T extends Object>(extensions: Ref<T>) {
|
||||
return computed({
|
||||
get() {
|
||||
return translate(cloneDeep(extensions.value));
|
||||
},
|
||||
set(newVal: T) {
|
||||
extensions.value = newVal;
|
||||
},
|
||||
});
|
||||
}
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
export function translate<T extends Record<string, any>>(obj: T) {
|
||||
obj = cloneDeep(obj);
|
||||
|
||||
Object.entries(obj).forEach(([key, val]) => {
|
||||
if (val && typeof val === 'object') (obj as Record<string, any>)[key] = translate(val);
|
||||
if (val && typeof val === 'string' && val.startsWith('$t:'))
|
||||
(obj as Record<string, any>)[key] = i18n.t(val.replace('$t:', ''));
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const layouts = getLayouts();
|
||||
const { layouts } = getLayouts();
|
||||
|
||||
const currentLayout = computed(() => {
|
||||
const layout = layouts.value.find((layout) => layout.id === props.value);
|
||||
|
||||
@@ -42,7 +42,7 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
const userStore = useUserStore();
|
||||
const modules = getModules();
|
||||
const { modules } = getModules();
|
||||
|
||||
const _modules = computed(() => {
|
||||
const customModuleListing = userStore.state.currentUser?.role.module_list;
|
||||
|
||||
@@ -61,7 +61,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const displays = getDisplays();
|
||||
const { displays } = getDisplays();
|
||||
const displayInfo = computed(() => displays.value.find((display) => display.id === props.display) || null);
|
||||
return { displayInfo };
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ export default defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const fieldsStore = useFieldsStore();
|
||||
const displays = getDisplays();
|
||||
const { displays } = getDisplays();
|
||||
|
||||
const regex = /({{.*?}})/g;
|
||||
|
||||
|
||||
5709
package-lock.json
generated
5709
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user