fix: fix design for popper components

This commit is contained in:
Alex Grozav 2023-06-21 14:10:41 +03:00
parent 73ffba931e
commit fd0896478a
123 changed files with 993 additions and 692 deletions

View file

@ -2,7 +2,7 @@ const { mergeConfig } = require('vite');
const { resolve } = require('path');
module.exports = {
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: [
'@storybook/addon-styling',
'@storybook/addon-links',

View file

@ -1,6 +1,6 @@
@use './fonts.scss';
@use '../src/css/base.scss'; // with (
@use '../src/css/base.scss'; // @TODO CHECK IF NEEDED with (
// $font-path: 'element-ui/lib/theme-chalk/fonts'
//);
@ -10,3 +10,8 @@
.multi-container > * {
margin-bottom: 10px;
}
#storybook-root > div:not([class]) > *,
#storybook-root > * {
margin: var(--spacing-5xs);
}

View file

@ -48,7 +48,7 @@ const TemplateForSlots: StoryFn = (args, { argTypes }) => ({
template: `<div style="position: relative; width: 100%; height: 300px;">
<n8n-alert v-bind="args">
<template #title>Title</template>
<template>Description</template>
Description
<template #aside><button>Button</button></template>
<template #icon>
<n8n-icon icon="grin-stars" size="xlarge" />

View file

@ -51,10 +51,10 @@ const template: StoryFn<Args> = (args, { argTypes }) => ({
template: `
<n8n-callout v-bind="args">
${args.default}
<template #actions v-if="actions">
<template #actions v-if="args.actions">
${args.actions}
</template>
<template #trailingContent v-if="trailingContent">
<template #trailingContent v-if="args.trailingContent">
${args.trailingContent}
</template>
</n8n-callout>

View file

@ -10,6 +10,7 @@ export default {
};
export const Default: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nCard,
@ -18,6 +19,7 @@ export const Default: StoryFn = (args, { argTypes }) => ({
});
export const Hoverable: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nCard,
@ -37,6 +39,7 @@ Hoverable.args = {
};
export const WithSlots: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nCard,
@ -45,21 +48,21 @@ export const WithSlots: StoryFn = (args, { argTypes }) => ({
N8nText,
},
template: `<n8n-card v-bind="args">
<template slot="prepend">
<template #prepend>
<n8n-icon icon="check" size="large" />
</template>
<template slot="header">
<template #header>
<strong>Card header</strong>
</template>
<n8n-text color="text-light" size="medium" class="mt-2xs mb-2xs">
This is the card body.
</n8n-text>
<template slot="footer">
<template #footer>
<n8n-text size="medium">
Card footer
</n8n-text>
</template>
<template slot="append">
<template #append>
<n8n-button>Click me</n8n-button>
</template>
</n8n-card>`,

View file

@ -14,7 +14,7 @@
<slot name="footer" />
</div>
</div>
<div :class="$style.actions" v-if="$slots.append">
<div v-if="$slots.append">
<slot name="append" />
</div>
</div>

View file

@ -14,6 +14,7 @@ const methods = {
};
const DefaultTemplate: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nCheckbox,

View file

@ -6,7 +6,7 @@
:disabled="disabled"
:indeterminate="indeterminate"
:modelValue="modelValue"
@change="onChange"
@update:modelValue="onUpdateModelValue"
>
<slot></slot>
<n8n-input-label
@ -57,8 +57,8 @@ export default defineComponent({
},
},
methods: {
onChange(event: Event) {
this.$emit('update:modelValue', event);
onUpdateModelValue(value: boolean) {
this.$emit('update:modelValue', value);
},
onLabelClick() {
const checkboxComponent = this.$refs.checkbox as ElCheckbox;

View file

@ -13,7 +13,7 @@ export default {
const methods = {
onSubmit: action('submit'),
onUpdateModelValue: action('update:modelValue'),
onChange: action('change'),
};
const Template: StoryFn = (args, { argTypes }) => ({
@ -22,8 +22,7 @@ const Template: StoryFn = (args, { argTypes }) => ({
components: {
N8nFormBox,
},
template:
'<n8n-form-box v-bind="args" @submit="onSubmit" @update:modelValue="onUpdateModelValue" />',
template: '<n8n-form-box v-bind="args" @submit="onSubmit" @change="onChange" />',
methods,
});

View file

@ -88,7 +88,7 @@ export default defineComponent({
},
methods: {
onUpdateModelValue(e: { name: string; value: string }) {
this.$emit('update:modelValue', e);
this.$emit('change', e);
},
onSubmit(e: { [key: string]: string }) {
this.$emit('submit', e);

View file

@ -20,7 +20,7 @@
:modelValue="modelValue"
:active-color="activeColor"
:inactive-color="inactiveColor"
@change="onUpdateModelValue"
@update:modelValue="onUpdateModelValue"
></el-switch>
</n8n-input-label>
<n8n-input-label

View file

@ -12,7 +12,7 @@ export default {
};
const methods = {
onUpdateModelValue: action('update:modelValue'),
onChange: action('change'),
onSubmit: action('submit'),
};
@ -22,8 +22,7 @@ const Template: StoryFn = (args, { argTypes }) => ({
components: {
N8nFormInputs,
},
template:
'<n8n-form-inputs v-bind="args" @submit="onSubmit" @update:modelValue="onUpdateModelValue" />',
template: '<n8n-form-inputs v-bind="args" @submit="onSubmit" @change="onChange" />',
methods,
});

View file

@ -27,7 +27,6 @@
:showValidationWarnings="showValidationWarnings"
@update:modelValue="(value) => onUpdateModelValue(input.name, value)"
@validate="(value) => onValidate(input.name, value)"
@change="(value) => onUpdateModelValue(input.name, value)"
@enter="onSubmit"
/>
</div>
@ -113,7 +112,7 @@ export default defineComponent({
...this.values,
[name]: value,
};
this.$emit('update:modelValue', { name, value });
this.$emit('change', { name, value });
},
onValidate(name: string, valid: boolean) {
this.validity = {

View file

@ -16,6 +16,7 @@ const methods = {
};
export const Default: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
components: {
N8nInfoAccordion,

View file

@ -43,8 +43,8 @@ import N8nText from '../N8nText';
import N8nIcon from '../N8nIcon';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import type { EventBus } from '@/utils';
import { createEventBus } from '@/utils';
import type { EventBus } from '../../utils';
import { createEventBus } from '../../utils';
export interface IAccordionItem {
id: string;

View file

@ -26,7 +26,7 @@
import { ElInput } from 'element-plus';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { uid } from '@/utils';
import { uid } from '../../utils';
type InputRef = InstanceType<typeof ElInput>;
@ -69,7 +69,7 @@ export default defineComponent({
},
rows: {
type: Number,
default: 3,
default: 2,
},
maxlength: {
type: Number,

View file

@ -180,26 +180,26 @@ export default defineComponent({
<style module lang="scss">
// Element menu-item overrides
:global(.el-menu-item),
:global(.el-submenu__title) {
:global(.el-sub-menu__title) {
--menu-font-color: var(--color-text-base);
--menu-item-active-background-color: var(--color-foreground-base);
--menu-item-active-font-color: var(--color-text-dark);
--menu-item-hover-fill: var(--color-foreground-base);
--menu-item-hover-font-color: var(--color-text-dark);
--menu-item-height: 35px;
--submenu-item-height: 27px;
--sub-menu-item-height: 27px;
}
.submenu {
background: none !important;
&.compact :global(.el-submenu__title) {
&.compact :global(.el-sub-menu__title) {
i {
display: none;
}
}
:global(.el-submenu__title) {
:global(.el-sub-menu__title) {
display: flex;
align-items: center;
border-radius: var(--border-radius-base) !important;
@ -221,7 +221,7 @@ export default defineComponent({
}
.menuItem {
height: var(--submenu-item-height) !important;
height: var(--sub-menu-item-height) !important;
min-width: auto !important;
margin: var(--spacing-2xs) 0 !important;
padding-left: var(--spacing-l) !important;
@ -248,7 +248,7 @@ export default defineComponent({
svg {
color: var(--color-text-dark) !important;
}
&:global(.el-submenu) {
&:global(.el-sub-menu) {
background-color: unset !important;
}
}
@ -256,7 +256,7 @@ export default defineComponent({
.active {
&,
& :global(.el-submenu__title) {
& :global(.el-sub-menu__title) {
background-color: var(--color-foreground-base);
border-radius: var(--border-radius-base);
.icon {

View file

@ -7,12 +7,27 @@ export default {
};
const Template: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
setup: () => {
const onUpdateCurrentPage = (currentPage: number) => {
args.currentPage = currentPage;
};
const onUpdatePageSize = (pageSize: number) => {
args.pageSize = pageSize;
};
return { onUpdateCurrentPage, onUpdatePageSize, args };
},
props: Object.keys(argTypes),
components: {
N8nPagination,
},
template: '<n8n-pagination v-bind="args" />',
template: `
<n8n-pagination
v-bind="args"
v-model:current-page="args.currentPage"
v-model:page-size="args.pageSize"
/>`,
});
export const Pagination: StoryFn = Template.bind({});

View file

@ -13,5 +13,9 @@ export default defineComponent({
</script>
<template>
<el-pagination background layout="prev, pager, next" v-bind="{ ...$props, ...$attrs }" />
<el-pagination
class="is-background"
layout="prev, pager, next"
v-bind="{ ...$props, ...$attrs }"
/>
</template>

View file

@ -9,13 +9,7 @@
}"
aria-checked="true"
>
<input
type="radio"
tabindex="-1"
autocomplete="off"
:class="$style.input"
:modelValue="value"
/>
<input type="radio" tabindex="-1" autocomplete="off" :class="$style.input" :value="value" />
<div
:class="{
[$style.button]: true,

View file

@ -196,12 +196,9 @@
@include mixins.e(main-wrapper) {
margin-bottom: 6px;
&::after {
content: '';
display: table;
clear: both;
}
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
}
@include mixins.e(btns) {

View file

@ -2,11 +2,15 @@
@use './common/var';
@use './popper';
@use 'element-plus/theme-chalk/src/dropdown-menu';
@include mixins.b(dropdown) {
display: inline-block;
position: relative;
color: var(--color-text-dark);
font-size: var.$font-size-base;
display: inline-flex;
line-height: 1;
vertical-align: top;
.el-button-group {
display: block;
@ -69,7 +73,6 @@
}
@include mixins.b(dropdown-menu) {
position: absolute;
top: 0;
left: 0;
z-index: 10;
@ -79,6 +82,8 @@
border: 1px solid var(--border-color-light);
border-radius: var(--border-radius-base);
box-shadow: var.$dropdown-menu-box-shadow;
position: relative;
list-style: none;
@include mixins.e(item) {
list-style: none;

View file

@ -80,5 +80,6 @@
// @use "./cascader-panel.scss";
// @use "./avatar.scss";
@use './drawer.scss';
@use './popper.scss';
// @use "./popconfirm.scss";
@use './utilities/index.scss';

View file

@ -245,12 +245,23 @@
min-width: 200px;
}
@include mixins.e(icon-arrow) {
right: 20px;
position: absolute;
top: 50%;
right: 20px;
margin-top: -7px;
margin-top: -6px;
transition: transform 0.3s;
margin-right: 0;
font-size: 12px;
width: 12px;
height: 12px;
svg {
width: 12px;
height: 12px;
position: relative;
display: block;
margin-top: -2px;
}
}
@include mixins.when(active) {
.el-sub-menu__title {

View file

@ -65,17 +65,29 @@
.btn-prev,
.btn-next {
background: center center no-repeat;
background-position: center center;
background-repeat: no-repeat;
background-size: 16px;
background-color: var.$pagination-background-color;
cursor: pointer;
margin: 0;
color: var.$pagination-button-color;
display: inline-flex;
align-items: center;
justify-content: center;
.el-icon {
display: block;
font-size: 12px;
width: 12px;
height: 12px;
font-weight: bold;
svg {
width: 12px;
height: 12px;
display: block;
pointer-events: none;
}
}
}
@ -220,7 +232,7 @@
border: 1px solid var(--color-foreground-base);
}
&.active {
&.is-active {
border: 1px solid var(--color-primary);
color: var(--color-primary);
}
@ -240,11 +252,13 @@
@include mixins.b(pager) {
user-select: none;
list-style: none;
display: inline-block;
vertical-align: top;
font-size: 0;
padding: 0;
margin: 0;
display: inline-flex;
align-items: center;
.more::before {
line-height: 30px;
@ -284,7 +298,7 @@
cursor: pointer;
}
&.active + li {
&.is-active + li {
border-left: 0;
}
@ -292,9 +306,231 @@
color: var.$pagination-hover-color;
}
&.active {
&.is-active {
color: var.$pagination-hover-color;
cursor: default;
}
}
}
//@mixin pagination-button {
// display: flex;
// justify-content: center;
// align-items: center;
// font-size: getCssVar('pagination-font-size');
// min-width: getCssVar('pagination-button-width');
// height: getCssVar('pagination-button-height');
// line-height: getCssVar('pagination-button-height');
// color: getCssVar('pagination-button-color');
// background: getCssVar('pagination-bg-color');
// padding: 0 4px;
// border: none;
// border-radius: getCssVar('pagination-border-radius');
// cursor: pointer;
// text-align: center;
// box-sizing: border-box;
//
// * {
// pointer-events: none;
// }
//
// &:focus {
// outline: none;
// }
//
// &:hover {
// color: getCssVar('pagination-hover-color');
// }
//
// &.is-active {
// color: getCssVar('pagination-hover-color');
// cursor: default;
// font-weight: bold;
//
// &.is-disabled {
// font-weight: bold;
// color: getCssVar('text-color', 'secondary');
// }
// }
//
// &:disabled,
// &.is-disabled {
// color: getCssVar('pagination-button-disabled-color');
// background-color: getCssVar('pagination-button-disabled-bg-color');
// cursor: not-allowed;
// }
//
// &:focus-visible {
// outline: 1px solid getCssVar('pagination-hover-color');
// outline-offset: -1px;
// }
//}
//
//@include b(pagination) {
// @include set-component-css-var('pagination', $pagination);
//
// white-space: nowrap;
// color: getCssVar('pagination-text-color');
// font-size: getCssVar('pagination-font-size');
// font-weight: normal;
// display: flex;
// align-items: center;
//
// .#{$namespace}-input__inner {
// text-align: center;
// -moz-appearance: textfield;
// }
//
// .#{$namespace}-select .#{$namespace}-input {
// width: 128px;
// }
//
// button {
// @include pagination-button;
// }
//
// .btn-prev,
// .btn-next {
// .#{$namespace}-icon {
// display: block;
// font-size: 12px;
// font-weight: bold;
// width: inherit;
// }
// }
//
// & > * {
// @include when(first) {
// margin-left: 0 !important;
// }
// @include when(last) {
// margin-right: 0 !important;
// }
// }
//
// .btn-prev {
// margin-left: getCssVar('pagination-item-gap');
// }
//
// @include e(sizes) {
// margin-left: getCssVar('pagination-item-gap');
// font-weight: normal;
// color: getCssVar('text-color', 'regular');
// }
//
// @include e(total) {
// margin-left: getCssVar('pagination-item-gap');
// font-weight: normal;
// color: getCssVar('text-color', 'regular');
//
// &[disabled='true'] {
// color: getCssVar('text-color', 'placeholder');
// }
// }
//
// @include e(jump) {
// display: flex;
// align-items: center;
// margin-left: getCssVar('pagination-item-gap');
// font-weight: normal;
// color: getCssVar('text-color', 'regular');
//
// &[disabled='true'] {
// color: getCssVar('text-color', 'placeholder');
// }
//
// @include e(goto) {
// margin-right: 8px;
// }
//
// @include e(editor) {
// text-align: center;
// box-sizing: border-box;
//
// &.#{$namespace}-input {
// width: 56px;
// }
//
// .#{$namespace}-input__inner::-webkit-inner-spin-button,
// .#{$namespace}-input__inner::-webkit-outer-spin-button {
// -webkit-appearance: none;
// margin: 0;
// }
// }
//
// @include e(classifier) {
// margin-left: 8px;
// }
// }
//
// @include e(rightwrapper) {
// flex: 1;
// display: flex;
// align-items: center;
// justify-content: flex-end;
// }
//
// @include when(background) {
// .btn-prev,
// .btn-next,
// .#{$namespace}-pager li {
// margin: 0 4px;
// background-color: getCssVar('pagination-button-bg-color');
//
// &.is-active {
// background-color: getCssVar('color-primary');
// color: getCssVar('color-white');
// }
//
// &:disabled,
// &.is-disabled {
// color: getCssVar('text-color', 'placeholder');
// background-color: getCssVar('disabled-bg-color');
//
// &.is-active {
// color: getCssVar('text-color', 'secondary');
// background-color: getCssVar('fill-color', 'dark');
// }
// }
// }
//
// .btn-prev {
// margin-left: getCssVar('pagination-item-gap');
// }
// }
//
// @include m(small) {
// .btn-prev,
// .btn-next,
// .#{$namespace}-pager li {
// height: getCssVar('pagination-button-height-small');
// line-height: getCssVar('pagination-button-height-small');
// font-size: getCssVar('pagination-font-size-small');
// min-width: getCssVar('pagination-button-width-small');
// }
//
// span:not([class*='suffix']),
// button {
// font-size: getCssVar('pagination-font-size-small');
// }
//
// .#{$namespace}-select .#{$namespace}-input {
// width: 100px;
// }
// }
//}
//
//@include b(pager) {
// user-select: none;
// list-style: none;
// font-size: 0;
// padding: 0;
// margin: 0;
// display: flex;
// align-items: center;
//
// li {
// @include pagination-button;
// }
//}

View file

@ -2,33 +2,55 @@
@use './common/var';
@include mixins.b(popper) {
.popper__arrow,
.popper__arrow::after {
position: absolute;
z-index: 2000;
min-width: 10px;
word-wrap: break-word;
visibility: visible;
.el-select-dropdown,
.el-dropdown-menu {
position: relative;
margin: 0;
}
&:not(.el-select__popper):not(.el-dropdown__popper) {
padding: var.$tooltip-padding;
font-size: var.$tooltip-font-size;
border-radius: 4px;
}
.el-popper__arrow {
position: absolute;
display: block;
width: 0;
height: 0;
z-index: 1010;
border-color: transparent;
border-style: solid;
}
.popper__arrow {
border-width: var.$popover-arrow-size;
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
}
.popper__arrow::after {
.el-popper__arrow::after {
display: block;
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
content: ' ';
border-width: var.$popover-arrow-size;
}
&[x-placement^='top'] {
margin-bottom: #{var.$popover-arrow-size + 6};
&[data-popper-placement^='top'] {
//margin-bottom: #{var.$popover-arrow-size + 6};
}
&[x-placement^='top'] .popper__arrow {
&[data-popper-placement^='top'] .el-popper__arrow {
bottom: -(var.$popover-arrow-size);
left: 50%;
margin-left: var.$popover-arrow-size;
margin-right: #{var.$tooltip-arrow-size * 0.5};
border-top-color: var.$popover-border-color;
border-bottom-width: 0;
@ -41,11 +63,11 @@
}
}
&[x-placement^='bottom'] {
margin-top: #{var.$popover-arrow-size + 6};
&[data-popper-placement^='bottom'] {
//margin-top: #{var.$popover-arrow-size + 6};
}
&[x-placement^='bottom'] .popper__arrow {
&[data-popper-placement^='bottom'] .el-popper__arrow {
top: -(var.$popover-arrow-size);
left: 50%;
margin-right: #{var.$tooltip-arrow-size * 0.5};
@ -60,11 +82,11 @@
}
}
&[x-placement^='right'] {
&[data-popper-placement^='right'] {
margin-left: #{var.$popover-arrow-size + 6};
}
&[x-placement^='right'] .popper__arrow {
&[data-popper-placement^='right'] .el-popper__arrow {
top: 50%;
left: -(var.$popover-arrow-size);
margin-bottom: #{var.$tooltip-arrow-size * 0.5};
@ -79,13 +101,13 @@
}
}
&[x-placement^='left'] {
&[data-popper-placement^='left'] {
margin-right: #{var.$popover-arrow-size + 6};
}
&[x-placement^='left'] .popper__arrow {
&[data-popper-placement^='left'] .el-popper__arrow {
top: 50%;
right: -(var.$popover-arrow-size);
//right: -(var.$popover-arrow-size);
margin-bottom: #{var.$tooltip-arrow-size * 0.5};
border-right-width: 0;
border-left-color: var.$popover-border-color;
@ -98,4 +120,42 @@
border-left-color: var.$popover-background-color;
}
}
@include mixins.when(dark) {
background: var.$tooltip-fill;
color: var.$tooltip-color;
&[data-popper-placement^='top'] .el-popper__arrow {
border-top-color: transparent;
&::after {
border-top-color: var.$tooltip-border-color;
}
}
&[data-popper-placement^='bottom'] .el-popper__arrow {
border-bottom-color: transparent;
&::after {
border-bottom-color: var.$tooltip-border-color;
}
}
&[data-popper-placement^='right'] .el-popper__arrow {
border-right-color: transparent;
&::after {
border-right-color: var.$tooltip-border-color;
}
}
&[data-popper-placement^='left'] .el-popper__arrow {
border-left-color: transparent;
&::after {
border-left-color: var.$tooltip-border-color;
}
}
}
}

View file

@ -23,8 +23,8 @@
font-weight: var(--font-weight-bold);
}
.popper__arrow,
.popper__arrow::after {
.el-popper__arrow,
.el-popper__arrow::after {
position: absolute;
display: block;
width: 0;
@ -33,20 +33,20 @@
border-style: solid;
}
.popper__arrow {
.el-popper__arrow {
border-width: var.$tooltip-arrow-size;
}
.popper__arrow::after {
.el-popper__arrow::after {
content: ' ';
border-width: 5px;
}
&[x-placement^='top'] {
&[data-popper-placement^='top'] {
margin-bottom: #{var.$tooltip-arrow-size + 6px};
}
&[x-placement^='top'] .popper__arrow {
&[data-popper-placement^='top'] .el-popper__arrow {
bottom: -(var.$tooltip-arrow-size);
border-top-color: var.$tooltip-border-color;
border-bottom-width: 0;
@ -59,11 +59,11 @@
}
}
&[x-placement^='bottom'] {
&[data-popper-placement^='bottom'] {
margin-top: #{var.$tooltip-arrow-size + 6px};
}
&[x-placement^='bottom'] .popper__arrow {
&[data-popper-placement^='bottom'] .el-popper__arrow {
top: -(var.$tooltip-arrow-size);
border-top-width: 0;
border-bottom-color: var.$tooltip-border-color;
@ -76,11 +76,11 @@
}
}
&[x-placement^='right'] {
&[data-popper-placement^='right'] {
margin-left: #{var.$tooltip-arrow-size + 6px};
}
&[x-placement^='right'] .popper__arrow {
&[data-popper-placement^='right'] .el-popper__arrow {
left: -(var.$tooltip-arrow-size);
border-right-color: var.$tooltip-border-color;
border-left-width: 0;
@ -93,11 +93,11 @@
}
}
&[x-placement^='left'] {
&[data-popper-placement^='left'] {
margin-right: #{var.$tooltip-arrow-size + 6px};
}
&[x-placement^='left'] .popper__arrow {
&[data-popper-placement^='left'] .el-popper__arrow {
right: -(var.$tooltip-arrow-size);
border-right-width: 0;
border-left-color: var.$tooltip-border-color;
@ -111,34 +111,34 @@
}
}
@include mixins.when(dark) {
@include mixins.when(is-dark) {
background: var.$tooltip-fill;
color: var.$tooltip-color;
}
@include mixins.when(light) {
@include mixins.when(is-light) {
background: var.$tooltip-color;
border: 1px solid var.$tooltip-fill;
&[x-placement^='top'] .popper__arrow {
&[data-popper-placement^='top'] .el-popper__arrow {
border-top-color: var.$tooltip-fill;
&::after {
border-top-color: var.$tooltip-color;
}
}
&[x-placement^='bottom'] .popper__arrow {
&[data-popper-placement^='bottom'] .el-popper__arrow {
border-bottom-color: var.$tooltip-fill;
&::after {
border-bottom-color: var.$tooltip-color;
}
}
&[x-placement^='left'] .popper__arrow {
&[data-popper-placement^='left'] .el-popper__arrow {
border-left-color: var.$tooltip-fill;
&::after {
border-left-color: var.$tooltip-color;
}
}
&[x-placement^='right'] .popper__arrow {
&[data-popper-placement^='right'] .el-popper__arrow {
border-right-color: var.$tooltip-fill;
&::after {
border-right-color: var.$tooltip-color;

View file

@ -17,7 +17,9 @@
</div>
<div id="content" :class="$style.content">
<keep-alive include="NodeView" :max="1">
<router-view />
<main>
<router-view />
</main>
</keep-alive>
</div>
<Modals />
@ -51,7 +53,7 @@ import {
} from '@/stores';
import { useHistoryHelper } from '@/composables/useHistoryHelper';
import { newVersions } from '@/mixins/newVersions';
import { useRoute } from 'vue-router/composables';
import { useRoute } from 'vue-router';
import { useExternalHooks } from '@/composables';
export default defineComponent({

View file

@ -1,6 +1,5 @@
import '@testing-library/jest-dom';
import { configure } from '@testing-library/vue';
import Vue from 'vue';
import '../plugins';
import { I18nPlugin } from '@/plugins/i18n';
import { config } from '@vue/test-utils';
@ -10,18 +9,17 @@ import { FontAwesomePlugin } from '@/plugins/icons';
configure({ testIdAttribute: 'data-test-id' });
Vue.config.productionTip = false;
Vue.config.devtools = false;
// Vue.config.productionTip = false;
// Vue.config.devtools = false;
Vue.use(I18nPlugin);
Vue.use(FontAwesomePlugin);
Vue.use(GlobalComponentsPlugin);
Vue.use(GlobalDirectivesPlugin);
config.plugins.VueWrapper.install(I18nPlugin);
config.plugins.VueWrapper.install(FontAwesomePlugin);
config.plugins.VueWrapper.install(GlobalComponentsPlugin);
config.plugins.VueWrapper.install(GlobalDirectivesPlugin);
// TODO: Investigate why this is needed
// Without having this 3rd party library imported like this, any component test using 'vue-json-pretty' fail with:
// [Vue warn]: Failed to mount component: template or render function not defined.
// Vue.component('vue-json-pretty', require('vue-json-pretty').default);
config.stubs['vue-json-pretty'] = require('vue-json-pretty').default;
window.ResizeObserver =

View file

@ -12,7 +12,7 @@
:inputs="config"
:eventBus="formBus"
:columnView="true"
@update:modelValue="onInput"
@change="onInput"
@submit="onSubmit"
/>
</template>

View file

@ -1,4 +1,4 @@
import Vue, { defineComponent } from 'vue';
import { defineComponent } from 'vue';
import { autocompletion } from '@codemirror/autocomplete';
import { localCompletionSource } from '@codemirror/lang-javascript';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
@ -16,11 +16,8 @@ import { itemFieldCompletions } from './completions/itemField.completions';
import { jsonFieldCompletions } from './completions/jsonField.completions';
import { variablesCompletions } from './completions/variables.completions';
import type { CodeNodeEditorMixin } from './types';
export const completerExtension = defineComponent({
mixins: [
Vue as CodeNodeEditorMixin,
baseCompletions,
requireCompletions,
executionCompletions,

View file

@ -1,9 +1,8 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import { NODE_TYPES_EXCLUDED_FROM_AUTOCOMPLETION } from '../constants';
import { addVarType } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { INodeUi } from '@/Interface';
import type { CodeNodeEditorMixin } from '../types';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows.store';
@ -13,7 +12,7 @@ function getAutoCompletableNodeNames(nodes: INodeUi[]) {
.map((node: INodeUi) => node.name);
}
export const baseCompletions = (Vue as CodeNodeEditorMixin).extend({
export const baseCompletions = defineComponent({
computed: {
...mapStores(useWorkflowsStore),
},

View file

@ -1,9 +1,8 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import { addVarType, escape } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
export const executionCompletions = (Vue as CodeNodeEditorMixin).extend({
export const executionCompletions = defineComponent({
methods: {
/**
* Complete `$execution.` to `.id .mode .resumeUrl`

View file

@ -1,9 +1,8 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import { addVarType, escape } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
export const itemFieldCompletions = (Vue as CodeNodeEditorMixin).extend({
export const itemFieldCompletions = defineComponent({
methods: {
/**
* - Complete `x.first().` to `.json .binary`

View file

@ -1,9 +1,8 @@
import Vue from 'vue';
import { escape } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import { defineComponent } from 'vue';
export const itemIndexCompletions = (Vue as CodeNodeEditorMixin).extend({
export const itemIndexCompletions = defineComponent({
methods: {
/**
* - Complete `$input.` to `.first() .last() .all() .itemMatching()` in all-items mode.

View file

@ -1,14 +1,13 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import { escape, toVariableOption } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { IDataObject, IPinData, IRunData } from 'n8n-workflow';
import type { CodeNodeEditorMixin } from '../types';
import { mapStores } from 'pinia';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useNDVStore } from '@/stores/ndv.store';
import { isAllowedInDotNotation } from '@/plugins/codemirror/completions/utils';
export const jsonFieldCompletions = (Vue as CodeNodeEditorMixin).extend({
export const jsonFieldCompletions = defineComponent({
computed: {
...mapStores(useNDVStore, useWorkflowsStore),
},

View file

@ -1,9 +1,8 @@
import Vue from 'vue';
import { escape } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import { defineComponent } from 'vue';
export const luxonCompletions = (Vue as CodeNodeEditorMixin).extend({
export const luxonCompletions = defineComponent({
methods: {
/**
* Complete `$today.` with luxon `DateTime` instance methods.

View file

@ -1,13 +1,12 @@
import Vue from 'vue';
import { addVarType } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import { defineComponent } from 'vue';
const DEFAULT_MATCHER = '$prevNode';
const escape = (str: string) => str.replace('$', '\\$');
export const prevNodeCompletions = (Vue as CodeNodeEditorMixin).extend({
export const prevNodeCompletions = defineComponent({
methods: {
/**
* Complete `$prevNode.` to `.name .outputIndex .runIndex`.

View file

@ -1,10 +1,9 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import { AUTOCOMPLETABLE_BUILT_IN_MODULES_JS } from '../constants';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import { useSettingsStore } from '@/stores/settings.store';
export const requireCompletions = (Vue as CodeNodeEditorMixin).extend({
export const requireCompletions = defineComponent({
methods: {
/**
* Complete `req` to `require('moduleName')` based on modules available in context.

View file

@ -1,12 +1,11 @@
import Vue from 'vue';
import { addVarType } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
import { useEnvironmentsStore } from '@/stores';
import { defineComponent } from 'vue';
const escape = (str: string) => str.replace('$', '\\$');
export const variablesCompletions = (Vue as CodeNodeEditorMixin).extend({
export const variablesCompletions = defineComponent({
methods: {
/**
* Complete `$workflow.` to `.id .name .active`.

View file

@ -1,11 +1,10 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import { addVarType } from '../utils';
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
import type { CodeNodeEditorMixin } from '../types';
const escape = (str: string) => str.replace('$', '\\$');
export const workflowCompletions = (Vue as CodeNodeEditorMixin).extend({
export const workflowCompletions = defineComponent({
methods: {
/**
* Complete `$workflow.` to `.id .name .active`.

View file

@ -1,4 +1,4 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import type { Diagnostic } from '@codemirror/lint';
import { linter as createLinter } from '@codemirror/lint';
import { jsonParseLinter } from '@codemirror/lang-json';
@ -10,9 +10,9 @@ import type { CodeNodeEditorLanguage } from 'n8n-workflow';
import { DEFAULT_LINTER_DELAY_IN_MS, DEFAULT_LINTER_SEVERITY } from './constants';
import { OFFSET_FOR_SCRIPT_WRAPPER } from './constants';
import { walk } from './utils';
import type { CodeNodeEditorMixin, RangeNode } from './types';
import type { RangeNode } from './types';
export const linterExtension = (Vue as CodeNodeEditorMixin).extend({
export const linterExtension = defineComponent({
methods: {
createLinter(language: CodeNodeEditorLanguage) {
switch (language) {

View file

@ -2,9 +2,10 @@ import type { EditorView } from '@codemirror/view';
import type { I18nClass } from '@/plugins/i18n';
import type { Workflow, CodeExecutionMode, CodeNodeEditorLanguage } from 'n8n-workflow';
import type { Node } from 'estree';
import type { DefineComponent } from 'vue';
export type CodeNodeEditorMixin = Vue.VueConstructor<
Vue & {
export type CodeNodeEditorMixin = InstanceType<
DefineComponent & {
$locale: I18nClass;
editor: EditorView | null;
mode: CodeExecutionMode;

View file

@ -15,9 +15,7 @@
v-if="mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE"
>
<n8n-info-tip theme="info" type="note" :bold="false">
<template>
<span v-text="getModalContent.description"></span>
</template>
<span v-text="getModalContent.description"></span>
</n8n-info-tip>
</div>
</template>

View file

@ -15,11 +15,11 @@
<CredentialIcon :credentialTypeName="defaultCredentialTypeName" />
</div>
<InlineNameEdit
:name="credentialName"
:modelValue="credentialName"
:subtitle="credentialType ? credentialType.displayName : ''"
:readonly="!credentialPermissions.updateName || !credentialType"
type="Credential"
@input="onNameEdit"
@update:modelValue="onNameEdit"
data-test-id="credential-name"
/>
</div>

View file

@ -1,7 +1,6 @@
<script lang="ts" setup>
import { computed, reactive, onBeforeMount, ref } from 'vue';
import debounce from 'lodash/debounce';
import type { PopoverPlacement } from 'element-ui/types/popover';
import type {
ExecutionFilterType,
ExecutionFilterMetadata,
@ -15,10 +14,11 @@ import { useSettingsStore } from '@/stores/settings.store';
import { useUsageStore } from '@/stores/usage.store';
import { useUIStore } from '@/stores/ui.store';
import { useTelemetry } from '@/composables';
import type { Placement } from '@floating-ui/core';
export type ExecutionFilterProps = {
workflows?: IWorkflowShortResponse[];
popoverPlacement?: PopoverPlacement;
popoverPlacement?: Placement;
};
const DATE_TIME_MASK = 'yyyy-MM-dd HH:mm';
@ -30,7 +30,7 @@ const uiStore = useUIStore();
const telemetry = useTelemetry();
const props = withDefaults(defineProps<ExecutionFilterProps>(), {
popoverPlacement: 'bottom',
popoverPlacement: 'bottom' as Placement,
});
const emit = defineEmits<{
(event: 'filterChanged', value: ExecutionFilterType): void;
@ -244,7 +244,7 @@ onBeforeMount(() => {
<div :class="$style.group">
<n8n-tooltip placement="right">
<template #content>
<i18n tag="span" path="executionsFilter.customData.docsTooltip">
<i18n-t tag="span" path="executionsFilter.customData.docsTooltip">
<template #link>
<a
target="_blank"
@ -253,7 +253,7 @@ onBeforeMount(() => {
{{ locale.baseText('executionsFilter.customData.docsTooltip.link') }}
</a>
</template>
</i18n>
</i18n-t>
</template>
<span :class="$style.label">
{{ locale.baseText('executionsFilter.savedData') }}
@ -266,7 +266,7 @@ onBeforeMount(() => {
}}</label>
<n8n-tooltip :disabled="isAdvancedExecutionFilterEnabled" placement="top">
<template #content>
<i18n tag="span" path="executionsFilter.customData.inputTooltip">
<i18n-t tag="span" path="executionsFilter.customData.inputTooltip">
<template #link>
<a
href="#"
@ -275,7 +275,7 @@ onBeforeMount(() => {
>{{ locale.baseText('executionsFilter.customData.inputTooltip.link') }}</a
>
</template>
</i18n>
</i18n-t>
</template>
<n8n-input
id="execution-filter-saved-data-key"
@ -284,8 +284,8 @@ onBeforeMount(() => {
size="medium"
:disabled="!isAdvancedExecutionFilterEnabled"
:placeholder="locale.baseText('executionsFilter.savedDataKeyPlaceholder')"
:value="filter.metadata[0]?.key"
@input="onFilterMetaChange(0, 'key', $event)"
:modelValue="filter.metadata[0]?.key"
@update:modelValue="onFilterMetaChange(0, 'key', $event)"
data-test-id="execution-filter-saved-data-key-input"
/>
</n8n-tooltip>
@ -294,13 +294,13 @@ onBeforeMount(() => {
}}</label>
<n8n-tooltip :disabled="isAdvancedExecutionFilterEnabled" placement="top">
<template #content>
<i18n tag="span" path="executionsFilter.customData.inputTooltip">
<i18n-t tag="span" path="executionsFilter.customData.inputTooltip">
<template #link>
<a href="#" @click.prevent="goToUpgrade">{{
locale.baseText('executionsFilter.customData.inputTooltip.link')
}}</a>
</template>
</i18n>
</i18n-t>
</template>
<n8n-input
id="execution-filter-saved-data-value"

View file

@ -93,7 +93,7 @@
<span v-if="isRunning(execution)" :class="$style.spinner">
<font-awesome-icon icon="spinner" spin />
</span>
<i18n
<i18n-t
v-if="!isWaitTillIndefinite(execution)"
:path="getStatusTextTranslationPath(execution)"
>
@ -115,7 +115,7 @@
</span>
<execution-time v-else :start-time="execution.startedAt" />
</template>
</i18n>
</i18n-t>
<n8n-tooltip v-else placement="top">
<template #content>
<span>{{ getStatusTooltipText(execution) }}</span>

View file

@ -6,7 +6,7 @@
</n8n-text>
</div>
<div v-else-if="!isTrialExpired && trialHasExecutionsLeft" :class="$style.usageText">
<i18n path="executionUsage.currentUsage">
<i18n-t path="executionUsage.currentUsage">
<template #text>
<n8n-text size="small" color="text-dark">
{{ locale.baseText('executionUsage.currentUsage.text') }}
@ -21,7 +21,7 @@
}}
</n8n-text>
</template>
</i18n>
</i18n-t>
</div>
<div v-else-if="!trialHasExecutionsLeft" :class="$style.usageText">
<n8n-text size="small">

View file

@ -135,7 +135,7 @@ import type { IExecutionUIData } from '@/mixins/executionsHelpers';
import { executionHelpers } from '@/mixins/executionsHelpers';
import { MODAL_CONFIRM, VIEWS } from '@/constants';
import { useUIStore } from '@/stores/ui.store';
import { Dropdown as ElDropdown } from 'element-ui';
import { ElDropdown } from 'element-plus';
type RetryDropdownRef = InstanceType<typeof ElDropdown> & { hide: () => void };

View file

@ -11,8 +11,8 @@
</div>
<div :class="$style.controls">
<el-checkbox
:value="autoRefresh"
@input="$emit('update:autoRefresh', $event)"
:modelValue="autoRefresh"
@update:modelValue="$emit('update:autoRefresh', $event)"
data-test-id="auto-refresh-checkbox"
>
{{ $locale.baseText('executionsList.autoRefresh') }}

View file

@ -1,8 +1,8 @@
<template>
<ExpandableInputBase :value="value" :placeholder="placeholder">
<ExpandableInputBase :value="modelValue" :placeholder="placeholder">
<input
class="el-input__inner"
:value="value"
:value="modelValue"
:placeholder="placeholder"
:maxlength="maxlength"
@input="onInput"
@ -25,7 +25,7 @@ export default defineComponent({
name: 'ExpandableInputEdit',
components: { ExpandableInputBase },
props: {
value: {},
modelValue: {},
placeholder: {},
maxlength: {},
autofocus: {},
@ -50,7 +50,7 @@ export default defineComponent({
}
},
onInput() {
this.$emit('input', (this.$refs.input as HTMLInputElement).value);
this.$emit('update:modelValue', (this.$refs.input as HTMLInputElement).value);
},
onEnter() {
this.$emit('enter', (this.$refs.input as HTMLInputElement).value);

View file

@ -2,7 +2,7 @@
<div v-if="dialogVisible" @keydown.stop>
<el-dialog
:visible="dialogVisible"
custom-class="expression-dialog classic"
class="expression-dialog classic"
append-to-body
width="80%"
:title="$locale.baseText('expressionEdit.editExpression')"
@ -44,7 +44,7 @@
</div>
<div class="expression-editor ph-no-capture">
<ExpressionEditorModalInput
:value="value"
:modelValue="modelValue"
:isReadOnly="isReadOnly"
:path="path"
@change="valueChanged"
@ -97,7 +97,7 @@ import type { Segment } from '@/types/expressions';
export default defineComponent({
name: 'ExpressionEdit',
mixins: [externalHooks, genericHelpers, debounceHelper],
props: ['dialogVisible', 'parameter', 'path', 'value', 'eventSource'],
props: ['dialogVisible', 'parameter', 'path', 'modelValue', 'eventSource'],
components: {
ExpressionEditorModalInput,
ExpressionEditorModalOutput,
@ -121,7 +121,7 @@ export default defineComponent({
if (forceUpdate === true) {
this.updateDisplayValue();
this.$emit('valueChanged', this.latestValue);
this.$emit('update:modelValue', this.latestValue);
} else {
void this.callDebounced('updateDisplayValue', { debounceTime: 500 });
}
@ -132,10 +132,10 @@ export default defineComponent({
},
closeDialog() {
if (this.latestValue !== this.value) {
if (this.latestValue !== this.modelValue) {
// Handle the close externally as the visible parameter is an external prop
// and is so not allowed to be changed here.
this.$emit('valueChanged', this.latestValue);
this.$emit('update:modelValue', this.latestValue);
}
this.$emit('closeDialog');
return false;
@ -146,7 +146,7 @@ export default defineComponent({
(this.$refs.inputFieldExpression as any).itemSelected(eventData);
void this.$externalHooks().run('expressionEdit.itemSelected', {
parameter: this.parameter,
value: this.value,
value: this.modelValue,
selectedItem: eventData,
});
@ -216,8 +216,8 @@ export default defineComponent({
},
watch: {
dialogVisible(newValue) {
this.displayValue = this.value;
this.latestValue = this.value;
this.displayValue = this.modelValue;
this.latestValue = this.modelValue;
const resolvedExpressionValue =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -226,14 +226,14 @@ export default defineComponent({
void this.$externalHooks().run('expressionEdit.dialogVisibleChanged', {
dialogVisible: newValue,
parameter: this.parameter,
value: this.value,
value: this.modelValue,
resolvedExpressionValue,
});
if (!newValue) {
const telemetryPayload = createExpressionTelemetryPayload(
this.segments,
this.value,
this.modelValue,
this.workflowsStore.workflowId,
this.ndvStore.sessionId,
this.ndvStore.activeNode?.type ?? '',
@ -288,7 +288,7 @@ export default defineComponent({
margin-top: 1em;
}
::v-deep .expression-dialog {
:deep(.expression-dialog) {
.el-dialog__header {
padding: 0;
}

View file

@ -24,7 +24,7 @@ export default defineComponent({
name: 'ExpressionEditorModalInput',
mixins: [expressionManager, completionManager, workflowHelpers],
props: {
value: {
modelValue: {
type: String,
},
path: {
@ -89,7 +89,7 @@ export default defineComponent({
this.editor = new EditorView({
parent: this.$refs.root as HTMLDivElement,
state: EditorState.create({
doc: this.value.startsWith('=') ? this.value.slice(1) : this.value,
doc: this.modelValue.startsWith('=') ? this.modelValue.slice(1) : this.modelValue,
extensions,
}),
});

View file

@ -11,7 +11,7 @@
<ExpressionFunctionIcon />
</div>
<InlineExpressionEditorInput
:value="value"
:modelValue="modelValue"
:isReadOnly="isReadOnly"
:targetItem="hoveringItem"
:isSingleLine="isForRecordLocator"
@ -37,7 +37,7 @@
</n8n-text>
<n8n-text :class="$style.body">
<InlineExpressionEditorOutput
:value="value"
:value="modelValue"
:isReadOnly="isReadOnly"
:segments="segments"
/>
@ -99,7 +99,7 @@ export default defineComponent({
path: {
type: String,
},
value: {
modelValue: {
type: String,
},
isReadOnly: {
@ -158,7 +158,7 @@ export default defineComponent({
if (wasFocused) {
const telemetryPayload = createExpressionTelemetryPayload(
this.segments,
this.value,
this.modelValue,
this.workflowsStore.workflowId,
this.ndvStore.sessionId,
this.ndvStore.activeNode?.type ?? '',
@ -172,9 +172,9 @@ export default defineComponent({
this.segments = segments;
if (value === '=' + this.value) return; // prevent report on change of target item
if (value === '=' + this.modelValue) return; // prevent report on change of target item
this.$emit('valueChanged', value);
this.$emit('update:modelValue', value);
},
},
});

View file

@ -337,11 +337,9 @@ export default defineComponent({
</script>
<style scoped lang="scss">
::v-deep {
.button {
--button-background-color: var(--color-background-base);
--button-border-color: var(--color-foreground-base);
}
:deep (.button) {
--button-background-color: var(--color-background-base);
--button-border-color: var(--color-foreground-base);
}
.fixed-collection-parameter {

View file

@ -25,7 +25,7 @@ export default defineComponent({
name: 'InlineExpressionEditorInput',
mixins: [completionManager, expressionManager, workflowHelpers],
props: {
value: {
modelValue: {
type: String,
},
isReadOnly: {
@ -46,7 +46,7 @@ export default defineComponent({
effects: editableConf.reconfigure(EditorView.editable.of(!newValue)),
});
},
value(newValue) {
modelValue(newValue) {
const isInternalChange = newValue === this.editor?.state.doc.toString();
if (isInternalChange) return;
@ -66,7 +66,7 @@ export default defineComponent({
changes: {
from: 0,
to: this.editor.state.doc.length,
insert: this.value,
insert: this.modelValue,
},
});
@ -133,7 +133,7 @@ export default defineComponent({
this.editor = new EditorView({
parent: this.$refs.root as HTMLDivElement,
state: EditorState.create({
doc: this.value.startsWith('=') ? this.value.slice(1) : this.value,
doc: this.modelValue.startsWith('=') ? this.modelValue.slice(1) : this.modelValue,
extensions,
}),
});

View file

@ -1,7 +1,7 @@
<template>
<div class="ph-no-capture" :class="$style.container">
<span v-if="readonly" :class="$style.headline">
{{ name }}
{{ modelValue }}
</span>
<div
v-else
@ -11,12 +11,12 @@
v-click-outside="disableNameEdit"
>
<div v-if="!isNameEdit">
<span>{{ name }}</span>
<span>{{ modelValue }}</span>
<i><font-awesome-icon icon="pen" /></i>
</div>
<div v-else :class="$style.nameInput">
<n8n-input
:modelValue="name"
:modelValue="modelValue"
size="xlarge"
ref="nameInput"
@update:modelValue="onNameEdit"
@ -38,7 +38,7 @@ import { useToast } from '@/composables';
export default defineComponent({
name: 'InlineNameEdit',
props: {
name: {
modelValue: {
type: String,
},
subtitle: {
@ -64,7 +64,7 @@ export default defineComponent({
},
methods: {
onNameEdit(value: string) {
this.$emit('input', value);
this.$emit('update:modelValue', value);
},
enableNameEdit() {
this.isNameEdit = true;
@ -77,8 +77,8 @@ export default defineComponent({
}, 0);
},
disableNameEdit() {
if (!this.name) {
this.$emit('input', `Untitled ${this.type}`);
if (!this.modelValue) {
this.$emit('update:modelValue', `Untitled ${this.type}`);
this.showToast({
title: 'Error',

View file

@ -3,11 +3,11 @@
<span v-if="isEditEnabled && !disabled">
<ExpandableInputEdit
:placeholder="placeholder"
:value="newValue"
:modelValue="newValue"
:maxlength="maxLength"
:autofocus="true"
:eventBus="inputBus"
@input="onInput"
@update:modelValue="onInput"
@esc="onEscape"
@blur="onBlur"
@enter="submit"

View file

@ -35,7 +35,7 @@
:inputs="config"
:eventBus="formBus"
:columnView="true"
@update:modelValue="onInput"
@change="onInput"
@submit="onSubmit"
/>
</template>

View file

@ -58,67 +58,65 @@
<span v-else class="tags"></span>
<PushConnectionTracker class="actions">
<template>
<span class="activator">
<WorkflowActivator :workflow-active="isWorkflowActive" :workflow-id="currentWorkflowId" />
</span>
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
<n8n-button
type="secondary"
class="mr-2xs"
@click="onShareButtonClick"
data-test-id="workflow-share-button"
>
{{ $locale.baseText('workflowDetails.share') }}
</n8n-button>
<template #fallback>
<n8n-tooltip>
<n8n-button type="secondary" :class="['mr-2xs', $style.disabledShareButton]">
{{ $locale.baseText('workflowDetails.share') }}
</n8n-button>
<template #content>
<i18n
:path="
contextBasedTranslationKeys.workflows.sharing.unavailable.description.tooltip
"
tag="span"
>
<template #action>
<a @click="goToUpgrade">
{{
$locale.baseText(
contextBasedTranslationKeys.workflows.sharing.unavailable.button,
)
}}
</a>
</template>
</i18n>
</template>
</n8n-tooltip>
</template>
</enterprise-edition>
<SaveButton
type="primary"
:saved="!this.isDirty && !this.isNewWorkflow"
:disabled="isWorkflowSaving || readOnly"
data-test-id="workflow-save-button"
@click="onSaveButtonClick"
<span class="activator">
<WorkflowActivator :workflow-active="isWorkflowActive" :workflow-id="currentWorkflowId" />
</span>
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
<n8n-button
type="secondary"
class="mr-2xs"
@click="onShareButtonClick"
data-test-id="workflow-share-button"
>
{{ $locale.baseText('workflowDetails.share') }}
</n8n-button>
<template #fallback>
<n8n-tooltip>
<n8n-button type="secondary" :class="['mr-2xs', $style.disabledShareButton]">
{{ $locale.baseText('workflowDetails.share') }}
</n8n-button>
<template #content>
<i18n-t
:path="
contextBasedTranslationKeys.workflows.sharing.unavailable.description.tooltip
"
tag="span"
>
<template #action>
<a @click="goToUpgrade">
{{
$locale.baseText(
contextBasedTranslationKeys.workflows.sharing.unavailable.button,
)
}}
</a>
</template>
</i18n-t>
</template>
</n8n-tooltip>
</template>
</enterprise-edition>
<SaveButton
type="primary"
:saved="!this.isDirty && !this.isNewWorkflow"
:disabled="isWorkflowSaving || readOnly"
data-test-id="workflow-save-button"
@click="onSaveButtonClick"
/>
<div :class="$style.workflowMenuContainer">
<input
:class="$style.hiddenInput"
type="file"
ref="importFile"
data-test-id="workflow-import-input"
@change="handleFileImport()"
/>
<div :class="$style.workflowMenuContainer">
<input
:class="$style.hiddenInput"
type="file"
ref="importFile"
data-test-id="workflow-import-input"
@change="handleFileImport()"
/>
<n8n-action-dropdown
:items="workflowMenuItems"
data-test-id="workflow-menu"
@select="onWorkflowMenuSelect"
/>
</div>
</template>
<n8n-action-dropdown
:items="workflowMenuItems"
data-test-id="workflow-menu"
@select="onWorkflowMenuSelect"
/>
</div>
</PushConnectionTracker>
</div>
</template>
@ -152,7 +150,7 @@ import type { IUser, IWorkflowDataUpdate, IWorkflowDb, IWorkflowToShare } from '
import { saveAs } from 'file-saver';
import { useTitleChange, useToast, useMessage } from '@/composables';
import type { MessageBoxInputData } from 'element-ui/types/message-box';
import type { MessageBoxInputData } from 'element-plus';
import {
useUIStore,
useSettingsStore,

View file

@ -110,7 +110,7 @@ import { workflowRun } from '@/mixins/workflowRun';
import { ABOUT_MODAL_KEY, VERSIONS_MODAL_KEY, VIEWS } from '@/constants';
import { userHelpers } from '@/mixins/userHelpers';
import { debounceHelper } from '@/mixins/debounce';
import Vue, { defineComponent } from 'vue';
import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import {
useUIStore,
@ -341,7 +341,7 @@ export default defineComponent({
} else {
this.uiStore.sidebarMenuCollapsed = false;
}
await Vue.nextTick();
await this.$nextTick();
this.fullyExpanded = !this.isCollapsed;
},
created() {
@ -453,22 +453,21 @@ export default defineComponent({
});
},
findFirstAccessibleSettingsRoute() {
// Get all settings rotes by filtering them by pageCategory property
const settingsRoutes = this.$router
.getRoutes()
.filter(
(category) =>
category.meta.telemetry && category.meta.telemetry.pageCategory === 'settings',
)
.map((route) => route.name || '');
let defaultSettingsRoute = null;
.find((route) => route.path === '/settings')!
.children.map((route) => route.name || '');
let defaultSettingsRoute = null;
for (const route of settingsRoutes) {
if (this.canUserAccessRouteByName(route)) {
defaultSettingsRoute = route;
break;
}
}
console.log(defaultSettingsRoute);
return defaultSettingsRoute;
},
onResize(event: UIEvent) {
@ -481,7 +480,7 @@ export default defineComponent({
checkWidthAndAdjustSidebar(width: number) {
if (width < 900) {
this.uiStore.sidebarMenuCollapsed = true;
Vue.nextTick(() => {
this.$nextTick(() => {
this.fullyExpanded = !this.isCollapsed;
});
}

View file

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { useRouter } from 'vue-router/composables';
import { useRouter } from 'vue-router';
import { createEventBus } from 'n8n-design-system/utils';
import { useI18n, useLoadingService, useMessage, useToast } from '@/composables';
import { useUIStore, useUsersStore, useVersionControlStore } from '@/stores';

View file

@ -2,10 +2,14 @@
<el-dialog
:visible="uiStore.isModalOpen(this.name)"
:before-close="closeDialog"
:class="{ 'dialog-wrapper': true, [$style.center]: center, scrollable: scrollable }"
:class="{
'dialog-wrapper': true,
[$style.center]: center,
scrollable: scrollable,
[getCustomClass()]: true,
}"
:width="width"
:show-close="showClose"
:custom-class="getCustomClass()"
:close-on-click-modal="closeOnClickModal"
:close-on-press-escape="closeOnPressEscape"
:style="styles"

View file

@ -7,7 +7,7 @@
:modal="modal"
:wrapperClosable="wrapperClosable"
>
<template #title>
<template #header>
<slot name="header" />
</template>
<template>

View file

@ -230,31 +230,30 @@ export default defineComponent({
}
}
::v-deep {
.button {
--button-background-color: var(--color-background-base);
--button-border-color: var(--color-foreground-base);
}
:deep(.button) {
--button-background-color: var(--color-background-base);
--button-border-color: var(--color-foreground-base);
}
.duplicate-parameter-item {
position: relative;
:deep(.duplicate-parameter-item) {
position: relative;
.multi > .delete-item {
top: 0.1em;
}
}
.duplicate-parameter-input-item {
margin: 0.5em 0 0.25em 2em;
}
.duplicate-parameter-item + .duplicate-parameter-item {
.collection-parameter-wrapper {
border-top: 1px dashed #999;
margin-top: var(--spacing-xs);
}
.multi > .delete-item {
top: 0.1em;
}
}
:deep(.duplicate-parameter-input-item) {
margin: 0.5em 0 0.25em 2em;
}
:deep(.duplicate-parameter-item + .duplicate-parameter-item) {
.collection-parameter-wrapper {
border-top: 1px dashed #999;
margin-top: var(--spacing-xs);
}
}
.no-items-exist {
margin: var(--spacing-xs) 0;
}

View file

@ -169,7 +169,7 @@
</template>
<script lang="ts">
import Vue, { defineComponent } from 'vue';
import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import {
CUSTOM_API_CALL_KEY,
@ -574,7 +574,7 @@ export default defineComponent({
workflow_id: this.workflowsStore.workflowId,
});
Vue.nextTick(() => {
this.$nextTick(() => {
// Wait a tick else vue causes problems because the data is gone
this.$emit('removeNode', this.data.name);
});
@ -585,7 +585,7 @@ export default defineComponent({
button_name: 'duplicate',
workflow_id: this.workflowsStore.workflowId,
});
Vue.nextTick(() => {
this.$nextTick(() => {
// Wait a tick else vue causes problems because the data is gone
this.$emit('duplicateNode', this.data.name);
});

View file

@ -28,16 +28,18 @@
</div>
</div>
</div>
<node-creator
:active="createNodeActive"
@nodeTypeSelected="nodeTypeSelected"
@closeNodeCreator="closeNodeCreator"
/>
<Suspense>
<NodeCreator
:active="createNodeActive"
@nodeTypeSelected="nodeTypeSelected"
@closeNodeCreator="closeNodeCreator"
/>
</Suspense>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { defineAsyncComponent, defineComponent } from 'vue';
import { getMidCanvasPosition } from '@/utils/nodeViewUtils';
import {
DEFAULT_STICKY_HEIGHT,
@ -48,10 +50,14 @@ import {
import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store';
const NodeCreator = defineAsyncComponent(
() => import('@/components/Node/NodeCreator/NodeCreator.vue'),
);
export default defineComponent({
name: 'node-creation',
components: {
NodeCreator: async () => import('@/components/Node/NodeCreator/NodeCreator.vue'),
NodeCreator,
},
props: {
nodeViewScale: {

View file

@ -124,8 +124,8 @@ function onBackButton() {
? searchPlaceholder
: $locale.baseText('nodeCreator.searchBar.searchNodes')
"
@input="onSearch"
:value="activeViewStack.search"
:modelValue="activeViewStack.search"
@update:modelValue="onSearch"
/>
<div :class="$style.renderedItems">
<!-- Actions mode -->

View file

@ -1,21 +1,21 @@
<template>
<div :class="$style.searchContainer" data-test-id="search-bar">
<div :class="{ [$style.prefix]: true, [$style.active]: value.length > 0 }">
<div :class="{ [$style.prefix]: true, [$style.active]: modelValue.length > 0 }">
<font-awesome-icon icon="search" size="sm" />
</div>
<div :class="$style.text">
<input
:placeholder="placeholder"
:value="value"
@input="onInput"
:value="modelValue"
:class="$style.input"
ref="inputRef"
autofocus
data-test-id="node-creator-search-bar"
tabindex="0"
@input="onInput"
/>
</div>
<div :class="$style.suffix" v-if="value.length > 0" @click="clear">
<div :class="$style.suffix" v-if="modelValue.length > 0" @click="clear">
<button :class="[$style.clear, $style.clickable]">
<font-awesome-icon icon="times-circle" />
</button>
@ -30,16 +30,16 @@ import { runExternalHook } from '@/utils';
export interface Props {
placeholder: string;
value: string;
modelValue: string;
}
withDefaults(defineProps<Props>(), {
placeholder: '',
value: '',
modelValue: '',
});
const emit = defineEmits<{
(event: 'input', value: string): void;
(event: 'update:modelValue', value: string): void;
}>();
const state = reactive({
@ -52,11 +52,11 @@ function focus() {
function onInput(event: Event) {
const input = event.target as HTMLInputElement;
emit('input', input.value);
emit('update:modelValue', input.value);
}
function clear() {
emit('input', '');
emit('update:modelValue', '');
}
onMounted(() => {

View file

@ -1,4 +1,4 @@
import { ref, set } from 'vue';
import { ref } from 'vue';
import { defineStore } from 'pinia';
export type KeyboardKey = (typeof WATCHED_KEYS)[number];
@ -125,7 +125,7 @@ export const useKeyboardNavigation = defineStore('nodeCreatorKeyboardNavigation'
function registerKeyHook(name: string, hook: KeyHook) {
hook.keyboardKeys.forEach((keyboardKey) => {
if (WATCHED_KEYS.includes(keyboardKey)) {
set(keysHooks.value, name, hook);
keysHooks.value = { ...keysHooks.value, [name]: hook };
} else {
throw new Error(`Key ${keyboardKey} is not supported`);
}

View file

@ -1,4 +1,4 @@
import { computed, ref, set } from 'vue';
import { computed, ref } from 'vue';
import { defineStore } from 'pinia';
import { v4 as uuid } from 'uuid';
import type { INodeCreateElement, NodeFilterType, SimplifiedNodeType } from '@/Interface';
@ -167,7 +167,10 @@ export const useViewStacks = defineStore('nodeCreatorViewStacks', () => {
// For each key in the stack, update the matched stack
Object.keys(stack).forEach((key) => {
const typedKey = key as keyof ViewStack;
set(viewStacks.value[matchedIndex], key, stack[typedKey]);
viewStacks.value[matchedIndex] = {
...viewStacks.value[matchedIndex],
[key]: stack[typedKey],
};
});
}

View file

@ -3,8 +3,7 @@
:visible="(!!activeNode || renaming) && !isActiveStickyNode"
:before-close="close"
:show-close="false"
custom-class="data-display-wrapper"
class="ndv-wrapper"
class="data-display-wrapper ndv-wrapper"
width="auto"
append-to-body
data-test-id="ndv"

View file

@ -9,11 +9,12 @@
<div :class="$style.header">
<div class="header-side-menu">
<NodeTitle
v-if="node"
class="node-name"
:value="node && node.name"
:modelValue="node.name"
:nodeType="nodeType"
:isReadOnly="isReadOnly"
@input="nameChanged"
@update:modelValue="nameChanged"
></NodeTitle>
<div v-if="isExecutable">
<NodeExecuteButton
@ -46,7 +47,7 @@
</div>
<div v-if="isCommunityNode" :class="$style.descriptionContainer">
<div class="mb-l">
<i18n
<i18n-t
path="nodeSettings.communityNodeUnknown.description"
tag="span"
@click="onMissingNodeTextClick"
@ -58,7 +59,7 @@
>{{ node.type.split('.')[0] }}</a
>
</template>
</i18n>
</i18n-t>
</div>
<n8n-link
:to="COMMUNITY_NODES_INSTALLATION_DOCS_URL"
@ -67,7 +68,7 @@
{{ $locale.baseText('nodeSettings.communityNodeUnknown.installLink.text') }}
</n8n-link>
</div>
<i18n v-else path="nodeSettings.nodeTypeUnknown.description" tag="span">
<i18n-t v-else path="nodeSettings.nodeTypeUnknown.description" tag="span">
<template #action>
<a
:href="CUSTOM_NODES_DOCS_URL"
@ -75,7 +76,7 @@
v-text="$locale.baseText('nodeSettings.nodeTypeUnknown.description.customNode')"
/>
</template>
</i18n>
</i18n-t>
</div>
<div class="node-parameters-wrapper" data-test-id="node-parameters" v-if="node && nodeValid">
<n8n-notice

View file

@ -1,7 +1,7 @@
<template>
<span :class="$style.container" data-test-id="node-title-container" @click="onEdit">
<span :class="$style.iconWrapper"><NodeIcon :nodeType="nodeType" :size="18" /></span>
<n8n-popover placement="right" width="200" :value="editName" :disabled="!editable">
<n8n-popover placement="right" width="200" :modelValue="editName" :disabled="!editable">
<div
:class="$style.editContainer"
@keydown.enter="onRename"
@ -29,7 +29,7 @@
</div>
<template #reference>
<div class="ph-no-capture" :class="{ [$style.title]: true, [$style.hoverable]: editable }">
{{ value }}
{{ modelValue }}
<div :class="$style.editIconContainer">
<font-awesome-icon :class="$style.editIcon" icon="pencil-alt" v-if="editable" />
</div>
@ -49,8 +49,9 @@ export default defineComponent({
NodeIcon,
},
props: {
value: {
modelValue: {
type: String,
default: '',
},
nodeType: {},
readOnly: {
@ -71,7 +72,7 @@ export default defineComponent({
},
methods: {
onEdit() {
this.newName = this.value;
this.newName = this.modelValue;
this.editName = true;
this.$nextTick(() => {
const inputRef = this.$refs.input as HTMLInputElement | undefined;
@ -82,7 +83,7 @@ export default defineComponent({
},
onRename() {
if (this.newName.trim() !== '') {
this.$emit('input', this.newName.trim());
this.$emit('update:modelValue', this.newName.trim());
}
this.editName = false;

View file

@ -4,7 +4,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
import type { ElNotificationComponent } from 'element-ui/types/notification';
import type { NotificationInstance } from 'element-plus';
import { sanitizeHtml } from '@/utils';
import { useToast } from '@/composables';
@ -26,7 +26,7 @@ export default defineComponent({
},
data() {
return {
alert: null as null | ElNotificationComponent,
alert: null as null | NotificationInstance,
};
},
mounted() {

View file

@ -2,7 +2,7 @@
<div @keydown.stop :class="parameterInputClasses">
<expression-edit
:dialogVisible="expressionEditDialogVisible"
:value="
:modelValue="
isResourceLocatorParameter && typeof value !== 'string' ? (value ? value.value : '') : value
"
:parameter="parameter"
@ -10,14 +10,14 @@
:eventSource="eventSource || 'ndv'"
:isReadOnly="isReadOnly"
@closeDialog="closeExpressionEditDialog"
@valueChanged="expressionUpdated"
@update:modelValue="expressionUpdated"
></expression-edit>
<div class="parameter-input ignore-key-press" :style="parameterInputWrapperStyle">
<resource-locator
v-if="isResourceLocatorParameter"
ref="resourceLocator"
:parameter="parameter"
:value="value"
:modelValue="value"
:dependentParametersValues="dependentParametersValues"
:displayTitle="displayTitle"
:expressionDisplayValue="expressionDisplayValue"
@ -29,7 +29,7 @@
:node="node"
:path="path"
:event-bus="eventBus"
@input="valueChanged"
@update:modelValue="valueChanged"
@modalOpenerClick="openExpressionEditorModal"
@focus="setFocus"
@blur="onBlur"
@ -37,11 +37,11 @@
/>
<ExpressionParameterInput
v-else-if="isValueExpression || forceShowExpression"
:value="expressionDisplayValue"
:modelValue="expressionDisplayValue"
:title="displayTitle"
:isReadOnly="isReadOnly"
:path="path"
@valueChanged="expressionUpdated"
@update:modelValue="expressionUpdated"
@modalOpenerClick="openExpressionEditorModal"
@focus="setFocus"
@blur="onBlur"
@ -390,7 +390,6 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useSettingsStore } from '@/stores/settings.store';
import { htmlEditorEventBus } from '@/event-bus';
import Vue from 'vue';
import type { EventBus } from 'n8n-design-system/utils';
import { createEventBus } from 'n8n-design-system/utils';
@ -976,7 +975,7 @@ export default defineComponent({
this.nodeName = this.node.name;
}
Vue.nextTick(() => {
this.$nextTick(() => {
// @ts-ignore
if (this.$refs.inputField?.focus && this.$refs.inputField?.$el) {
// @ts-ignore
@ -1217,7 +1216,7 @@ export default defineComponent({
display: inline-block;
}
::v-deep .color-input {
:deep(.color-input) {
display: flex;
.el-color-picker__trigger {

View file

@ -19,40 +19,38 @@
@menu-expanded="onMenuExpanded"
/>
</template>
<template>
<parameter-input-wrapper
ref="param"
inputSize="large"
:parameter="parameter"
:value="value"
:path="parameter.name"
:hideIssues="true"
:documentationUrl="documentationUrl"
:errorHighlight="showRequiredErrors"
:isForCredential="true"
:eventSource="eventSource"
:hint="!showRequiredErrors ? hint : ''"
:event-bus="eventBus"
@focus="onFocus"
@blur="onBlur"
@textInput="valueChanged"
@valueChanged="valueChanged"
/>
<div :class="$style.errors" v-if="showRequiredErrors">
<n8n-text color="danger" size="small">
{{ $locale.baseText('parameterInputExpanded.thisFieldIsRequired') }}
<n8n-link
v-if="documentationUrl"
:to="documentationUrl"
size="small"
:underline="true"
@click="onDocumentationUrlClick"
>
{{ $locale.baseText('parameterInputExpanded.openDocs') }}
</n8n-link>
</n8n-text>
</div>
</template>
<parameter-input-wrapper
ref="param"
inputSize="large"
:parameter="parameter"
:value="value"
:path="parameter.name"
:hideIssues="true"
:documentationUrl="documentationUrl"
:errorHighlight="showRequiredErrors"
:isForCredential="true"
:eventSource="eventSource"
:hint="!showRequiredErrors ? hint : ''"
:event-bus="eventBus"
@focus="onFocus"
@blur="onBlur"
@textInput="valueChanged"
@valueChanged="valueChanged"
/>
<div :class="$style.errors" v-if="showRequiredErrors">
<n8n-text color="danger" size="small">
{{ $locale.baseText('parameterInputExpanded.thisFieldIsRequired') }}
<n8n-link
v-if="documentationUrl"
:to="documentationUrl"
size="small"
:underline="true"
@click="onDocumentationUrlClick"
>
{{ $locale.baseText('parameterInputExpanded.openDocs') }}
</n8n-link>
</n8n-text>
</div>
</n8n-input-label>
</template>

View file

@ -5,7 +5,7 @@
:data-test-id="`resource-locator-${parameter.name}`"
>
<resource-locator-dropdown
:value="value ? value.value : ''"
:modelValue="modelValue ? modelValue.value : ''"
:show="showResourceDropdown"
:filterable="isSearchable"
:filterRequired="requiresSearchFilter"
@ -16,7 +16,7 @@
:errorView="currentQueryError"
:width="width"
:event-bus="eventBus"
@input="onListItemSelected"
@update:modelValue="onListItemSelected"
@hide="onDropdownHide"
@filter="onSearchFilter"
@loadMore="loadResourcesDebounced"
@ -88,10 +88,10 @@
>
<ExpressionParameterInput
v-if="isValueExpression || forceShowExpression"
:value="expressionDisplayValue"
:modelValue="expressionDisplayValue"
:path="path"
isForRecordLocator
@valueChanged="onInputChange"
@update:modelValue="onInputChange"
@modalOpenerClick="$emit('modalOpenerClick')"
ref="input"
/>
@ -200,7 +200,7 @@ export default defineComponent({
type: Object as PropType<INodeProperties>,
required: true,
},
value: {
modelValue: {
type: [Object, String] as PropType<
INodeParameterResourceLocator | NodeParameterValue | undefined
>,
@ -281,16 +281,16 @@ export default defineComponent({
return getAppNameFromNodeName(nodeType?.displayName || '');
},
selectedMode(): string {
if (typeof this.value !== 'object') {
if (typeof this.modelValue !== 'object') {
// legacy mode
return '';
}
if (!this.value) {
if (!this.modelValue) {
return this.parameter.modes ? this.parameter.modes[0].name : '';
}
return this.value.mode;
return this.modelValue.mode;
},
isListMode(): boolean {
return this.selectedMode === 'list';
@ -335,19 +335,19 @@ export default defineComponent({
return hasOnlyListMode(this.parameter);
},
valueToDisplay(): NodeParameterValue {
if (typeof this.value !== 'object') {
return this.value;
if (typeof this.modelValue !== 'object') {
return this.modelValue;
}
if (this.isListMode) {
return this.value ? this.value.cachedResultName || this.value.value : '';
return this.modelValue ? this.modelValue.cachedResultName || this.modelValue.value : '';
}
return this.value ? this.value.value : '';
return this.modelValue ? this.modelValue.value : '';
},
urlValue(): string | null {
if (this.isListMode && typeof this.value === 'object') {
return (this.value && this.value.cachedResultUrl) || null;
if (this.isListMode && typeof this.modelValue === 'object') {
return (this.modelValue && this.modelValue.cachedResultUrl) || null;
}
if (this.selectedMode === 'url') {
@ -454,10 +454,10 @@ export default defineComponent({
if (
mode.extractValue &&
mode.extractValue.regex &&
isResourceLocatorValue(this.value) &&
this.value.__regex !== mode.extractValue.regex
isResourceLocatorValue(this.modelValue) &&
this.modelValue.__regex !== mode.extractValue.regex
) {
this.$emit('input', { ...this.value, __regex: mode.extractValue.regex });
this.$emit('update:modelValue', { ...this.modelValue, __regex: mode.extractValue.regex });
}
},
dependentParametersValues(currentValue, oldValue) {
@ -465,12 +465,12 @@ export default defineComponent({
// Reset value if dependent parameters change
if (
isUpdated &&
this.value &&
isResourceLocatorValue(this.value) &&
this.value.value !== ''
this.modelValue &&
isResourceLocatorValue(this.modelValue) &&
this.modelValue.value !== ''
) {
this.$emit('input', {
...this.value,
this.$emit('update:modelValue', {
...this.modelValue,
cachedResultName: '',
cachedResultUrl: '',
value: '',
@ -590,17 +590,26 @@ export default defineComponent({
params.cachedResultUrl = resource.url;
}
}
this.$emit('input', params);
this.$emit('update:modelValue', params);
},
onModeSelected(value: string): void {
if (typeof this.value !== 'object') {
this.$emit('input', { __rl: true, value: this.value, mode: value });
} else if (value === 'url' && this.value && this.value.cachedResultUrl) {
this.$emit('input', { __rl: true, mode: value, value: this.value.cachedResultUrl });
} else if (value === 'id' && this.selectedMode === 'list' && this.value && this.value.value) {
this.$emit('input', { __rl: true, mode: value, value: this.value.value });
if (typeof this.modelValue !== 'object') {
this.$emit('update:modelValue', { __rl: true, value: this.modelValue, mode: value });
} else if (value === 'url' && this.modelValue && this.modelValue.cachedResultUrl) {
this.$emit('update:modelValue', {
__rl: true,
mode: value,
value: this.modelValue.cachedResultUrl,
});
} else if (
value === 'id' &&
this.selectedMode === 'list' &&
this.modelValue &&
this.modelValue.value
) {
this.$emit('update:modelValue', { __rl: true, mode: value, value: this.modelValue.value });
} else {
this.$emit('input', { __rl: true, mode: value, value: '' });
this.$emit('update:modelValue', { __rl: true, mode: value, value: '' });
}
this.trackEvent('User changed resource locator mode', { mode: value });
@ -728,9 +737,10 @@ export default defineComponent({
}
if (mode) {
this.$emit('input', {
this.$emit('update:modelValue', {
__rl: true,
value: this.value && typeof this.value === 'object' ? this.value.value : '',
value:
this.modelValue && typeof this.modelValue === 'object' ? this.modelValue.value : '',
mode: mode.name,
});
}

View file

@ -3,7 +3,7 @@
placement="bottom"
:width="width"
:popper-class="$style.popover"
:value="show"
:modelValue="show"
trigger="manual"
data-test-id="resource-locator-dropdown"
v-click-outside="onClickOutside"
@ -16,7 +16,7 @@
size="medium"
:modelValue="filter"
:clearable="true"
@input="onFilterInput"
@update:modelValue="onFilterInput"
ref="search"
:placeholder="$locale.baseText('resourceLocator.search.placeholder')"
>
@ -45,7 +45,7 @@
:key="result.value"
:class="{
[$style.resourceItem]: true,
[$style.selected]: result.value === value,
[$style.selected]: result.value === modelValue,
[$style.hovering]: hoverIndex === i,
}"
class="ph-no-capture"
@ -91,7 +91,7 @@ const SCROLL_MARGIN_PX = 10;
export default defineComponent({
name: 'resource-locator-dropdown',
props: {
value: {
modelValue: {
type: [String, Number],
},
show: {
@ -149,7 +149,7 @@ export default defineComponent({
}
seen.add(item.value);
if (this.value && item.value === this.value) {
if (this.modelValue && item.value === this.modelValue) {
acc.selected = item;
} else {
acc.notSelected.push(item);
@ -210,7 +210,7 @@ export default defineComponent({
}
}
} else if (e.key === 'Enter') {
this.$emit('input', this.sortedResources[this.hoverIndex].value);
this.$emit('update:modelValue', this.sortedResources[this.hoverIndex].value);
}
},
onFilterInput(value: string) {
@ -220,7 +220,7 @@ export default defineComponent({
this.$emit('hide');
},
onItemClick(selected: string) {
this.$emit('input', selected);
this.$emit('update:modelValue', selected);
},
onItemHover(index: number) {
this.hoverIndex = index;

View file

@ -65,14 +65,14 @@
<n8n-tooltip placement="bottom-end">
<template #content>
<div>
<i18n path="dataMapping.tableView.tableColumnsExceeded.tooltip">
<i18n-t path="dataMapping.tableView.tableColumnsExceeded.tooltip">
<template #columnLimit>{{ columnLimit }}</template>
<template #link>
<a @click="switchToJsonView">{{
$locale.baseText('dataMapping.tableView.tableColumnsExceeded.tooltip.link')
}}</a>
</template>
</i18n>
</i18n-t>
</div>
</template>
<span>

View file

@ -1,18 +1,15 @@
<script lang="ts" setup>
import { Notification } from 'element-ui';
import { useSSOStore } from '@/stores/sso.store';
import { useToast } from '@/composables';
const ssoStore = useSSOStore();
const toast = useToast();
const onSSOLogin = async () => {
try {
window.location.href = await ssoStore.getSSORedirectUrl();
} catch (error) {
Notification.error({
title: 'Error',
message: error.message,
position: 'bottom-right',
});
toast.showError(error, 'Error', error.message);
}
};
</script>

View file

@ -22,12 +22,12 @@
<div :class="$style.header">
<div :class="$style.destinationInfo">
<InlineNameEdit
:name="headerLabel"
:modelValue="headerLabel"
:subtitle="!isTypeAbstract ? $locale.baseText(typeLabelName) : 'Select type'"
:readonly="isTypeAbstract"
type="Credential"
data-test-id="subtitle-showing-type"
@input="onLabelChange"
@update:modelValue="onLabelChange"
/>
</div>
<div :class="$style.destinationActions">
@ -158,7 +158,7 @@
<event-selection
class=""
:destinationId="destination.id"
@input="onInput"
@update:modelValue="onInput"
@change="valueChanged"
:readonly="!isInstanceOwner"
/>

View file

@ -7,9 +7,9 @@
>
<!-- <template #header> -->
<checkbox
:value="group.selected"
:modelValue="group.selected"
:indeterminate="!group.selected && group.indeterminate"
@input="onInput"
@update:modelValue="onInput"
@change="onCheckboxChecked(group.name, $event)"
:disabled="readonly"
>
@ -28,8 +28,8 @@
</checkbox>
<checkbox
v-if="group.name === 'n8n.audit'"
:value="logStreamingStore.items[destinationId]?.destination.anonymizeAuditMessages"
@input="onInput"
:modelValue="logStreamingStore.items[destinationId]?.destination.anonymizeAuditMessages"
@update:modelValue="onInput"
@change="anonymizeAuditMessagesChanged"
:disabled="readonly"
>
@ -49,10 +49,10 @@
:class="`${$style.eventListItem} ${group.selected ? $style.eventListItemDisabled : ''}`"
>
<checkbox
:value="event.selected || group.selected"
:modelValue="event.selected || group.selected"
:indeterminate="event.indeterminate"
:disabled="group.selected || readonly"
@input="onInput"
@update:modelValue="onInput"
@change="onCheckboxChecked(event.name, $event)"
>
{{ event.label }}
@ -69,7 +69,7 @@
</template>
<script lang="ts">
import { Checkbox } from 'element-ui';
import { ElCheckbox as Checkbox } from 'element-plus';
import { mapStores } from 'pinia';
import type { BaseTextKey } from '@/plugins/i18n';
import { useLogStreamingStore } from '@/stores/logStreaming.store';

View file

@ -56,7 +56,7 @@
</template>
<script lang="ts">
import Vue, { defineComponent } from 'vue';
import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import { externalHooks } from '@/mixins/externalHooks';
@ -163,7 +163,7 @@ export default defineComponent({
},
methods: {
deleteNode() {
Vue.nextTick(() => {
this.$nextTick(() => {
// Wait a tick else vue causes problems because the data is gone
this.$emit('removeNode', this.data.name);
});

View file

@ -250,7 +250,7 @@ export default defineComponent({
<style lang="scss" scoped>
$--max-input-height: 60px;
::v-deep .el-select {
:deep(.el-select) {
.el-select__tags {
max-height: $--max-input-height;
overflow-y: scroll;

View file

@ -107,7 +107,7 @@
</template>
<script lang="ts">
import type { Table as ElTable } from 'element-ui';
import type { ElTable } from 'element-plus';
import { MAX_TAG_NAME_LENGTH } from '@/constants';
import type { ITagRow } from '@/Interface';
import { defineComponent } from 'vue';
@ -248,7 +248,7 @@ export default defineComponent({
margin-left: 2px;
}
::v-deep tr.disabled {
:deep(tr.disabled) {
pointer-events: none;
}

View file

@ -13,9 +13,7 @@
{{ `${$locale.baseText('versionCard.version')} ${version.name}` }}
</div>
<WarningTooltip v-if="version.hasSecurityIssue">
<template>
<span v-html="$locale.baseText('versionCard.thisVersionHasASecurityIssue')"></span>
</template>
<span v-html="$locale.baseText('versionCard.thisVersionHasASecurityIssue')"></span>
</WarningTooltip>
<Badge
v-if="version.hasSecurityFix"

View file

@ -8,7 +8,7 @@ import type { VersionControlAggregatedFile } from '@/Interface';
import { useI18n, useLoadingService, useToast } from '@/composables';
import { useVersionControlStore } from '@/stores/versionControl.store';
import { useUIStore } from '@/stores';
import { useRoute } from 'vue-router/composables';
import { useRoute } from 'vue-router';
const props = defineProps({
data: {

View file

@ -162,7 +162,7 @@ export default defineComponent({
margin-left: 0.5em;
}
::v-deep .el-loading-spinner {
:deep(.el-loading-spinner) {
margin-top: -10px;
}
</style>

View file

@ -70,14 +70,14 @@
</n8n-users-list>
<template #fallback>
<n8n-text>
<i18n
<i18n-t
:path="
uiStore.contextBasedTranslationKeys.workflows.sharing.unavailable.description
"
tag="span"
>
<template #action />
</i18n>
</i18n-t>
</n8n-text>
</template>
</enterprise-edition>

View file

@ -16,7 +16,7 @@
</n8n-button>
</template>
<div :class="$style['filters-dropdown']" data-test-id="resources-list-filters-dropdown">
<slot :filters="value" :setKeyValue="setKeyValue" />
<slot :filters="modelValue" :setKeyValue="setKeyValue" />
<enterprise-edition
class="mb-s"
:features="[EnterpriseEditionFeature.Sharing]"
@ -32,7 +32,7 @@
<n8n-user-select
:users="ownedByUsers"
:currentUserId="usersStore.currentUser.id"
:modelValue="value.ownedBy"
:modelValue="modelValue.ownedBy"
size="medium"
@update:modelValue="setKeyValue('ownedBy', $event)"
/>
@ -48,7 +48,7 @@
<n8n-user-select
:users="sharedWithUsers"
:currentUserId="usersStore.currentUser.id"
:modelValue="value.sharedWith"
:modelValue="modelValue.sharedWith"
size="medium"
@update:modelValue="setKeyValue('sharedWith', $event)"
/>
@ -74,7 +74,7 @@ export type IResourceFiltersType = Record<string, boolean | string | string[]>;
export default defineComponent({
props: {
value: {
modelValue: {
type: Object as PropType<IResourceFiltersType>,
default: () => ({}),
},
@ -99,12 +99,12 @@ export default defineComponent({
...mapStores(useUsersStore),
ownedByUsers(): IUser[] {
return this.usersStore.allUsers.map((user) =>
user.id === this.value.sharedWith ? { ...user, disabled: true } : user,
user.id === this.modelValue.sharedWith ? { ...user, disabled: true } : user,
);
},
sharedWithUsers(): IUser[] {
return this.usersStore.allUsers.map((user) =>
user.id === this.value.ownedBy ? { ...user, disabled: true } : user,
user.id === this.modelValue.ownedBy ? { ...user, disabled: true } : user,
);
},
filtersLength(): number {
@ -116,7 +116,9 @@ export default defineComponent({
}
length += (
Array.isArray(this.value[key]) ? this.value[key].length > 0 : this.value[key] !== ''
Array.isArray(this.modelValue[key])
? this.modelValue[key].length > 0
: this.modelValue[key] !== ''
)
? 1
: 0;
@ -131,23 +133,23 @@ export default defineComponent({
methods: {
setKeyValue(key: string, value: unknown) {
const filters = {
...this.value,
...this.modelValue,
[key]: value,
};
this.$emit('input', filters);
this.$emit('update:modelValue', filters);
},
resetFilters() {
if (this.reset) {
this.reset();
} else {
const filters = { ...this.value };
const filters = { ...this.modelValue };
(this.keys as string[]).forEach((key) => {
filters[key] = Array.isArray(this.value[key]) ? [] : '';
filters[key] = Array.isArray(this.modelValue[key]) ? [] : '';
});
this.$emit('input', filters);
this.$emit('update:modelValue', filters);
}
},
},

View file

@ -88,9 +88,9 @@
v-if="showFiltersDropdown"
:keys="filterKeys"
:reset="resetFilters"
:value="filters"
:modelValue="filters"
:shareable="shareable"
@input="$emit('update:filters', $event)"
@update:modelValue="$emit('update:filters', $event)"
@update:filtersLength="onUpdateFiltersLength"
>
<template #default="resourceFiltersSlotProps">

View file

@ -23,7 +23,7 @@ vi.mock('@/stores/history.store', () => {
};
});
vi.mock('@/stores/ui.store');
vi.mock('vue-router/composables', () => ({
vi.mock('vue-router', () => ({
useRoute: () => ({}),
}));

View file

@ -224,6 +224,7 @@ export default function useCanvasMouseSelect() {
});
return {
selectActive,
getMousePositionWithinNodeView,
mouseUpMouseSelect,
mouseDownMouseSelect,

View file

@ -2,7 +2,7 @@
* Creates event listeners for `data-action` attribute to allow for actions to be called from locale without using
* unsafe onclick attribute
*/
import { reactive, del, computed, onMounted, onUnmounted, getCurrentInstance } from 'vue';
import { reactive, computed, onMounted, onUnmounted, getCurrentInstance } from 'vue';
import { globalLinkActionsEventBus } from '@/event-bus';
const state = reactive({
@ -14,7 +14,8 @@ export default () => {
state.customActions[key] = action;
}
function unregisterCustomAction(key: string) {
del(state.customActions, key);
const { [key]: _, ...rest } = state.customActions;
state.customActions = rest;
}
function delegateClick(e: MouseEvent) {
const clickedElement = e.target;

View file

@ -1,6 +1,6 @@
import { ref } from 'vue';
import { useI18n } from '@/composables/useI18n';
import { Loading } from 'element-ui';
import { ElLoading as Loading } from 'element-plus';
interface LoadingService {
text: string;

View file

@ -1,5 +1,5 @@
import type { ElMessageBoxOptions } from 'element-ui/types/message-box';
import { Message, MessageBox } from 'element-ui';
import type { ElMessageBoxOptions } from 'element-plus';
import { ElMessageBox as MessageBox } from 'element-plus';
export function useMessage() {
const handleCancelOrClose = (e: unknown) => {
@ -65,6 +65,6 @@ export function useMessage() {
alert,
confirm,
prompt,
message: Message,
message: MessageBox,
};
}

View file

@ -1,18 +1,17 @@
import { Notification } from 'element-ui';
import type { ElNotificationComponent, ElNotificationOptions } from 'element-ui/types/notification';
import type { MessageType } from 'element-ui/types/message';
import { ElNotification as Notification } from 'element-plus';
import type { NotificationInstance, NotificationOptions, MessageBoxState } from 'element-plus';
import { sanitizeHtml } from '@/utils';
import { useTelemetry } from '@/composables/useTelemetry';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useI18n } from './useI18n';
import { useExternalHooks } from './useExternalHooks';
const messageDefaults: Partial<Omit<ElNotificationOptions, 'message'>> = {
const messageDefaults: Partial<Omit<NotificationOptions, 'message'>> = {
dangerouslyUseHTMLString: true,
position: 'bottom-right',
};
const stickyNotificationQueue: ElNotificationComponent[] = [];
const stickyNotificationQueue: NotificationInstance[] = [];
export function useToast() {
const telemetry = useTelemetry();
@ -21,7 +20,7 @@ export function useToast() {
const { i18n } = useI18n();
function showMessage(
messageData: Omit<ElNotificationOptions, 'message'> & { message?: string },
messageData: Omit<NotificationOptions, 'message'> & { message?: string },
track = true,
) {
messageData = { ...messageDefaults, ...messageData };
@ -29,7 +28,8 @@ export function useToast() {
? sanitizeHtml(messageData.message)
: messageData.message;
const notification = Notification(messageData as ElNotificationOptions);
// @TODO Check if still working
const notification = Notification(messageData as NotificationOptions);
if (messageData.duration === 0) {
stickyNotificationQueue.push(notification);
@ -55,11 +55,11 @@ export function useToast() {
duration?: number;
customClass?: string;
closeOnClick?: boolean;
type?: MessageType;
type?: MessageBoxState['type'];
dangerouslyUseHTMLString?: boolean;
}) {
// eslint-disable-next-line prefer-const
let notification: ElNotificationComponent;
let notification: NotificationInstance;
if (config.closeOnClick) {
const cb = config.onClick;
config.onClick = () => {
@ -138,7 +138,7 @@ export function useToast() {
});
}
function showAlert(config: ElNotificationOptions): ElNotificationComponent {
function showAlert(config: NotificationOptions): NotificationInstance {
return Notification(config);
}

View file

@ -1,6 +1,6 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import { createApp } from 'vue';
import 'vue-json-pretty/lib/styles.css';
import '@jsplumb/browser-ui/css/jsplumbtoolkit.css';
@ -26,24 +26,30 @@ import { runExternalHook } from '@/utils';
import { createPinia, PiniaVuePlugin } from 'pinia';
import { useWebhooksStore } from '@/stores';
Vue.config.productionTip = false;
Vue.use(TelemetryPlugin);
Vue.use(PiniaVuePlugin);
Vue.use(I18nPlugin);
Vue.use(FontAwesomePlugin);
Vue.use(GlobalComponentsPlugin);
Vue.use(GlobalDirectivesPlugin);
const pinia = createPinia();
new Vue({
i18n: i18nInstance,
router,
pinia,
render: (h) => h(App),
}).$mount('#app');
const app = createApp(App);
// Vue.config.productionTip = false;
app.use(TelemetryPlugin);
app.use(PiniaVuePlugin);
app.use(I18nPlugin);
app.use(FontAwesomePlugin);
app.use(GlobalComponentsPlugin);
app.use(GlobalDirectivesPlugin);
app.use(pinia);
app.use(router);
app.use(i18nInstance);
app.mount('#app');
// new Vue({
// i18n: i18nInstance,
// router,
// pinia,
// render: (h) => h(App),
// }).$mount('#app');
router.afterEach((to, from) => {
void runExternalHook('main.routeChange', useWebhooksStore(), { from, to });

View file

@ -1,4 +1,4 @@
import Vue from 'vue';
import { defineComponent } from 'vue';
import type { INodeUi } from '@/Interface';
import type { INodeTypeDescription, IPinData } from 'n8n-workflow';
import { stringSizeInBytes } from '@/utils';
@ -13,7 +13,7 @@ export interface IPinDataContext {
$showError(error: Error, title: string): void;
}
export const pinData = (Vue as Vue.VueConstructor<Vue & IPinDataContext>).extend({
export const pinData = defineComponent({
setup() {
return {
...useToast(),

Some files were not shown because too many files have changed in this diff Show more