Skip to content

Commit f6fd4e7

Browse files
committed
fix: improve UX for long-strings translation: autoconvert multiline/single-line input, crop too long strings from table
1 parent 9c6e19e commit f6fd4e7

File tree

4 files changed

+128
-2
lines changed

4 files changed

+128
-2
lines changed

custom/ListCell.vue

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<template>
2+
<div class="text-sm text-gray-900 dark:text-white min-w-32">
3+
{{ limitedText }}
4+
</div>
5+
</template>
6+
7+
<script setup lang="ts">
8+
import { computed } from 'vue';
9+
import { AdminForthResourceColumnCommon, AdminForthResourceCommon, AdminUser } from '@/types/Common';
10+
11+
12+
const limitedText = computed(() => {
13+
const text = props.record[props.column.name];
14+
return text?.length > 50 ? text.slice(0, 50) + '...' : text;
15+
});
16+
17+
const props = defineProps<{
18+
column: AdminForthResourceColumnCommon;
19+
record: any;
20+
meta: any;
21+
resource: AdminForthResourceCommon;
22+
adminUser: AdminUser;
23+
}>();
24+
25+
</script>

custom/SingleMultiInput.vue

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<template>
2+
<Input
3+
ref="input"
4+
v-if="shortValue"
5+
class="w-full"
6+
:modelValue="props.record[props.column.name]"
7+
:disabled="props.column.editReadonly"
8+
@update:modelValue="($event) => emit('update:value', $event)" />
9+
<textarea
10+
v-else
11+
ref="textarea"
12+
:disabled="props.column.editReadonly"
13+
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg block w-full p-2.5
14+
dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
15+
:value="props.record[props.column.name]"
16+
@input="($event) => { autoResizeTextArea(); emit('update:value', $event.target.value) }" />
17+
18+
</template>
19+
20+
<script setup lang="ts">
21+
import { Input } from "@/afcl";
22+
import type {
23+
AdminForthResourceColumnCommon,
24+
AdminForthResourceCommon,
25+
AdminUser,
26+
} from "@/types/Common";
27+
import { computed, defineProps, defineEmits, type Ref, watch, nextTick, ref, onMounted } from "vue";
28+
29+
const shortValue: Ref<boolean> = computed(() => (props.record[props.column.name]?.length || 0) < 50);
30+
31+
const props = defineProps<{
32+
column: AdminForthResourceColumnCommon;
33+
record: any;
34+
meta: any;
35+
resource: AdminForthResourceCommon;
36+
adminUser: AdminUser;
37+
}>();
38+
39+
const emit = defineEmits(["update:value"]);
40+
41+
const input: Ref<HTMLInputElement | HTMLTextAreaElement | null> = ref(null);
42+
const textarea: Ref<HTMLInputElement | HTMLTextAreaElement | null> = ref(null);
43+
44+
// Auto resize function
45+
const autoResizeTextArea = () => {
46+
// Use nextTick to ensure the DOM update is done before measuring
47+
nextTick(() => {
48+
if (!textarea.value) return
49+
50+
// Reset the height to shrink if content is removed
51+
textarea.value.style.height = 'auto'
52+
// Set it to the scrollHeight to make it grow
53+
textarea.value.style.height = textarea.value.scrollHeight + 3 + 'px'
54+
})
55+
}
56+
57+
watch(() => shortValue.value, async () => {
58+
await nextTick();
59+
if (shortValue.value) {
60+
input.value?.focus();
61+
} else {
62+
textarea.value?.focus();
63+
await nextTick();
64+
autoResizeTextArea();
65+
}
66+
});
67+
68+
69+
onMounted(async () => {
70+
if (!shortValue.value) {
71+
await nextTick();
72+
autoResizeTextArea();
73+
}
74+
});
75+
</script>
76+

custom/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
"paths": {
55
"@/*": [
66
// "node_modules/adminforth/dist/spa/src/*"
7-
"../../../spa/src/*"
7+
"../../../adminforth/spa/src/*"
88
],
99
"*": [
1010
// "node_modules/adminforth/dist/spa/node_modules/*"
11-
"../../../spa/node_modules/*"
11+
"../../../adminforth/spa/node_modules/*"
1212
],
1313
"@@/*": [
1414
// "node_modules/adminforth/dist/spa/src/*"

index.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,21 @@ export default class I18nPlugin extends AdminForthPlugin {
165165
throw new Error(`Field ${this.trFieldNames[lang]} not found for storing translation for language ${lang}
166166
in resource ${resourceConfig.resourceId}, consider adding it to columns or change trFieldNames option to remap it to existing column`);
167167
}
168+
169+
column.components = column.components || {};
170+
171+
// set edit and create custom component - SingleMultiInput.vue
172+
column.components.edit = {
173+
file: this.componentPath('SingleMultiInput.vue'),
174+
};
175+
column.components.create = {
176+
file: this.componentPath('SingleMultiInput.vue'),
177+
};
178+
179+
// set ListCell for list
180+
column.components.list = {
181+
file: this.componentPath('ListCell.vue'),
182+
};
168183
}
169184

170185
this.enFieldName = this.trFieldNames['en'] || 'en_string';
@@ -186,6 +201,16 @@ export default class I18nPlugin extends AdminForthPlugin {
186201
throw new Error(`Field ${this.enFieldName} not found column to store english original string in resource ${resourceConfig.resourceId}`);
187202
}
188203

204+
enColumn.components = enColumn.components || {};
205+
enColumn.components.edit = {
206+
file: this.componentPath('SingleMultiInput.vue'),
207+
};
208+
enColumn.components.create = {
209+
file: this.componentPath('SingleMultiInput.vue'),
210+
};
211+
enColumn.components.list = {
212+
file: this.componentPath('ListCell.vue'),
213+
};
189214
enColumn.editReadonly = true;
190215

191216
// if sourceFieldName defined, check it exists

0 commit comments

Comments
 (0)