feat(editor): Add drag and drop data mapping (#3708)
* commit package lock * refactor param options out * use action toggle * handle click on toggle * update color toggle * fix toggle * show options * update expression color * update pointer * fix readonly * fix readonly * fix expression spacing * refactor input label * show icon for headers * center icon * fix multi params * add credential options * increase spacing * update expression view * update transition * update el padding * rename side to options * fix label overflow * fix bug with unnessary lines * add overlay * fix bug affecting other pages * clean up spacing * rename * update icon size * fix toggle in users * clean up func * clean up css * use css var * fix overlay bug * clean up input * clean up input * clean up unnessary css * revert * update quotes * rename method * remove console errors * refactor data table * add drag button * make hoverable cells * add drag hint * disabel for output panel * add drag * disable for readonly * Add dragging * add draggable pill * add mapping targets * remove font color * Transferable * fix linting issue * teleport component * fix line * disable for readonly * fix position of data pill * fix position of data pill * ignore import * add droppable state * remove draggable key * update bg color * add value drop * use direct input * remove transition * add animation * shorten name * handle empty value * fix switch bug * fix up animation * add notification * add hint * add tooltip * show draggable hintm * fix multiple expre * fix hoverable * keep options on focus * increase timeouts * fix bug in set node * add transition on hover out * fix tooltip onboarding bug * only update expression if changes * add open delay * fix header highlight issue * update text * dont show tooltip always * update docs url * update ee border * add sticky behav * hide error highlight if dropping * switch out grip icon * increase timeout * add delay * show hint on execprev * add telemetry event * add telemetry event * add telemetry event * fire event on hint showing * fix telemetry event * add path * fix drag hint issue * decrease bottom margin * update mapping keys * remove file * hide overflow * sort params * add space * prevent scrolling * remove dropshadow * force cursor * address some comments * add thead tbody * add size opt
This commit is contained in:
@@ -7,7 +7,11 @@ export default {
|
||||
argTypes: {
|
||||
placement: {
|
||||
type: 'select',
|
||||
options: ['top', 'bottom'],
|
||||
options: ['top', 'top-start', 'top-end', 'bottom', 'bottom-end'],
|
||||
},
|
||||
size: {
|
||||
type: 'select',
|
||||
options: ['mini', 'small', 'medium'],
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<span :class="$style.container">
|
||||
<el-dropdown :placement="placement" trigger="click" @command="onCommand">
|
||||
<span :class="$style.button">
|
||||
<el-dropdown :placement="placement" :size="size" trigger="click" @command="onCommand" @visible-change="onVisibleChange">
|
||||
<span :class="{[$style.button]: true, [$style[theme]]: !!theme}">
|
||||
<component :is="$options.components.N8nIcon"
|
||||
icon="ellipsis-v"
|
||||
:size="iconSize"
|
||||
/>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
@@ -11,6 +12,7 @@
|
||||
v-for="action in actions"
|
||||
:key="action.value"
|
||||
:command="action.value"
|
||||
:disabled="action.disabled"
|
||||
>
|
||||
{{action.label}}
|
||||
</el-dropdown-item>
|
||||
@@ -42,12 +44,30 @@ export default {
|
||||
type: String,
|
||||
default: 'bottom',
|
||||
validator: (value: string): boolean =>
|
||||
['top', 'bottom'].includes(value),
|
||||
['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'].includes(value),
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
validator: (value: string): boolean =>
|
||||
['mini', 'small', 'medium'].includes(value),
|
||||
},
|
||||
iconSize: {
|
||||
type: String,
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
validator: (value: string): boolean =>
|
||||
['default', 'dark'].includes(value),
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onCommand(value: string) {
|
||||
this.$emit('action', value) ;
|
||||
this.$emit('action', value);
|
||||
},
|
||||
onVisibleChange(value: boolean) {
|
||||
this.$emit('visible-change', value);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -62,6 +82,18 @@ export default {
|
||||
cursor: pointer;
|
||||
padding: var(--spacing-4xs);
|
||||
border-radius: var(--border-radius-base);
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
color: var(--color-text-dark);
|
||||
|
||||
&:focus {
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<template functional>
|
||||
<div :class="{[$style.inputLabelContainer]: !props.labelHoverableOnly}">
|
||||
<div :class="$options.methods.getLabelClass(props, $style)">
|
||||
<component v-if="props.label" :is="$options.components.N8nText" :bold="props.bold" :size="props.size" :compact="!props.underline">
|
||||
{{ props.label }}
|
||||
<component :is="$options.components.N8nText" color="primary" :bold="props.bold" :size="props.size" v-if="props.required">*</component>
|
||||
</component>
|
||||
<span :class="[$style.infoIcon, props.showTooltip ? $style.showIcon: $style.hiddenIcon]" v-if="props.tooltipText">
|
||||
<component :is="$options.components.N8nTooltip" placement="top" :popper-class="$style.tooltipPopper">
|
||||
<component :is="$options.components.N8nIcon" icon="question-circle" size="small" />
|
||||
<div slot="content" v-html="$options.methods.addTargetBlank(props.tooltipText)"></div>
|
||||
</component>
|
||||
<template>
|
||||
<div :class="$style.container">
|
||||
<div :class="{
|
||||
[this.$style.label]: !!this.label,
|
||||
[this.$style.underline]: this.underline,
|
||||
[this.$style[this.size]]: true,
|
||||
}">
|
||||
<div :class="$style.title" v-if="label">
|
||||
<n8n-text :bold="bold" :size="size" :compact="!underline">
|
||||
{{ label }}
|
||||
<n8n-text color="primary" :bold="bold" :size="size" v-if="required">*</n8n-text>
|
||||
</n8n-text>
|
||||
</div>
|
||||
<span :class="[$style.infoIcon, showTooltip ? $style.visible: $style.hidden]" v-if="tooltipText && label">
|
||||
<n8n-tooltip placement="top" :popper-class="$style.tooltipPopper">
|
||||
<n8n-icon icon="question-circle" size="small" />
|
||||
<div slot="content" v-html="addTargetBlank(tooltipText)"></div>
|
||||
</n8n-tooltip>
|
||||
</span>
|
||||
<div v-if="$slots.options && label" :class="{[$style.overlay]: true, [$style.visible]: showOptions}"><div></div></div>
|
||||
<div v-if="$slots.options" :class="{[$style.options]: true, [$style.visible]: showOptions}">
|
||||
<slot name="options"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -56,74 +66,104 @@ export default {
|
||||
showTooltip: {
|
||||
type: Boolean,
|
||||
},
|
||||
labelHoverableOnly: {
|
||||
showOptions: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addTargetBlank,
|
||||
getLabelClass(props: {label: string, size: string, underline: boolean}, $style: any) {
|
||||
if (!props.label) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const classes = [];
|
||||
if (props.underline) {
|
||||
classes.push($style[`label-${props.size}-underline`]);
|
||||
}
|
||||
else {
|
||||
classes.push($style[`label-${props.size}`]);
|
||||
}
|
||||
|
||||
if (props.labelHoverableOnly) {
|
||||
classes.push($style.inputLabel);
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.inputLabelContainer:hover {
|
||||
> div > .infoIcon {
|
||||
display: inline-block;
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container:hover,.inputLabel:hover {
|
||||
.infoIcon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.options {
|
||||
opacity: 1;
|
||||
transition: opacity 100ms ease-in; // transition on hover in
|
||||
}
|
||||
|
||||
.overlay {
|
||||
opacity: 1;
|
||||
transition: opacity 100ms ease-in; // transition on hover in
|
||||
}
|
||||
}
|
||||
|
||||
.inputLabel:hover {
|
||||
> .infoIcon {
|
||||
display: inline-block;
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
|
||||
> * {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.infoIcon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-text-light);
|
||||
padding-left: var(--spacing-4xs);
|
||||
background-color: var(--color-background-xlight);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.showIcon {
|
||||
display: inline-block;
|
||||
}
|
||||
.options {
|
||||
opacity: 0;
|
||||
background-color: var(--color-background-xlight);
|
||||
transition: opacity 250ms cubic-bezier(.98,-0.06,.49,-0.2); // transition on hover out
|
||||
|
||||
.hiddenIcon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.label {
|
||||
* {
|
||||
margin-right: var(--spacing-5xs);
|
||||
> * {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.label-small {
|
||||
composes: label;
|
||||
margin-bottom: var(--spacing-4xs);
|
||||
.overlay {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 250ms cubic-bezier(.98,-0.06,.49,-0.2); // transition on hover out
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
width: 60px;
|
||||
height: 19px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 0;
|
||||
|
||||
background: linear-gradient(270deg, var(--color-foreground-xlight) 72.19%, rgba(255, 255, 255, 0) 107.45%);
|
||||
}
|
||||
}
|
||||
|
||||
.label-medium {
|
||||
composes: label;
|
||||
.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
overflow-x: hidden;
|
||||
overflow-y: clip;
|
||||
}
|
||||
|
||||
.small {
|
||||
margin-bottom: var(--spacing-5xs);
|
||||
}
|
||||
|
||||
.medium {
|
||||
margin-bottom: var(--spacing-2xs);
|
||||
}
|
||||
|
||||
@@ -131,16 +171,6 @@ export default {
|
||||
border-bottom: var(--border-base);
|
||||
}
|
||||
|
||||
.label-small-underline {
|
||||
composes: label-small;
|
||||
composes: underline;
|
||||
}
|
||||
|
||||
.label-medium-underline {
|
||||
composes: label-medium;
|
||||
composes: underline;
|
||||
}
|
||||
|
||||
.tooltipPopper {
|
||||
max-width: 400px;
|
||||
|
||||
@@ -148,4 +178,5 @@ export default {
|
||||
margin-left: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<label role="radio" tabindex="-1" :class="$style.container" aria-checked="true">
|
||||
<label role="radio" tabindex="-1" :class="{[$style.container]: true, [$style.hoverable]: !this.disabled}" aria-checked="true">
|
||||
<input type="radio" tabindex="-1" autocomplete="off" :class="$style.input" :value="value">
|
||||
<div :class="{[$style.button]: true, [$style.active]: active}" @click="$emit('click')">{{ label }}</div>
|
||||
<div :class="{[$style.button]: true, [$style.active]: active, [$style[size]]: true, [$style.disabled]: disabled}" @click="$emit('click')">{{ label }}</div>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
@@ -21,6 +21,15 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'medium',
|
||||
validator: (value: string): boolean =>
|
||||
['small', 'medium'].includes(value),
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -30,11 +39,11 @@ export default {
|
||||
display: inline-block;
|
||||
outline: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.button:not(.active) {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.hoverable:hover {
|
||||
.button:not(.active) {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,16 +56,29 @@ export default {
|
||||
|
||||
.button {
|
||||
border-radius: 0;
|
||||
padding: 0 var(--spacing-xs);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 26px;
|
||||
font-size: var(--font-size-2xs);
|
||||
border-radius: var(--border-radius-base);
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-text-base);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.medium {
|
||||
height: 26px;
|
||||
font-size: var(--font-size-2xs);
|
||||
padding: 0 var(--spacing-xs);
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: var(--font-size-3xs);
|
||||
height: 15px;
|
||||
padding: 0 var(--spacing-4xs);
|
||||
}
|
||||
|
||||
.active {
|
||||
|
||||
@@ -6,6 +6,10 @@ export default {
|
||||
title: 'Atoms/RadioButtons',
|
||||
component: N8nRadioButtons,
|
||||
argTypes: {
|
||||
size: {
|
||||
type: 'select',
|
||||
options: ['small', 'medium'],
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
backgrounds: { default: '--color-background-xlight' },
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<div role="radiogroup" :class="$style.radioGroup">
|
||||
<div role="radiogroup" :class="{[$style.radioGroup]: true, [$style.disabled]: disabled}">
|
||||
<RadioButton
|
||||
v-for="option in options"
|
||||
:key="option.value"
|
||||
v-bind="option"
|
||||
:active="value === option.value"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
@click="(e) => onClick(option.value, e)"
|
||||
/>
|
||||
</div>
|
||||
@@ -21,12 +23,21 @@ export default {
|
||||
},
|
||||
options: {
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
components: {
|
||||
RadioButton,
|
||||
},
|
||||
methods: {
|
||||
onClick(value) {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
this.$emit('input', value);
|
||||
},
|
||||
},
|
||||
@@ -45,5 +56,9 @@ export default {
|
||||
border-radius: var(--border-radius-base);
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
v-if="!user.isOwner"
|
||||
placement="bottom"
|
||||
:actions="getActions(user)"
|
||||
theme="dark"
|
||||
@action="(action) => onUserAction(user, action)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@ import VariableTable from './VariableTable.vue';
|
||||
<Canvas>
|
||||
<Story name="font-size">
|
||||
{{
|
||||
template: `<sizes :variables="['--font-size-2xs','--font-size-xs','--font-size-s','--font-size-m','--font-size-l','--font-size-xl','--font-size-2xl']" attr="font-size" />`,
|
||||
template: `<sizes :variables="['--font-size-3xs', '--font-size-2xs','--font-size-xs','--font-size-s','--font-size-m','--font-size-l','--font-size-xl','--font-size-2xl']" attr="font-size" />`,
|
||||
components: {
|
||||
Sizes,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user