mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
refactor(editor): Fix types issues in src/components/Node/* (no-changelog) (#9444)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
@@ -16,7 +16,11 @@ export const retry = async (
|
|||||||
try {
|
try {
|
||||||
resolve(assertion());
|
resolve(assertion());
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Date.now() - startTime > timeout ? reject(err) : tryAgain();
|
if (Date.now() - startTime > timeout) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
tryAgain();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, interval);
|
}, interval);
|
||||||
};
|
};
|
||||||
@@ -62,7 +66,10 @@ export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = {
|
|||||||
saveDataErrorExecution: 'all',
|
saveDataErrorExecution: 'all',
|
||||||
saveDataSuccessExecution: 'all',
|
saveDataSuccessExecution: 'all',
|
||||||
saveManualExecutions: false,
|
saveManualExecutions: false,
|
||||||
binaryDataMode: 'default',
|
initialized: false,
|
||||||
|
mfa: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDropdownItems = async (dropdownTriggerParent: HTMLElement) => {
|
export const getDropdownItems = async (dropdownTriggerParent: HTMLElement) => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
|
v-if="data"
|
||||||
:id="nodeId"
|
:id="nodeId"
|
||||||
:ref="data.name"
|
:ref="data.name"
|
||||||
:class="nodeWrapperClass"
|
:class="nodeWrapperClass"
|
||||||
@@ -262,6 +263,16 @@ export default defineComponent({
|
|||||||
callDebounced,
|
callDebounced,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isTouchActive: false,
|
||||||
|
nodeSubtitle: '',
|
||||||
|
showTriggerNodeTooltip: false,
|
||||||
|
pinDataDiscoveryTooltipVisible: false,
|
||||||
|
dragging: false,
|
||||||
|
unwatchWorkflowDataItems: () => {},
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapStores(useNodeTypesStore, useNDVStore, useUIStore, useWorkflowsStore),
|
...mapStores(useNodeTypesStore, useNDVStore, useUIStore, useWorkflowsStore),
|
||||||
showPinnedDataInfo(): boolean {
|
showPinnedDataInfo(): boolean {
|
||||||
@@ -325,6 +336,7 @@ export default defineComponent({
|
|||||||
return !!this.nodeType?.polling;
|
return !!this.nodeType?.polling;
|
||||||
},
|
},
|
||||||
isExecuting(): boolean {
|
isExecuting(): boolean {
|
||||||
|
if (!this.data) return false;
|
||||||
return this.workflowsStore.isNodeExecuting(this.data.name);
|
return this.workflowsStore.isNodeExecuting(this.data.name);
|
||||||
},
|
},
|
||||||
isSingleActiveTriggerNode(): boolean {
|
isSingleActiveTriggerNode(): boolean {
|
||||||
@@ -336,12 +348,15 @@ export default defineComponent({
|
|||||||
return nodes.length === 1;
|
return nodes.length === 1;
|
||||||
},
|
},
|
||||||
isManualTypeNode(): boolean {
|
isManualTypeNode(): boolean {
|
||||||
return this.data.type === MANUAL_TRIGGER_NODE_TYPE;
|
return this.data?.type === MANUAL_TRIGGER_NODE_TYPE;
|
||||||
},
|
},
|
||||||
isConfigNode(): boolean {
|
isConfigNode(): boolean {
|
||||||
return this.nodeTypesStore.isConfigNode(this.workflow, this.data, this.data?.type ?? '');
|
if (!this.data) return false;
|
||||||
|
return this.nodeTypesStore.isConfigNode(this.workflow, this.data, this.data.type ?? '');
|
||||||
},
|
},
|
||||||
isConfigurableNode(): boolean {
|
isConfigurableNode(): boolean {
|
||||||
|
if (!this.data) return false;
|
||||||
|
|
||||||
return this.nodeTypesStore.isConfigurableNode(
|
return this.nodeTypesStore.isConfigurableNode(
|
||||||
this.workflow,
|
this.workflow,
|
||||||
this.data,
|
this.data,
|
||||||
@@ -365,10 +380,10 @@ export default defineComponent({
|
|||||||
return this.workflowsStore.nodesByName[this.name] as INodeUi | undefined;
|
return this.workflowsStore.nodesByName[this.name] as INodeUi | undefined;
|
||||||
},
|
},
|
||||||
sameTypeNodes(): INodeUi[] {
|
sameTypeNodes(): INodeUi[] {
|
||||||
return this.workflowsStore.allNodes.filter((node: INodeUi) => node.type === this.data.type);
|
return this.workflowsStore.allNodes.filter((node: INodeUi) => node.type === this.data?.type);
|
||||||
},
|
},
|
||||||
nodeWrapperClass(): object {
|
nodeWrapperClass() {
|
||||||
const classes = {
|
const classes: Record<string, boolean> = {
|
||||||
'node-wrapper': true,
|
'node-wrapper': true,
|
||||||
'node-wrapper--trigger': this.isTriggerNode,
|
'node-wrapper--trigger': this.isTriggerNode,
|
||||||
'node-wrapper--configurable': this.isConfigurableNode,
|
'node-wrapper--configurable': this.isConfigurableNode,
|
||||||
@@ -389,7 +404,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
},
|
},
|
||||||
nodeWrapperStyles(): object {
|
nodeWrapperStyles() {
|
||||||
const styles: {
|
const styles: {
|
||||||
[key: string]: string | number;
|
[key: string]: string | number;
|
||||||
} = {
|
} = {
|
||||||
@@ -435,7 +450,7 @@ export default defineComponent({
|
|||||||
nodeClass(): object {
|
nodeClass(): object {
|
||||||
return {
|
return {
|
||||||
'node-box': true,
|
'node-box': true,
|
||||||
disabled: this.data.disabled,
|
disabled: this.data?.disabled,
|
||||||
executing: this.isExecuting,
|
executing: this.isExecuting,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -466,7 +481,7 @@ export default defineComponent({
|
|||||||
return issues;
|
return issues;
|
||||||
},
|
},
|
||||||
nodeDisabledTitle(): string {
|
nodeDisabledTitle(): string {
|
||||||
return this.data.disabled
|
return this.data?.disabled
|
||||||
? this.$locale.baseText('node.enable')
|
? this.$locale.baseText('node.enable')
|
||||||
: this.$locale.baseText('node.disable');
|
: this.$locale.baseText('node.disable');
|
||||||
},
|
},
|
||||||
@@ -476,21 +491,21 @@ export default defineComponent({
|
|||||||
showDisabledLinethrough(): boolean {
|
showDisabledLinethrough(): boolean {
|
||||||
return (
|
return (
|
||||||
!this.isConfigurableNode &&
|
!this.isConfigurableNode &&
|
||||||
!!(this.data.disabled && this.inputs.length === 1 && this.outputs.length === 1)
|
!!(this.data?.disabled && this.inputs.length === 1 && this.outputs.length === 1)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
shortNodeType(): string {
|
shortNodeType(): string {
|
||||||
return this.$locale.shortNodeType(this.data.type);
|
return this.$locale.shortNodeType(this.data?.type ?? '');
|
||||||
},
|
},
|
||||||
nodeTitle(): string {
|
nodeTitle(): string {
|
||||||
if (this.data.name === 'Start') {
|
if (this.data?.name === 'Start') {
|
||||||
return this.$locale.headerText({
|
return this.$locale.headerText({
|
||||||
key: 'headers.start.displayName',
|
key: 'headers.start.displayName',
|
||||||
fallback: 'Start',
|
fallback: 'Start',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.data.name;
|
return this.data?.name ?? '';
|
||||||
},
|
},
|
||||||
waiting(): string | undefined {
|
waiting(): string | undefined {
|
||||||
const workflowExecution = this.workflowsStore.getWorkflowExecution as ExecutionSummary;
|
const workflowExecution = this.workflowsStore.getWorkflowExecution as ExecutionSummary;
|
||||||
@@ -518,7 +533,7 @@ export default defineComponent({
|
|||||||
workflowRunning(): boolean {
|
workflowRunning(): boolean {
|
||||||
return this.uiStore.isActionActive('workflowRunning');
|
return this.uiStore.isActionActive('workflowRunning');
|
||||||
},
|
},
|
||||||
nodeStyle(): object {
|
nodeStyle() {
|
||||||
const returnStyles: {
|
const returnStyles: {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
} = {};
|
} = {};
|
||||||
@@ -529,7 +544,7 @@ export default defineComponent({
|
|||||||
borderColor = '--color-foreground-dark';
|
borderColor = '--color-foreground-dark';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.data.disabled) {
|
if (this.data?.disabled) {
|
||||||
borderColor = '--color-foreground-base';
|
borderColor = '--color-foreground-base';
|
||||||
} else if (!this.isExecuting) {
|
} else if (!this.isExecuting) {
|
||||||
if (this.hasIssues && !this.hideNodeIssues) {
|
if (this.hasIssues && !this.hideNodeIssues) {
|
||||||
@@ -559,7 +574,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
isSelected(): boolean {
|
isSelected(): boolean {
|
||||||
return (
|
return (
|
||||||
this.uiStore.getSelectedNodes.find((node: INodeUi) => node.name === this.data.name) !==
|
this.uiStore.getSelectedNodes.find((node: INodeUi) => node.name === this.data?.name) !==
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -668,23 +683,13 @@ export default defineComponent({
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isTouchActive: false,
|
|
||||||
nodeSubtitle: '',
|
|
||||||
showTriggerNodeTooltip: false,
|
|
||||||
pinDataDiscoveryTooltipVisible: false,
|
|
||||||
dragging: false,
|
|
||||||
unwatchWorkflowDataItems: () => {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
showPinDataDiscoveryTooltip(dataItemsCount: number): void {
|
showPinDataDiscoveryTooltip(dataItemsCount: number): void {
|
||||||
if (
|
if (
|
||||||
!this.isTriggerNode ||
|
!this.isTriggerNode ||
|
||||||
this.isManualTypeNode ||
|
this.isManualTypeNode ||
|
||||||
this.isScheduledGroup ||
|
this.isScheduledGroup ||
|
||||||
this.uiStore.isModalActive ||
|
this.uiStore.isAnyModalOpen ||
|
||||||
dataItemsCount === 0
|
dataItemsCount === 0
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
@@ -695,13 +700,14 @@ export default defineComponent({
|
|||||||
this.unwatchWorkflowDataItems();
|
this.unwatchWorkflowDataItems();
|
||||||
},
|
},
|
||||||
setSubtitle() {
|
setSubtitle() {
|
||||||
|
if (!this.data || !this.nodeType) return;
|
||||||
// why is this not a computed property? because it's a very expensive operation
|
// why is this not a computed property? because it's a very expensive operation
|
||||||
// it requires expressions to resolve each subtitle...
|
// it requires expressions to resolve each subtitle...
|
||||||
// and ends up bogging down the UI with big workflows, for example when pasting a workflow or even opening a node...
|
// and ends up bogging down the UI with big workflows, for example when pasting a workflow or even opening a node...
|
||||||
// so we only update it when necessary (when node is mounted and when it's opened and closed (isActive))
|
// so we only update it when necessary (when node is mounted and when it's opened and closed (isActive))
|
||||||
try {
|
try {
|
||||||
const nodeSubtitle =
|
const nodeSubtitle =
|
||||||
this.nodeHelpers.getNodeSubtitle(this.data, this.nodeType, this.workflow) || '';
|
this.nodeHelpers.getNodeSubtitle(this.data, this.nodeType, this.workflow) ?? '';
|
||||||
|
|
||||||
this.nodeSubtitle = nodeSubtitle.includes(CUSTOM_API_CALL_KEY) ? '' : nodeSubtitle;
|
this.nodeSubtitle = nodeSubtitle.includes(CUSTOM_API_CALL_KEY) ? '' : nodeSubtitle;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -727,9 +733,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
executeNode() {
|
executeNode() {
|
||||||
this.$emit('runWorkflow', this.data.name, 'Node.executeNode');
|
this.$emit('runWorkflow', this.data?.name, 'Node.executeNode');
|
||||||
this.$telemetry.track('User clicked node hover button', {
|
this.$telemetry.track('User clicked node hover button', {
|
||||||
node_type: this.data.type,
|
node_type: this.data?.type,
|
||||||
button_name: 'execute',
|
button_name: 'execute',
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: this.workflowsStore.workflowId,
|
||||||
});
|
});
|
||||||
@@ -737,18 +743,18 @@ export default defineComponent({
|
|||||||
|
|
||||||
deleteNode() {
|
deleteNode() {
|
||||||
this.$telemetry.track('User clicked node hover button', {
|
this.$telemetry.track('User clicked node hover button', {
|
||||||
node_type: this.data.type,
|
node_type: this.data?.type,
|
||||||
button_name: 'delete',
|
button_name: 'delete',
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: this.workflowsStore.workflowId,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$emit('removeNode', this.data.name);
|
this.$emit('removeNode', this.data?.name);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleDisableNode(event: MouseEvent) {
|
toggleDisableNode(event: MouseEvent) {
|
||||||
(event.currentTarget as HTMLButtonElement).blur();
|
(event.currentTarget as HTMLButtonElement).blur();
|
||||||
this.$telemetry.track('User clicked node hover button', {
|
this.$telemetry.track('User clicked node hover button', {
|
||||||
node_type: this.data.type,
|
node_type: this.data?.type,
|
||||||
button_name: 'disable',
|
button_name: 'disable',
|
||||||
workflow_id: this.workflowsStore.workflowId,
|
workflow_id: this.workflowsStore.workflowId,
|
||||||
});
|
});
|
||||||
@@ -759,7 +765,8 @@ export default defineComponent({
|
|||||||
void this.callDebounced(this.onClickDebounced, { debounceTime: 50, trailing: true }, event);
|
void this.callDebounced(this.onClickDebounced, { debounceTime: 50, trailing: true }, event);
|
||||||
},
|
},
|
||||||
|
|
||||||
onClickDebounced(event: MouseEvent) {
|
onClickDebounced(...args: unknown[]) {
|
||||||
|
const event = args[0] as MouseEvent;
|
||||||
const isDoubleClick = event.detail >= 2;
|
const isDoubleClick = event.detail >= 2;
|
||||||
if (isDoubleClick) {
|
if (isDoubleClick) {
|
||||||
this.setNodeActive();
|
this.setNodeActive();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, computed, toRefs, getCurrentInstance } from 'vue';
|
import { reactive, computed, toRefs } from 'vue';
|
||||||
import type { ActionTypeDescription, SimplifiedNodeType } from '@/Interface';
|
import type { ActionTypeDescription, SimplifiedNodeType } from '@/Interface';
|
||||||
import { WEBHOOK_NODE_TYPE, DRAG_EVENT_DATA_KEY } from '@/constants';
|
import { WEBHOOK_NODE_TYPE, DRAG_EVENT_DATA_KEY } from '@/constants';
|
||||||
|
|
||||||
@@ -30,6 +30,7 @@ import NodeIcon from '@/components/NodeIcon.vue';
|
|||||||
|
|
||||||
import { useViewStacks } from '../composables/useViewStacks';
|
import { useViewStacks } from '../composables/useViewStacks';
|
||||||
import { useActions } from '../composables/useActions';
|
import { useActions } from '../composables/useActions';
|
||||||
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
nodeType: SimplifiedNodeType;
|
nodeType: SimplifiedNodeType;
|
||||||
@@ -37,9 +38,7 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
const telemetry = useTelemetry();
|
||||||
const instance = getCurrentInstance();
|
|
||||||
const telemetry = instance?.proxy.$telemetry;
|
|
||||||
|
|
||||||
const { getActionData, getAddedNodesAndConnections, setAddedNodeActionParameters } = useActions();
|
const { getActionData, getAddedNodesAndConnections, setAddedNodeActionParameters } = useActions();
|
||||||
const { activeViewStack } = useViewStacks();
|
const { activeViewStack } = useViewStacks();
|
||||||
@@ -104,7 +103,7 @@ function onDragOver(event: DragEvent): void {
|
|||||||
state.draggablePosition = { x, y };
|
state.draggablePosition = { x, y };
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragEnd(event: DragEvent): void {
|
function onDragEnd(): void {
|
||||||
if (state.storeWatcher) state.storeWatcher();
|
if (state.storeWatcher) state.storeWatcher();
|
||||||
document.body.removeEventListener('dragend', onDragEnd);
|
document.body.removeEventListener('dragend', onDragEnd);
|
||||||
document.body.removeEventListener('dragover', onDragOver);
|
document.body.removeEventListener('dragover', onDragOver);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export interface Props {
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
active: false,
|
active: false,
|
||||||
|
subcategory: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
@@ -105,8 +106,7 @@ const hasActions = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const nodeActions = computed(() => {
|
const nodeActions = computed(() => {
|
||||||
const nodeActions = actions[props.nodeType.name] || [];
|
return actions[props.nodeType.name] || [];
|
||||||
return nodeActions;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const shortNodeType = computed<string>(() => i18n.shortNodeType(props.nodeType.name) || '');
|
const shortNodeType = computed<string>(() => i18n.shortNodeType(props.nodeType.name) || '');
|
||||||
@@ -119,11 +119,11 @@ const draggableStyle = computed<{ top: string; left: string }>(() => ({
|
|||||||
const isCommunityNode = computed<boolean>(() => isCommunityPackageName(props.nodeType.name));
|
const isCommunityNode = computed<boolean>(() => isCommunityPackageName(props.nodeType.name));
|
||||||
|
|
||||||
const displayName = computed<string>(() => {
|
const displayName = computed<string>(() => {
|
||||||
const displayName = props.nodeType.displayName.trimEnd();
|
const trimmedDisplayName = props.nodeType.displayName.trimEnd();
|
||||||
|
|
||||||
return i18n.headerText({
|
return i18n.headerText({
|
||||||
key: `headers.${shortNodeType.value}.displayName`,
|
key: `headers.${shortNodeType.value}.displayName`,
|
||||||
fallback: hasActions.value ? displayName.replace('Trigger', '') : displayName,
|
fallback: hasActions.value ? trimmedDisplayName.replace('Trigger', '') : trimmedDisplayName,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ function onDragOver(event: DragEvent): void {
|
|||||||
draggablePosition.value = { x, y };
|
draggablePosition.value = { x, y };
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDragEnd(event: DragEvent): void {
|
function onDragEnd(): void {
|
||||||
document.body.removeEventListener('dragover', onDragOver);
|
document.body.removeEventListener('dragover', onDragOver);
|
||||||
|
|
||||||
dragging.value = false;
|
dragging.value = false;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<n8n-node-creator-node
|
<n8n-node-creator-node
|
||||||
:class="$style.subCategory"
|
:class="$style.subCategory"
|
||||||
:title="i18n.baseText(`nodeCreator.subcategoryNames.${subcategoryName}`)"
|
:title="i18n.baseText(`nodeCreator.subcategoryNames.${subcategoryName}` as BaseTextKey)"
|
||||||
:is-trigger="false"
|
:is-trigger="false"
|
||||||
:description="i18n.baseText(`nodeCreator.subcategoryDescriptions.${subcategoryName}`)"
|
:description="
|
||||||
|
i18n.baseText(`nodeCreator.subcategoryDescriptions.${subcategoryName}` as BaseTextKey)
|
||||||
|
"
|
||||||
:show-action-arrow="true"
|
:show-action-arrow="true"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -23,6 +25,7 @@ import type { SubcategoryItemProps } from '@/Interface';
|
|||||||
import { camelCase } from 'lodash-es';
|
import { camelCase } from 'lodash-es';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
import type { BaseTextKey } from '@/plugins/i18n';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
item: SubcategoryItemProps;
|
item: SubcategoryItemProps;
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, getCurrentInstance, onMounted, defineComponent, h } from 'vue';
|
import { computed, onMounted, defineComponent, h } from 'vue';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import type {
|
import type {
|
||||||
INodeCreateElement,
|
INodeCreateElement,
|
||||||
ActionTypeDescription,
|
|
||||||
NodeFilterType,
|
NodeFilterType,
|
||||||
IUpdateInformation,
|
IUpdateInformation,
|
||||||
ActionCreateElement,
|
ActionCreateElement,
|
||||||
|
NodeCreateElement,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import {
|
import {
|
||||||
HTTP_REQUEST_NODE_TYPE,
|
HTTP_REQUEST_NODE_TYPE,
|
||||||
@@ -27,18 +27,17 @@ import { useViewStacks } from '../composables/useViewStacks';
|
|||||||
import ItemsRenderer from '../Renderers/ItemsRenderer.vue';
|
import ItemsRenderer from '../Renderers/ItemsRenderer.vue';
|
||||||
import CategorizedItemsRenderer from '../Renderers/CategorizedItemsRenderer.vue';
|
import CategorizedItemsRenderer from '../Renderers/CategorizedItemsRenderer.vue';
|
||||||
import type { IDataObject } from 'n8n-workflow';
|
import type { IDataObject } from 'n8n-workflow';
|
||||||
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
|
|
||||||
const emit = defineEmits({
|
const emit = defineEmits({
|
||||||
nodeTypeSelected: (nodeTypes: string[]) => true,
|
nodeTypeSelected: (_nodeTypes: string[]) => true,
|
||||||
});
|
});
|
||||||
const instance = getCurrentInstance();
|
const telemetry = useTelemetry();
|
||||||
const telemetry = instance?.proxy.$telemetry;
|
|
||||||
|
|
||||||
const { userActivated } = useUsersStore();
|
const { userActivated } = useUsersStore();
|
||||||
const { popViewStack, updateCurrentViewStack } = useViewStacks();
|
const { popViewStack, updateCurrentViewStack } = useViewStacks();
|
||||||
const { registerKeyHook } = useKeyboardNavigation();
|
const { registerKeyHook } = useKeyboardNavigation();
|
||||||
const {
|
const {
|
||||||
getNodeTypesWithManualTrigger,
|
|
||||||
setAddedNodeActionParameters,
|
setAddedNodeActionParameters,
|
||||||
getActionData,
|
getActionData,
|
||||||
getPlaceholderTriggerActions,
|
getPlaceholderTriggerActions,
|
||||||
@@ -133,13 +132,15 @@ function arrowLeft() {
|
|||||||
|
|
||||||
function onKeySelect(activeItemId: string) {
|
function onKeySelect(activeItemId: string) {
|
||||||
const mergedActions = [...actions.value, ...placeholderTriggerActions];
|
const mergedActions = [...actions.value, ...placeholderTriggerActions];
|
||||||
const activeAction = mergedActions.find((a) => a.uuid === activeItemId);
|
const activeAction = mergedActions.find((a): a is NodeCreateElement => a.uuid === activeItemId);
|
||||||
|
|
||||||
if (activeAction) onSelected(activeAction);
|
if (activeAction) onSelected(activeAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelected(actionCreateElement: INodeCreateElement) {
|
function onSelected(actionCreateElement: INodeCreateElement) {
|
||||||
const actionData = getActionData(actionCreateElement.properties as ActionTypeDescription);
|
if (actionCreateElement.type !== 'action') return;
|
||||||
|
|
||||||
|
const actionData = getActionData(actionCreateElement.properties);
|
||||||
const isPlaceholderTriggerAction = placeholderTriggerActions.some(
|
const isPlaceholderTriggerAction = placeholderTriggerActions.some(
|
||||||
(p) => p.key === actionCreateElement.key,
|
(p) => p.key === actionCreateElement.key,
|
||||||
);
|
);
|
||||||
@@ -211,6 +212,7 @@ const OrderSwitcher = defineComponent({
|
|||||||
props: {
|
props: {
|
||||||
rootView: {
|
rootView: {
|
||||||
type: String as PropType<NodeFilterType>,
|
type: String as PropType<NodeFilterType>,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
@@ -232,7 +234,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<OrderSwitcher :root-view="rootView">
|
<OrderSwitcher v-if="rootView" :root-view="rootView">
|
||||||
<template v-if="isTriggerRootView || parsedTriggerActionsBaseline.length !== 0" #triggers>
|
<template v-if="isTriggerRootView || parsedTriggerActionsBaseline.length !== 0" #triggers>
|
||||||
<!-- Triggers Category -->
|
<!-- Triggers Category -->
|
||||||
<CategorizedItemsRenderer
|
<CategorizedItemsRenderer
|
||||||
@@ -255,7 +257,7 @@ onMounted(() => {
|
|||||||
<span
|
<span
|
||||||
v-html="
|
v-html="
|
||||||
$locale.baseText('nodeCreator.actionsCallout.noTriggerItems', {
|
$locale.baseText('nodeCreator.actionsCallout.noTriggerItems', {
|
||||||
interpolate: { nodeName: subcategory },
|
interpolate: { nodeName: subcategory ?? '' },
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -295,7 +297,7 @@ onMounted(() => {
|
|||||||
<span
|
<span
|
||||||
v-html="
|
v-html="
|
||||||
$locale.baseText('nodeCreator.actionsCallout.noActionItems', {
|
$locale.baseText('nodeCreator.actionsCallout.noActionItems', {
|
||||||
interpolate: { nodeName: subcategory },
|
interpolate: { nodeName: subcategory ?? '' },
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
@@ -316,7 +318,7 @@ onMounted(() => {
|
|||||||
@click.prevent="addHttpNode"
|
@click.prevent="addHttpNode"
|
||||||
v-html="
|
v-html="
|
||||||
$locale.baseText('nodeCreator.actionsList.apiCall', {
|
$locale.baseText('nodeCreator.actionsList.apiCall', {
|
||||||
interpolate: { node: subcategory },
|
interpolate: { node: subcategory ?? '' },
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits({
|
const emit = defineEmits({
|
||||||
nodeTypeSelected: (nodeTypes: string[]) => true,
|
nodeTypeSelected: (_nodeTypes: string[]) => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
@@ -98,7 +98,7 @@ function onSelected(item: INodeCreateElement) {
|
|||||||
subcategory: item.properties.displayName,
|
subcategory: item.properties.displayName,
|
||||||
title: item.properties.displayName,
|
title: item.properties.displayName,
|
||||||
nodeIcon: {
|
nodeIcon: {
|
||||||
color: item.properties.defaults?.color || '',
|
color: item.properties.defaults?.color?.toString(),
|
||||||
icon,
|
icon,
|
||||||
iconType: item.properties.iconUrl ? 'file' : 'icon',
|
iconType: item.properties.iconUrl ? 'file' : 'icon',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, watch, ref, getCurrentInstance } from 'vue';
|
import { computed, watch, ref } from 'vue';
|
||||||
import type { INodeCreateElement } from '@/Interface';
|
import type { INodeCreateElement } from '@/Interface';
|
||||||
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
@@ -8,6 +8,7 @@ import { useKeyboardNavigation } from '../composables/useKeyboardNavigation';
|
|||||||
import { useViewStacks } from '../composables/useViewStacks';
|
import { useViewStacks } from '../composables/useViewStacks';
|
||||||
import ItemsRenderer from './ItemsRenderer.vue';
|
import ItemsRenderer from './ItemsRenderer.vue';
|
||||||
import CategoryItem from '../ItemTypes/CategoryItem.vue';
|
import CategoryItem from '../ItemTypes/CategoryItem.vue';
|
||||||
|
import { useTelemetry } from '@/composables/useTelemetry';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
elements: INodeCreateElement[];
|
elements: INodeCreateElement[];
|
||||||
@@ -23,8 +24,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
elements: () => [],
|
elements: () => [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const instance = getCurrentInstance();
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
const { popViewStack } = useViewStacks();
|
const { popViewStack } = useViewStacks();
|
||||||
const { registerKeyHook } = useKeyboardNavigation();
|
const { registerKeyHook } = useKeyboardNavigation();
|
||||||
const { workflowId } = useWorkflowsStore();
|
const { workflowId } = useWorkflowsStore();
|
||||||
@@ -41,7 +41,7 @@ function setExpanded(isExpanded: boolean) {
|
|||||||
expanded.value = isExpanded;
|
expanded.value = isExpanded;
|
||||||
|
|
||||||
if (expanded.value) {
|
if (expanded.value) {
|
||||||
instance?.proxy.$telemetry.trackNodesPanel('nodeCreateList.onCategoryExpanded', {
|
telemetry.trackNodesPanel('nodeCreateList.onCategoryExpanded', {
|
||||||
category_name: props.category,
|
category_name: props.category,
|
||||||
workflow_id: workflowId,
|
workflow_id: workflowId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: 'selected', element: INodeCreateElement, $e?: Event): void;
|
selected: [element: INodeCreateElement, $e?: Event];
|
||||||
(event: 'dragstart', element: INodeCreateElement, $e: Event): void;
|
dragstart: [element: INodeCreateElement, $e: Event];
|
||||||
(event: 'dragend', element: INodeCreateElement, $e: Event): void;
|
dragend: [element: INodeCreateElement, $e: Event];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const renderedItems = ref<INodeCreateElement[]>([]);
|
const renderedItems = ref<INodeCreateElement[]>([]);
|
||||||
@@ -61,7 +61,23 @@ function wrappedEmit(
|
|||||||
) {
|
) {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case 'dragstart':
|
||||||
|
if ($e) {
|
||||||
|
emit('dragstart', element, $e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'dragend':
|
||||||
|
if ($e) {
|
||||||
|
emit('dragend', element, $e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'selected':
|
||||||
|
emit('selected', element, $e);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
emit(event, element, $e);
|
emit(event, element, $e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeEnter(el: HTMLElement) {
|
function beforeEnter(el: HTMLElement) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
mockViewCreateElement,
|
mockViewCreateElement,
|
||||||
mockSectionCreateElement,
|
mockSectionCreateElement,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import ItemsRenderer from '../Renderers/ItemsRenderer.vue';
|
import ItemsRenderer from '@/components/Node/NodeCreator/Renderers/ItemsRenderer.vue';
|
||||||
import { createComponentRenderer } from '@/__tests__/render';
|
import { createComponentRenderer } from '@/__tests__/render';
|
||||||
|
|
||||||
const renderComponent = createComponentRenderer(ItemsRenderer);
|
const renderComponent = createComponentRenderer(ItemsRenderer);
|
||||||
@@ -88,8 +88,21 @@ describe('ItemsRenderer', () => {
|
|||||||
|
|
||||||
for (const [index, itemType] of Object.keys(itemTypes).entries()) {
|
for (const [index, itemType] of Object.keys(itemTypes).entries()) {
|
||||||
const itemElement = itemTypes[itemType as keyof typeof itemTypes];
|
const itemElement = itemTypes[itemType as keyof typeof itemTypes];
|
||||||
await fireEvent.click(itemElement!);
|
if (itemElement) {
|
||||||
expect(emitted().selected[index][0].type).toBe(itemType);
|
await fireEvent.click(itemElement);
|
||||||
|
const emittedEvent = emitted().selected[index];
|
||||||
|
// Use a type guard to check if emittedEvent is an array and if its first element has a 'type' property
|
||||||
|
if (
|
||||||
|
Array.isArray(emittedEvent) &&
|
||||||
|
emittedEvent.length > 0 &&
|
||||||
|
typeof emittedEvent[0] === 'object' &&
|
||||||
|
'type' in emittedEvent[0]
|
||||||
|
) {
|
||||||
|
expect(emittedEvent[0].type).toBe(itemType);
|
||||||
|
} else {
|
||||||
|
fail('Emitted event is not an array or does not have a type property');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [
|
[NODE_NAME]: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -117,7 +117,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [
|
[NODE_NAME]: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -156,7 +156,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [],
|
[NODE_NAME]: [],
|
||||||
});
|
});
|
||||||
@@ -189,7 +189,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [
|
[NODE_NAME]: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -251,7 +251,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [
|
[NODE_NAME]: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -326,7 +326,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [
|
[NODE_NAME]: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -371,7 +371,7 @@ describe('useActionsGenerator', () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { actions } = generateMergedNodesAndActions([node]);
|
const { actions } = generateMergedNodesAndActions([node], []);
|
||||||
expect(actions).toEqual({
|
expect(actions).toEqual({
|
||||||
[NODE_NAME]: [
|
[NODE_NAME]: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const mockSimplifiedNodeType = (
|
|||||||
defaults: {
|
defaults: {
|
||||||
color: '#ffffff',
|
color: '#ffffff',
|
||||||
},
|
},
|
||||||
|
outputs: [],
|
||||||
...overrides,
|
...overrides,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,7 +124,7 @@ export const mockLabelCreateElement = (
|
|||||||
uuid: uuidv4(),
|
uuid: uuidv4(),
|
||||||
key: uuidv4(),
|
key: uuidv4(),
|
||||||
type: 'label',
|
type: 'label',
|
||||||
subcategory: subcategory || 'sampleSubcategory',
|
subcategory: subcategory ?? 'sampleSubcategory',
|
||||||
properties: mockLabelItemProps(overrides),
|
properties: mockLabelItemProps(overrides),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,6 +135,6 @@ export const mockActionCreateElement = (
|
|||||||
uuid: uuidv4(),
|
uuid: uuidv4(),
|
||||||
key: uuidv4(),
|
key: uuidv4(),
|
||||||
type: 'action',
|
type: 'action',
|
||||||
subcategory: subcategory || 'sampleSubcategory',
|
subcategory: subcategory ?? 'sampleSubcategory',
|
||||||
properties: mockActionTypeDescription(overrides),
|
properties: mockActionTypeDescription(overrides),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getCurrentInstance, computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { IDataObject, INodeParameters } from 'n8n-workflow';
|
import type { IDataObject, INodeParameters } from 'n8n-workflow';
|
||||||
import type {
|
import type {
|
||||||
ActionTypeDescription,
|
ActionTypeDescription,
|
||||||
@@ -27,7 +27,6 @@ import {
|
|||||||
TRIGGER_NODE_CREATOR_VIEW,
|
TRIGGER_NODE_CREATOR_VIEW,
|
||||||
WEBHOOK_NODE_TYPE,
|
WEBHOOK_NODE_TYPE,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import { i18n } from '@/plugins/i18n';
|
|
||||||
|
|
||||||
import type { BaseTextKey } from '@/plugins/i18n';
|
import type { BaseTextKey } from '@/plugins/i18n';
|
||||||
import type { Telemetry } from '@/plugins/telemetry';
|
import type { Telemetry } from '@/plugins/telemetry';
|
||||||
@@ -37,10 +36,11 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|||||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||||
|
|
||||||
import { sortNodeCreateElements, transformNodeType } from '../utils';
|
import { sortNodeCreateElements, transformNodeType } from '../utils';
|
||||||
|
import { useI18n } from '@/composables/useI18n';
|
||||||
|
|
||||||
export const useActions = () => {
|
export const useActions = () => {
|
||||||
const nodeCreatorStore = useNodeCreatorStore();
|
const nodeCreatorStore = useNodeCreatorStore();
|
||||||
const instance = getCurrentInstance();
|
const i18n = useI18n();
|
||||||
|
|
||||||
const singleNodeOpenSources = [
|
const singleNodeOpenSources = [
|
||||||
NODE_CREATOR_OPEN_SOURCES.PLUS_ENDPOINT,
|
NODE_CREATOR_OPEN_SOURCES.PLUS_ENDPOINT,
|
||||||
@@ -50,8 +50,8 @@ export const useActions = () => {
|
|||||||
|
|
||||||
const actionsCategoryLocales = computed(() => {
|
const actionsCategoryLocales = computed(() => {
|
||||||
return {
|
return {
|
||||||
actions: instance?.proxy.$locale.baseText('nodeCreator.actionsCategory.actions') ?? '',
|
actions: i18n.baseText('nodeCreator.actionsCategory.actions') ?? '',
|
||||||
triggers: instance?.proxy.$locale.baseText('nodeCreator.actionsCategory.triggers') ?? '',
|
triggers: i18n.baseText('nodeCreator.actionsCategory.triggers') ?? '',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ export const useActions = () => {
|
|||||||
if (transformed.type === 'action') {
|
if (transformed.type === 'action') {
|
||||||
const nameBase = node.name.replace('n8n-nodes-base.', '');
|
const nameBase = node.name.replace('n8n-nodes-base.', '');
|
||||||
const localeKey = `nodeCreator.actionsPlaceholderNode.${nameBase}` as BaseTextKey;
|
const localeKey = `nodeCreator.actionsPlaceholderNode.${nameBase}` as BaseTextKey;
|
||||||
const overwriteLocale = instance?.proxy.$locale.baseText(localeKey) as string;
|
const overwriteLocale = i18n.baseText(localeKey);
|
||||||
|
|
||||||
// If the locale key is not the same as the node name, it means it contain a translation
|
// If the locale key is not the same as the node name, it means it contain a translation
|
||||||
// and we should use it
|
// and we should use it
|
||||||
@@ -127,10 +127,12 @@ export const useActions = () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertIndex = firstIndexMap.get(label)! + insertedLabels;
|
const insertIndex = firstIndexMap.get(label);
|
||||||
extendedActions.splice(insertIndex, 0, newLabel);
|
if (insertIndex !== undefined) {
|
||||||
|
extendedActions.splice(insertIndex + insertedLabels, 0, newLabel);
|
||||||
insertedLabels++;
|
insertedLabels++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return extendedActions;
|
return extendedActions;
|
||||||
}
|
}
|
||||||
@@ -148,7 +150,7 @@ export const useActions = () => {
|
|||||||
function getActionData(actionItem: ActionTypeDescription): IUpdateInformation {
|
function getActionData(actionItem: ActionTypeDescription): IUpdateInformation {
|
||||||
const displayOptions = actionItem.displayOptions;
|
const displayOptions = actionItem.displayOptions;
|
||||||
|
|
||||||
const displayConditions = Object.keys(displayOptions?.show || {}).reduce(
|
const displayConditions = Object.keys(displayOptions?.show ?? {}).reduce(
|
||||||
(acc: IDataObject, showCondition: string) => {
|
(acc: IDataObject, showCondition: string) => {
|
||||||
acc[showCondition] = displayOptions?.show?.[showCondition]?.[0];
|
acc[showCondition] = displayOptions?.show?.[showCondition]?.[0];
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const customNodeActionsParsers: {
|
|||||||
displayName: cachedBaseText('nodeCreator.actionsCategory.onEvent', {
|
displayName: cachedBaseText('nodeCreator.actionsCategory.onEvent', {
|
||||||
interpolate: { event: startCase(categoryItem.name) },
|
interpolate: { event: startCase(categoryItem.name) },
|
||||||
}),
|
}),
|
||||||
description: categoryItem.description || '',
|
description: categoryItem.description ?? '',
|
||||||
displayOptions: matchedProperty.displayOptions,
|
displayOptions: matchedProperty.displayOptions,
|
||||||
values: { eventsUi: { eventValues: [{ name: categoryItem.value }] } },
|
values: { eventsUi: { eventValues: [{ name: categoryItem.value }] } },
|
||||||
}),
|
}),
|
||||||
@@ -56,7 +56,7 @@ function getNodeTypeBase(nodeTypeDescription: INodeTypeDescription, label?: stri
|
|||||||
name: nodeTypeDescription.name,
|
name: nodeTypeDescription.name,
|
||||||
group: nodeTypeDescription.group,
|
group: nodeTypeDescription.group,
|
||||||
codex: {
|
codex: {
|
||||||
label: label || '',
|
label: label ?? '',
|
||||||
categories: [category],
|
categories: [category],
|
||||||
},
|
},
|
||||||
iconUrl: nodeTypeDescription.iconUrl,
|
iconUrl: nodeTypeDescription.iconUrl,
|
||||||
@@ -139,7 +139,7 @@ function triggersCategory(nodeTypeDescription: INodeTypeDescription): ActionType
|
|||||||
cachedBaseText('nodeCreator.actionsCategory.onEvent', {
|
cachedBaseText('nodeCreator.actionsCategory.onEvent', {
|
||||||
interpolate: { event: startCase(categoryItem.name) },
|
interpolate: { event: startCase(categoryItem.name) },
|
||||||
}),
|
}),
|
||||||
description: categoryItem.description || '',
|
description: categoryItem.description ?? '',
|
||||||
displayOptions: matchedProperty.displayOptions,
|
displayOptions: matchedProperty.displayOptions,
|
||||||
values: {
|
values: {
|
||||||
[matchedProperty.name]:
|
[matchedProperty.name]:
|
||||||
@@ -159,14 +159,14 @@ function resourceCategories(nodeTypeDescription: INodeTypeDescription): ActionTy
|
|||||||
matchedProperties.forEach((property) => {
|
matchedProperties.forEach((property) => {
|
||||||
((property.options as INodePropertyOptions[]) || [])
|
((property.options as INodePropertyOptions[]) || [])
|
||||||
.filter((option) => option.value !== CUSTOM_API_CALL_KEY)
|
.filter((option) => option.value !== CUSTOM_API_CALL_KEY)
|
||||||
.forEach((resourceOption, i, options) => {
|
.forEach((resourceOption, _i, options) => {
|
||||||
const isSingleResource = options.length === 1;
|
const isSingleResource = options.length === 1;
|
||||||
|
|
||||||
// Match operations for the resource by checking if displayOptions matches or contains the resource name
|
// Match operations for the resource by checking if displayOptions matches or contains the resource name
|
||||||
const operations = nodeTypeDescription.properties.find((operation) => {
|
const operations = nodeTypeDescription.properties.find((operation) => {
|
||||||
const isOperation = operation.name === 'operation';
|
const isOperation = operation.name === 'operation';
|
||||||
const isMatchingResource =
|
const isMatchingResource =
|
||||||
operation.displayOptions?.show?.resource?.includes(resourceOption.value) ||
|
operation.displayOptions?.show?.resource?.includes(resourceOption.value) ??
|
||||||
isSingleResource;
|
isSingleResource;
|
||||||
|
|
||||||
// If the operation doesn't have a version defined, it should be
|
// If the operation doesn't have a version defined, it should be
|
||||||
@@ -178,7 +178,9 @@ function resourceCategories(nodeTypeDescription: INodeTypeDescription): ActionTy
|
|||||||
: [nodeTypeDescription.version];
|
: [nodeTypeDescription.version];
|
||||||
|
|
||||||
const isMatchingVersion = operationVersions
|
const isMatchingVersion = operationVersions
|
||||||
? operationVersions.some((version) => nodeTypeVersions.includes(version))
|
? operationVersions.some(
|
||||||
|
(version) => typeof version === 'number' && nodeTypeVersions.includes(version),
|
||||||
|
)
|
||||||
: true;
|
: true;
|
||||||
|
|
||||||
return isOperation && isMatchingResource && isMatchingVersion;
|
return isOperation && isMatchingResource && isMatchingVersion;
|
||||||
@@ -286,10 +288,17 @@ export function useActionsGenerator() {
|
|||||||
actions[app.name] = appActions;
|
actions[app.name] = appActions;
|
||||||
|
|
||||||
if (app.name === HTTP_REQUEST_NODE_TYPE) {
|
if (app.name === HTTP_REQUEST_NODE_TYPE) {
|
||||||
const credentialOnlyNodes = httpOnlyCredentials.map((credentialType) =>
|
const credentialOnlyNodes = httpOnlyCredentials.map((credentialType) => {
|
||||||
getSimplifiedNodeType(getCredentialOnlyNodeType(app, credentialType)),
|
const credsOnlyNode = getCredentialOnlyNodeType(app, credentialType);
|
||||||
|
if (credsOnlyNode) return getSimplifiedNodeType(credsOnlyNode);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredNodes = credentialOnlyNodes.filter(
|
||||||
|
(node): node is SimplifiedNodeType => node !== null,
|
||||||
);
|
);
|
||||||
mergedNodes.push(...credentialOnlyNodes);
|
|
||||||
|
mergedNodes.push(...filteredNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedNodes.push(getSimplifiedNodeType(app));
|
mergedNodes.push(getSimplifiedNodeType(app));
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ interface ViewStack {
|
|||||||
baseFilter?: (item: INodeCreateElement) => boolean;
|
baseFilter?: (item: INodeCreateElement) => boolean;
|
||||||
itemsMapper?: (item: INodeCreateElement) => INodeCreateElement;
|
itemsMapper?: (item: INodeCreateElement) => INodeCreateElement;
|
||||||
panelClass?: string;
|
panelClass?: string;
|
||||||
sections?: NodeViewItemSection[];
|
sections?: string[] | NodeViewItemSection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useViewStacks = defineStore('nodeCreatorViewStacks', () => {
|
export const useViewStacks = defineStore('nodeCreatorViewStacks', () => {
|
||||||
@@ -128,7 +128,7 @@ export const useViewStacks = defineStore('nodeCreatorViewStacks', () => {
|
|||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
|
|
||||||
let nodesByConnectionType: { [key: string]: string[] };
|
let nodesByConnectionType: { [key: string]: string[] };
|
||||||
let relatedAIView: NodeViewItem | { properties: { title: string; icon: string } } | undefined;
|
let relatedAIView: { properties: NodeViewItem['properties'] } | undefined;
|
||||||
|
|
||||||
if (isOutput === true) {
|
if (isOutput === true) {
|
||||||
nodesByConnectionType = useNodeTypesStore().visibleNodeTypesByInputConnectionTypeNames;
|
nodesByConnectionType = useNodeTypesStore().visibleNodeTypesByInputConnectionTypeNames;
|
||||||
|
|||||||
@@ -86,16 +86,20 @@ export function flattenCreateElements(items: INodeCreateElement[]): INodeCreateE
|
|||||||
|
|
||||||
export function groupItemsInSections(
|
export function groupItemsInSections(
|
||||||
items: INodeCreateElement[],
|
items: INodeCreateElement[],
|
||||||
sections: NodeViewItemSection[],
|
sections: string[] | NodeViewItemSection[],
|
||||||
): INodeCreateElement[] {
|
): INodeCreateElement[] {
|
||||||
|
const filteredSections = sections.filter(
|
||||||
|
(section): section is NodeViewItemSection => typeof section === 'object',
|
||||||
|
);
|
||||||
|
|
||||||
const itemsBySection = items.reduce((acc: Record<string, INodeCreateElement[]>, item) => {
|
const itemsBySection = items.reduce((acc: Record<string, INodeCreateElement[]>, item) => {
|
||||||
const section = sections.find((s) => s.items.includes(item.key));
|
const section = filteredSections.find((s) => s.items.includes(item.key));
|
||||||
const key = section?.key ?? 'other';
|
const key = section?.key ?? 'other';
|
||||||
acc[key] = [...(acc[key] ?? []), item];
|
acc[key] = [...(acc[key] ?? []), item];
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const result: SectionCreateElement[] = sections
|
const result: SectionCreateElement[] = filteredSections
|
||||||
.map(
|
.map(
|
||||||
(section): SectionCreateElement => ({
|
(section): SectionCreateElement => ({
|
||||||
type: 'section',
|
type: 'section',
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ export interface NodeViewItem {
|
|||||||
type: string;
|
type: string;
|
||||||
properties: {
|
properties: {
|
||||||
name?: string;
|
name?: string;
|
||||||
title: string;
|
title?: string;
|
||||||
icon: string;
|
icon?: string;
|
||||||
iconProps?: {
|
iconProps?: {
|
||||||
color?: string;
|
color?: string;
|
||||||
};
|
};
|
||||||
@@ -81,8 +81,14 @@ export interface NodeViewItem {
|
|||||||
group?: string[];
|
group?: string[];
|
||||||
sections?: NodeViewItemSection[];
|
sections?: NodeViewItemSection[];
|
||||||
description?: string;
|
description?: string;
|
||||||
|
displayName?: string;
|
||||||
tag?: string;
|
tag?: string;
|
||||||
forceIncludeNodes?: string[];
|
forceIncludeNodes?: string[];
|
||||||
|
iconData?: {
|
||||||
|
type: string;
|
||||||
|
icon?: string;
|
||||||
|
fileBuffer?: string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
category?: string | string[];
|
category?: string | string[];
|
||||||
}
|
}
|
||||||
@@ -264,7 +270,7 @@ export function AINodesView(_nodes: SimplifiedNodeType[]): NodeView {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TriggerView(nodes: SimplifiedNodeType[]) {
|
export function TriggerView() {
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
|
|
||||||
const view: NodeView = {
|
const view: NodeView = {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@
|
|||||||
:show-tooltip="showTooltip"
|
:show-tooltip="showTooltip"
|
||||||
:tooltip-position="tooltipPosition"
|
:tooltip-position="tooltipPosition"
|
||||||
:badge="badge"
|
:badge="badge"
|
||||||
@click="(e) => $emit('click')"
|
@click="() => $emit('click')"
|
||||||
></n8n-node-icon>
|
></n8n-node-icon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { IVersionNode } from '@/Interface';
|
import type { ActionTypeDescription, IVersionNode, SimplifiedNodeType } from '@/Interface';
|
||||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
@@ -32,7 +32,10 @@ export default defineComponent({
|
|||||||
name: 'NodeIcon',
|
name: 'NodeIcon',
|
||||||
props: {
|
props: {
|
||||||
nodeType: {
|
nodeType: {
|
||||||
type: Object as PropType<INodeTypeDescription | IVersionNode | null>,
|
type: Object as PropType<
|
||||||
|
INodeTypeDescription | IVersionNode | SimplifiedNodeType | ActionTypeDescription | null
|
||||||
|
>,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|||||||
Reference in New Issue
Block a user