mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
refactor: Refactor input components to composition API (no-changelog) (#9744)
This commit is contained in:
@@ -21,72 +21,49 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { i18n } from '@/plugins/i18n';
|
||||
<script setup lang="ts">
|
||||
import { useClipboard } from '@/composables/useClipboard';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
},
|
||||
copyButtonText: {
|
||||
type: String,
|
||||
default(): string {
|
||||
return i18n.baseText('generic.copy');
|
||||
},
|
||||
},
|
||||
toastTitle: {
|
||||
type: String,
|
||||
default(): string {
|
||||
return i18n.baseText('generic.copiedToClipboard');
|
||||
},
|
||||
},
|
||||
toastMessage: {
|
||||
type: String,
|
||||
},
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'large',
|
||||
},
|
||||
redactValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const clipboard = useClipboard();
|
||||
|
||||
return {
|
||||
clipboard,
|
||||
...useToast(),
|
||||
type Props = {
|
||||
label?: string;
|
||||
hint?: string;
|
||||
value?: string;
|
||||
copyButtonText: string;
|
||||
toastTitle?: string;
|
||||
toastMessage?: string;
|
||||
size?: 'medium' | 'large';
|
||||
collapse?: boolean;
|
||||
redactValue?: boolean;
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
copy(): void {
|
||||
this.$emit('copy');
|
||||
void this.clipboard.copy(this.value ?? '');
|
||||
|
||||
this.showMessage({
|
||||
title: this.toastTitle,
|
||||
message: this.toastMessage,
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
value: '',
|
||||
placeholder: '',
|
||||
label: '',
|
||||
hint: '',
|
||||
size: 'medium',
|
||||
copyButtonText: useI18n().baseText('generic.copy'),
|
||||
toastTitle: useI18n().baseText('generic.copiedToClipboard'),
|
||||
});
|
||||
const emit = defineEmits<{
|
||||
(event: 'copy'): void;
|
||||
}>();
|
||||
|
||||
const clipboard = useClipboard();
|
||||
const { showMessage } = useToast();
|
||||
|
||||
function copy() {
|
||||
emit('copy');
|
||||
void clipboard.copy(props.value ?? '');
|
||||
|
||||
showMessage({
|
||||
title: props.toastTitle,
|
||||
message: props.toastMessage,
|
||||
type: 'success',
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -144,7 +144,12 @@ import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
|
||||
import type { ICredentialType, INodeProperties, INodeTypeDescription } from 'n8n-workflow';
|
||||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialType,
|
||||
INodeProperties,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { getAppNameFromCredType, isCommunityPackageName } from '@/utils/nodeTypesUtils';
|
||||
|
||||
import Banner from '../Banner.vue';
|
||||
@@ -161,7 +166,7 @@ import { useRootStore } from '@/stores/n8nRoot.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
import type { ICredentialsResponse } from '@/Interface';
|
||||
import type { ICredentialsResponse, IUpdateInformation } from '@/Interface';
|
||||
import AuthTypeSelector from '@/components/CredentialEdit/AuthTypeSelector.vue';
|
||||
import GoogleAuthButton from './GoogleAuthButton.vue';
|
||||
import EnterpriseEdition from '@/components/EnterpriseEdition.ee.vue';
|
||||
@@ -190,7 +195,10 @@ export default defineComponent({
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
credentialData: {},
|
||||
credentialData: {
|
||||
type: Object as PropType<ICredentialDataDecryptedObject>,
|
||||
required: true,
|
||||
},
|
||||
credentialId: {
|
||||
type: String,
|
||||
default: '',
|
||||
@@ -351,7 +359,7 @@ export default defineComponent({
|
||||
getCredentialOptions(type: string): ICredentialsResponse[] {
|
||||
return this.credentialsStore.allUsableCredentialsByType[type];
|
||||
},
|
||||
onDataChange(event: { name: string; value: string | number | boolean | Date | null }): void {
|
||||
onDataChange(event: IUpdateInformation): void {
|
||||
this.$emit('update', event);
|
||||
},
|
||||
onDocumentationUrlClick(): void {
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
<ParameterInputExpanded
|
||||
v-else
|
||||
:parameter="parameter"
|
||||
:value="credentialData[parameter.name]"
|
||||
:value="credentialDataValues[parameter.name]"
|
||||
:documentation-url="documentationUrl"
|
||||
:show-validation-warnings="showValidationWarnings"
|
||||
:label="label"
|
||||
:label="{ size: 'medium' }"
|
||||
event-source="credentials"
|
||||
@update="valueChanged"
|
||||
/>
|
||||
@@ -23,41 +23,41 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import type { IParameterLabel } from 'n8n-workflow';
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
ICredentialDataDecryptedObject,
|
||||
INodeProperties,
|
||||
NodeParameterValueType,
|
||||
} from 'n8n-workflow';
|
||||
import type { IUpdateInformation } from '@/Interface';
|
||||
import ParameterInputExpanded from '../ParameterInputExpanded.vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CredentialsInput',
|
||||
components: {
|
||||
ParameterInputExpanded,
|
||||
},
|
||||
props: [
|
||||
'credentialProperties',
|
||||
'credentialData', // ICredentialsDecryptedResponse
|
||||
'documentationUrl',
|
||||
'showValidationWarnings',
|
||||
],
|
||||
data(): { label: IParameterLabel } {
|
||||
return {
|
||||
label: {
|
||||
size: 'medium',
|
||||
},
|
||||
type Props = {
|
||||
credentialProperties: INodeProperties[];
|
||||
credentialData: ICredentialDataDecryptedObject;
|
||||
documentationUrl: string;
|
||||
showValidationWarnings?: boolean;
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
const name = parameterData.name.split('.').pop();
|
||||
|
||||
this.$emit('update', {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const credentialDataValues = computed(
|
||||
() => props.credentialData as Record<string, NodeParameterValueType>,
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'update', value: IUpdateInformation): void;
|
||||
}>();
|
||||
|
||||
function valueChanged(parameterData: IUpdateInformation) {
|
||||
const name = parameterData.name.split('.').pop() ?? parameterData.name;
|
||||
|
||||
emit('update', {
|
||||
name,
|
||||
value: parameterData.value,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -5,23 +5,24 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExpandableInputBase',
|
||||
props: ['modelValue', 'placeholder', 'staticSize'],
|
||||
computed: {
|
||||
hiddenValue() {
|
||||
let value = (this.modelValue as string).replace(/\s/g, '.'); // force input to expand on space chars
|
||||
type Props = {
|
||||
modelValue: string;
|
||||
placeholder?: string;
|
||||
staticSize?: boolean;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), { staticSize: false, placeholder: '' });
|
||||
|
||||
const hiddenValue = computed(() => {
|
||||
let value = props.modelValue.replace(/\s/g, '.'); // force input to expand on space chars
|
||||
if (!value) {
|
||||
// @ts-ignore
|
||||
value = this.placeholder;
|
||||
value = props.placeholder;
|
||||
}
|
||||
|
||||
return `${value}`; // adjust for padding
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<ExpandableInputBase :model-value="modelValue" :placeholder="placeholder">
|
||||
<input
|
||||
ref="input"
|
||||
ref="inputRef"
|
||||
v-on-click-outside="onClickOutside"
|
||||
class="el-input__inner"
|
||||
:value="modelValue"
|
||||
@@ -15,58 +15,66 @@
|
||||
</ExpandableInputBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import ExpandableInputBase from './ExpandableInputBase.vue';
|
||||
import type { PropType } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import type { EventBus } from 'n8n-design-system';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import ExpandableInputBase from './ExpandableInputBase.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExpandableInputEdit',
|
||||
components: { ExpandableInputBase },
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
placeholder: { type: String, required: true },
|
||||
maxlength: { type: Number },
|
||||
autofocus: { type: Boolean },
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'enter', 'blur', 'esc'],
|
||||
mounted() {
|
||||
type Props = {
|
||||
modelValue: string;
|
||||
placeholder: string;
|
||||
maxlength?: number;
|
||||
autofocus?: boolean;
|
||||
eventBus?: EventBus;
|
||||
};
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:model-value', value: string): void;
|
||||
(event: 'enter', value: string): void;
|
||||
(event: 'blur', value: string): void;
|
||||
(event: 'esc'): void;
|
||||
}>();
|
||||
|
||||
const inputRef = ref<HTMLInputElement>();
|
||||
|
||||
onMounted(() => {
|
||||
// autofocus on input element is not reliable
|
||||
if (this.autofocus && this.$refs.input) {
|
||||
this.focus();
|
||||
if (props.autofocus && inputRef.value) {
|
||||
focus();
|
||||
}
|
||||
this.eventBus?.on('focus', this.focus);
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.eventBus?.off('focus', this.focus);
|
||||
},
|
||||
methods: {
|
||||
focus() {
|
||||
if (this.$refs.input) {
|
||||
(this.$refs.input as HTMLInputElement).focus();
|
||||
}
|
||||
},
|
||||
onInput() {
|
||||
this.$emit('update:modelValue', (this.$refs.input as HTMLInputElement).value);
|
||||
},
|
||||
onEnter() {
|
||||
this.$emit('enter', (this.$refs.input as HTMLInputElement).value);
|
||||
},
|
||||
onClickOutside(e: Event) {
|
||||
if (e.type === 'click') {
|
||||
this.$emit('blur', (this.$refs.input as HTMLInputElement).value);
|
||||
}
|
||||
},
|
||||
onEscape() {
|
||||
this.$emit('esc');
|
||||
},
|
||||
},
|
||||
props.eventBus?.on('focus', focus);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
props.eventBus?.off('focus', focus);
|
||||
});
|
||||
|
||||
function focus() {
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function onInput() {
|
||||
if (inputRef.value) {
|
||||
emit('update:model-value', inputRef.value.value);
|
||||
}
|
||||
}
|
||||
|
||||
function onEnter() {
|
||||
if (inputRef.value) {
|
||||
emit('enter', inputRef.value.value);
|
||||
}
|
||||
}
|
||||
|
||||
function onClickOutside(e: Event) {
|
||||
if (e.type === 'click' && inputRef.value) {
|
||||
emit('blur', inputRef.value.value);
|
||||
}
|
||||
}
|
||||
|
||||
function onEscape() {
|
||||
emit('esc');
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
</ExpandableInputBase>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import ExpandableInputBase from './ExpandableInputBase.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ExpandableInputPreview',
|
||||
components: { ExpandableInputBase },
|
||||
props: ['modelValue'],
|
||||
});
|
||||
type Props = {
|
||||
modelValue: string;
|
||||
};
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -105,13 +105,13 @@ const rightParameter = computed<INodeProperties>(() => {
|
||||
|
||||
const debouncedEmitUpdate = debounce(() => emit('update', condition.value), { debounceTime: 500 });
|
||||
|
||||
const onLeftValueChange = (update: IUpdateInformation<NodeParameterValue>): void => {
|
||||
condition.value.leftValue = update.value;
|
||||
const onLeftValueChange = (update: IUpdateInformation): void => {
|
||||
condition.value.leftValue = update.value as NodeParameterValue;
|
||||
debouncedEmitUpdate();
|
||||
};
|
||||
|
||||
const onRightValueChange = (update: IUpdateInformation<NodeParameterValue>): void => {
|
||||
condition.value.rightValue = update.value;
|
||||
const onRightValueChange = (update: IUpdateInformation): void => {
|
||||
condition.value.rightValue = update.value as NodeParameterValue;
|
||||
debouncedEmitUpdate();
|
||||
};
|
||||
|
||||
|
||||
@@ -54,124 +54,114 @@
|
||||
</n8n-input-label>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script setup lang="ts">
|
||||
import type { IUpdateInformation } from '@/Interface';
|
||||
import ParameterOptions from './ParameterOptions.vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import ParameterInputWrapper from './ParameterInputWrapper.vue';
|
||||
import { isValueExpression } from '@/utils/nodeTypesUtils';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { isValueExpression as isValueExpressionUtil } from '@/utils/nodeTypesUtils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import type {
|
||||
INodeParameterResourceLocator,
|
||||
INodeProperties,
|
||||
IParameterLabel,
|
||||
NodeParameterValueType,
|
||||
} from 'n8n-workflow';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import { computed, ref } from 'vue';
|
||||
import ParameterInputWrapper from './ParameterInputWrapper.vue';
|
||||
import ParameterOptions from './ParameterOptions.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ParameterInputExpanded',
|
||||
components: {
|
||||
ParameterOptions,
|
||||
ParameterInputWrapper,
|
||||
},
|
||||
props: {
|
||||
parameter: {
|
||||
type: Object as PropType<INodeProperties>,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: Object as PropType<NodeParameterValueType>,
|
||||
},
|
||||
showValidationWarnings: {
|
||||
type: Boolean,
|
||||
},
|
||||
documentationUrl: {
|
||||
type: String,
|
||||
},
|
||||
eventSource: {
|
||||
type: String,
|
||||
},
|
||||
label: {
|
||||
type: Object as PropType<IParameterLabel>,
|
||||
default: () => ({
|
||||
size: 'small',
|
||||
}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false,
|
||||
blurredEver: false,
|
||||
menuExpanded: false,
|
||||
eventBus: createEventBus(),
|
||||
type Props = {
|
||||
parameter: INodeProperties;
|
||||
value: NodeParameterValueType;
|
||||
showValidationWarnings?: boolean;
|
||||
documentationUrl?: string;
|
||||
eventSource?: string;
|
||||
label?: IParameterLabel;
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useWorkflowsStore),
|
||||
showRequiredErrors(): boolean {
|
||||
if (!this.parameter.required) {
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
label: () => ({ size: 'small' }),
|
||||
});
|
||||
const emit = defineEmits<{
|
||||
(event: 'update', value: IUpdateInformation): void;
|
||||
}>();
|
||||
|
||||
const focused = ref(false);
|
||||
const blurredEver = ref(false);
|
||||
const menuExpanded = ref(false);
|
||||
const eventBus = ref(createEventBus());
|
||||
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
|
||||
const i18n = useI18n();
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
const showRequiredErrors = computed(() => {
|
||||
if (!props.parameter.required) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.blurredEver || this.showValidationWarnings) {
|
||||
if (this.parameter.type === 'string') {
|
||||
return !this.value;
|
||||
if (blurredEver.value || props.showValidationWarnings) {
|
||||
if (props.parameter.type === 'string') {
|
||||
return !props.value;
|
||||
}
|
||||
|
||||
if (this.parameter.type === 'number') {
|
||||
if (typeof this.value === 'string' && this.value.startsWith('=')) {
|
||||
if (props.parameter.type === 'number') {
|
||||
if (typeof props.value === 'string' && props.value.startsWith('=')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return typeof this.value !== 'number';
|
||||
return typeof props.value !== 'number';
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
hint(): string | null {
|
||||
if (this.isValueExpression) {
|
||||
});
|
||||
|
||||
const hint = computed(() => {
|
||||
if (isValueExpression.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.$locale.credText().hint(this.parameter);
|
||||
},
|
||||
isValueExpression(): boolean {
|
||||
return isValueExpression(
|
||||
this.parameter,
|
||||
this.value as string | INodeParameterResourceLocator,
|
||||
return i18n.credText().hint(props.parameter);
|
||||
});
|
||||
|
||||
const isValueExpression = computed(() => {
|
||||
return isValueExpressionUtil(
|
||||
props.parameter,
|
||||
props.value as string | INodeParameterResourceLocator,
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
},
|
||||
onBlur() {
|
||||
this.blurredEver = true;
|
||||
this.focused = false;
|
||||
},
|
||||
onMenuExpanded(expanded: boolean) {
|
||||
this.menuExpanded = expanded;
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
this.eventBus.emit('optionSelected', command);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('update', parameterData);
|
||||
},
|
||||
onDocumentationUrlClick(): void {
|
||||
this.$telemetry.track('User clicked credential modal docs link', {
|
||||
docs_link: this.documentationUrl,
|
||||
});
|
||||
|
||||
function onFocus() {
|
||||
focused.value = true;
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
blurredEver.value = true;
|
||||
focused.value = false;
|
||||
}
|
||||
|
||||
function onMenuExpanded(expanded: boolean) {
|
||||
menuExpanded.value = expanded;
|
||||
}
|
||||
|
||||
function optionSelected(command: string) {
|
||||
eventBus.value.emit('optionSelected', command);
|
||||
}
|
||||
|
||||
function valueChanged(parameterData: IUpdateInformation) {
|
||||
emit('update', parameterData);
|
||||
}
|
||||
|
||||
function onDocumentationUrlClick(): void {
|
||||
telemetry.track('User clicked credential modal docs link', {
|
||||
docs_link: props.documentationUrl,
|
||||
source: 'field',
|
||||
workflow_id: this.workflowsStore.workflowId,
|
||||
});
|
||||
},
|
||||
},
|
||||
workflow_id: workflowsStore.workflowId,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
type="mapping"
|
||||
:disabled="isDropDisabled"
|
||||
:sticky="true"
|
||||
:sticky-offset="isValueExpression ? [26, 3] : [3, 3]"
|
||||
:sticky-offset="isExpression ? [26, 3] : [3, 3]"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<template #default="{ droppable, activeDrop }">
|
||||
@@ -77,258 +77,192 @@
|
||||
</n8n-input-label>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import type { INodeUi, IRunDataDisplayMode, IUpdateInformation } from '@/Interface';
|
||||
import type { IUpdateInformation } from '@/Interface';
|
||||
|
||||
import ParameterOptions from '@/components/ParameterOptions.vue';
|
||||
import DraggableTarget from '@/components/DraggableTarget.vue';
|
||||
import ParameterInputWrapper from '@/components/ParameterInputWrapper.vue';
|
||||
import ParameterOptions from '@/components/ParameterOptions.vue';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { hasExpressionMapping, hasOnlyListMode, isValueExpression } from '@/utils/nodeTypesUtils';
|
||||
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
||||
import ParameterInputWrapper from '@/components/ParameterInputWrapper.vue';
|
||||
import type {
|
||||
INodeProperties,
|
||||
INodePropertyMode,
|
||||
IParameterLabel,
|
||||
NodeParameterValueType,
|
||||
} from 'n8n-workflow';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useSegment } from '@/stores/segment.store';
|
||||
import { getMappedResult } from '@/utils/mappingUtils';
|
||||
import { hasExpressionMapping, hasOnlyListMode, isValueExpression } from '@/utils/nodeTypesUtils';
|
||||
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import type { INodeProperties, IParameterLabel, NodeParameterValueType } from 'n8n-workflow';
|
||||
import InlineExpressionTip from './InlineExpressionEditor/InlineExpressionTip.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ParameterInputFull',
|
||||
components: {
|
||||
ParameterOptions,
|
||||
DraggableTarget,
|
||||
ParameterInputWrapper,
|
||||
InlineExpressionTip,
|
||||
},
|
||||
props: {
|
||||
displayOptions: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
optionsPosition: {
|
||||
type: String as PropType<'bottom' | 'top'>,
|
||||
default: 'top',
|
||||
},
|
||||
hideHint: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
isAssignment: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hideIssues: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
parameter: {
|
||||
type: Object as PropType<INodeProperties>,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
value: {
|
||||
type: [Number, String, Boolean, Array, Object] as PropType<NodeParameterValueType>,
|
||||
},
|
||||
label: {
|
||||
type: Object as PropType<IParameterLabel>,
|
||||
default: () => ({
|
||||
size: 'small',
|
||||
}),
|
||||
},
|
||||
entryIndex: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const eventBus = createEventBus();
|
||||
const i18n = useI18n();
|
||||
type Props = {
|
||||
parameter: INodeProperties;
|
||||
path: string;
|
||||
value: NodeParameterValueType;
|
||||
label?: IParameterLabel;
|
||||
displayOptions?: boolean;
|
||||
optionsPosition?: 'bottom' | 'top';
|
||||
hideHint?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
rows?: number;
|
||||
isAssignment?: boolean;
|
||||
hideLabel?: boolean;
|
||||
hideIssues?: boolean;
|
||||
entryIndex?: number;
|
||||
};
|
||||
|
||||
return {
|
||||
i18n,
|
||||
eventBus,
|
||||
...useToast(),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
focused: false,
|
||||
menuExpanded: false,
|
||||
forceShowExpression: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNDVStore),
|
||||
node(): INodeUi | null {
|
||||
return this.ndvStore.activeNode;
|
||||
},
|
||||
hint(): string {
|
||||
return this.i18n.nodeText().hint(this.parameter, this.path);
|
||||
},
|
||||
isInputTypeString(): boolean {
|
||||
return this.parameter.type === 'string';
|
||||
},
|
||||
isInputTypeNumber(): boolean {
|
||||
return this.parameter.type === 'number';
|
||||
},
|
||||
isResourceLocator(): boolean {
|
||||
return this.parameter.type === 'resourceLocator';
|
||||
},
|
||||
isDropDisabled(): boolean {
|
||||
return this.parameter.noDataExpression || this.isReadOnly || this.isResourceLocator;
|
||||
},
|
||||
isValueExpression(): boolean {
|
||||
return isValueExpression(this.parameter, this.value);
|
||||
},
|
||||
showExpressionSelector(): boolean {
|
||||
return this.isResourceLocator ? !hasOnlyListMode(this.parameter) : true;
|
||||
},
|
||||
isInputDataEmpty(): boolean {
|
||||
return this.ndvStore.isNDVDataEmpty('input');
|
||||
},
|
||||
displayMode(): IRunDataDisplayMode {
|
||||
return this.ndvStore.inputPanelDisplayMode;
|
||||
},
|
||||
showDragnDropTip(): boolean {
|
||||
return (
|
||||
this.focused &&
|
||||
(this.isInputTypeString || this.isInputTypeNumber) &&
|
||||
!this.isValueExpression &&
|
||||
!this.isDropDisabled &&
|
||||
(!this.ndvStore.hasInputData || !this.isInputDataEmpty) &&
|
||||
!this.ndvStore.isMappingOnboarded &&
|
||||
this.ndvStore.isInputParentOfActiveNode
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
optionsPosition: 'top',
|
||||
hideHint: false,
|
||||
isReadOnly: false,
|
||||
rows: 5,
|
||||
hideLabel: false,
|
||||
hideIssues: false,
|
||||
label: () => ({ size: 'small' }),
|
||||
});
|
||||
const emit = defineEmits<{
|
||||
(event: 'blur'): void;
|
||||
(event: 'update', value: IUpdateInformation): void;
|
||||
}>();
|
||||
|
||||
const i18n = useI18n();
|
||||
const toast = useToast();
|
||||
|
||||
const eventBus = ref(createEventBus());
|
||||
const focused = ref(false);
|
||||
const menuExpanded = ref(false);
|
||||
const forceShowExpression = ref(false);
|
||||
|
||||
const ndvStore = useNDVStore();
|
||||
|
||||
const node = computed(() => ndvStore.activeNode);
|
||||
const hint = computed(() => i18n.nodeText().hint(props.parameter, props.path));
|
||||
const isInputTypeString = computed(() => props.parameter.type === 'string');
|
||||
const isInputTypeNumber = computed(() => props.parameter.type === 'number');
|
||||
const isResourceLocator = computed(() => props.parameter.type === 'resourceLocator');
|
||||
const isDropDisabled = computed(
|
||||
() => props.parameter.noDataExpression || props.isReadOnly || isResourceLocator.value,
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
if (!this.parameter.noDataExpression) {
|
||||
this.ndvStore.setMappableNDVInputFocus(this.parameter.displayName);
|
||||
const isExpression = computed(() => isValueExpression(props.parameter, props.value));
|
||||
const showExpressionSelector = computed(() =>
|
||||
isResourceLocator.value ? !hasOnlyListMode(props.parameter) : true,
|
||||
);
|
||||
const isInputDataEmpty = computed(() => ndvStore.isNDVDataEmpty('input'));
|
||||
const showDragnDropTip = computed(
|
||||
() =>
|
||||
focused.value &&
|
||||
(isInputTypeString.value || isInputTypeNumber.value) &&
|
||||
!isExpression.value &&
|
||||
!isDropDisabled.value &&
|
||||
(!ndvStore.hasInputData || !isInputDataEmpty.value) &&
|
||||
!ndvStore.isMappingOnboarded &&
|
||||
ndvStore.isInputParentOfActiveNode,
|
||||
);
|
||||
|
||||
function onFocus() {
|
||||
focused.value = true;
|
||||
if (!props.parameter.noDataExpression) {
|
||||
ndvStore.setMappableNDVInputFocus(props.parameter.displayName);
|
||||
}
|
||||
this.ndvStore.setFocusedInputPath(this.path ?? '');
|
||||
},
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
ndvStore.setFocusedInputPath(props.path ?? '');
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
focused.value = false;
|
||||
if (
|
||||
!this.parameter.noDataExpression &&
|
||||
this.ndvStore.focusedMappableInput === this.parameter.displayName
|
||||
!props.parameter.noDataExpression &&
|
||||
ndvStore.focusedMappableInput === props.parameter.displayName
|
||||
) {
|
||||
this.ndvStore.setMappableNDVInputFocus('');
|
||||
ndvStore.setMappableNDVInputFocus('');
|
||||
}
|
||||
this.ndvStore.setFocusedInputPath('');
|
||||
this.$emit('blur');
|
||||
},
|
||||
onMenuExpanded(expanded: boolean) {
|
||||
this.menuExpanded = expanded;
|
||||
},
|
||||
optionSelected(command: string) {
|
||||
this.eventBus.emit('optionSelected', command);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('update', parameterData);
|
||||
},
|
||||
onTextInput(parameterData: IUpdateInformation) {
|
||||
if (isValueExpression(this.parameter, parameterData.value)) {
|
||||
this.eventBus.emit('optionSelected', 'addExpression');
|
||||
ndvStore.setFocusedInputPath('');
|
||||
emit('blur');
|
||||
}
|
||||
},
|
||||
onDrop(newParamValue: string) {
|
||||
const value = this.value;
|
||||
const updatedValue = getMappedResult(this.parameter, newParamValue, value);
|
||||
const prevValue =
|
||||
this.isResourceLocator && isResourceLocatorValue(value) ? value.value : value;
|
||||
|
||||
function onMenuExpanded(expanded: boolean) {
|
||||
menuExpanded.value = expanded;
|
||||
}
|
||||
|
||||
function optionSelected(command: string) {
|
||||
eventBus.value.emit('optionSelected', command);
|
||||
}
|
||||
|
||||
function valueChanged(parameterData: IUpdateInformation) {
|
||||
emit('update', parameterData);
|
||||
}
|
||||
|
||||
function onTextInput(parameterData: IUpdateInformation) {
|
||||
if (isValueExpression(props.parameter, parameterData.value)) {
|
||||
eventBus.value.emit('optionSelected', 'addExpression');
|
||||
}
|
||||
}
|
||||
|
||||
function onDrop(newParamValue: string) {
|
||||
const value = props.value;
|
||||
const updatedValue = getMappedResult(props.parameter, newParamValue, value);
|
||||
const prevValue = isResourceLocator.value && isResourceLocatorValue(value) ? value.value : value;
|
||||
|
||||
if (updatedValue.startsWith('=')) {
|
||||
this.forceShowExpression = true;
|
||||
forceShowExpression.value = true;
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (this.node) {
|
||||
if (node.value) {
|
||||
let parameterData;
|
||||
if (this.isResourceLocator) {
|
||||
if (!isResourceLocatorValue(this.value)) {
|
||||
if (isResourceLocator.value) {
|
||||
if (!isResourceLocatorValue(props.value)) {
|
||||
parameterData = {
|
||||
node: this.node.name,
|
||||
name: this.path,
|
||||
node: node.value.name,
|
||||
name: props.path,
|
||||
value: { __rl: true, value: updatedValue, mode: '' },
|
||||
};
|
||||
} else if (
|
||||
this.value.mode === 'list' &&
|
||||
this.parameter.modes &&
|
||||
this.parameter.modes.length > 1
|
||||
props.value.mode === 'list' &&
|
||||
props.parameter.modes &&
|
||||
props.parameter.modes.length > 1
|
||||
) {
|
||||
let mode =
|
||||
this.parameter.modes.find((mode: INodePropertyMode) => mode.name === 'id') || null;
|
||||
let mode = props.parameter.modes.find((m) => m.name === 'id') ?? null;
|
||||
if (!mode) {
|
||||
mode = this.parameter.modes.filter(
|
||||
(mode: INodePropertyMode) => mode.name !== 'list',
|
||||
)[0];
|
||||
mode = props.parameter.modes.filter((m) => m.name !== 'list')[0];
|
||||
}
|
||||
|
||||
parameterData = {
|
||||
node: this.node.name,
|
||||
name: this.path,
|
||||
node: node.value.name,
|
||||
name: props.path,
|
||||
value: { __rl: true, value: updatedValue, mode: mode ? mode.name : '' },
|
||||
};
|
||||
} else {
|
||||
parameterData = {
|
||||
node: this.node.name,
|
||||
name: this.path,
|
||||
value: { __rl: true, value: updatedValue, mode: this.value.mode },
|
||||
node: node.value.name,
|
||||
name: props.path,
|
||||
value: { __rl: true, value: updatedValue, mode: props.value?.mode },
|
||||
};
|
||||
}
|
||||
} else {
|
||||
parameterData = {
|
||||
node: this.node.name,
|
||||
name: this.path,
|
||||
node: node.value.name,
|
||||
name: props.path,
|
||||
value: updatedValue,
|
||||
};
|
||||
}
|
||||
|
||||
this.valueChanged(parameterData);
|
||||
this.eventBus.emit('drop', updatedValue);
|
||||
valueChanged(parameterData);
|
||||
eventBus.value.emit('drop', updatedValue);
|
||||
|
||||
if (!this.ndvStore.isMappingOnboarded) {
|
||||
this.showMessage({
|
||||
title: this.i18n.baseText('dataMapping.success.title'),
|
||||
message: this.i18n.baseText('dataMapping.success.moreInfo'),
|
||||
if (!ndvStore.isMappingOnboarded) {
|
||||
toast.showMessage({
|
||||
title: i18n.baseText('dataMapping.success.title'),
|
||||
message: i18n.baseText('dataMapping.success.moreInfo'),
|
||||
type: 'success',
|
||||
dangerouslyUseHTMLString: true,
|
||||
});
|
||||
|
||||
this.ndvStore.setMappingOnboarded();
|
||||
ndvStore.setMappingOnboarded();
|
||||
}
|
||||
|
||||
this.ndvStore.setMappingTelemetry({
|
||||
dest_node_type: this.node.type,
|
||||
dest_parameter: this.path,
|
||||
ndvStore.setMappingTelemetry({
|
||||
dest_node_type: node.value.type,
|
||||
dest_parameter: props.path,
|
||||
dest_parameter_mode:
|
||||
typeof prevValue === 'string' && prevValue.startsWith('=') ? 'expression' : 'fixed',
|
||||
dest_parameter_empty: prevValue === '' || prevValue === undefined,
|
||||
@@ -342,11 +276,9 @@ export default defineComponent({
|
||||
const segment = useSegment();
|
||||
segment.track(segment.EVENTS.MAPPED_DATA);
|
||||
}
|
||||
this.forceShowExpression = false;
|
||||
forceShowExpression.value = false;
|
||||
}, 200);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -1,46 +1,51 @@
|
||||
<template>
|
||||
<n8n-text v-if="hint" size="small" color="text-base" tag="div">
|
||||
<div v-if="!renderHTML" :class="classes"><span v-html="simplyText"></span></div>
|
||||
<div
|
||||
v-if="!renderHTML"
|
||||
:class="{
|
||||
[$style.singleline]: singleLine,
|
||||
[$style.highlight]: highlight,
|
||||
}"
|
||||
>
|
||||
<span v-html="simplyText"></span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
ref="hint"
|
||||
ref="hintTextRef"
|
||||
:class="{ [$style.singleline]: singleLine, [$style.highlight]: highlight }"
|
||||
v-html="sanitizeHtml(hint)"
|
||||
></div>
|
||||
</n8n-text>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { sanitizeHtml } from '@/utils/htmlUtils';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputHint',
|
||||
props: {
|
||||
hint: {
|
||||
type: String,
|
||||
},
|
||||
highlight: {
|
||||
type: Boolean,
|
||||
},
|
||||
singleLine: {
|
||||
type: Boolean,
|
||||
},
|
||||
renderHTML: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
classes() {
|
||||
return {
|
||||
[this.$style.singleline]: this.singleLine,
|
||||
[this.$style.highlight]: this.highlight,
|
||||
type Props = {
|
||||
hint: string;
|
||||
highlight?: boolean;
|
||||
singleLine?: boolean;
|
||||
renderHTML?: boolean;
|
||||
};
|
||||
},
|
||||
simplyText(): string {
|
||||
if (this.hint) {
|
||||
return String(this.hint)
|
||||
|
||||
const hintTextRef = ref<HTMLDivElement>();
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
highlight: false,
|
||||
singleLine: false,
|
||||
renderHTML: false,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (hintTextRef.value) {
|
||||
hintTextRef.value.querySelectorAll('a').forEach((a) => (a.target = '_blank'));
|
||||
}
|
||||
});
|
||||
|
||||
const simplyText = computed(() => {
|
||||
if (props.hint) {
|
||||
return String(props.hint)
|
||||
.replace(/&/g, '&') // allows us to keep spaces at the beginning of an expression
|
||||
.replace(/</g, '<') // prevent XSS exploits since we are rendering HTML
|
||||
.replace(/>/g, '>')
|
||||
@@ -49,16 +54,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.$refs.hint) {
|
||||
(this.$refs.hint as Element).querySelectorAll('a').forEach((a) => (a.target = '_blank'));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sanitizeHtml,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -161,27 +161,26 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script setup lang="ts">
|
||||
import type {
|
||||
INodeParameters,
|
||||
INodeProperties,
|
||||
INodeTypeDescription,
|
||||
NodeParameterValue,
|
||||
NodeParameterValueType,
|
||||
} from 'n8n-workflow';
|
||||
import { deepCopy } from 'n8n-workflow';
|
||||
import { mapStores } from 'pinia';
|
||||
import type { PropType } from 'vue';
|
||||
import { defineAsyncComponent, defineComponent, onErrorCaptured, ref } from 'vue';
|
||||
import { computed, defineAsyncComponent, onErrorCaptured, ref, watch } from 'vue';
|
||||
|
||||
import type { INodeUi, IUpdateInformation } from '@/Interface';
|
||||
import type { IUpdateInformation } from '@/Interface';
|
||||
|
||||
import AssignmentCollection from '@/components/AssignmentCollection/AssignmentCollection.vue';
|
||||
import FilterConditions from '@/components/FilterConditions/FilterConditions.vue';
|
||||
import ImportCurlParameter from '@/components/ImportCurlParameter.vue';
|
||||
import MultipleParameter from '@/components/MultipleParameter.vue';
|
||||
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
||||
import ResourceMapper from '@/components/ResourceMapper/ResourceMapper.vue';
|
||||
import FilterConditions from '@/components/FilterConditions/FilterConditions.vue';
|
||||
import AssignmentCollection from '@/components/AssignmentCollection/AssignmentCollection.vue';
|
||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { KEEP_AUTH_IN_NDV_FOR_NODES } from '@/constants';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||
@@ -191,9 +190,7 @@ import {
|
||||
isAuthRelatedParameter,
|
||||
} from '@/utils/nodeTypesUtils';
|
||||
import { get, set } from 'lodash-es';
|
||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
|
||||
const FixedCollectionParameter = defineAsyncComponent(
|
||||
async () => await import('./FixedCollectionParameter.vue'),
|
||||
@@ -202,59 +199,32 @@ const CollectionParameter = defineAsyncComponent(
|
||||
async () => await import('./CollectionParameter.vue'),
|
||||
);
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ParameterInputList',
|
||||
components: {
|
||||
MultipleParameter,
|
||||
ParameterInputFull,
|
||||
FixedCollectionParameter,
|
||||
CollectionParameter,
|
||||
ImportCurlParameter,
|
||||
ResourceMapper,
|
||||
FilterConditions,
|
||||
AssignmentCollection,
|
||||
},
|
||||
props: {
|
||||
nodeValues: {
|
||||
type: Object as PropType<INodeParameters>,
|
||||
required: true,
|
||||
},
|
||||
parameters: {
|
||||
type: Array as PropType<INodeProperties[]>,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hideDelete: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
indent: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
hiddenIssuesInputs: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
entryIndex: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
type Props = {
|
||||
nodeValues: INodeParameters;
|
||||
parameters: INodeProperties[];
|
||||
path?: string;
|
||||
hideDelete?: boolean;
|
||||
indent?: boolean;
|
||||
isReadOnly?: boolean;
|
||||
hiddenIssuesInputs?: string[];
|
||||
entryIndex?: number;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), { path: '', hiddenIssuesInputs: () => [] });
|
||||
const emit = defineEmits<{
|
||||
(event: 'activate'): void;
|
||||
(event: 'valueChanged', value: IUpdateInformation): void;
|
||||
(event: 'parameterBlur', value: string): void;
|
||||
}>();
|
||||
|
||||
const nodeTypesStore = useNodeTypesStore();
|
||||
const ndvStore = useNDVStore();
|
||||
|
||||
const nodeHelpers = useNodeHelpers();
|
||||
const asyncLoadingError = ref(false);
|
||||
const router = useRouter();
|
||||
const workflowHelpers = useWorkflowHelpers({ router });
|
||||
|
||||
// This will catch errors in async components
|
||||
onErrorCaptured((e, component) => {
|
||||
if (
|
||||
!['FixedCollectionParameter', 'CollectionParameter'].includes(
|
||||
@@ -274,76 +244,56 @@ export default defineComponent({
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
nodeHelpers,
|
||||
asyncLoadingError,
|
||||
workflowHelpers,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNodeTypesStore, useNDVStore),
|
||||
nodeTypeVersion(): number | null {
|
||||
if (this.node) {
|
||||
return this.node.typeVersion;
|
||||
const nodeType = computed(() => {
|
||||
if (node.value) {
|
||||
return nodeTypesStore.getNodeType(node.value.type, node.value.typeVersion);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
nodeTypeName(): string {
|
||||
if (this.node) {
|
||||
return this.node.type;
|
||||
}
|
||||
return '';
|
||||
},
|
||||
nodeType(): INodeTypeDescription | null {
|
||||
if (this.node) {
|
||||
return this.nodeTypesStore.getNodeType(this.node.type, this.node.typeVersion);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
filteredParameters(): INodeProperties[] {
|
||||
return this.parameters.filter((parameter: INodeProperties) =>
|
||||
this.displayNodeParameter(parameter),
|
||||
);
|
||||
},
|
||||
filteredParameterNames(): string[] {
|
||||
return this.filteredParameters.map((parameter) => parameter.name);
|
||||
},
|
||||
node(): INodeUi | null {
|
||||
return this.ndvStore.activeNode;
|
||||
},
|
||||
nodeAuthFields(): INodeProperties[] {
|
||||
return getNodeAuthFields(this.nodeType);
|
||||
},
|
||||
credentialsParameterIndex(): number {
|
||||
return this.filteredParameters.findIndex((parameter) => parameter.type === 'credentials');
|
||||
},
|
||||
indexToShowSlotAt(): number {
|
||||
const credentialsParameterIndex = this.credentialsParameterIndex;
|
||||
});
|
||||
|
||||
if (credentialsParameterIndex !== -1) {
|
||||
return credentialsParameterIndex;
|
||||
const filteredParameters = computed(() => {
|
||||
return props.parameters.filter((parameter: INodeProperties) => displayNodeParameter(parameter));
|
||||
});
|
||||
|
||||
const filteredParameterNames = computed(() => {
|
||||
return filteredParameters.value.map((parameter) => parameter.name);
|
||||
});
|
||||
|
||||
const node = computed(() => ndvStore.activeNode);
|
||||
|
||||
const nodeAuthFields = computed(() => {
|
||||
return getNodeAuthFields(nodeType.value);
|
||||
});
|
||||
|
||||
const credentialsParameterIndex = computed(() => {
|
||||
return filteredParameters.value.findIndex((parameter) => parameter.type === 'credentials');
|
||||
});
|
||||
|
||||
const indexToShowSlotAt = computed(() => {
|
||||
if (credentialsParameterIndex.value !== -1) {
|
||||
return credentialsParameterIndex.value;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
// For nodes that use old credentials UI, keep credentials below authentication field in NDV
|
||||
// otherwise credentials will use auth filed position since the auth field is moved to credentials modal
|
||||
const fieldOffset = KEEP_AUTH_IN_NDV_FOR_NODES.includes(this.nodeType?.name || '') ? 1 : 0;
|
||||
const credentialsDependencies = this.getCredentialsDependencies();
|
||||
const fieldOffset = KEEP_AUTH_IN_NDV_FOR_NODES.includes(nodeType.value?.name || '') ? 1 : 0;
|
||||
const credentialsDependencies = getCredentialsDependencies();
|
||||
|
||||
this.filteredParameters.forEach((prop, propIndex) => {
|
||||
filteredParameters.value.forEach((prop, propIndex) => {
|
||||
if (credentialsDependencies.has(prop.name)) {
|
||||
index = propIndex + fieldOffset;
|
||||
}
|
||||
});
|
||||
|
||||
return Math.min(index, this.filteredParameters.length - 1);
|
||||
},
|
||||
mainNodeAuthField(): INodeProperties | null {
|
||||
return getMainAuthField(this.nodeType || null);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
filteredParameterNames(newValue, oldValue) {
|
||||
return Math.min(index, filteredParameters.value.length - 1);
|
||||
});
|
||||
|
||||
const mainNodeAuthField = computed(() => {
|
||||
return getMainAuthField(nodeType.value || null);
|
||||
});
|
||||
|
||||
watch(filteredParameterNames, (newValue, oldValue) => {
|
||||
if (newValue === undefined) {
|
||||
return;
|
||||
}
|
||||
@@ -353,42 +303,38 @@ export default defineComponent({
|
||||
for (const parameter of oldValue) {
|
||||
if (!newValue.includes(parameter)) {
|
||||
const parameterData = {
|
||||
name: `${this.path}.${parameter}`,
|
||||
node: this.ndvStore.activeNode?.name || '',
|
||||
name: `${props.path}.${parameter}`,
|
||||
node: ndvStore.activeNode?.name || '',
|
||||
value: undefined,
|
||||
};
|
||||
this.$emit('valueChanged', parameterData);
|
||||
emit('valueChanged', parameterData);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onParameterBlur(parameterName: string) {
|
||||
this.$emit('parameterBlur', parameterName);
|
||||
},
|
||||
getCredentialsDependencies() {
|
||||
});
|
||||
|
||||
function onParameterBlur(parameterName: string) {
|
||||
emit('parameterBlur', parameterName);
|
||||
}
|
||||
|
||||
function getCredentialsDependencies() {
|
||||
const dependencies = new Set();
|
||||
const nodeType = this.nodeTypesStore.getNodeType(
|
||||
this.node?.type || '',
|
||||
this.node?.typeVersion,
|
||||
);
|
||||
|
||||
// Get names of all fields that credentials rendering depends on (using displayOptions > show)
|
||||
if (nodeType?.credentials) {
|
||||
for (const cred of nodeType.credentials) {
|
||||
if (nodeType.value?.credentials) {
|
||||
for (const cred of nodeType.value.credentials) {
|
||||
if (cred.displayOptions?.show) {
|
||||
Object.keys(cred.displayOptions.show).forEach((fieldName) =>
|
||||
dependencies.add(fieldName),
|
||||
);
|
||||
Object.keys(cred.displayOptions.show).forEach((fieldName) => dependencies.add(fieldName));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
},
|
||||
multipleValues(parameter: INodeProperties): boolean {
|
||||
return this.getArgument('multipleValues', parameter) === true;
|
||||
},
|
||||
getArgument(
|
||||
}
|
||||
|
||||
function multipleValues(parameter: INodeProperties): boolean {
|
||||
return getArgument('multipleValues', parameter) === true;
|
||||
}
|
||||
|
||||
function getArgument(
|
||||
argumentName: string,
|
||||
parameter: INodeProperties,
|
||||
): string | string[] | number | boolean | undefined {
|
||||
@@ -401,22 +347,27 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return parameter.typeOptions[argumentName];
|
||||
},
|
||||
getPath(parameterName: string): string {
|
||||
return (this.path ? `${this.path}.` : '') + parameterName;
|
||||
},
|
||||
deleteOption(optionName: string): void {
|
||||
}
|
||||
|
||||
function getPath(parameterName: string): string {
|
||||
return (props.path ? `${props.path}.` : '') + parameterName;
|
||||
}
|
||||
|
||||
function deleteOption(optionName: string): void {
|
||||
const parameterData = {
|
||||
name: this.getPath(optionName),
|
||||
name: getPath(optionName),
|
||||
value: undefined,
|
||||
};
|
||||
|
||||
// TODO: If there is only one option it should delete the whole one
|
||||
|
||||
this.$emit('valueChanged', parameterData);
|
||||
},
|
||||
emit('valueChanged', parameterData);
|
||||
}
|
||||
|
||||
mustHideDuringCustomApiCall(parameter: INodeProperties, nodeValues: INodeParameters): boolean {
|
||||
function mustHideDuringCustomApiCall(
|
||||
parameter: INodeProperties,
|
||||
nodeValues: INodeParameters,
|
||||
): boolean {
|
||||
if (parameter?.displayOptions?.hide) return true;
|
||||
|
||||
const MUST_REMAIN_VISIBLE = [
|
||||
@@ -427,25 +378,25 @@ export default defineComponent({
|
||||
];
|
||||
|
||||
return !MUST_REMAIN_VISIBLE.includes(parameter.name);
|
||||
},
|
||||
displayNodeParameter(parameter: INodeProperties): boolean {
|
||||
}
|
||||
|
||||
function displayNodeParameter(parameter: INodeProperties): boolean {
|
||||
if (parameter.type === 'hidden') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
this.nodeHelpers.isCustomApiCallSelected(this.nodeValues) &&
|
||||
this.mustHideDuringCustomApiCall(parameter, this.nodeValues)
|
||||
nodeHelpers.isCustomApiCallSelected(props.nodeValues) &&
|
||||
mustHideDuringCustomApiCall(parameter, props.nodeValues)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide authentication related fields since it will now be part of credentials modal
|
||||
if (
|
||||
!KEEP_AUTH_IN_NDV_FOR_NODES.includes(this.node?.type || '') &&
|
||||
this.mainNodeAuthField &&
|
||||
(parameter.name === this.mainNodeAuthField?.name ||
|
||||
this.shouldHideAuthRelatedParameter(parameter))
|
||||
!KEEP_AUTH_IN_NDV_FOR_NODES.includes(node.value?.type || '') &&
|
||||
mainNodeAuthField.value &&
|
||||
(parameter.name === mainNodeAuthField.value?.name || shouldHideAuthRelatedParameter(parameter))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -456,9 +407,9 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const nodeValues: INodeParameters = {};
|
||||
let rawValues = this.nodeValues;
|
||||
if (this.path) {
|
||||
rawValues = get(this.nodeValues, this.path) as INodeParameters;
|
||||
let rawValues = props.nodeValues;
|
||||
if (props.path) {
|
||||
rawValues = get(props.nodeValues, props.path) as INodeParameters;
|
||||
}
|
||||
|
||||
if (!rawValues) {
|
||||
@@ -484,7 +435,7 @@ export default defineComponent({
|
||||
} else {
|
||||
// Contains probably no expression with a missing parameter so resolve
|
||||
try {
|
||||
nodeValues[key] = this.workflowHelpers.resolveExpression(
|
||||
nodeValues[key] = workflowHelpers.resolveExpression(
|
||||
value,
|
||||
nodeValues,
|
||||
) as NodeParameterValue;
|
||||
@@ -506,51 +457,58 @@ export default defineComponent({
|
||||
} while (resolveKeys.length !== 0);
|
||||
|
||||
if (parameterGotResolved) {
|
||||
if (this.path) {
|
||||
rawValues = deepCopy(this.nodeValues);
|
||||
set(rawValues, this.path, nodeValues);
|
||||
return this.nodeHelpers.displayParameter(rawValues, parameter, this.path, this.node);
|
||||
if (props.path) {
|
||||
rawValues = deepCopy(props.nodeValues);
|
||||
set(rawValues, props.path, nodeValues);
|
||||
return nodeHelpers.displayParameter(rawValues, parameter, props.path, node.value);
|
||||
} else {
|
||||
return this.nodeHelpers.displayParameter(nodeValues, parameter, '', this.node);
|
||||
return nodeHelpers.displayParameter(nodeValues, parameter, '', node.value);
|
||||
}
|
||||
}
|
||||
|
||||
return this.nodeHelpers.displayParameter(this.nodeValues, parameter, this.path, this.node);
|
||||
},
|
||||
valueChanged(parameterData: IUpdateInformation): void {
|
||||
this.$emit('valueChanged', parameterData);
|
||||
},
|
||||
onNoticeAction(action: string) {
|
||||
if (action === 'activate') {
|
||||
this.$emit('activate');
|
||||
return nodeHelpers.displayParameter(props.nodeValues, parameter, props.path, node.value);
|
||||
}
|
||||
},
|
||||
|
||||
function valueChanged(parameterData: IUpdateInformation): void {
|
||||
emit('valueChanged', parameterData);
|
||||
}
|
||||
|
||||
function onNoticeAction(action: string) {
|
||||
if (action === 'activate') {
|
||||
emit('activate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles default node button parameter type actions
|
||||
* @param parameter
|
||||
*/
|
||||
onButtonAction(parameter: INodeProperties) {
|
||||
function onButtonAction(parameter: INodeProperties) {
|
||||
const action: string | undefined = parameter.typeOptions?.action;
|
||||
|
||||
switch (action) {
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
isNodeAuthField(name: string): boolean {
|
||||
return this.nodeAuthFields.find((field) => field.name === name) !== undefined;
|
||||
},
|
||||
shouldHideAuthRelatedParameter(parameter: INodeProperties): boolean {
|
||||
}
|
||||
|
||||
function isNodeAuthField(name: string): boolean {
|
||||
return nodeAuthFields.value.find((field) => field.name === name) !== undefined;
|
||||
}
|
||||
|
||||
function shouldHideAuthRelatedParameter(parameter: INodeProperties): boolean {
|
||||
// TODO: For now, hide all fields that are used in authentication fields displayOptions
|
||||
// Ideally, we should check if any non-auth field depends on it before hiding it but
|
||||
// since there is no such case, omitting it to avoid additional computation
|
||||
return isAuthRelatedParameter(this.nodeAuthFields, parameter);
|
||||
},
|
||||
shouldShowOptions(parameter: INodeProperties): boolean {
|
||||
return isAuthRelatedParameter(nodeAuthFields.value, parameter);
|
||||
}
|
||||
|
||||
function shouldShowOptions(parameter: INodeProperties): boolean {
|
||||
return parameter.type !== 'resourceMapper';
|
||||
},
|
||||
getDependentParametersValues(parameter: INodeProperties): string | null {
|
||||
const loadOptionsDependsOn = this.getArgument('loadOptionsDependsOn', parameter) as
|
||||
}
|
||||
|
||||
function getDependentParametersValues(parameter: INodeProperties): string | null {
|
||||
const loadOptionsDependsOn = getArgument('loadOptionsDependsOn', parameter) as
|
||||
| string[]
|
||||
| undefined;
|
||||
|
||||
@@ -559,9 +517,9 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
// Get the resolved parameter values of the current node
|
||||
const currentNodeParameters = this.ndvStore.activeNode?.parameters;
|
||||
const currentNodeParameters = ndvStore.activeNode?.parameters;
|
||||
try {
|
||||
const resolvedNodeParameters = this.workflowHelpers.resolveParameter(currentNodeParameters);
|
||||
const resolvedNodeParameters = workflowHelpers.resolveParameter(currentNodeParameters);
|
||||
|
||||
const returnValues: string[] = [];
|
||||
for (const parameterPath of loadOptionsDependsOn) {
|
||||
@@ -572,12 +530,13 @@ export default defineComponent({
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getParameterValue<T extends NodeParameterValueType = NodeParameterValueType>(name: string): T {
|
||||
return this.nodeHelpers.getParameterValue(this.nodeValues, name, this.path) as T;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function getParameterValue<T extends NodeParameterValueType = NodeParameterValueType>(
|
||||
name: string,
|
||||
): T {
|
||||
return nodeHelpers.getParameterValue(props.nodeValues, name, props.path) as T;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -45,234 +45,194 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { mapStores } from 'pinia';
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import type { INodeUi, IUpdateInformation, InputSize, TargetItem } from '@/Interface';
|
||||
<script setup lang="ts">
|
||||
import type { IUpdateInformation, InputSize } from '@/Interface';
|
||||
import ParameterInput from '@/components/ParameterInput.vue';
|
||||
import InputHint from '@/components/ParameterInputHint.vue';
|
||||
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
|
||||
import {
|
||||
isResourceLocatorValue,
|
||||
type IDataObject,
|
||||
type INodeProperties,
|
||||
type INodePropertyMode,
|
||||
type IParameterLabel,
|
||||
type NodeParameterValueType,
|
||||
type Result,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import useEnvironmentsStore from '@/stores/environments.ee.store';
|
||||
import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store';
|
||||
import { useNDVStore } from '@/stores/ndv.store';
|
||||
import { stringifyExpressionResult } from '@/utils/expressions';
|
||||
import { isValueExpression, parseResourceMapperFieldName } from '@/utils/nodeTypesUtils';
|
||||
import type {
|
||||
IDataObject,
|
||||
INodeProperties,
|
||||
INodePropertyMode,
|
||||
IParameterLabel,
|
||||
NodeParameterValueType,
|
||||
Result,
|
||||
} from 'n8n-workflow';
|
||||
import { isResourceLocatorValue } from 'n8n-workflow';
|
||||
|
||||
import type { EventBus } from 'n8n-design-system/utils';
|
||||
import { createEventBus } from 'n8n-design-system/utils';
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { stringifyExpressionResult } from '@/utils/expressions';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ParameterInputWrapper',
|
||||
components: {
|
||||
ParameterInput,
|
||||
InputHint,
|
||||
},
|
||||
props: {
|
||||
additionalExpressionData: {
|
||||
type: Object as PropType<IDataObject>,
|
||||
default: () => ({}),
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
},
|
||||
rows: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
isAssignment: {
|
||||
type: Boolean,
|
||||
},
|
||||
parameter: {
|
||||
type: Object as PropType<INodeProperties>,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number, Boolean, Array, Object] as PropType<NodeParameterValueType>,
|
||||
},
|
||||
droppable: {
|
||||
type: Boolean,
|
||||
},
|
||||
activeDrop: {
|
||||
type: Boolean,
|
||||
},
|
||||
forceShowExpression: {
|
||||
type: Boolean,
|
||||
},
|
||||
hint: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
hideHint: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
},
|
||||
inputSize: {
|
||||
type: String as PropType<InputSize>,
|
||||
},
|
||||
hideIssues: {
|
||||
type: Boolean,
|
||||
},
|
||||
documentationUrl: {
|
||||
type: String as PropType<string | undefined>,
|
||||
},
|
||||
errorHighlight: {
|
||||
type: Boolean,
|
||||
},
|
||||
isForCredential: {
|
||||
type: Boolean,
|
||||
},
|
||||
eventSource: {
|
||||
type: String,
|
||||
},
|
||||
label: {
|
||||
type: Object as PropType<IParameterLabel>,
|
||||
default: () => ({
|
||||
size: 'small',
|
||||
}),
|
||||
},
|
||||
eventBus: {
|
||||
type: Object as PropType<EventBus>,
|
||||
default: () => createEventBus(),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
type Props = {
|
||||
parameter: INodeProperties;
|
||||
path: string;
|
||||
modelValue: NodeParameterValueType;
|
||||
additionalExpressionData?: IDataObject;
|
||||
rows?: number;
|
||||
isReadOnly?: boolean;
|
||||
isAssignment?: boolean;
|
||||
droppable?: boolean;
|
||||
activeDrop?: boolean;
|
||||
forceShowExpression?: boolean;
|
||||
hint?: string;
|
||||
hideHint?: boolean;
|
||||
inputSize?: InputSize;
|
||||
hideIssues?: boolean;
|
||||
documentationUrl?: string;
|
||||
errorHighlight?: boolean;
|
||||
isForCredential?: boolean;
|
||||
eventSource?: string;
|
||||
label?: IParameterLabel;
|
||||
eventBus?: EventBus;
|
||||
};
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
additionalExpressionData: () => ({}),
|
||||
rows: 5,
|
||||
label: () => ({ size: 'small' }),
|
||||
eventBus: () => createEventBus(),
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'focus'): void;
|
||||
(event: 'blur'): void;
|
||||
(event: 'drop', value: string): void;
|
||||
(event: 'update', value: IUpdateInformation): void;
|
||||
(event: 'textInput', value: IUpdateInformation): void;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const workflowHelpers = useWorkflowHelpers({ router });
|
||||
|
||||
return {
|
||||
workflowHelpers,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useNDVStore, useExternalSecretsStore, useEnvironmentsStore),
|
||||
isValueExpression() {
|
||||
return isValueExpression(this.parameter, this.modelValue);
|
||||
},
|
||||
activeNode(): INodeUi | null {
|
||||
return this.ndvStore.activeNode;
|
||||
},
|
||||
selectedRLMode(): INodePropertyMode | undefined {
|
||||
const ndvStore = useNDVStore();
|
||||
const externalSecretsStore = useExternalSecretsStore();
|
||||
const environmentsStore = useEnvironmentsStore();
|
||||
|
||||
const isExpression = computed(() => {
|
||||
return isValueExpression(props.parameter, props.modelValue);
|
||||
});
|
||||
|
||||
const activeNode = computed(() => ndvStore.activeNode);
|
||||
|
||||
const selectedRLMode = computed(() => {
|
||||
if (
|
||||
typeof this.modelValue !== 'object' ||
|
||||
this.parameter.type !== 'resourceLocator' ||
|
||||
!isResourceLocatorValue(this.modelValue)
|
||||
typeof props.modelValue !== 'object' ||
|
||||
props.parameter.type !== 'resourceLocator' ||
|
||||
!isResourceLocatorValue(props.modelValue)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const mode = this.modelValue.mode;
|
||||
const mode = props.modelValue.mode;
|
||||
if (mode) {
|
||||
return this.parameter.modes?.find((m: INodePropertyMode) => m.name === mode);
|
||||
return props.parameter.modes?.find((m: INodePropertyMode) => m.name === mode);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
parameterHint(): string | undefined {
|
||||
if (this.isValueExpression) {
|
||||
});
|
||||
|
||||
const parameterHint = computed(() => {
|
||||
if (isExpression.value) {
|
||||
return undefined;
|
||||
}
|
||||
if (this.selectedRLMode?.hint) {
|
||||
return this.selectedRLMode.hint;
|
||||
if (selectedRLMode.value?.hint) {
|
||||
return selectedRLMode.value.hint;
|
||||
}
|
||||
|
||||
return this.hint;
|
||||
},
|
||||
targetItem(): TargetItem | null {
|
||||
return this.ndvStore.hoveringItem;
|
||||
},
|
||||
isInputParentOfActiveNode(): boolean {
|
||||
return this.ndvStore.isInputParentOfActiveNode;
|
||||
},
|
||||
evaluatedExpression(): Result<unknown, Error> {
|
||||
const value = isResourceLocatorValue(this.modelValue)
|
||||
? this.modelValue.value
|
||||
: this.modelValue;
|
||||
return props.hint;
|
||||
});
|
||||
|
||||
if (!this.activeNode || !this.isValueExpression || typeof value !== 'string') {
|
||||
const targetItem = computed(() => ndvStore.hoveringItem);
|
||||
|
||||
const isInputParentOfActiveNode = computed(() => ndvStore.isInputParentOfActiveNode);
|
||||
|
||||
const evaluatedExpression = computed<Result<unknown, Error>>(() => {
|
||||
const value = isResourceLocatorValue(props.modelValue)
|
||||
? props.modelValue.value
|
||||
: props.modelValue;
|
||||
|
||||
if (!activeNode.value || !isExpression.value || typeof value !== 'string') {
|
||||
return { ok: false, error: new Error() };
|
||||
}
|
||||
|
||||
try {
|
||||
let opts: Parameters<typeof this.workflowHelpers.resolveExpression>[2] = {
|
||||
isForCredential: this.isForCredential,
|
||||
let opts: Parameters<typeof workflowHelpers.resolveExpression>[2] = {
|
||||
isForCredential: props.isForCredential,
|
||||
};
|
||||
if (this.ndvStore.isInputParentOfActiveNode) {
|
||||
if (ndvStore.isInputParentOfActiveNode) {
|
||||
opts = {
|
||||
...opts,
|
||||
targetItem: this.targetItem ?? undefined,
|
||||
inputNodeName: this.ndvStore.ndvInputNodeName,
|
||||
inputRunIndex: this.ndvStore.ndvInputRunIndex,
|
||||
inputBranchIndex: this.ndvStore.ndvInputBranchIndex,
|
||||
additionalKeys: this.resolvedAdditionalExpressionData,
|
||||
targetItem: targetItem.value ?? undefined,
|
||||
inputNodeName: ndvStore.ndvInputNodeName,
|
||||
inputRunIndex: ndvStore.ndvInputRunIndex,
|
||||
inputBranchIndex: ndvStore.ndvInputBranchIndex,
|
||||
additionalKeys: resolvedAdditionalExpressionData.value,
|
||||
};
|
||||
}
|
||||
|
||||
return { ok: true, result: this.workflowHelpers.resolveExpression(value, undefined, opts) };
|
||||
return { ok: true, result: workflowHelpers.resolveExpression(value, undefined, opts) };
|
||||
} catch (error) {
|
||||
return { ok: false, error };
|
||||
}
|
||||
},
|
||||
evaluatedExpressionValue(): unknown {
|
||||
const evaluated = this.evaluatedExpression;
|
||||
});
|
||||
|
||||
const evaluatedExpressionValue = computed(() => {
|
||||
const evaluated = evaluatedExpression.value;
|
||||
return evaluated.ok ? evaluated.result : null;
|
||||
},
|
||||
evaluatedExpressionString(): string | null {
|
||||
return stringifyExpressionResult(this.evaluatedExpression);
|
||||
},
|
||||
expressionOutput(): string | null {
|
||||
if (this.isValueExpression && this.evaluatedExpressionString) {
|
||||
return this.evaluatedExpressionString;
|
||||
});
|
||||
|
||||
const evaluatedExpressionString = computed(() => {
|
||||
return stringifyExpressionResult(evaluatedExpression.value);
|
||||
});
|
||||
|
||||
const expressionOutput = computed(() => {
|
||||
if (isExpression.value && evaluatedExpressionString.value) {
|
||||
return evaluatedExpressionString.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
resolvedAdditionalExpressionData() {
|
||||
return {
|
||||
$vars: this.environmentsStore.variablesAsObject,
|
||||
...(this.externalSecretsStore.isEnterpriseExternalSecretsEnabled && this.isForCredential
|
||||
? { $secrets: this.externalSecretsStore.secretsAsObject }
|
||||
: {}),
|
||||
...this.additionalExpressionData,
|
||||
};
|
||||
},
|
||||
parsedParameterName() {
|
||||
return parseResourceMapperFieldName(this.parameter?.name ?? '');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onFocus() {
|
||||
this.$emit('focus');
|
||||
},
|
||||
onBlur() {
|
||||
this.$emit('blur');
|
||||
},
|
||||
onDrop(data: string) {
|
||||
this.$emit('drop', data);
|
||||
},
|
||||
onValueChanged(parameterData: IUpdateInformation) {
|
||||
this.$emit('update', parameterData);
|
||||
},
|
||||
onTextInput(parameterData: IUpdateInformation) {
|
||||
this.$emit('textInput', parameterData);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const resolvedAdditionalExpressionData = computed(() => {
|
||||
return {
|
||||
$vars: environmentsStore.variablesAsObject,
|
||||
...(externalSecretsStore.isEnterpriseExternalSecretsEnabled && props.isForCredential
|
||||
? { $secrets: externalSecretsStore.secretsAsObject }
|
||||
: {}),
|
||||
...props.additionalExpressionData,
|
||||
};
|
||||
});
|
||||
|
||||
const parsedParameterName = computed(() => {
|
||||
return parseResourceMapperFieldName(props.parameter?.name ?? '');
|
||||
});
|
||||
|
||||
function onFocus() {
|
||||
emit('focus');
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
emit('blur');
|
||||
}
|
||||
|
||||
function onDrop(data: string) {
|
||||
emit('drop', data);
|
||||
}
|
||||
|
||||
function onValueChanged(parameterData: IUpdateInformation) {
|
||||
emit('update', parameterData);
|
||||
}
|
||||
|
||||
function onTextInput(parameterData: IUpdateInformation) {
|
||||
emit('textInput', parameterData);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
Reference in New Issue
Block a user