mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-13 16:14:07 -08:00
refactor(editor): Migrate FixedCollectionParameter to composition API (#11555)
Co-authored-by: Elias Meire <elias@meire.dev>
This commit is contained in:
parent
93fae5d8a7
commit
499c54b29a
|
@ -22,6 +22,7 @@ import DraggableTarget from './DraggableTarget.vue';
|
||||||
import { dropInExpressionEditor } from '@/plugins/codemirror/dragAndDrop';
|
import { dropInExpressionEditor } from '@/plugins/codemirror/dragAndDrop';
|
||||||
|
|
||||||
import { APP_MODALS_ELEMENT_ID } from '@/constants';
|
import { APP_MODALS_ELEMENT_ID } from '@/constants';
|
||||||
|
import { N8nInput, N8nText } from 'n8n-design-system';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
parameter: INodeProperties;
|
parameter: INodeProperties;
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
import { cleanupAppModals, createAppModals, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
|
||||||
|
import FixedCollectionParameter, { type Props } from '@/components/FixedCollectionParameter.vue';
|
||||||
|
import { STORES } from '@/constants';
|
||||||
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { setActivePinia } from 'pinia';
|
||||||
|
describe('FixedCollectionParameter.vue', () => {
|
||||||
|
const pinia = createTestingPinia({
|
||||||
|
initialState: {
|
||||||
|
[STORES.SETTINGS]: {
|
||||||
|
settings: SETTINGS_STORE_DEFAULT_STATE.settings,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setActivePinia(pinia);
|
||||||
|
|
||||||
|
const props: Props = {
|
||||||
|
parameter: {
|
||||||
|
displayName: 'Routing Rules',
|
||||||
|
name: 'rules',
|
||||||
|
placeholder: 'Add Routing Rule',
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'values',
|
||||||
|
displayName: 'Values',
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
displayName: 'Output Name',
|
||||||
|
name: 'outputKey',
|
||||||
|
type: 'string',
|
||||||
|
default: 'Default Output Name',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
path: 'parameters.rules',
|
||||||
|
nodeValues: {
|
||||||
|
parameters: {
|
||||||
|
rules: { values: [{ outputKey: 'Test Output Name' }] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
values: [{ outputKey: 'Test Output Name' }],
|
||||||
|
},
|
||||||
|
isReadOnly: false,
|
||||||
|
};
|
||||||
|
const renderComponent = createComponentRenderer(FixedCollectionParameter, { props });
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
createAppModals();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupAppModals();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the component', () => {
|
||||||
|
const { getByTestId } = renderComponent();
|
||||||
|
expect(getByTestId('fixed-collection-rules')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('fixed-collection-add')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('fixed-collection-delete')).toBeInTheDocument();
|
||||||
|
expect(getByTestId('parameter-item')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('computes placeholder text correctly', () => {
|
||||||
|
const { getByTestId } = renderComponent();
|
||||||
|
expect(getByTestId('fixed-collection-add')).toHaveTextContent('Add Routing Rule');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits valueChanged event on option creation', async () => {
|
||||||
|
const { getByTestId, emitted } = renderComponent();
|
||||||
|
await userEvent.click(getByTestId('fixed-collection-add'));
|
||||||
|
expect(emitted('valueChanged')).toEqual([
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'parameters.rules.values',
|
||||||
|
value: [{ outputKey: 'Test Output Name' }, { outputKey: 'Default Output Name' }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emits valueChanged event on option deletion', async () => {
|
||||||
|
const { getByTestId, emitted } = renderComponent({
|
||||||
|
props: {
|
||||||
|
...props,
|
||||||
|
values: {
|
||||||
|
values: [{ outputKey: 'Test' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await userEvent.click(getByTestId('fixed-collection-delete'));
|
||||||
|
expect(emitted('valueChanged')).toEqual([
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'parameters.rules.values',
|
||||||
|
value: undefined,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,226 +1,236 @@
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import type { PropType } from 'vue';
|
|
||||||
import type { IUpdateInformation } from '@/Interface';
|
import type { IUpdateInformation } from '@/Interface';
|
||||||
|
|
||||||
import type { INodeParameters, INodeProperties, INodePropertyCollection } from 'n8n-workflow';
|
import type { INodeParameters, INodeProperties, NodeParameterValueType } from 'n8n-workflow';
|
||||||
import { deepCopy, isINodePropertyCollectionList } from 'n8n-workflow';
|
import { deepCopy, isINodePropertyCollectionList } from 'n8n-workflow';
|
||||||
|
|
||||||
import { get } from 'lodash-es';
|
import { get } from 'lodash-es';
|
||||||
|
|
||||||
export default defineComponent({
|
import { computed, ref, watch, onBeforeMount } from 'vue';
|
||||||
name: 'FixedCollectionParameter',
|
import { useI18n } from '@/composables/useI18n';
|
||||||
props: {
|
import {
|
||||||
nodeValues: {
|
N8nIconButton,
|
||||||
type: Object as PropType<INodeParameters>,
|
N8nSelect,
|
||||||
required: true,
|
N8nOption,
|
||||||
},
|
N8nInputLabel,
|
||||||
parameter: {
|
N8nText,
|
||||||
type: Object as PropType<INodeProperties>,
|
N8nButton,
|
||||||
required: true,
|
} from 'n8n-design-system';
|
||||||
},
|
import ParameterInputList from './ParameterInputList.vue';
|
||||||
path: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
values: {
|
|
||||||
type: Object as PropType<Record<string, INodeParameters[]>>,
|
|
||||||
default: () => ({}),
|
|
||||||
},
|
|
||||||
isReadOnly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedOption: undefined,
|
|
||||||
mutableValues: {} as Record<string, INodeParameters[]>,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
getPlaceholderText(): string {
|
|
||||||
const placeholder = this.$locale.nodeText().placeholder(this.parameter, this.path);
|
|
||||||
return placeholder ? placeholder : this.$locale.baseText('fixedCollectionParameter.choose');
|
|
||||||
},
|
|
||||||
getProperties(): INodePropertyCollection[] {
|
|
||||||
const returnProperties = [];
|
|
||||||
let tempProperties;
|
|
||||||
for (const name of this.propertyNames) {
|
|
||||||
tempProperties = this.getOptionProperties(name);
|
|
||||||
if (tempProperties !== undefined) {
|
|
||||||
returnProperties.push(tempProperties);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnProperties;
|
|
||||||
},
|
|
||||||
multipleValues(): boolean {
|
|
||||||
return !!this.parameter.typeOptions?.multipleValues;
|
|
||||||
},
|
|
||||||
parameterOptions(): INodePropertyCollection[] {
|
|
||||||
if (this.multipleValues && isINodePropertyCollectionList(this.parameter.options)) {
|
|
||||||
return this.parameter.options;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (this.parameter.options as INodePropertyCollection[]).filter((option) => {
|
const locale = useI18n();
|
||||||
return !this.propertyNames.includes(option.name);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
propertyNames(): string[] {
|
|
||||||
return Object.keys(this.mutableValues || {});
|
|
||||||
},
|
|
||||||
sortable(): boolean {
|
|
||||||
return !!this.parameter.typeOptions?.sortable;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
values: {
|
|
||||||
handler(newValues: Record<string, INodeParameters[]>) {
|
|
||||||
this.mutableValues = deepCopy(newValues);
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.mutableValues = deepCopy(this.values);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
deleteOption(optionName: string, index?: number) {
|
|
||||||
const currentOptionsOfSameType = this.mutableValues[optionName];
|
|
||||||
if (!currentOptionsOfSameType || currentOptionsOfSameType.length > 1) {
|
|
||||||
// it's not the only option of this type, so just remove it.
|
|
||||||
this.$emit('valueChanged', {
|
|
||||||
name: this.getPropertyPath(optionName, index),
|
|
||||||
value: undefined,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// it's the only option, so remove the whole type
|
|
||||||
this.$emit('valueChanged', {
|
|
||||||
name: this.getPropertyPath(optionName),
|
|
||||||
value: undefined,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getPropertyPath(name: string, index?: number) {
|
|
||||||
return `${this.path}.${name}` + (index !== undefined ? `[${index}]` : '');
|
|
||||||
},
|
|
||||||
getOptionProperties(optionName: string): INodePropertyCollection | undefined {
|
|
||||||
if (isINodePropertyCollectionList(this.parameter.options)) {
|
|
||||||
for (const option of this.parameter.options) {
|
|
||||||
if (option.name === optionName) {
|
|
||||||
return option;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
},
|
|
||||||
moveOptionDown(optionName: string, index: number) {
|
|
||||||
if (Array.isArray(this.mutableValues[optionName])) {
|
|
||||||
this.mutableValues[optionName].splice(
|
|
||||||
index + 1,
|
|
||||||
0,
|
|
||||||
this.mutableValues[optionName].splice(index, 1)[0],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parameterData = {
|
export type Props = {
|
||||||
name: this.getPropertyPath(optionName),
|
nodeValues: INodeParameters;
|
||||||
value: this.mutableValues[optionName],
|
parameter: INodeProperties;
|
||||||
type: 'optionsOrderChanged',
|
path: string;
|
||||||
};
|
values?: Record<string, INodeParameters[]>;
|
||||||
|
isReadOnly?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
this.$emit('valueChanged', parameterData);
|
type ValueChangedEvent = {
|
||||||
},
|
name: string;
|
||||||
moveOptionUp(optionName: string, index: number) {
|
value: NodeParameterValueType;
|
||||||
if (Array.isArray(this.mutableValues[optionName])) {
|
type?: 'optionsOrderChanged';
|
||||||
this.mutableValues?.[optionName].splice(
|
};
|
||||||
index - 1,
|
|
||||||
0,
|
|
||||||
this.mutableValues[optionName].splice(index, 1)[0],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parameterData = {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
name: this.getPropertyPath(optionName),
|
values: () => ({}),
|
||||||
value: this.mutableValues[optionName],
|
isReadOnly: false,
|
||||||
type: 'optionsOrderChanged',
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$emit('valueChanged', parameterData);
|
|
||||||
},
|
|
||||||
optionSelected(optionName: string) {
|
|
||||||
const option = this.getOptionProperties(optionName);
|
|
||||||
if (option === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const name = `${this.path}.${option.name}`;
|
|
||||||
|
|
||||||
const newParameterValue: INodeParameters = {};
|
|
||||||
|
|
||||||
for (const optionParameter of option.values) {
|
|
||||||
if (
|
|
||||||
optionParameter.type === 'fixedCollection' &&
|
|
||||||
optionParameter.typeOptions !== undefined &&
|
|
||||||
optionParameter.typeOptions.multipleValues === true
|
|
||||||
) {
|
|
||||||
newParameterValue[optionParameter.name] = {};
|
|
||||||
} else if (
|
|
||||||
optionParameter.typeOptions !== undefined &&
|
|
||||||
optionParameter.typeOptions.multipleValues === true
|
|
||||||
) {
|
|
||||||
// Multiple values are allowed so append option to array
|
|
||||||
const multiValue = get(this.nodeValues, [this.path, optionParameter.name], []);
|
|
||||||
|
|
||||||
if (Array.isArray(optionParameter.default)) {
|
|
||||||
multiValue.push(...deepCopy(optionParameter.default));
|
|
||||||
} else if (
|
|
||||||
optionParameter.default !== '' &&
|
|
||||||
typeof optionParameter.default !== 'object'
|
|
||||||
) {
|
|
||||||
multiValue.push(deepCopy(optionParameter.default));
|
|
||||||
}
|
|
||||||
|
|
||||||
newParameterValue[optionParameter.name] = multiValue;
|
|
||||||
} else {
|
|
||||||
// Add a new option
|
|
||||||
newParameterValue[optionParameter.name] = deepCopy(optionParameter.default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let newValue;
|
|
||||||
if (this.multipleValues) {
|
|
||||||
newValue = get(this.nodeValues, name, []) as INodeParameters[];
|
|
||||||
|
|
||||||
newValue.push(newParameterValue);
|
|
||||||
} else {
|
|
||||||
newValue = newParameterValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parameterData = {
|
|
||||||
name,
|
|
||||||
value: newValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.$emit('valueChanged', parameterData);
|
|
||||||
this.selectedOption = undefined;
|
|
||||||
},
|
|
||||||
valueChanged(parameterData: IUpdateInformation) {
|
|
||||||
this.$emit('valueChanged', parameterData);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
valueChanged: [value: ValueChangedEvent];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const getPlaceholderText = computed(() => {
|
||||||
|
const placeholder = locale.nodeText().placeholder(props.parameter, props.path);
|
||||||
|
return placeholder ? placeholder : locale.baseText('fixedCollectionParameter.choose');
|
||||||
|
});
|
||||||
|
const mutableValues = ref({} as Record<string, INodeParameters[]>);
|
||||||
|
const selectedOption = ref<string | null | undefined>(null);
|
||||||
|
const propertyNames = computed(() => {
|
||||||
|
return new Set(Object.keys(mutableValues.value || {}));
|
||||||
|
});
|
||||||
|
const getProperties = computed(() => {
|
||||||
|
const returnProperties = [];
|
||||||
|
let tempProperties;
|
||||||
|
for (const name of propertyNames.value) {
|
||||||
|
tempProperties = getOptionProperties(name);
|
||||||
|
if (tempProperties !== undefined) {
|
||||||
|
returnProperties.push(tempProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnProperties;
|
||||||
|
});
|
||||||
|
const multipleValues = computed(() => {
|
||||||
|
return !!props.parameter.typeOptions?.multipleValues;
|
||||||
|
});
|
||||||
|
const parameterOptions = computed(() => {
|
||||||
|
if (!isINodePropertyCollectionList(props.parameter.options)) return [];
|
||||||
|
|
||||||
|
if (multipleValues.value) {
|
||||||
|
return props.parameter.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (props.parameter.options ?? []).filter((option) => {
|
||||||
|
return !propertyNames.value.has(option.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortable = computed(() => {
|
||||||
|
return !!props.parameter.typeOptions?.sortable;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.values,
|
||||||
|
(newValues: Record<string, INodeParameters[]>) => {
|
||||||
|
mutableValues.value = deepCopy(newValues);
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
mutableValues.value = deepCopy(props.values);
|
||||||
|
});
|
||||||
|
|
||||||
|
const deleteOption = (optionName: string, index?: number) => {
|
||||||
|
const currentOptionsOfSameType = mutableValues.value[optionName];
|
||||||
|
if (!currentOptionsOfSameType || currentOptionsOfSameType.length > 1) {
|
||||||
|
// it's not the only option of this type, so just remove it.
|
||||||
|
emit('valueChanged', {
|
||||||
|
name: getPropertyPath(optionName, index),
|
||||||
|
value: undefined,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// it's the only option, so remove the whole type
|
||||||
|
emit('valueChanged', {
|
||||||
|
name: getPropertyPath(optionName),
|
||||||
|
value: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPropertyPath = (name: string, index?: number) => {
|
||||||
|
return `${props.path}.${name}` + (index !== undefined ? `[${index}]` : '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOptionProperties = (optionName: string) => {
|
||||||
|
if (isINodePropertyCollectionList(props.parameter.options)) {
|
||||||
|
for (const option of props.parameter.options) {
|
||||||
|
if (option.name === optionName) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveOptionDown = (optionName: string, index: number) => {
|
||||||
|
if (Array.isArray(mutableValues.value[optionName])) {
|
||||||
|
mutableValues.value[optionName].splice(
|
||||||
|
index + 1,
|
||||||
|
0,
|
||||||
|
mutableValues.value[optionName].splice(index, 1)[0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameterData: ValueChangedEvent = {
|
||||||
|
name: getPropertyPath(optionName),
|
||||||
|
value: mutableValues.value[optionName],
|
||||||
|
type: 'optionsOrderChanged',
|
||||||
|
};
|
||||||
|
|
||||||
|
emit('valueChanged', parameterData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveOptionUp = (optionName: string, index: number) => {
|
||||||
|
if (Array.isArray(mutableValues.value[optionName])) {
|
||||||
|
mutableValues.value?.[optionName].splice(
|
||||||
|
index - 1,
|
||||||
|
0,
|
||||||
|
mutableValues.value[optionName].splice(index, 1)[0],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameterData: ValueChangedEvent = {
|
||||||
|
name: getPropertyPath(optionName),
|
||||||
|
value: mutableValues.value[optionName],
|
||||||
|
type: 'optionsOrderChanged',
|
||||||
|
};
|
||||||
|
|
||||||
|
emit('valueChanged', parameterData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const optionSelected = (optionName: string) => {
|
||||||
|
const option = getOptionProperties(optionName);
|
||||||
|
if (option === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = `${props.path}.${option.name}`;
|
||||||
|
|
||||||
|
const newParameterValue: INodeParameters = {};
|
||||||
|
|
||||||
|
for (const optionParameter of option.values) {
|
||||||
|
if (
|
||||||
|
optionParameter.type === 'fixedCollection' &&
|
||||||
|
optionParameter.typeOptions !== undefined &&
|
||||||
|
optionParameter.typeOptions.multipleValues === true
|
||||||
|
) {
|
||||||
|
newParameterValue[optionParameter.name] = {};
|
||||||
|
} else if (
|
||||||
|
optionParameter.typeOptions !== undefined &&
|
||||||
|
optionParameter.typeOptions.multipleValues === true
|
||||||
|
) {
|
||||||
|
// Multiple values are allowed so append option to array
|
||||||
|
const multiValue = get(props.nodeValues, [props.path, optionParameter.name], []);
|
||||||
|
|
||||||
|
if (Array.isArray(optionParameter.default)) {
|
||||||
|
multiValue.push(...deepCopy(optionParameter.default));
|
||||||
|
} else if (optionParameter.default !== '' && typeof optionParameter.default !== 'object') {
|
||||||
|
multiValue.push(deepCopy(optionParameter.default));
|
||||||
|
}
|
||||||
|
|
||||||
|
newParameterValue[optionParameter.name] = multiValue;
|
||||||
|
} else {
|
||||||
|
// Add a new option
|
||||||
|
newParameterValue[optionParameter.name] = deepCopy(optionParameter.default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let newValue: NodeParameterValueType;
|
||||||
|
if (multipleValues.value) {
|
||||||
|
newValue = get(props.nodeValues, name, []) as INodeParameters[];
|
||||||
|
|
||||||
|
newValue.push(newParameterValue);
|
||||||
|
} else {
|
||||||
|
newValue = newParameterValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameterData = {
|
||||||
|
name,
|
||||||
|
value: newValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
emit('valueChanged', parameterData);
|
||||||
|
selectedOption.value = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const valueChanged = (parameterData: IUpdateInformation) => {
|
||||||
|
emit('valueChanged', parameterData);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="fixed-collection-parameter"
|
class="fixed-collection-parameter"
|
||||||
:data-test-id="`fixed-collection-${parameter.name}`"
|
:data-test-id="`fixed-collection-${props.parameter?.name}`"
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
>
|
>
|
||||||
<div v-if="getProperties.length === 0" class="no-items-exist">
|
<div v-if="getProperties.length === 0" class="no-items-exist">
|
||||||
<n8n-text size="small">{{
|
<N8nText size="small">{{
|
||||||
$locale.baseText('fixedCollectionParameter.currentlyNoItemsExist')
|
locale.baseText('fixedCollectionParameter.currentlyNoItemsExist')
|
||||||
}}</n8n-text>
|
}}</N8nText>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -228,9 +238,9 @@ export default defineComponent({
|
||||||
:key="property.name"
|
:key="property.name"
|
||||||
class="fixed-collection-parameter-property"
|
class="fixed-collection-parameter-property"
|
||||||
>
|
>
|
||||||
<n8n-input-label
|
<N8nInputLabel
|
||||||
v-if="property.displayName !== '' && parameter.options && parameter.options.length !== 1"
|
v-if="property.displayName !== '' && parameter.options && parameter.options.length !== 1"
|
||||||
:label="$locale.nodeText().inputLabelDisplayName(property, path)"
|
:label="locale.nodeText().inputLabelDisplayName(property, path)"
|
||||||
:underline="true"
|
:underline="true"
|
||||||
size="small"
|
size="small"
|
||||||
color="text-dark"
|
color="text-dark"
|
||||||
|
@ -245,32 +255,33 @@ export default defineComponent({
|
||||||
:class="index ? 'border-top-dashed parameter-item-wrapper ' : 'parameter-item-wrapper'"
|
:class="index ? 'border-top-dashed parameter-item-wrapper ' : 'parameter-item-wrapper'"
|
||||||
>
|
>
|
||||||
<div v-if="!isReadOnly" class="delete-option">
|
<div v-if="!isReadOnly" class="delete-option">
|
||||||
<n8n-icon-button
|
<N8nIconButton
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
text
|
text
|
||||||
size="mini"
|
size="mini"
|
||||||
icon="trash"
|
icon="trash"
|
||||||
:title="$locale.baseText('fixedCollectionParameter.deleteItem')"
|
data-test-id="fixed-collection-delete"
|
||||||
|
:title="locale.baseText('fixedCollectionParameter.deleteItem')"
|
||||||
@click="deleteOption(property.name, index)"
|
@click="deleteOption(property.name, index)"
|
||||||
></n8n-icon-button>
|
></N8nIconButton>
|
||||||
<n8n-icon-button
|
<N8nIconButton
|
||||||
v-if="sortable && index !== 0"
|
v-if="sortable && index !== 0"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
text
|
text
|
||||||
size="mini"
|
size="mini"
|
||||||
icon="angle-up"
|
icon="angle-up"
|
||||||
:title="$locale.baseText('fixedCollectionParameter.moveUp')"
|
:title="locale.baseText('fixedCollectionParameter.moveUp')"
|
||||||
@click="moveOptionUp(property.name, index)"
|
@click="moveOptionUp(property.name, index)"
|
||||||
></n8n-icon-button>
|
></N8nIconButton>
|
||||||
<n8n-icon-button
|
<N8nIconButton
|
||||||
v-if="sortable && index !== mutableValues[property.name].length - 1"
|
v-if="sortable && index !== mutableValues[property.name].length - 1"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
text
|
text
|
||||||
size="mini"
|
size="mini"
|
||||||
icon="angle-down"
|
icon="angle-down"
|
||||||
:title="$locale.baseText('fixedCollectionParameter.moveDown')"
|
:title="locale.baseText('fixedCollectionParameter.moveDown')"
|
||||||
@click="moveOptionDown(property.name, index)"
|
@click="moveOptionDown(property.name, index)"
|
||||||
></n8n-icon-button>
|
></N8nIconButton>
|
||||||
</div>
|
</div>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<ParameterInputList
|
<ParameterInputList
|
||||||
|
@ -288,14 +299,15 @@ export default defineComponent({
|
||||||
<div v-else class="parameter-item">
|
<div v-else class="parameter-item">
|
||||||
<div class="parameter-item-wrapper">
|
<div class="parameter-item-wrapper">
|
||||||
<div v-if="!isReadOnly" class="delete-option">
|
<div v-if="!isReadOnly" class="delete-option">
|
||||||
<n8n-icon-button
|
<N8nIconButton
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
text
|
text
|
||||||
size="mini"
|
size="mini"
|
||||||
icon="trash"
|
icon="trash"
|
||||||
:title="$locale.baseText('fixedCollectionParameter.deleteItem')"
|
data-test-id="fixed-collection-delete"
|
||||||
|
:title="locale.baseText('fixedCollectionParameter.deleteItem')"
|
||||||
@click="deleteOption(property.name)"
|
@click="deleteOption(property.name)"
|
||||||
></n8n-icon-button>
|
></N8nIconButton>
|
||||||
</div>
|
</div>
|
||||||
<ParameterInputList
|
<ParameterInputList
|
||||||
:parameters="property.values"
|
:parameters="property.values"
|
||||||
|
@ -311,7 +323,7 @@ export default defineComponent({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="controls">
|
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="controls">
|
||||||
<n8n-button
|
<N8nButton
|
||||||
v-if="parameter.options && parameter.options.length === 1"
|
v-if="parameter.options && parameter.options.length === 1"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
block
|
block
|
||||||
|
@ -320,20 +332,20 @@ export default defineComponent({
|
||||||
@click="optionSelected(parameter.options[0].name)"
|
@click="optionSelected(parameter.options[0].name)"
|
||||||
/>
|
/>
|
||||||
<div v-else class="add-option">
|
<div v-else class="add-option">
|
||||||
<n8n-select
|
<N8nSelect
|
||||||
v-model="selectedOption"
|
v-model="selectedOption"
|
||||||
:placeholder="getPlaceholderText"
|
:placeholder="getPlaceholderText"
|
||||||
size="small"
|
size="small"
|
||||||
filterable
|
filterable
|
||||||
@update:model-value="optionSelected"
|
@update:model-value="optionSelected"
|
||||||
>
|
>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
v-for="item in parameterOptions"
|
v-for="item in parameterOptions"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
:label="$locale.nodeText().collectionOptionDisplayName(parameter, item, path)"
|
:label="locale.nodeText().collectionOptionDisplayName(parameter, item, path)"
|
||||||
:value="item.name"
|
:value="item.name"
|
||||||
></n8n-option>
|
></N8nOption>
|
||||||
</n8n-select>
|
</N8nSelect>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { useI18n } from '@/composables/useI18n';
|
||||||
import type { IUpdateInformation } from '@/Interface';
|
import type { IUpdateInformation } from '@/Interface';
|
||||||
import CollectionParameter from '@/components/CollectionParameter.vue';
|
import CollectionParameter from '@/components/CollectionParameter.vue';
|
||||||
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
||||||
|
import { N8nButton, N8nInputLabel, N8nText } from 'n8n-design-system';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
|
||||||
defineOptions({ name: 'MultipleParameter' });
|
defineOptions({ name: 'MultipleParameter' });
|
||||||
|
|
||||||
|
@ -42,7 +44,7 @@ watch(
|
||||||
);
|
);
|
||||||
|
|
||||||
const addButtonText = computed(() => {
|
const addButtonText = computed(() => {
|
||||||
if (!props.parameter.typeOptions || !props.parameter.typeOptions.multipleValueButtonText) {
|
if (!props.parameter.typeOptions?.multipleValueButtonText) {
|
||||||
return i18n.baseText('multipleParameter.addItem');
|
return i18n.baseText('multipleParameter.addItem');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +107,7 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="duplicate-parameter" @keydown.stop>
|
<div class="duplicate-parameter" @keydown.stop>
|
||||||
<n8n-input-label
|
<N8nInputLabel
|
||||||
:label="i18n.nodeText().inputLabelDisplayName(parameter, path)"
|
:label="i18n.nodeText().inputLabelDisplayName(parameter, path)"
|
||||||
:tooltip-text="i18n.nodeText().inputLabelDescription(parameter, path)"
|
:tooltip-text="i18n.nodeText().inputLabelDescription(parameter, path)"
|
||||||
:underline="true"
|
:underline="true"
|
||||||
|
@ -120,20 +122,20 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
||||||
:class="parameter.type"
|
:class="parameter.type"
|
||||||
>
|
>
|
||||||
<div v-if="!isReadOnly" class="delete-item clickable">
|
<div v-if="!isReadOnly" class="delete-item clickable">
|
||||||
<font-awesome-icon
|
<FontAwesomeIcon
|
||||||
icon="trash"
|
icon="trash"
|
||||||
:title="i18n.baseText('multipleParameter.deleteItem')"
|
:title="i18n.baseText('multipleParameter.deleteItem')"
|
||||||
@click="deleteItem(index)"
|
@click="deleteItem(index)"
|
||||||
/>
|
/>
|
||||||
<div v-if="sortable">
|
<div v-if="sortable">
|
||||||
<font-awesome-icon
|
<FontAwesomeIcon
|
||||||
v-if="index !== 0"
|
v-if="index !== 0"
|
||||||
icon="angle-up"
|
icon="angle-up"
|
||||||
class="clickable"
|
class="clickable"
|
||||||
:title="i18n.baseText('multipleParameter.moveUp')"
|
:title="i18n.baseText('multipleParameter.moveUp')"
|
||||||
@click="moveOptionUp(index)"
|
@click="moveOptionUp(index)"
|
||||||
/>
|
/>
|
||||||
<font-awesome-icon
|
<FontAwesomeIcon
|
||||||
v-if="index !== mutableValues.length - 1"
|
v-if="index !== mutableValues.length - 1"
|
||||||
icon="angle-down"
|
icon="angle-down"
|
||||||
class="clickable"
|
class="clickable"
|
||||||
|
@ -173,11 +175,11 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
||||||
v-if="(mutableValues && mutableValues.length === 0) || isReadOnly"
|
v-if="(mutableValues && mutableValues.length === 0) || isReadOnly"
|
||||||
class="no-items-exist"
|
class="no-items-exist"
|
||||||
>
|
>
|
||||||
<n8n-text size="small">{{
|
<N8nText size="small">{{
|
||||||
i18n.baseText('multipleParameter.currentlyNoItemsExist')
|
i18n.baseText('multipleParameter.currentlyNoItemsExist')
|
||||||
}}</n8n-text>
|
}}</N8nText>
|
||||||
</div>
|
</div>
|
||||||
<n8n-button
|
<N8nButton
|
||||||
v-if="!isReadOnly"
|
v-if="!isReadOnly"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
block
|
block
|
||||||
|
|
|
@ -61,7 +61,7 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import { isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes';
|
import { isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes';
|
||||||
import { N8nInput, N8nSelect } from 'n8n-design-system';
|
import { N8nIcon, N8nInput, N8nInputNumber, N8nOption, N8nSelect } from 'n8n-design-system';
|
||||||
import type { EventBus } from 'n8n-design-system/utils';
|
import type { EventBus } from 'n8n-design-system/utils';
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
@ -1172,7 +1172,7 @@ onUpdated(async () => {
|
||||||
@update:model-value="valueChangedDebounced"
|
@update:model-value="valueChangedDebounced"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n8n-icon
|
<N8nIcon
|
||||||
v-if="!editorIsReadOnly"
|
v-if="!editorIsReadOnly"
|
||||||
data-test-id="code-editor-fullscreen-button"
|
data-test-id="code-editor-fullscreen-button"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
|
@ -1195,7 +1195,7 @@ onUpdated(async () => {
|
||||||
@update:model-value="valueChangedDebounced"
|
@update:model-value="valueChangedDebounced"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n8n-icon
|
<N8nIcon
|
||||||
data-test-id="code-editor-fullscreen-button"
|
data-test-id="code-editor-fullscreen-button"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
size="xsmall"
|
size="xsmall"
|
||||||
|
@ -1216,7 +1216,7 @@ onUpdated(async () => {
|
||||||
@update:model-value="valueChangedDebounced"
|
@update:model-value="valueChangedDebounced"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n8n-icon
|
<N8nIcon
|
||||||
data-test-id="code-editor-fullscreen-button"
|
data-test-id="code-editor-fullscreen-button"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
size="xsmall"
|
size="xsmall"
|
||||||
|
@ -1237,7 +1237,7 @@ onUpdated(async () => {
|
||||||
@update:model-value="valueChangedDebounced"
|
@update:model-value="valueChangedDebounced"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n8n-icon
|
<N8nIcon
|
||||||
v-if="!editorIsReadOnly"
|
v-if="!editorIsReadOnly"
|
||||||
data-test-id="code-editor-fullscreen-button"
|
data-test-id="code-editor-fullscreen-button"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
|
@ -1258,7 +1258,7 @@ onUpdated(async () => {
|
||||||
@update:model-value="valueChangedDebounced"
|
@update:model-value="valueChangedDebounced"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n8n-icon
|
<N8nIcon
|
||||||
data-test-id="code-editor-fullscreen-button"
|
data-test-id="code-editor-fullscreen-button"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
size="xsmall"
|
size="xsmall"
|
||||||
|
@ -1302,7 +1302,7 @@ onUpdated(async () => {
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<n8n-icon
|
<N8nIcon
|
||||||
v-if="!isReadOnly && !isSecretParameter"
|
v-if="!isReadOnly && !isSecretParameter"
|
||||||
icon="external-link-alt"
|
icon="external-link-alt"
|
||||||
size="xsmall"
|
size="xsmall"
|
||||||
|
@ -1422,9 +1422,9 @@ onUpdated(async () => {
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
>
|
>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
v-for="option in parameterOptions"
|
v-for="option in parameterOptions"
|
||||||
:key="option.value"
|
:key="option.value.toString()"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:label="getOptionsOptionDisplayName(option)"
|
:label="getOptionsOptionDisplayName(option)"
|
||||||
>
|
>
|
||||||
|
@ -1441,7 +1441,7 @@ onUpdated(async () => {
|
||||||
v-n8n-html="getOptionsOptionDescription(option)"
|
v-n8n-html="getOptionsOptionDescription(option)"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-option>
|
</N8nOption>
|
||||||
</N8nSelect>
|
</N8nSelect>
|
||||||
|
|
||||||
<N8nSelect
|
<N8nSelect
|
||||||
|
@ -1460,9 +1460,9 @@ onUpdated(async () => {
|
||||||
@focus="setFocus"
|
@focus="setFocus"
|
||||||
@blur="onBlur"
|
@blur="onBlur"
|
||||||
>
|
>
|
||||||
<n8n-option
|
<N8nOption
|
||||||
v-for="option in parameterOptions"
|
v-for="option in parameterOptions"
|
||||||
:key="option.value"
|
:key="option.value.toString()"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:label="getOptionsOptionDisplayName(option)"
|
:label="getOptionsOptionDisplayName(option)"
|
||||||
>
|
>
|
||||||
|
@ -1474,7 +1474,7 @@ onUpdated(async () => {
|
||||||
v-n8n-html="getOptionsOptionDescription(option)"
|
v-n8n-html="getOptionsOptionDescription(option)"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</n8n-option>
|
</N8nOption>
|
||||||
</N8nSelect>
|
</N8nSelect>
|
||||||
|
|
||||||
<!-- temporary state of booleans while data is mapped -->
|
<!-- temporary state of booleans while data is mapped -->
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { hasExpressionMapping, hasOnlyListMode, isValueExpression } from '@/util
|
||||||
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
import type { INodeProperties, IParameterLabel, NodeParameterValueType } from 'n8n-workflow';
|
import type { INodeProperties, IParameterLabel, NodeParameterValueType } from 'n8n-workflow';
|
||||||
|
import { N8nInputLabel } from 'n8n-design-system';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
parameter: INodeProperties;
|
parameter: INodeProperties;
|
||||||
|
@ -191,7 +192,7 @@ function onDrop(newParamValue: string) {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n8n-input-label
|
<N8nInputLabel
|
||||||
:class="[$style.wrapper]"
|
:class="[$style.wrapper]"
|
||||||
:label="hideLabel ? '' : i18n.nodeText().inputLabelDisplayName(parameter, path)"
|
:label="hideLabel ? '' : i18n.nodeText().inputLabelDisplayName(parameter, path)"
|
||||||
:tooltip-text="hideLabel ? '' : i18n.nodeText().inputLabelDescription(parameter, path)"
|
:tooltip-text="hideLabel ? '' : i18n.nodeText().inputLabelDescription(parameter, path)"
|
||||||
|
@ -262,7 +263,7 @@ function onDrop(newParamValue: string) {
|
||||||
@menu-expanded="onMenuExpanded"
|
@menu-expanded="onMenuExpanded"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n8n-input-label>
|
</N8nInputLabel>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
import { get, set } from 'lodash-es';
|
import { get, set } from 'lodash-es';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { captureException } from '@sentry/vue';
|
import { captureException } from '@sentry/vue';
|
||||||
|
import { N8nNotice, N8nIconButton, N8nInputLabel, N8nText, N8nIcon } from 'n8n-design-system';
|
||||||
|
|
||||||
const LazyFixedCollectionParameter = defineAsyncComponent(
|
const LazyFixedCollectionParameter = defineAsyncComponent(
|
||||||
async () => await import('./FixedCollectionParameter.vue'),
|
async () => await import('./FixedCollectionParameter.vue'),
|
||||||
|
@ -498,7 +499,7 @@ function getParameterValue<T extends NodeParameterValueType = NodeParameterValue
|
||||||
@value-changed="valueChanged"
|
@value-changed="valueChanged"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n8n-notice
|
<N8nNotice
|
||||||
v-else-if="parameter.type === 'notice'"
|
v-else-if="parameter.type === 'notice'"
|
||||||
:class="['parameter-item', parameter.typeOptions?.containerClass ?? '']"
|
:class="['parameter-item', parameter.typeOptions?.containerClass ?? '']"
|
||||||
:content="$locale.nodeText().inputLabelDisplayName(parameter, path)"
|
:content="$locale.nodeText().inputLabelDisplayName(parameter, path)"
|
||||||
|
@ -519,7 +520,7 @@ function getParameterValue<T extends NodeParameterValueType = NodeParameterValue
|
||||||
v-else-if="['collection', 'fixedCollection'].includes(parameter.type)"
|
v-else-if="['collection', 'fixedCollection'].includes(parameter.type)"
|
||||||
class="multi-parameter"
|
class="multi-parameter"
|
||||||
>
|
>
|
||||||
<n8n-icon-button
|
<N8nIconButton
|
||||||
v-if="hideDelete !== true && !isReadOnly && !parameter.isNodeSetting"
|
v-if="hideDelete !== true && !isReadOnly && !parameter.isNodeSetting"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
text
|
text
|
||||||
|
@ -528,8 +529,8 @@ function getParameterValue<T extends NodeParameterValueType = NodeParameterValue
|
||||||
class="delete-option"
|
class="delete-option"
|
||||||
:title="$locale.baseText('parameterInputList.delete')"
|
:title="$locale.baseText('parameterInputList.delete')"
|
||||||
@click="deleteOption(parameter.name)"
|
@click="deleteOption(parameter.name)"
|
||||||
></n8n-icon-button>
|
></N8nIconButton>
|
||||||
<n8n-input-label
|
<N8nInputLabel
|
||||||
:label="$locale.nodeText().inputLabelDisplayName(parameter, path)"
|
:label="$locale.nodeText().inputLabelDisplayName(parameter, path)"
|
||||||
:tooltip-text="$locale.nodeText().inputLabelDescription(parameter, path)"
|
:tooltip-text="$locale.nodeText().inputLabelDescription(parameter, path)"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -558,16 +559,16 @@ function getParameterValue<T extends NodeParameterValueType = NodeParameterValue
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
<n8n-text size="small" class="async-notice">
|
<N8nText size="small" class="async-notice">
|
||||||
<n8n-icon icon="sync-alt" size="xsmall" :spin="true" />
|
<N8nIcon icon="sync-alt" size="xsmall" :spin="true" />
|
||||||
{{ $locale.baseText('parameterInputList.loadingFields') }}
|
{{ $locale.baseText('parameterInputList.loadingFields') }}
|
||||||
</n8n-text>
|
</N8nText>
|
||||||
</template>
|
</template>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
<n8n-text v-else size="small" color="danger" class="async-notice">
|
<N8nText v-else size="small" color="danger" class="async-notice">
|
||||||
<n8n-icon icon="exclamation-triangle" size="xsmall" />
|
<N8nIcon icon="exclamation-triangle" size="xsmall" />
|
||||||
{{ $locale.baseText('parameterInputList.loadingError') }}
|
{{ $locale.baseText('parameterInputList.loadingError') }}
|
||||||
</n8n-text>
|
</N8nText>
|
||||||
</div>
|
</div>
|
||||||
<ResourceMapper
|
<ResourceMapper
|
||||||
v-else-if="parameter.type === 'resourceMapper'"
|
v-else-if="parameter.type === 'resourceMapper'"
|
||||||
|
@ -602,7 +603,7 @@ function getParameterValue<T extends NodeParameterValueType = NodeParameterValue
|
||||||
v-else-if="displayNodeParameter(parameter) && credentialsParameterIndex !== index"
|
v-else-if="displayNodeParameter(parameter) && credentialsParameterIndex !== index"
|
||||||
class="parameter-item"
|
class="parameter-item"
|
||||||
>
|
>
|
||||||
<n8n-icon-button
|
<N8nIconButton
|
||||||
v-if="hideDelete !== true && !isReadOnly && !parameter.isNodeSetting"
|
v-if="hideDelete !== true && !isReadOnly && !parameter.isNodeSetting"
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
text
|
text
|
||||||
|
@ -611,7 +612,7 @@ function getParameterValue<T extends NodeParameterValueType = NodeParameterValue
|
||||||
class="delete-option"
|
class="delete-option"
|
||||||
:title="$locale.baseText('parameterInputList.delete')"
|
:title="$locale.baseText('parameterInputList.delete')"
|
||||||
@click="deleteOption(parameter.name)"
|
@click="deleteOption(parameter.name)"
|
||||||
></n8n-icon-button>
|
></N8nIconButton>
|
||||||
|
|
||||||
<ParameterInputFull
|
<ParameterInputFull
|
||||||
:parameter="parameter"
|
:parameter="parameter"
|
||||||
|
|
Loading…
Reference in a new issue