refactor(editor): Migrate TagsTable to composition api (no-changelog) (#10898)

This commit is contained in:
Milorad FIlipović 2024-09-23 10:17:50 +02:00 committed by GitHub
parent bf28d0965c
commit 9141d15823
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,138 +1,138 @@
<script lang="ts"> <script setup lang="ts">
import type { ElTable } from 'element-plus'; import { ElTable } from 'element-plus';
import { MAX_TAG_NAME_LENGTH } from '@/constants'; import { MAX_TAG_NAME_LENGTH } from '@/constants';
import type { ITagRow } from '@/Interface'; import type { ITagRow } from '@/Interface';
import type { PropType } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { defineComponent } from 'vue'; import { N8nInput } from 'n8n-design-system';
import type { N8nInput } from 'n8n-design-system';
import type { BaseTextKey } from '@/plugins/i18n'; import type { BaseTextKey } from '@/plugins/i18n';
type TableRef = InstanceType<typeof ElTable>; interface Props {
type N8nInputRef = InstanceType<typeof N8nInput>; rows: ITagRow[];
isLoading: boolean;
newName: string;
isSaving: boolean;
usageColumnTitleLocaleKey: BaseTextKey;
}
const props = withDefaults(defineProps<Props>(), {
usageColumnTitleLocaleKey: 'tagsTable.usage',
});
const emit = defineEmits<{
updateEnable: [id: string];
newNameChange: [name: string];
deleteEnable: [id: string];
cancelOperation: [];
applyOperation: [];
}>();
const INPUT_TRANSITION_TIMEOUT = 350; const INPUT_TRANSITION_TIMEOUT = 350;
const DELETE_TRANSITION_TIMEOUT = 100; const DELETE_TRANSITION_TIMEOUT = 100;
export default defineComponent({ const table = ref<InstanceType<typeof ElTable> | null>(null);
name: 'TagsTable', // ESLint: false positive, this is not a redundant type
props: { // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
rows: { const nameInput = ref<InstanceType<typeof N8nInput> | null>(null);
type: Array as () => ITagRow[],
required: true, const maxLength = ref(MAX_TAG_NAME_LENGTH);
},
isLoading: { const getRowClasses = ({ row }: { row: ITagRow }): string => {
type: Boolean, return row.disable ? 'disabled' : '';
required: true, };
},
newName: { const getSpan = ({
type: String, row,
required: true, columnIndex,
}, }: {
isSaving: { row: ITagRow;
type: Boolean, columnIndex: number;
required: true, }): { rowspan: number; colspan: number } | undefined => {
}, // expand text column with delete message
usageColumnTitleLocaleKey: { if (columnIndex === 0 && row.tag && row.delete) {
type: String as PropType<BaseTextKey>, return { rowspan: 1, colspan: 2 };
default: 'tagsTable.usage', }
}, // hide usage column on delete
}, if (columnIndex === 1 && row.tag && row.delete) {
data() { return { rowspan: 0, colspan: 0 };
return { }
maxLength: MAX_TAG_NAME_LENGTH,
}; return { rowspan: 1, colspan: 1 };
}, };
watch: {
rows(newValue: ITagRow[] | undefined) { const enableUpdate = (row: ITagRow): void => {
if (newValue?.[0] && newValue[0].create) { if (row.tag) {
this.focusOnCreate(); emit('updateEnable', row.tag.id);
} emit('newNameChange', row.tag.name);
}, focusOnInput();
}, }
mounted() { };
if (this.rows.length === 1 && this.rows[0].create) {
this.focusOnInput(); const enableDelete = (row: ITagRow): void => {
if (row.tag) {
emit('deleteEnable', row.tag.id);
focusOnDelete();
}
};
const cancel = (): void => {
emit('cancelOperation');
};
const apply = (): void => {
emit('applyOperation');
};
const onNewNameChange = (name: string): void => {
emit('newNameChange', name);
};
const focusOnInput = (): void => {
setTimeout(() => {
if (nameInput.value?.focus) {
nameInput.value.focus();
}
}, INPUT_TRANSITION_TIMEOUT);
};
const focusOnDelete = (): void => {
setTimeout(() => {
const inputRef = nameInput.value;
if (inputRef?.focus) {
inputRef.focus();
}
}, DELETE_TRANSITION_TIMEOUT);
};
const focusOnCreate = (): void => {
const bodyWrapperRef = table.value?.$refs.bodyWrapper as HTMLElement;
if (bodyWrapperRef) {
bodyWrapperRef.scrollTop = 0;
}
focusOnInput();
};
watch(
() => props.rows,
(newValue: ITagRow[] | undefined) => {
if (newValue?.[0] && newValue[0].create) {
focusOnCreate();
} }
}, },
methods: { );
getRowClasses: ({ row }: { row: ITagRow }): string => {
return row.disable ? 'disabled' : '';
},
getSpan({ row, columnIndex }: { row: ITagRow; columnIndex: number }): number | number[] { onMounted(() => {
// expand text column with delete message if (props.rows.length === 1 && props.rows[0].create) {
if (columnIndex === 0 && row.tag && row.delete) { focusOnInput();
return [1, 2]; }
}
// hide usage column on delete
if (columnIndex === 1 && row.tag && row.delete) {
return [0, 0];
}
return 1;
},
enableUpdate(row: ITagRow): void {
if (row.tag) {
this.$emit('updateEnable', row.tag.id);
this.$emit('newNameChange', row.tag.name);
this.focusOnInput();
}
},
enableDelete(row: ITagRow): void {
if (row.tag) {
this.$emit('deleteEnable', row.tag.id);
this.focusOnDelete();
}
},
cancel(): void {
this.$emit('cancelOperation');
},
apply(): void {
this.$emit('applyOperation');
},
onNewNameChange(name: string): void {
this.$emit('newNameChange', name);
},
focusOnInput(): void {
setTimeout(() => {
const inputRef = this.$refs.nameInput as N8nInputRef | undefined;
if (inputRef?.focus) {
inputRef.focus();
}
}, INPUT_TRANSITION_TIMEOUT);
},
focusOnDelete(): void {
setTimeout(() => {
const inputRef = this.$refs.deleteHiddenInput as N8nInputRef | undefined;
if (inputRef?.focus) {
inputRef.focus();
}
}, DELETE_TRANSITION_TIMEOUT);
},
focusOnCreate(): void {
const bodyWrapperRef = (this.$refs.table as TableRef).$refs.bodyWrapper as HTMLElement;
if (bodyWrapperRef) {
bodyWrapperRef.scrollTop = 0;
}
this.focusOnInput();
},
},
}); });
</script> </script>
<template> <template>
<el-table <ElTable
ref="table" ref="table"
v-loading="isLoading" v-loading="isLoading"
class="tags-table" :class="$style['tags-table']"
stripe stripe
max-height="450" max-height="450"
:empty-text="$locale.baseText('tagsTable.noMatchingTagsExist')" :empty-text="$locale.baseText('tagsTable.noMatchingTagsExist')"
@ -142,20 +142,20 @@ export default defineComponent({
> >
<el-table-column :label="$locale.baseText('tagsTable.name')"> <el-table-column :label="$locale.baseText('tagsTable.name')">
<template #default="scope"> <template #default="scope">
<div :key="scope.row.id" class="name" @keydown.stop> <div :key="scope.row.id" :class="$style.name" @keydown.stop>
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
<n8n-input <N8nInput
v-if="scope.row.create || scope.row.update" v-if="scope.row.create || scope.row.update"
ref="nameInput" ref="nameInput"
:model-value="newName" :model-value="newName"
:maxlength="maxLength" :maxlength="maxLength"
@update:model-value="onNewNameChange" @update:model-value="onNewNameChange"
></n8n-input> ></N8nInput>
<span v-else-if="scope.row.delete"> <span v-else-if="scope.row.delete">
<span>{{ $locale.baseText('tagsTable.areYouSureYouWantToDeleteThisTag') }}</span> <span>{{ $locale.baseText('tagsTable.areYouSureYouWantToDeleteThisTag') }}</span>
<input ref="deleteHiddenInput" class="hidden" /> <input ref="deleteHiddenInput" :class="$style.hidden" />
</span> </span>
<span v-else :class="{ disabled: scope.row.disable }"> <span v-else :class="{ [$style.disabled]: scope.row.disable }">
{{ scope.row.tag.name }} {{ scope.row.tag.name }}
</span> </span>
</transition> </transition>
@ -167,7 +167,7 @@ export default defineComponent({
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
<div <div
v-if="!scope.row.create && !scope.row.delete" v-if="!scope.row.create && !scope.row.delete"
:class="{ disabled: scope.row.disable }" :class="{ [$style.disabled]: scope.row.disable }"
> >
{{ scope.row.usage }} {{ scope.row.usage }}
</div> </div>
@ -177,7 +177,7 @@ export default defineComponent({
<el-table-column> <el-table-column>
<template #default="scope"> <template #default="scope">
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
<div v-if="scope.row.create" class="ops"> <div v-if="scope.row.create" :class="$style.ops">
<n8n-button <n8n-button
:label="$locale.baseText('tagsTable.cancel')" :label="$locale.baseText('tagsTable.cancel')"
type="secondary" type="secondary"
@ -190,7 +190,7 @@ export default defineComponent({
@click.stop="apply" @click.stop="apply"
/> />
</div> </div>
<div v-else-if="scope.row.update" class="ops"> <div v-else-if="scope.row.update" :class="$style.ops">
<n8n-button <n8n-button
:label="$locale.baseText('tagsTable.cancel')" :label="$locale.baseText('tagsTable.cancel')"
type="secondary" type="secondary"
@ -203,7 +203,7 @@ export default defineComponent({
@click.stop="apply" @click.stop="apply"
/> />
</div> </div>
<div v-else-if="scope.row.delete" class="ops"> <div v-else-if="scope.row.delete" :class="$style.ops">
<n8n-button <n8n-button
:label="$locale.baseText('tagsTable.cancel')" :label="$locale.baseText('tagsTable.cancel')"
type="secondary" type="secondary"
@ -216,7 +216,7 @@ export default defineComponent({
@click.stop="apply" @click.stop="apply"
/> />
</div> </div>
<div v-else-if="!scope.row.disable" class="ops main"> <div v-else-if="!scope.row.disable" :class="[$style.ops, $style.main]">
<n8n-icon-button <n8n-icon-button
:title="$locale.baseText('tagsTable.editTag')" :title="$locale.baseText('tagsTable.editTag')"
icon="pen" icon="pen"
@ -234,16 +234,15 @@ export default defineComponent({
</transition> </transition>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </ElTable>
</template> </template>
<style lang="scss" scoped> <style lang="scss" module>
.tags-table { .tags-table {
:deep(tr.disabled) { :deep(tr.disabled) {
pointer-events: none; pointer-events: none;
} }
} }
.name { .name {
min-height: 45px; min-height: 45px;
display: flex; display: flex;
@ -261,6 +260,11 @@ export default defineComponent({
> * { > * {
margin: 2px; margin: 2px;
} }
&.main {
display: none;
margin-left: 2px;
}
} }
.disabled { .disabled {
@ -273,15 +277,12 @@ export default defineComponent({
opacity: 0; opacity: 0;
} }
.ops.main {
display: none;
margin-left: 2px;
}
tr:hover .ops:not(.disabled) { tr:hover .ops:not(.disabled) {
display: flex; display: flex;
} }
</style>
<style lang="scss" scoped>
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity 0.2s; transition: opacity 0.2s;