Fix perf regression in translations (#5041)

* Only translate extensions on language load

* Make table-row functional
This commit is contained in:
Rijk van Zanten
2021-04-13 17:00:22 -04:00
committed by GitHub
parent e2558eb6ed
commit bcac4b80c2
30 changed files with 5674 additions and 219 deletions

View File

@@ -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');

View File

@@ -175,8 +175,6 @@ export default defineComponent({
} else {
return 'grid';
}
return null;
});
return { formFields: formFieldsParsed, gridClass, isDisabled };

View File

@@ -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>

View File

@@ -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}`" />

View File

@@ -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);

View File

@@ -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 };
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -42,7 +42,7 @@ export default defineComponent({
},
},
setup(props, { parent }) {
const interfaces = getInterfaces();
const { interfaces } = getInterfaces();
const values = inject('values', ref<Record<string, any>>({}));

View File

@@ -29,7 +29,7 @@ export default defineComponent({
},
},
setup(props, { emit }) {
const interfaces = getInterfaces();
const { interfaces } = getInterfaces();
const values = inject('values', ref<Record<string, any>>({}));

View File

@@ -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 };
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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 };
}

View File

@@ -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);
});
}

View File

@@ -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 };
}

View File

@@ -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 = [];
}

View File

@@ -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);

View File

@@ -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';

View File

@@ -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: {

View File

@@ -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);

View File

@@ -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[]>([]);

View File

@@ -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 === '+');

View File

@@ -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) => {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 };
},

View File

@@ -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

File diff suppressed because it is too large Load Diff