mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(editor): Fix schema view pill highlighting (#10936)
This commit is contained in:
parent
9bd247f06b
commit
1b973dcd8d
|
@ -121,6 +121,8 @@ async function onDrop(value: string, event: MouseEvent) {
|
||||||
|
|
||||||
const droppedSelection = await dropInEditor(toRaw(editor), event, value);
|
const droppedSelection = await dropInEditor(toRaw(editor), event, value);
|
||||||
|
|
||||||
|
if (!ndvStore.isMappingOnboarded) ndvStore.setMappingOnboarded();
|
||||||
|
|
||||||
if (!ndvStore.isAutocompleteOnboarded) {
|
if (!ndvStore.isAutocompleteOnboarded) {
|
||||||
setCursorPosition((droppedSelection.ranges.at(0)?.head ?? 3) - 3);
|
setCursorPosition((droppedSelection.ranges.at(0)?.head ?? 3) - 3);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
|
@ -90,8 +90,8 @@ const allIssues = computed(() => {
|
||||||
const now = computed(() => DateTime.now().toISO());
|
const now = computed(() => DateTime.now().toISO());
|
||||||
|
|
||||||
const leftParameter = computed<INodeProperties>(() => ({
|
const leftParameter = computed<INodeProperties>(() => ({
|
||||||
name: '',
|
name: 'left',
|
||||||
displayName: '',
|
displayName: 'Left',
|
||||||
default: '',
|
default: '',
|
||||||
placeholder:
|
placeholder:
|
||||||
operator.value.type === 'dateTime'
|
operator.value.type === 'dateTime'
|
||||||
|
@ -103,8 +103,8 @@ const leftParameter = computed<INodeProperties>(() => ({
|
||||||
const rightParameter = computed<INodeProperties>(() => {
|
const rightParameter = computed<INodeProperties>(() => {
|
||||||
const type = operator.value.rightType ?? operator.value.type;
|
const type = operator.value.rightType ?? operator.value.type;
|
||||||
return {
|
return {
|
||||||
name: '',
|
name: 'right',
|
||||||
displayName: '',
|
displayName: 'Right',
|
||||||
default: '',
|
default: '',
|
||||||
placeholder:
|
placeholder:
|
||||||
type === 'dateTime' ? now.value : i18n.baseText('filter.condition.placeholderRight'),
|
type === 'dateTime' ? now.value : i18n.baseText('filter.condition.placeholderRight'),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, onBeforeUnmount, ref, watch } from 'vue';
|
||||||
import { EditorSelection, EditorState, type SelectionRange } from '@codemirror/state';
|
import { EditorSelection, EditorState, type SelectionRange } from '@codemirror/state';
|
||||||
import { type Completion, CompletionContext } from '@codemirror/autocomplete';
|
import { type Completion, CompletionContext } from '@codemirror/autocomplete';
|
||||||
import { datatypeCompletions } from '@/plugins/codemirror/completions/datatype.completions';
|
import { datatypeCompletions } from '@/plugins/codemirror/completions/datatype.completions';
|
||||||
|
@ -75,10 +75,18 @@ function getCompletionsWithDot(): readonly Completion[] {
|
||||||
return completionResult?.options ?? [];
|
return completionResult?.options ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(tip, (newTip) => {
|
onBeforeUnmount(() => {
|
||||||
ndvStore.setHighlightDraggables(!ndvStore.isMappingOnboarded && newTip === 'drag');
|
ndvStore.setHighlightDraggables(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
tip,
|
||||||
|
(newTip) => {
|
||||||
|
ndvStore.setHighlightDraggables(!ndvStore.isMappingOnboarded && newTip === 'drag');
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
watchDebounced(
|
watchDebounced(
|
||||||
[() => props.selection, () => props.unresolvedExpression],
|
[() => props.selection, () => props.unresolvedExpression],
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -27,6 +27,7 @@ describe('InlineExpressionTip.vue', () => {
|
||||||
mockNdvState = {
|
mockNdvState = {
|
||||||
hasInputData: true,
|
hasInputData: true,
|
||||||
isNDVDataEmpty: vi.fn(() => true),
|
isNDVDataEmpty: vi.fn(() => true),
|
||||||
|
setHighlightDraggables: vi.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,11 +44,16 @@ describe('InlineExpressionTip.vue', () => {
|
||||||
hasInputData: true,
|
hasInputData: true,
|
||||||
isNDVDataEmpty: vi.fn(() => false),
|
isNDVDataEmpty: vi.fn(() => false),
|
||||||
focusedMappableInput: 'Some Input',
|
focusedMappableInput: 'Some Input',
|
||||||
|
setHighlightDraggables: vi.fn(),
|
||||||
};
|
};
|
||||||
const { container } = renderComponent(InlineExpressionTip, {
|
const { container, unmount } = renderComponent(InlineExpressionTip, {
|
||||||
pinia: createTestingPinia(),
|
pinia: createTestingPinia(),
|
||||||
});
|
});
|
||||||
|
expect(mockNdvState.setHighlightDraggables).toHaveBeenCalledWith(true);
|
||||||
expect(container).toHaveTextContent('Tip: Drag aninput fieldfrom the left to use it here.');
|
expect(container).toHaveTextContent('Tip: Drag aninput fieldfrom the left to use it here.');
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
expect(mockNdvState.setHighlightDraggables).toHaveBeenCalledWith(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,6 +64,7 @@ describe('InlineExpressionTip.vue', () => {
|
||||||
isInputParentOfActiveNode: true,
|
isInputParentOfActiveNode: true,
|
||||||
isNDVDataEmpty: vi.fn(() => false),
|
isNDVDataEmpty: vi.fn(() => false),
|
||||||
focusedMappableInput: 'Some Input',
|
focusedMappableInput: 'Some Input',
|
||||||
|
setHighlightDraggables: vi.fn(),
|
||||||
};
|
};
|
||||||
const { container } = renderComponent(InlineExpressionTip, {
|
const { container } = renderComponent(InlineExpressionTip, {
|
||||||
pinia: createTestingPinia(),
|
pinia: createTestingPinia(),
|
||||||
|
|
|
@ -510,6 +510,28 @@ const isCodeNode = computed(
|
||||||
|
|
||||||
const isHtmlNode = computed(() => !!node.value && node.value.type === HTML_NODE_TYPE);
|
const isHtmlNode = computed(() => !!node.value && node.value.type === HTML_NODE_TYPE);
|
||||||
|
|
||||||
|
const isInputTypeString = computed(() => props.parameter.type === 'string');
|
||||||
|
const isInputTypeNumber = computed(() => props.parameter.type === 'number');
|
||||||
|
|
||||||
|
const isInputDataEmpty = computed(() => ndvStore.isNDVDataEmpty('input'));
|
||||||
|
const isDropDisabled = computed(
|
||||||
|
() =>
|
||||||
|
props.parameter.noDataExpression ||
|
||||||
|
props.isReadOnly ||
|
||||||
|
isResourceLocatorParameter.value ||
|
||||||
|
isModelValueExpression.value,
|
||||||
|
);
|
||||||
|
const showDragnDropTip = computed(
|
||||||
|
() =>
|
||||||
|
isFocused.value &&
|
||||||
|
(isInputTypeString.value || isInputTypeNumber.value) &&
|
||||||
|
!isModelValueExpression.value &&
|
||||||
|
!isDropDisabled.value &&
|
||||||
|
(!ndvStore.hasInputData || !isInputDataEmpty.value) &&
|
||||||
|
!ndvStore.isMappingOnboarded &&
|
||||||
|
ndvStore.isInputParentOfActiveNode,
|
||||||
|
);
|
||||||
|
|
||||||
function isRemoteParameterOption(option: INodePropertyOptions) {
|
function isRemoteParameterOption(option: INodePropertyOptions) {
|
||||||
return remoteParameterOptionsKeys.value.includes(option.name);
|
return remoteParameterOptionsKeys.value.includes(option.name);
|
||||||
}
|
}
|
||||||
|
@ -965,7 +987,11 @@ onUpdated(async () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="wrapper" :class="parameterInputClasses" @keydown.stop>
|
<div
|
||||||
|
ref="wrapper"
|
||||||
|
:class="[parameterInputClasses, { [$style.tipVisible]: showDragnDropTip }]"
|
||||||
|
@keydown.stop
|
||||||
|
>
|
||||||
<ExpressionEditModal
|
<ExpressionEditModal
|
||||||
:dialog-visible="expressionEditDialogVisible"
|
:dialog-visible="expressionEditDialogVisible"
|
||||||
:model-value="modelValueExpressionEdit"
|
:model-value="modelValueExpressionEdit"
|
||||||
|
@ -1447,6 +1473,9 @@ onUpdated(async () => {
|
||||||
:disabled="isReadOnly"
|
:disabled="isReadOnly"
|
||||||
@update:model-value="valueChanged"
|
@update:model-value="valueChanged"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="showDragnDropTip" :class="$style.tip">
|
||||||
|
<InlineExpressionTip />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ParameterIssues
|
<ParameterIssues
|
||||||
|
@ -1477,6 +1506,7 @@ onUpdated(async () => {
|
||||||
|
|
||||||
.parameter-input {
|
.parameter-input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
:deep(.color-input) {
|
:deep(.color-input) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1609,3 +1639,23 @@ onUpdated(async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.tipVisible {
|
||||||
|
--input-border-bottom-left-radius: 0;
|
||||||
|
--input-border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 100%;
|
||||||
|
background: var(--color-code-background);
|
||||||
|
border: var(--border-base);
|
||||||
|
border-top: none;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 2px 6px 0 rgba(#441c17, 0.1);
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -13,7 +13,6 @@ import { hasExpressionMapping, hasOnlyListMode, isValueExpression } from '@/util
|
||||||
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
import { isResourceLocatorValue } from '@/utils/typeGuards';
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
import type { INodeProperties, IParameterLabel, NodeParameterValueType } from 'n8n-workflow';
|
import type { INodeProperties, IParameterLabel, NodeParameterValueType } from 'n8n-workflow';
|
||||||
import InlineExpressionTip from './InlineExpressionEditor/InlineExpressionTip.vue';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
parameter: INodeProperties;
|
parameter: INodeProperties;
|
||||||
|
@ -57,8 +56,7 @@ const ndvStore = useNDVStore();
|
||||||
|
|
||||||
const node = computed(() => ndvStore.activeNode);
|
const node = computed(() => ndvStore.activeNode);
|
||||||
const hint = computed(() => i18n.nodeText().hint(props.parameter, props.path));
|
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(
|
const isResourceLocator = computed(
|
||||||
() => props.parameter.type === 'resourceLocator' || props.parameter.type === 'workflowSelector',
|
() => props.parameter.type === 'resourceLocator' || props.parameter.type === 'workflowSelector',
|
||||||
);
|
);
|
||||||
|
@ -73,17 +71,6 @@ const isExpression = computed(() => isValueExpression(props.parameter, props.val
|
||||||
const showExpressionSelector = computed(() =>
|
const showExpressionSelector = computed(() =>
|
||||||
isResourceLocator.value ? !hasOnlyListMode(props.parameter) : true,
|
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() {
|
function onFocus() {
|
||||||
focused.value = true;
|
focused.value = true;
|
||||||
|
@ -205,7 +192,7 @@ function onDrop(newParamValue: string) {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n8n-input-label
|
<n8n-input-label
|
||||||
:class="[$style.wrapper, { [$style.tipVisible]: showDragnDropTip }]"
|
:class="[$style.wrapper]"
|
||||||
:label="hideLabel ? '' : i18n.nodeText().inputLabelDisplayName(parameter, path)"
|
:label="hideLabel ? '' : i18n.nodeText().inputLabelDisplayName(parameter, path)"
|
||||||
:tooltip-text="hideLabel ? '' : i18n.nodeText().inputLabelDescription(parameter, path)"
|
:tooltip-text="hideLabel ? '' : i18n.nodeText().inputLabelDescription(parameter, path)"
|
||||||
:show-tooltip="focused"
|
:show-tooltip="focused"
|
||||||
|
@ -258,9 +245,6 @@ function onDrop(newParamValue: string) {
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</DraggableTarget>
|
</DraggableTarget>
|
||||||
<div v-if="showDragnDropTip" :class="$style.tip">
|
|
||||||
<InlineExpressionTip />
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
[$style.options]: true,
|
[$style.options]: true,
|
||||||
|
@ -292,24 +276,6 @@ function onDrop(newParamValue: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tipVisible {
|
|
||||||
--input-border-bottom-left-radius: 0;
|
|
||||||
--input-border-bottom-right-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
top: 100%;
|
|
||||||
background: var(--color-code-background);
|
|
||||||
border: var(--border-base);
|
|
||||||
border-top: none;
|
|
||||||
width: 100%;
|
|
||||||
box-shadow: 0 2px 6px 0 rgba(#441c17, 0.1);
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -22px;
|
bottom: -22px;
|
||||||
|
|
|
@ -54,6 +54,7 @@ describe('ParameterInput.vue', () => {
|
||||||
type: 'test',
|
type: 'test',
|
||||||
typeVersion: 1,
|
typeVersion: 1,
|
||||||
},
|
},
|
||||||
|
isNDVDataEmpty: vi.fn(() => false),
|
||||||
};
|
};
|
||||||
mockNodeTypesState = {
|
mockNodeTypesState = {
|
||||||
allNodeTypes: [],
|
allNodeTypes: [],
|
||||||
|
|
Loading…
Reference in a new issue