mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Add settings icons to the node on canvas (#15467)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_650_18839)">
|
||||
<path d="M5.4989 0.351562C5.96754 -0.117074 6.72753 -0.117074 7.19617 0.351562L11.996 5.15137C12.0955 5.25089 12.1731 5.36418 12.2303 5.48438C12.2567 5.53953 12.279 5.59683 12.2968 5.65625C12.3136 5.71272 12.3255 5.77017 12.3339 5.82812C12.3419 5.88433 12.3475 5.94156 12.3475 6C12.3475 6.06071 12.3416 6.12043 12.3329 6.17871C12.3299 6.19893 12.3272 6.21919 12.3231 6.23926C12.3023 6.34228 12.2669 6.43964 12.2216 6.53125C12.2163 6.54195 12.2116 6.55291 12.2059 6.56348C12.198 6.57845 12.1891 6.59284 12.1805 6.60742C12.1682 6.62838 12.1562 6.64959 12.1425 6.66992C12.0992 6.73406 12.0505 6.79412 11.996 6.84863L7.19617 11.6484C6.72753 12.1171 5.96754 12.1171 5.4989 11.6484C5.03045 11.1798 5.03033 10.4197 5.4989 9.95117L8.25085 7.2002H1.54773C0.884978 7.2002 0.347534 6.66275 0.347534 6C0.347702 5.33739 0.885082 4.80078 1.54773 4.80078H8.25183L5.4989 2.04883C5.03033 1.58025 5.03045 0.820214 5.4989 0.351562Z" fill="#909398"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_650_18839">
|
||||
<rect width="12" height="12" fill="white" transform="translate(0.347534)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_650_18836)">
|
||||
<path d="M4.73335 9.60167C5.30603 9.89454 5.57535 10.5792 5.33688 11.1915C5.08244 11.8446 4.34649 12.1675 3.69339 11.913C2.89755 11.6029 2.15254 11.127 1.51297 10.4874C0.953268 9.9277 0.518901 9.28719 0.211269 8.60257L0.087408 8.30693L0.0459264 8.18365C-0.125896 7.56369 0.196633 6.90193 0.808955 6.66337C1.42121 6.4249 2.10584 6.69422 2.39869 7.26692L2.45186 7.38553L2.52664 7.56198C2.71023 7.97058 2.96972 8.35447 3.30778 8.69254C3.69419 9.07896 4.14008 9.36356 4.61475 9.5485L4.73335 9.60167ZM1.51297 1.51303C2.15254 0.873444 2.89755 0.397506 3.69339 0.0874113L3.81666 0.0459281C4.4366 -0.1259 5.09833 0.19664 5.33688 0.808985C5.59124 1.46209 5.26782 2.19752 4.61475 2.45195C4.14008 2.63689 3.69419 2.92149 3.30778 3.30791C2.96972 3.64599 2.71023 4.02987 2.52664 4.43847L2.45186 4.61492L2.39869 4.73353C2.10584 5.30623 1.42121 5.57556 0.808955 5.33708C0.15585 5.08263 -0.167031 4.34666 0.087408 3.69353C0.397491 2.89766 0.873411 2.15262 1.51297 1.51303ZM8.69222 8.69254C9.03028 8.35447 9.28977 7.97058 9.47336 7.56198L9.54814 7.38553L9.60131 7.26692C9.89416 6.69422 10.5788 6.4249 11.191 6.66337C11.8034 6.90193 12.1259 7.56369 11.9541 8.18365L11.9126 8.30693L11.7887 8.60257C11.4811 9.28719 11.0467 9.9277 10.487 10.4874C9.84746 11.127 9.10245 11.6029 8.30661 11.913C7.65351 12.1675 6.91756 11.8446 6.66312 11.1915C6.40876 10.5384 6.73218 9.80294 7.38525 9.5485L7.5617 9.47371C7.97028 9.29012 8.35415 9.03062 8.69222 8.69254ZM10.487 1.51303C11.1266 2.15262 11.6025 2.89766 11.9126 3.69353C12.167 4.34666 11.8441 5.08263 11.191 5.33708C10.5788 5.57556 9.89416 5.30623 9.60131 4.73353L9.54814 4.61492L9.47336 4.43847C9.28977 4.02987 9.03028 3.64599 8.69222 3.30791C8.30581 2.92149 7.85992 2.63689 7.38525 2.45195C6.73218 2.19752 6.40876 1.46209 6.66312 0.808985C6.91756 0.155856 7.65351 -0.167037 8.30661 0.0874113L8.60224 0.211277C9.28683 0.51892 9.92732 0.953304 10.487 1.51303Z" fill="#909398"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_650_18836">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_650_18838)">
|
||||
<path d="M10.3926 5.23047C11.0302 5.23047 11.5477 5.74714 11.5479 6.38477V8.69434C11.5479 9.75721 10.6859 10.6191 9.62305 10.6191H4.23438V11.6143C4.23438 11.9572 3.81963 12.1292 3.57715 11.8867L1.42676 9.73633C1.27682 9.58605 1.27675 9.34261 1.42676 9.19238L3.57715 7.04199C3.81965 6.79969 4.23438 6.97159 4.23438 7.31445V8.30957H9.23828V6.38477C9.23843 5.7472 9.755 5.23056 10.3926 5.23047ZM7.31348 0.385742C7.31348 0.0428174 7.72822 -0.129203 7.9707 0.113281L10.1201 2.26367C10.2704 2.41399 10.2704 2.6573 10.1201 2.80762L7.9707 4.95801C7.72822 5.20049 7.31348 5.02847 7.31348 4.68555V3.69043H2.30957V5.61523C2.30957 6.25299 1.79205 6.77051 1.1543 6.77051C0.516755 6.77027 0 6.25284 0 5.61523V3.30566C0.000146327 2.24287 0.86198 1.38184 1.9248 1.38184H7.31348V0.385742Z" fill="#909398"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_650_18838">
|
||||
<rect width="12" height="12" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.52246 0.00585938C7.12758 0.0672969 7.59961 0.578867 7.59961 1.2002V9.60059H9.59961C10.2623 9.60059 10.7996 10.1372 10.7998 10.7998C10.7998 11.4625 10.2624 12 9.59961 12H3.2002C2.53745 12 2 11.4625 2 10.7998C2.00016 10.1372 2.53755 9.60059 3.2002 9.60059H5.2002V2.40039H3.2002C2.53752 2.40039 2.00011 1.86284 2 1.2002C2 0.537456 2.53745 0 3.2002 0H6.40039L6.52246 0.00585938Z" fill="#909398"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 508 B |
@@ -1,8 +1,12 @@
|
||||
import Binary from './custom/binary.svg';
|
||||
import BoltFilled from './custom/bolt-filled.svg';
|
||||
import Continue from './custom/Continue.svg';
|
||||
import EmptyOutput from './custom/EmptyOutput.svg';
|
||||
import GripLinesVertical from './custom/grip-lines-vertical.svg';
|
||||
import Json from './custom/json.svg';
|
||||
import PopOut from './custom/pop-out.svg';
|
||||
import Retry from './custom/Retry.svg';
|
||||
import RunOnce from './custom/RunOnce.svg';
|
||||
import Schema from './custom/schema.svg';
|
||||
import Spinner from './custom/spinner.svg';
|
||||
import StatusCanceled from './custom/status-canceled.svg';
|
||||
@@ -406,6 +410,10 @@ export const updatedIconSet = {
|
||||
'status-unknown': StatusUnknown,
|
||||
'status-warning': StatusWarning,
|
||||
'vector-square': VectorSquare,
|
||||
'continue-on-error': Continue,
|
||||
'always-output-data': EmptyOutput,
|
||||
'retry-on-fail': Retry,
|
||||
'execute-once': RunOnce,
|
||||
schema: Schema,
|
||||
json: Json,
|
||||
binary: Binary,
|
||||
|
||||
@@ -1252,6 +1252,11 @@
|
||||
"node.discovery.pinData.canvas": "You can pin this output instead of waiting for a test event. Open node to do so.",
|
||||
"node.discovery.pinData.ndv": "You can pin this output instead of waiting for a test event.",
|
||||
"node.executionError.openNode": "Open node",
|
||||
"node.settings.continuesOnError": "Execution will continue even if the node fails",
|
||||
"node.settings.continuesOnError.title": "Continue On Fail",
|
||||
"node.settings.retriesOnFailure": "This node will automatically retry if it fails",
|
||||
"node.settings.executeOnce": "This node executes only once, no matter how many input items there are",
|
||||
"node.settings.alwaysOutputData": "This node will output an empty item if nothing would normally be returned",
|
||||
"nodeBase.clickToAddNodeOrDragToConnect": "Click to add node \n or drag to connect",
|
||||
"nodeCreator.actionsPlaceholderNode.scheduleTrigger": "On a Schedule",
|
||||
"nodeCreator.actionsPlaceholderNode.webhook": "On a Webhook call",
|
||||
@@ -2429,6 +2434,15 @@
|
||||
"ndv.search.noMatchSchema.description": "To search field values, switch to table or JSON view. {link}",
|
||||
"ndv.search.noMatchSchema.description.link": "Clear filter",
|
||||
"ndv.search.items": "{matched} of {count} item | {matched} of {count} items",
|
||||
"ndv.nodeHints.disabled": "This node is disabled, and will simply pass the input through",
|
||||
"ndv.nodeHints.alwaysOutputData": "This node will output an empty item if nothing would normally be returned",
|
||||
"ndv.nodeHints.alwaysOutputData.short": "output an empty item if nothing would normally be returned",
|
||||
"ndv.nodeHints.executeOnce": "This node will execute only once, no matter how many input items there are",
|
||||
"ndv.nodeHints.executeOnce.short": "execute only once, no matter how many input items there are",
|
||||
"ndv.nodeHints.retryOnFail": "This node will automatically retry if it fails",
|
||||
"ndv.nodeHints.retryOnFail.short": "automatically retry if it fails",
|
||||
"ndv.nodeHints.continueOnError": "Execution will continue even if the node fails",
|
||||
"ndv.nodeHints.continueOnError.short": "continue executing even if the node fails",
|
||||
"updatesPanel.andIs": "and is",
|
||||
"updatesPanel.behindTheLatest": "behind the latest and greatest n8n",
|
||||
"updatesPanel.howToUpdateYourN8nVersion": "How to update your n8n version",
|
||||
|
||||
123
packages/frontend/editor-ui/src/components/NodeSettingsHint.vue
Normal file
123
packages/frontend/editor-ui/src/components/NodeSettingsHint.vue
Normal file
@@ -0,0 +1,123 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import type { INodeUi } from '@/Interface';
|
||||
import type { IconName } from '@n8n/design-system/src/components/N8nIcon/icons';
|
||||
|
||||
interface Props {
|
||||
node: INodeUi | null;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const i18n = useI18n();
|
||||
|
||||
const activeSettings = computed(() => {
|
||||
if (!props.node || props.node.disabled) {
|
||||
return props.node?.disabled
|
||||
? [
|
||||
{
|
||||
key: 'disabled',
|
||||
message: i18n.baseText('ndv.nodeHints.disabled'),
|
||||
icon: 'power-off' as IconName,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
}
|
||||
|
||||
const settings = [];
|
||||
|
||||
if (props.node.alwaysOutputData) {
|
||||
settings.push({
|
||||
key: 'alwaysOutputData',
|
||||
message: i18n.baseText('ndv.nodeHints.alwaysOutputData'),
|
||||
icon: 'always-output-data' as IconName,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.node.executeOnce) {
|
||||
settings.push({
|
||||
key: 'executeOnce',
|
||||
message: i18n.baseText('ndv.nodeHints.executeOnce'),
|
||||
icon: 'execute-once' as IconName,
|
||||
});
|
||||
}
|
||||
|
||||
if (props.node.retryOnFail) {
|
||||
settings.push({
|
||||
key: 'retryOnFail',
|
||||
message: i18n.baseText('ndv.nodeHints.retryOnFail'),
|
||||
icon: 'retry-on-fail' as IconName,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
props.node.onError === 'continueRegularOutput' ||
|
||||
props.node.onError === 'continueErrorOutput'
|
||||
) {
|
||||
settings.push({
|
||||
key: 'continueOnError',
|
||||
message: i18n.baseText('ndv.nodeHints.continueOnError'),
|
||||
icon: 'continue-on-error' as IconName,
|
||||
});
|
||||
}
|
||||
|
||||
return settings;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="activeSettings.length > 0" :class="$style.settingsHint">
|
||||
<div v-for="setting in activeSettings" :key="setting.key" :class="$style.settingItem">
|
||||
<div :class="$style.iconWrapper">
|
||||
<FontAwesomeIcon v-if="setting.icon === 'power'" icon="power" :class="$style.icon" />
|
||||
<N8nIcon v-else :icon="setting.icon" :class="$style.icon" />
|
||||
</div>
|
||||
<N8nText size="small" :class="$style.message">
|
||||
{{ setting.message }}
|
||||
</N8nText>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.settingsHint {
|
||||
background-color: var(--color-callout-info-background);
|
||||
border-radius: var(--border-radius-base);
|
||||
border: var(--border-width-base) var(--border-style-base);
|
||||
border-color: var(--color-callout-info-border);
|
||||
color: var(--color-callout-info-font);
|
||||
margin-top: var(--spacing-2xs);
|
||||
margin-bottom: var(--spacing-xs);
|
||||
margin-left: var(--spacing-s);
|
||||
margin-right: var(--spacing-s);
|
||||
padding: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.settingItem {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-xs);
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: var(--spacing-xs);
|
||||
}
|
||||
}
|
||||
|
||||
.iconWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--color-callout-info-icon);
|
||||
font-size: var(--font-size-s);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.message {
|
||||
line-height: var(--font-line-height-regular);
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ViewableMimeTypes } from '@n8n/api-types';
|
||||
import { useStorage } from '@/composables/useStorage';
|
||||
import { saveAs } from 'file-saver';
|
||||
import NodeSettingsHint from '@/components/NodeSettingsHint.vue';
|
||||
import type {
|
||||
IBinaryData,
|
||||
IConnectedNode,
|
||||
@@ -818,7 +819,6 @@ function getNodeHints(): NodeHint[] {
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
function onItemHover(itemIndex: number | null) {
|
||||
if (itemIndex === null) {
|
||||
emit('itemHover', null);
|
||||
@@ -1575,7 +1575,7 @@ defineExpose({ enterEditMode });
|
||||
</slot>
|
||||
</N8nCallout>
|
||||
</div>
|
||||
|
||||
<NodeSettingsHint v-if="props.paneType === 'output'" :node="node" />
|
||||
<N8nCallout
|
||||
v-for="hint in getNodeHints()"
|
||||
:key="hint.message"
|
||||
@@ -2347,6 +2347,42 @@ defineExpose({ enterEditMode });
|
||||
padding: 0 var(--ndv-spacing);
|
||||
}
|
||||
|
||||
.messageSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.singleIcon {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.multipleIcons {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--spacing-2xs, 8px);
|
||||
}
|
||||
|
||||
.multipleIcons .iconStack {
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.iconStack {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4xs, 4px);
|
||||
flex-shrink: 0;
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: var(--color-callout-info-icon);
|
||||
line-height: 1;
|
||||
font-size: var(--font-size-xs);
|
||||
}
|
||||
|
||||
.executingMessage {
|
||||
.compact & {
|
||||
color: var(--color-text-light);
|
||||
|
||||
@@ -165,6 +165,7 @@ exports[`InputPanel > should render 1`] = `
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
|
||||
|
||||
<!--v-if-->
|
||||
|
||||
@@ -4,12 +4,17 @@ import { createCanvasNodeProvide, createCanvasProvide } from '@/__tests__/data';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { CanvasNodeRenderType } from '@/types';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { createTestWorkflowObject } from '@/__tests__/mocks';
|
||||
|
||||
const renderComponent = createComponentRenderer(CanvasNodeRenderer);
|
||||
|
||||
beforeEach(() => {
|
||||
const pinia = createTestingPinia();
|
||||
setActivePinia(pinia);
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowObject = createTestWorkflowObject(workflowsStore.workflow);
|
||||
workflowsStore.getCurrentWorkflow = vi.fn().mockReturnValue(workflowObject);
|
||||
});
|
||||
|
||||
describe('CanvasNodeRenderer', () => {
|
||||
|
||||
@@ -6,6 +6,8 @@ import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { CanvasConnectionMode, CanvasNodeRenderType } from '@/types';
|
||||
import { fireEvent } from '@testing-library/vue';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { createTestWorkflowObject } from '@/__tests__/mocks';
|
||||
|
||||
const renderComponent = createComponentRenderer(CanvasNodeDefault, {
|
||||
global: {
|
||||
@@ -18,6 +20,9 @@ const renderComponent = createComponentRenderer(CanvasNodeDefault, {
|
||||
beforeEach(() => {
|
||||
const pinia = createTestingPinia();
|
||||
setActivePinia(pinia);
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
const workflowObject = createTestWorkflowObject(workflowsStore.workflow);
|
||||
workflowsStore.getCurrentWorkflow = vi.fn().mockReturnValue(workflowObject);
|
||||
});
|
||||
|
||||
describe('CanvasNodeDefault', () => {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { useI18n } from '@n8n/i18n';
|
||||
import { useCanvasNode } from '@/composables/useCanvasNode';
|
||||
import type { CanvasNodeDefaultRender } from '@/types';
|
||||
import { useCanvas } from '@/composables/useCanvas';
|
||||
import CanvasNodeSettingsIcons from '@/components/canvas/elements/nodes/render-types/parts/CanvasNodeSettingsIcons.vue';
|
||||
import { useNodeHelpers } from '@/composables/useNodeHelpers';
|
||||
import { calculateNodeSize } from '@/utils/nodeViewUtils';
|
||||
import ExperimentalInPlaceNodeSettings from '@/components/canvas/experimental/components/ExperimentalEmbeddedNodeDetails.vue';
|
||||
|
||||
@@ -43,6 +45,7 @@ const { mainOutputs, mainOutputConnections, mainInputs, mainInputConnections, no
|
||||
connections,
|
||||
});
|
||||
|
||||
const nodeHelpers = useNodeHelpers();
|
||||
const renderOptions = computed(() => render.value.options as CanvasNodeDefaultRender['options']);
|
||||
|
||||
const classes = computed(() => {
|
||||
@@ -153,6 +156,13 @@ function onActivate(event: MouseEvent) {
|
||||
:disabled="isDisabled"
|
||||
:class="$style.icon"
|
||||
/>
|
||||
<CanvasNodeSettingsIcons
|
||||
v-if="
|
||||
!renderOptions.configuration &&
|
||||
!isDisabled &&
|
||||
!(hasPinnedData && !nodeHelpers.isProductionExecutionPreview.value)
|
||||
"
|
||||
/>
|
||||
<CanvasNodeDisabledStrikeThrough v-if="isStrikethroughVisible" />
|
||||
<div :class="$style.description">
|
||||
<div v-if="label" :class="$style.label">
|
||||
|
||||
@@ -25,6 +25,14 @@ exports[`CanvasNodeDefault > configurable > should render configurable node corr
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="settingsIcons"
|
||||
>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="description"
|
||||
@@ -71,6 +79,7 @@ exports[`CanvasNodeDefault > configuration > should render configurable configur
|
||||
</div>
|
||||
</div>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="description"
|
||||
>
|
||||
@@ -116,6 +125,7 @@ exports[`CanvasNodeDefault > configuration > should render configuration node co
|
||||
</div>
|
||||
</div>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="description"
|
||||
>
|
||||
@@ -160,6 +170,14 @@ exports[`CanvasNodeDefault > should render node correctly 1`] = `
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="settingsIcons"
|
||||
>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="description"
|
||||
@@ -205,6 +223,14 @@ exports[`CanvasNodeDefault > trigger > should render trigger node correctly 1`]
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="settingsIcons"
|
||||
>
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
<!--v-if-->
|
||||
<div
|
||||
class="description"
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useCanvasNode } from '@/composables/useCanvasNode';
|
||||
import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { N8nIcon } from '@n8n/design-system';
|
||||
|
||||
const { name } = useCanvasNode();
|
||||
const i18n = useI18n();
|
||||
const workflowHelpers = useWorkflowHelpers();
|
||||
|
||||
const workflow = computed(() => workflowHelpers.getCurrentWorkflow());
|
||||
const node = computed(() => workflow.value.getNode(name.value));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="$style.settingsIcons">
|
||||
<N8nTooltip v-if="node?.alwaysOutputData">
|
||||
<template #content>
|
||||
<div :class="$style.tooltipHeader">
|
||||
<N8nIcon icon="always-output-data" />
|
||||
<strong :class="$style.tooltipTitle">{{
|
||||
i18n.baseText('nodeSettings.alwaysOutputData.displayName')
|
||||
}}</strong>
|
||||
</div>
|
||||
<div>
|
||||
{{ i18n.baseText('node.settings.alwaysOutputData') }}
|
||||
</div>
|
||||
</template>
|
||||
<div data-test-id="canvas-node-status-always-output-data">
|
||||
<N8nIcon icon="always-output-data" />
|
||||
</div>
|
||||
</N8nTooltip>
|
||||
|
||||
<N8nTooltip v-if="node?.executeOnce">
|
||||
<template #content>
|
||||
<div :class="$style.tooltipHeader">
|
||||
<N8nIcon icon="execute-once" />
|
||||
<strong :class="$style.tooltipTitle">{{
|
||||
i18n.baseText('nodeSettings.executeOnce.displayName')
|
||||
}}</strong>
|
||||
</div>
|
||||
<div>
|
||||
{{ i18n.baseText('node.settings.executeOnce') }}
|
||||
</div>
|
||||
</template>
|
||||
<div data-test-id="canvas-node-status-execute-once">
|
||||
<N8nIcon icon="execute-once" />
|
||||
</div>
|
||||
</N8nTooltip>
|
||||
|
||||
<N8nTooltip v-if="node?.retryOnFail">
|
||||
<template #content>
|
||||
<div :class="$style.tooltipHeader">
|
||||
<N8nIcon icon="retry-on-fail" />
|
||||
<strong :class="$style.tooltipTitle">{{
|
||||
i18n.baseText('nodeSettings.retryOnFail.displayName')
|
||||
}}</strong>
|
||||
</div>
|
||||
<div>
|
||||
{{ i18n.baseText('node.settings.retriesOnFailure') }}
|
||||
</div>
|
||||
</template>
|
||||
<div data-test-id="canvas-node-status-retry-on-fail">
|
||||
<N8nIcon icon="retry-on-fail" />
|
||||
</div>
|
||||
</N8nTooltip>
|
||||
|
||||
<N8nTooltip
|
||||
v-if="node?.onError === 'continueRegularOutput' || node?.onError === 'continueErrorOutput'"
|
||||
>
|
||||
<template #content>
|
||||
<div :class="$style.tooltipHeader">
|
||||
<N8nIcon icon="continue-on-error" />
|
||||
<strong :class="$style.tooltipTitle">{{
|
||||
i18n.baseText('node.settings.continuesOnError.title')
|
||||
}}</strong>
|
||||
</div>
|
||||
<div>
|
||||
{{ i18n.baseText('node.settings.continuesOnError') }}
|
||||
</div>
|
||||
</template>
|
||||
<div data-test-id="canvas-node-status-continue-on-error">
|
||||
<N8nIcon icon="continue-on-error" />
|
||||
</div>
|
||||
</N8nTooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.settingsIcons {
|
||||
position: absolute;
|
||||
top: var(--canvas-node--status-icons-offset);
|
||||
right: var(--canvas-node--status-icons-offset);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.tooltipHeader {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.tooltipTitle {
|
||||
font-weight: 600;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user