mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(editor): Migrate TagsTable
to composition api (no-changelog) (#10898)
This commit is contained in:
parent
bf28d0965c
commit
9141d15823
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue