refactor(editor): Improve linting for component and prop names (no-changelog) (#8169)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-12-28 09:49:58 +01:00 committed by GitHub
parent 639afcd7a5
commit 68cff4c59e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
304 changed files with 3428 additions and 3516 deletions

View file

@ -4,7 +4,7 @@
module.exports = {
plugins: ['vue'],
extends: ['plugin:vue/vue3-essential', '@vue/typescript', './base'],
extends: ['plugin:vue/vue3-recommended', '@vue/typescript', './base'],
env: {
browser: true,
@ -37,6 +37,22 @@ module.exports = {
'vue/no-unused-components': 'error',
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'error',
'vue/component-name-in-template-casing': [
'error',
'PascalCase',
{
registeredComponentsOnly: true,
},
],
'vue/no-reserved-component-names': [
'error',
{
disallowVueBuiltInComponents: true,
disallowVue3BuiltInComponents: false,
},
],
'vue/prop-name-casing': ['error', 'camelCase'],
'vue/attribute-hyphenation': ['error', 'always'],
// TODO: fix these
'@typescript-eslint/no-unsafe-call': 'off',

View file

@ -1,37 +1,37 @@
<template>
<div :class="['n8n-action-box', $style.container]" data-test-id="action-box">
<div :class="$style.emoji" v-if="emoji">
<div v-if="emoji" :class="$style.emoji">
{{ emoji }}
</div>
<div :class="$style.heading" v-if="heading || $slots.heading">
<n8n-heading size="xlarge" align="center">
<div v-if="heading || $slots.heading" :class="$style.heading">
<N8nHeading size="xlarge" align="center">
<slot name="heading">{{ heading }}</slot>
</n8n-heading>
</N8nHeading>
</div>
<div :class="$style.description" @click="$emit('descriptionClick', $event)">
<n8n-text color="text-base">
<N8nText color="text-base">
<slot name="description">
<span v-html="description"></span>
</slot>
</n8n-text>
</N8nText>
</div>
<n8n-button
<N8nButton
v-if="buttonText"
:label="buttonText"
:type="buttonType"
size="large"
@click="$emit('click:button', $event)"
/>
<n8n-callout
<N8nCallout
v-if="calloutText"
:theme="calloutTheme"
:icon="calloutIcon"
:class="$style.callout"
>
<n8n-text color="text-base">
<N8nText color="text-base">
<span size="small" v-html="calloutText"></span>
</n8n-text>
</n8n-callout>
</N8nText>
</N8nCallout>
</div>
</template>
@ -43,7 +43,7 @@ import N8nCallout from '../N8nCallout';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-action-box',
name: 'N8nActionBox',
components: {
N8nButton,
N8nHeading,

View file

@ -1,27 +1,27 @@
<template>
<div :class="['action-dropdown-container', $style.actionDropdownContainer]">
<el-dropdown
<ElDropdown
ref="elementDropdown"
:placement="placement"
:trigger="trigger"
@command="onSelect"
:popper-class="{ [$style.shadow]: true, [$style.hideArrow]: hideArrow }"
@command="onSelect"
@visible-change="onVisibleChange"
ref="elementDropdown"
>
<slot v-if="$slots.activator" name="activator" />
<n8n-icon-button
v-else
@blur="onButtonBlur"
type="tertiary"
text
:class="$style.activator"
:size="activatorSize"
:icon="activatorIcon"
@blur="onButtonBlur"
/>
<template #dropdown>
<el-dropdown-menu :class="$style.userActionsMenu">
<el-dropdown-item
<ElDropdownMenu :class="$style.userActionsMenu">
<ElDropdownItem
v-for="item in items"
:key="item.id"
:command="item.id"
@ -31,22 +31,22 @@
>
<div :class="getItemClasses(item)" :data-test-id="`${testIdPrefix}-item-${item.id}`">
<span v-if="item.icon" :class="$style.icon">
<n8n-icon :icon="item.icon" :size="iconSize" />
<N8nIcon :icon="item.icon" :size="iconSize" />
</span>
<span :class="$style.label">
{{ item.label }}
</span>
<n8n-keyboard-shortcut
<N8nKeyboardShortcut
v-if="item.shortcut"
v-bind="item.shortcut"
:class="$style.shortcut"
>
</n8n-keyboard-shortcut>
</N8nKeyboardShortcut>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</ElDropdownItem>
</ElDropdownMenu>
</template>
</el-dropdown>
</ElDropdown>
</div>
</template>
@ -75,7 +75,7 @@ export interface IActionDropdownItem {
// It can be used in different parts of editor UI while ActionToggle
// is designed to be used in card components.
export default defineComponent({
name: 'n8n-action-dropdown',
name: 'N8nActionDropdown',
components: {
ElDropdown,
ElDropdownMenu,
@ -83,10 +83,6 @@ export default defineComponent({
N8nIcon,
N8nKeyboardShortcut,
},
data() {
const testIdPrefix = this.$attrs['data-test-id'];
return { testIdPrefix };
},
props: {
items: {
type: Array as PropType<IActionDropdownItem[]>,
@ -121,6 +117,10 @@ export default defineComponent({
default: false,
},
},
data() {
const testIdPrefix = this.$attrs['data-test-id'];
return { testIdPrefix };
},
methods: {
getItemClasses(item: IActionDropdownItem): Record<string, boolean> {
return {

View file

@ -1,6 +1,6 @@
<template>
<span @click.stop.prevent :class="$style.container" data-test-id="action-toggle">
<el-dropdown
<span :class="$style.container" data-test-id="action-toggle" @click.stop.prevent>
<ElDropdown
:placement="placement"
:size="size"
trigger="click"
@ -9,7 +9,7 @@
>
<slot>
<span :class="{ [$style.button]: true, [$style[theme]]: !!theme }">
<n8n-icon
<N8nIcon
:icon="iconOrientation === 'horizontal' ? 'ellipsis-h' : 'ellipsis-v'"
:size="iconSize"
/>
@ -17,8 +17,8 @@
</slot>
<template #dropdown>
<el-dropdown-menu data-test-id="action-toggle-dropdown">
<el-dropdown-item
<ElDropdownMenu data-test-id="action-toggle-dropdown">
<ElDropdownItem
v-for="action in actions"
:key="action.value"
:command="action.value"
@ -27,17 +27,17 @@
>
{{ action.label }}
<div :class="$style.iconContainer">
<n8n-icon
<N8nIcon
v-if="action.type === 'external-link'"
icon="external-link-alt"
size="xsmall"
color="text-base"
/>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</ElDropdownItem>
</ElDropdownMenu>
</template>
</el-dropdown>
</ElDropdown>
</span>
</template>
@ -49,7 +49,7 @@ import N8nIcon from '../N8nIcon';
import type { UserAction } from '@/types';
export default defineComponent({
name: 'n8n-action-toggle',
name: 'N8nActionToggle',
components: {
ElDropdown,
ElDropdownMenu,

View file

@ -2,7 +2,7 @@
<div :class="alertBoxClassNames" role="alert">
<div :class="$style.content">
<span v-if="showIcon || $slots.icon" :class="$style.icon">
<n8n-icon v-if="showIcon" :icon="icon" />
<N8nIcon v-if="showIcon" :icon="icon" />
<slot v-else-if="$slots.icon" name="icon" />
</span>
<div :class="$style.text">

View file

@ -24,7 +24,10 @@ const sizes: { [size: string]: number } = {
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-avatar',
name: 'N8nAvatar',
components: {
Avatar,
},
props: {
firstName: {
type: String,
@ -48,9 +51,6 @@ export default defineComponent({
],
},
},
components: {
Avatar,
},
computed: {
initials() {
return (

View file

@ -1,8 +1,8 @@
<template>
<span :class="['n8n-badge', $style[theme]]">
<n8n-text :size="size" :bold="bold" :compact="true">
<N8nText :size="size" :bold="bold" :compact="true">
<slot></slot>
</n8n-text>
</N8nText>
</span>
</template>
@ -12,6 +12,9 @@ import N8nText from '../N8nText';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
N8nText,
},
props: {
theme: {
type: String,
@ -30,9 +33,6 @@ export default defineComponent({
default: false,
},
},
components: {
N8nText,
},
});
</script>

View file

@ -9,9 +9,9 @@
aria-live="polite"
v-bind="$attrs"
>
<span :class="$style.icon" v-if="loading || icon">
<n8n-spinner v-if="loading" :size="size" />
<n8n-icon v-else-if="icon" :icon="icon" :size="size" />
<span v-if="loading || icon" :class="$style.icon">
<N8nSpinner v-if="loading" :size="size" />
<N8nIcon v-else-if="icon" :icon="icon" :size="size" />
</span>
<span v-if="label || $slots.default">
<slot>{{ label }}</slot>

View file

@ -1,12 +1,12 @@
<template>
<div :class="classes" role="alert">
<div :class="$style.messageSection">
<div :class="$style.icon" v-if="!iconless">
<n8n-icon :icon="getIcon" :size="getIconSize" />
<div v-if="!iconless" :class="$style.icon">
<N8nIcon :icon="getIcon" :size="getIconSize" />
</div>
<n8n-text size="small">
<N8nText size="small">
<slot />
</n8n-text>
</N8nText>
&nbsp;
<slot name="actions" />
</div>
@ -28,7 +28,7 @@ const CALLOUT_DEFAULT_ICONS: { [key: string]: string } = {
};
export default defineComponent({
name: 'n8n-callout',
name: 'N8nCallout',
components: {
N8nText,
N8nIcon,

View file

@ -1,16 +1,16 @@
<template>
<div :class="classes" v-bind="$attrs">
<div :class="$style.icon" v-if="$slots.prepend">
<div v-if="$slots.prepend" :class="$style.icon">
<slot name="prepend" />
</div>
<div :class="$style.content">
<div :class="$style.header" v-if="$slots.header">
<div v-if="$slots.header" :class="$style.header">
<slot name="header" />
</div>
<div :class="$style.body" v-if="$slots.default">
<div v-if="$slots.default" :class="$style.body">
<slot />
</div>
<div :class="$style.footer" v-if="$slots.footer">
<div v-if="$slots.footer" :class="$style.footer">
<slot name="footer" />
</div>
</div>
@ -24,7 +24,7 @@
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-card',
name: 'N8nCard',
inheritAttrs: true,
props: {
hoverable: {

View file

@ -1,23 +1,23 @@
<template>
<el-checkbox
<ElCheckbox
v-bind="$props"
ref="checkbox"
:class="['n8n-checkbox', $style.n8nCheckbox]"
:disabled="disabled"
:indeterminate="indeterminate"
:modelValue="modelValue"
:model-value="modelValue"
@update:modelValue="onUpdateModelValue"
>
<slot></slot>
<n8n-input-label
<N8nInputLabel
v-if="label"
:label="label"
:tooltipText="tooltipText"
:tooltip-text="tooltipText"
:bold="false"
:size="labelSize"
@click.prevent="onLabelClick"
/>
</el-checkbox>
</ElCheckbox>
</template>
<script lang="ts">
@ -26,7 +26,7 @@ import { ElCheckbox } from 'element-plus';
import N8nInputLabel from '../N8nInputLabel';
export default defineComponent({
name: 'n8n-checkbox',
name: 'N8nCheckbox',
components: {
ElCheckbox,
N8nInputLabel,

View file

@ -64,21 +64,21 @@ const onActiveChange = (value: string) => {
</script>
<template>
<span :class="['n8n-color-picker', $style.component]">
<el-color-picker
<ElColorPicker
v-model="model"
v-bind="colorPickerProps"
@change="onChange"
@active-change="onActiveChange"
/>
<n8n-input
<N8nInput
v-if="showInput"
:class="$style.input"
:disabled="props.disabled"
:size="props.size"
:modelValue="color"
:model-value="color"
:name="name"
@update:modelValue="onInput"
type="text"
@update:modelValue="onInput"
/>
</span>
</template>

View file

@ -9,13 +9,12 @@ import N8nOption from '../N8nOption';
import N8nPagination from '../N8nPagination';
export default defineComponent({
name: 'n8n-datatable',
name: 'N8nDatatable',
components: {
N8nSelect,
N8nOption,
N8nPagination,
},
emits: ['update:currentPage', 'update:rowsPerPage'],
props: {
columns: {
type: Array as PropType<DatatableColumn[]>,
@ -38,6 +37,7 @@ export default defineComponent({
default: 10,
},
},
emits: ['update:currentPage', 'update:rowsPerPage'],
setup(props, { emit }) {
const { t } = useI18n();
const rowsPerPageOptions = ref([10, 25, 50, 100]);
@ -130,10 +130,10 @@ export default defineComponent({
</thead>
<tbody>
<template v-for="row in visibleRows">
<slot name="row" :columns="columns" :row="row" :getTdValue="getTdValue">
<slot name="row" :columns="columns" :row="row" :get-td-value="getTdValue">
<tr :key="row.id">
<td v-for="column in columns" :key="column.id" :class="column.classes">
<component v-if="column.render" :is="column.render" :row="row" :column="column" />
<component :is="column.render" v-if="column.render" :row="row" :column="column" />
<span v-else>{{ getTdValue(row, column) }}</span>
</td>
</tr>
@ -143,33 +143,33 @@ export default defineComponent({
</table>
<div :class="$style.pagination">
<n8n-pagination
<N8nPagination
v-if="totalPages > 1"
background
:pager-count="5"
:page-size="rowsPerPage"
layout="prev, pager, next"
:total="totalRows"
:currentPage="currentPage"
:current-page="currentPage"
@update:currentPage="onUpdateCurrentPage"
/>
<div :class="$style.pageSizeSelector">
<n8n-select
<N8nSelect
size="mini"
:modelValue="rowsPerPage"
@update:modelValue="onRowsPerPageChange"
:model-value="rowsPerPage"
teleported
@update:modelValue="onRowsPerPageChange"
>
<template #prepend>{{ t('datatable.pageSize') }}</template>
<n8n-option
<N8nOption
v-for="size in rowsPerPageOptions"
:key="size"
:label="`${size}`"
:value="size"
/>
<n8n-option :label="`All`" value="*"> </n8n-option>
</n8n-select>
<N8nOption :label="`All`" value="*"> </N8nOption>
</N8nSelect>
</div>
</div>
</div>

View file

@ -1,26 +1,26 @@
<template>
<div :class="['n8n-form-box', $style.container]">
<div v-if="title" :class="$style.heading">
<n8n-heading size="xlarge">
<N8nHeading size="xlarge">
{{ title }}
</n8n-heading>
</N8nHeading>
</div>
<div :class="$style.inputsContainer">
<n8n-form-inputs
<N8nFormInputs
:inputs="inputs"
:eventBus="formBus"
:columnView="true"
:event-bus="formBus"
:column-view="true"
@update="onUpdateModelValue"
@submit="onSubmit"
/>
</div>
<div :class="$style.buttonsContainer" v-if="secondaryButtonText || buttonText">
<div v-if="secondaryButtonText || buttonText" :class="$style.buttonsContainer">
<span v-if="secondaryButtonText" :class="$style.secondaryButtonContainer">
<n8n-link size="medium" theme="text" @click="onSecondaryButtonClick">
<N8nLink size="medium" theme="text" @click="onSecondaryButtonClick">
{{ secondaryButtonText }}
</n8n-link>
</N8nLink>
</span>
<n8n-button
<N8nButton
v-if="buttonText"
:label="buttonText"
:loading="buttonLoading"
@ -30,9 +30,9 @@
/>
</div>
<div :class="$style.actionContainer">
<n8n-link v-if="redirectText && redirectLink" :to="redirectLink">
<N8nLink v-if="redirectText && redirectLink" :to="redirectLink">
{{ redirectText }}
</n8n-link>
</N8nLink>
</div>
<slot></slot>
</div>
@ -47,7 +47,7 @@ import N8nButton from '../N8nButton';
import { createEventBus } from '../../utils';
export default defineComponent({
name: 'n8n-form-box',
name: 'N8nFormBox',
components: {
N8nHeading,
N8nFormInputs,

View file

@ -1,52 +1,52 @@
<template>
<n8n-checkbox
<N8nCheckbox
v-if="type === 'checkbox'"
v-bind="$props"
ref="inputRef"
@update:modelValue="onUpdateModelValue"
@focus="onFocus"
ref="inputRef"
/>
<n8n-input-label
<N8nInputLabel
v-else-if="type === 'toggle'"
:inputName="name"
:input-name="name"
:label="label"
:tooltipText="tooltipText"
:tooltip-text="tooltipText"
:required="required && showRequiredAsterisk"
>
<template #content>
{{ tooltipText }}
</template>
<el-switch
:modelValue="modelValue"
<ElSwitch
:model-value="modelValue"
:active-color="activeColor"
:inactive-color="inactiveColor"
@update:modelValue="onUpdateModelValue"
></el-switch>
</n8n-input-label>
<n8n-input-label
></ElSwitch>
</N8nInputLabel>
<N8nInputLabel
v-else
:inputName="name"
:input-name="name"
:label="label"
:tooltipText="tooltipText"
:tooltip-text="tooltipText"
:required="required && showRequiredAsterisk"
>
<div :class="showErrors ? $style.errorInput : ''" @keydown.stop @keydown.enter="onEnter">
<slot v-if="hasDefaultSlot" />
<n8n-select
:class="{ [$style.multiSelectSmallTags]: tagSize === 'small' }"
<N8nSelect
v-else-if="type === 'select' || type === 'multi-select'"
:modelValue="modelValue"
:class="{ [$style.multiSelectSmallTags]: tagSize === 'small' }"
:model-value="modelValue"
:placeholder="placeholder"
:multiple="type === 'multi-select'"
ref="inputRef"
:disabled="disabled"
:name="name"
:teleported="teleported"
@update:modelValue="onUpdateModelValue"
@focus="onFocus"
@blur="onBlur"
:name="name"
:teleported="teleported"
ref="inputRef"
>
<n8n-option
<N8nOption
v-for="option in options || []"
:key="option.value"
:value="option.value"
@ -54,38 +54,38 @@
:disabled="!!option.disabled"
size="small"
/>
</n8n-select>
<n8n-input
</N8nSelect>
<N8nInput
v-else
:name="name"
ref="inputRef"
:type="type"
:placeholder="placeholder"
:modelValue="modelValue"
:model-value="modelValue"
:maxlength="maxlength"
:autocomplete="autocomplete"
:disabled="disabled"
@update:modelValue="onUpdateModelValue"
@blur="onBlur"
@focus="onFocus"
ref="inputRef"
/>
</div>
<div :class="$style.errors" v-if="showErrors">
<div v-if="showErrors" :class="$style.errors">
<span v-text="validationError" />
<n8n-link
v-if="documentationUrl && documentationText"
:to="documentationUrl"
:newWindow="true"
:new-window="true"
size="small"
theme="danger"
>
{{ documentationText }}
</n8n-link>
</div>
<div :class="$style.infoText" v-else-if="infoText">
<div v-else-if="infoText" :class="$style.infoText">
<span size="small" v-text="infoText" />
</div>
</n8n-input-label>
</N8nInputLabel>
</template>
<script lang="ts" setup>

View file

@ -8,8 +8,8 @@
:class="{ [`mt-${verticalSpacing}`]: verticalSpacing && index > 0 }"
>
<n8n-text
color="text-base"
v-if="input.properties.type === 'info'"
color="text-base"
tag="div"
:size="input.properties.labelSize"
:align="input.properties.labelAlignment"
@ -17,16 +17,16 @@
>
{{ input.properties.label }}
</n8n-text>
<n8n-form-input
<N8nFormInput
v-else
v-bind="input.properties"
:name="input.name"
:label="input.properties.label || ''"
:modelValue="values[input.name]"
:model-value="values[input.name]"
:data-test-id="input.name"
:showValidationWarnings="showValidationWarnings"
:show-validation-warnings="showValidationWarnings"
:teleported="teleported"
:tagSize="tagSize"
:tag-size="tagSize"
@update:modelValue="(value) => onUpdateModelValue(input.name, value)"
@validate="(value) => onValidate(input.name, value)"
@enter="onSubmit"
@ -47,7 +47,7 @@ import type { EventBus } from '../../utils';
import { createEventBus } from '../../utils';
export default defineComponent({
name: 'n8n-form-inputs',
name: 'N8nFormInputs',
components: {
N8nFormInput,
ResizeObserver,
@ -87,20 +87,6 @@ export default defineComponent({
validity: {} as { [key: string]: boolean },
};
},
mounted() {
this.inputs.forEach((input) => {
if (input.hasOwnProperty('initialValue')) {
this.values = {
...this.values,
[input.name]: input.initialValue,
};
}
});
if (this.eventBus) {
this.eventBus.on('submit', () => this.onSubmit());
}
},
computed: {
filteredInputs(): IFormInput[] {
return this.inputs.filter((input) =>
@ -117,6 +103,25 @@ export default defineComponent({
return true;
},
},
watch: {
isReadyToSubmit(ready: boolean) {
this.$emit('ready', ready);
},
},
mounted() {
this.inputs.forEach((input) => {
if (input.hasOwnProperty('initialValue')) {
this.values = {
...this.values,
[input.name]: input.initialValue,
};
}
});
if (this.eventBus) {
this.eventBus.on('submit', () => this.onSubmit());
}
},
methods: {
onUpdateModelValue(name: string, value: unknown) {
this.values = {
@ -146,11 +151,6 @@ export default defineComponent({
}
},
},
watch: {
isReadyToSubmit(ready: boolean) {
this.$emit('ready', ready);
},
},
});
</script>

View file

@ -8,7 +8,7 @@
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-heading',
name: 'N8nHeading',
props: {
tag: {
type: String,

View file

@ -1,7 +1,7 @@
<template>
<n8n-text :size="size" :color="color" :compact="true" class="n8n-icon" v-bind="$attrs">
<font-awesome-icon :icon="icon" :spin="spin" :class="$style[size]" />
</n8n-text>
<N8nText :size="size" :color="color" :compact="true" class="n8n-icon" v-bind="$attrs">
<FontAwesomeIcon :icon="icon" :spin="spin" :class="$style[size]" />
</N8nText>
</template>
<script lang="ts">
@ -11,7 +11,7 @@ import N8nText from '../N8nText';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-icon',
name: 'N8nIcon',
components: {
FontAwesomeIcon,
N8nText,

View file

@ -1,5 +1,5 @@
<template>
<n8n-button square v-bind="{ ...$attrs, ...$props }" />
<N8nButton square v-bind="{ ...$attrs, ...$props }" />
</template>
<script lang="ts">
@ -8,7 +8,7 @@ import N8nButton from '../N8nButton';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-icon-button',
name: 'N8nIconButton',
components: {
N8nButton,
},

View file

@ -1,17 +1,17 @@
<template>
<div :class="['accordion', $style.container]">
<div :class="{ [$style.header]: true, [$style.expanded]: expanded }" @click="toggle">
<n8n-icon
<N8nIcon
v-if="headerIcon"
:icon="headerIcon.icon"
:color="headerIcon.color"
size="small"
class="mr-2xs"
/>
<n8n-text :class="$style.headerText" color="text-base" size="small" align="left" bold>{{
<N8nText :class="$style.headerText" color="text-base" size="small" align="left" bold>{{
title
}}</n8n-text>
<n8n-icon :icon="expanded ? 'chevron-up' : 'chevron-down'" bold />
}}</N8nText>
<N8nIcon :icon="expanded ? 'chevron-up' : 'chevron-down'" bold />
</div>
<div
v-if="expanded"
@ -23,16 +23,16 @@
<div v-for="item in items" :key="item.id" :class="$style.accordionItem">
<n8n-tooltip :disabled="!item.tooltip">
<template #content>
<div v-html="item.tooltip" @click="onTooltipClick(item.id, $event)"></div>
<div @click="onTooltipClick(item.id, $event)" v-html="item.tooltip"></div>
</template>
<n8n-icon :icon="item.icon" :color="item.iconColor" size="small" class="mr-2xs" />
<N8nIcon :icon="item.icon" :color="item.iconColor" size="small" class="mr-2xs" />
</n8n-tooltip>
<n8n-text size="small" color="text-base">{{ item.label }}</n8n-text>
<N8nText size="small" color="text-base">{{ item.label }}</N8nText>
</div>
</div>
<n8n-text color="text-base" size="small" align="left">
<N8nText color="text-base" size="small" align="left">
<span v-html="description"></span>
</n8n-text>
</N8nText>
<slot name="customContent"></slot>
</div>
</div>
@ -55,7 +55,7 @@ export interface IAccordionItem {
}
export default defineComponent({
name: 'n8n-info-accordion',
name: 'N8nInfoAccordion',
components: {
N8nText,
N8nIcon,
@ -84,17 +84,17 @@ export default defineComponent({
default: () => createEventBus(),
},
},
data() {
return {
expanded: false,
};
},
mounted() {
this.eventBus.on('expand', () => {
this.expanded = true;
});
this.expanded = this.initiallyExpanded;
},
data() {
return {
expanded: false,
};
},
methods: {
toggle() {
this.expanded = !this.expanded;

View file

@ -8,23 +8,23 @@
[$style.bold]: bold,
}"
>
<n8n-tooltip
<N8nTooltip
v-if="type === 'tooltip'"
:placement="tooltipPlacement"
:popperClass="$style.tooltipPopper"
:popper-class="$style.tooltipPopper"
:disabled="type !== 'tooltip'"
>
<span :class="$style.iconText" :style="{ color: iconData.color }">
<n8n-icon :icon="iconData.icon" />
<N8nIcon :icon="iconData.icon" />
</span>
<template #content>
<span>
<slot />
</span>
</template>
</n8n-tooltip>
<span :class="$style.iconText" v-else>
<n8n-icon :icon="iconData.icon" />
</N8nTooltip>
<span v-else :class="$style.iconText">
<N8nIcon :icon="iconData.icon" />
<span>
<slot />
</span>
@ -39,7 +39,7 @@ import N8nTooltip from '../N8nTooltip';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-info-tip',
name: 'N8nInfoTip',
components: {
N8nIcon,
N8nTooltip,

View file

@ -1,25 +1,25 @@
<template>
<el-input
<ElInput
ref="innerInput"
:size="computedSize"
:class="['n8n-input', ...classes]"
:autoComplete="autocomplete"
:autocomplete="autocomplete"
:name="name"
ref="innerInput"
v-bind="{ ...$props, ...$attrs }"
>
<template #prepend v-if="$slots.prepend">
<template v-if="$slots.prepend" #prepend>
<slot name="prepend" />
</template>
<template #append v-if="$slots.append">
<template v-if="$slots.append" #append>
<slot name="append" />
</template>
<template #prefix v-if="$slots.prefix">
<template v-if="$slots.prefix" #prefix>
<slot name="prefix" />
</template>
<template #suffix v-if="$slots.suffix">
<template v-if="$slots.suffix" #suffix>
<slot name="suffix" />
</template>
</el-input>
</ElInput>
</template>
<script lang="ts">
@ -31,7 +31,7 @@ import { uid } from '../../utils';
type InputRef = InstanceType<typeof ElInput>;
export default defineComponent({
name: 'n8n-input',
name: 'N8nInput',
components: {
ElInput,
},

View file

@ -7,7 +7,7 @@ exports[`N8nInput > should render correctly 1`] = `
<!--v-if-->
<div class="el-input__wrapper">
<!-- prefix slot -->
<!--v-if--><input class="el-input__inner" autocomplete="off" name="input" rows="2" maxlength="Infinity" title="" type="text" tabindex="0" placeholder=""><!-- suffix slot -->
<!--v-if--><input class="el-input__inner" name="input" rows="2" maxlength="Infinity" title="" type="text" autocomplete="off" tabindex="0" placeholder=""><!-- suffix slot -->
<!--v-if-->
</div><!-- append slot -->
<!--v-if-->

View file

@ -12,22 +12,22 @@
[$style.overflow]: !!$slots.options,
}"
>
<div :class="$style.title" v-if="label">
<n8n-text :bold="bold" :size="size" :compact="compact" :color="color">
<div v-if="label" :class="$style.title">
<N8nText :bold="bold" :size="size" :compact="compact" :color="color">
{{ label }}
<n8n-text color="primary" :bold="bold" :size="size" v-if="required">*</n8n-text>
</n8n-text>
<N8nText v-if="required" color="primary" :bold="bold" :size="size">*</N8nText>
</N8nText>
</div>
<span
:class="[$style.infoIcon, showTooltip ? $style.visible : $style.hidden]"
v-if="tooltipText && label"
:class="[$style.infoIcon, showTooltip ? $style.visible : $style.hidden]"
>
<n8n-tooltip placement="top" :popper-class="$style.tooltipPopper">
<n8n-icon icon="question-circle" size="small" />
<N8nTooltip placement="top" :popper-class="$style.tooltipPopper">
<N8nIcon icon="question-circle" size="small" />
<template #content>
<div v-html="addTargetBlank(tooltipText)" />
</template>
</n8n-tooltip>
</N8nTooltip>
</span>
<div
v-if="$slots.options && label"
@ -55,7 +55,7 @@ import { addTargetBlank } from '../utils/helpers';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-input-label',
name: 'N8nInputLabel',
components: {
N8nText,
N8nIcon,

View file

@ -4,15 +4,15 @@ import { defineComponent } from 'vue';
export default defineComponent({
name: 'N8nInputNumber',
props: {
...ElInputNumber.props,
},
components: {
ElInputNumber,
},
props: {
...ElInputNumber.props,
},
});
</script>
<template>
<el-input-number v-bind="{ ...$props, ...$attrs }" />
<ElInputNumber v-bind="{ ...$props, ...$attrs }" />
</template>

View file

@ -31,7 +31,7 @@ const keys = computed(() => {
<template>
<div :class="$style.shortcut">
<div v-for="key of keys" :class="$style.keyWrapper" :key="key">
<div v-for="key of keys" :key="key" :class="$style.keyWrapper">
<div :class="$style.key">{{ key }}</div>
</div>
</div>

View file

@ -1,11 +1,11 @@
<template>
<n8n-route :to="to" :newWindow="newWindow" v-bind="$attrs" class="n8n-link">
<N8nRoute :to="to" :new-window="newWindow" v-bind="$attrs" class="n8n-link">
<span :class="$style[`${underline ? `${theme}-underline` : theme}`]">
<n8n-text :size="size" :bold="bold">
<N8nText :size="size" :bold="bold">
<slot></slot>
</n8n-text>
</N8nText>
</span>
</n8n-route>
</N8nRoute>
</template>
<script lang="ts">
@ -14,7 +14,11 @@ import N8nText from '../N8nText';
import N8nRoute from '../N8nRoute';
export default defineComponent({
name: 'n8n-link',
name: 'N8nLink',
components: {
N8nText,
N8nRoute,
},
props: {
size: {
type: String,
@ -41,10 +45,6 @@ export default defineComponent({
['primary', 'danger', 'text', 'secondary'].includes(value),
},
},
components: {
N8nText,
N8nRoute,
},
});
</script>

View file

@ -1,5 +1,5 @@
<template>
<el-skeleton
<ElSkeleton
:loading="loading"
:animated="animated"
:class="['n8n-loading', `n8n-loading-${variant}`]"
@ -13,7 +13,7 @@
[$style.h1Last]: item === rows && rows > 1 && shrinkLast,
}"
>
<el-skeleton-item :variant="variant" />
<ElSkeletonItem :variant="variant" />
</div>
</div>
<div v-else-if="variant === 'p'">
@ -24,15 +24,15 @@
[$style.pLast]: item === rows && rows > 1 && shrinkLast,
}"
>
<el-skeleton-item :variant="variant" />
<ElSkeletonItem :variant="variant" />
</div>
</div>
<div :class="$style.custom" v-else-if="variant === 'custom'">
<el-skeleton-item />
<div v-else-if="variant === 'custom'" :class="$style.custom">
<ElSkeletonItem />
</div>
<el-skeleton-item v-else :variant="variant" />
<ElSkeletonItem v-else :variant="variant" />
</template>
</el-skeleton>
</ElSkeleton>
</template>
<script lang="ts">
@ -40,7 +40,7 @@ import { ElSkeleton, ElSkeletonItem } from 'element-plus';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-loading',
name: 'N8nLoading',
components: {
ElSkeleton,
ElSkeletonItem,

View file

@ -4,12 +4,12 @@
v-if="!loading"
ref="editor"
:class="$style[theme]"
v-html="htmlContent"
@click="onClick"
v-html="htmlContent"
/>
<div v-else :class="$style.markdown">
<div v-for="(block, index) in loadingBlocks" :key="index">
<n8n-loading :loading="loading" :rows="loadingRows" animated variant="p" />
<N8nLoading :loading="loading" :rows="loadingRows" animated variant="p" />
<div :class="$style.spacer" />
</div>
</div>
@ -62,10 +62,10 @@ export interface Options {
}
export default defineComponent({
name: 'N8nMarkdown',
components: {
N8nLoading,
},
name: 'n8n-markdown',
props: {
content: {
type: String,

View file

@ -15,33 +15,33 @@
<div v-if="$slots.menuPrefix" :class="$style.menuPrefix">
<slot name="menuPrefix"></slot>
</div>
<el-menu :defaultActive="defaultActive" :collapse="collapsed">
<n8n-menu-item
<ElMenu :default-active="defaultActive" :collapse="collapsed">
<N8nMenuItem
v-for="item in upperMenuItems"
:key="item.id"
:item="item"
:compact="collapsed"
:tooltipDelay="tooltipDelay"
:tooltip-delay="tooltipDelay"
:mode="mode"
:activeTab="activeTab"
:active-tab="activeTab"
:handle-select="onSelect"
/>
</el-menu>
</ElMenu>
</div>
<div :class="[$style.lowerContent, 'pb-2xs']">
<slot name="beforeLowerMenu"></slot>
<el-menu :defaultActive="defaultActive" :collapse="collapsed">
<n8n-menu-item
<ElMenu :default-active="defaultActive" :collapse="collapsed">
<N8nMenuItem
v-for="item in lowerMenuItems"
:key="item.id"
:item="item"
:compact="collapsed"
:tooltipDelay="tooltipDelay"
:tooltip-delay="tooltipDelay"
:mode="mode"
:activeTab="activeTab"
:active-tab="activeTab"
:handle-select="onSelect"
/>
</el-menu>
</ElMenu>
<div v-if="$slots.menuSuffix" :class="$style.menuSuffix">
<slot name="menuSuffix"></slot>
</div>
@ -61,16 +61,11 @@ import { defineComponent } from 'vue';
import type { IMenuItem, RouteObject } from '../../types';
export default defineComponent({
name: 'n8n-menu',
name: 'N8nMenu',
components: {
ElMenu,
N8nMenuItem,
},
data() {
return {
activeTab: this.value,
};
},
props: {
type: {
type: String,
@ -106,22 +101,10 @@ export default defineComponent({
default: '',
},
},
mounted() {
if (this.mode === 'router') {
const found = this.items.find((item) => {
return (
(Array.isArray(item.activateOnRouteNames) &&
item.activateOnRouteNames.includes(this.currentRoute.name || '')) ||
(Array.isArray(item.activateOnRoutePaths) &&
item.activateOnRoutePaths.includes(this.currentRoute.path))
);
});
this.activeTab = found ? found.id : '';
} else {
this.activeTab = this.items.length > 0 ? this.items[0].id : '';
}
this.$emit('update:modelValue', this.activeTab);
data() {
return {
activeTab: this.value,
};
},
computed: {
upperMenuItems(): IMenuItem[] {
@ -143,6 +126,23 @@ export default defineComponent({
);
},
},
mounted() {
if (this.mode === 'router') {
const found = this.items.find((item) => {
return (
(Array.isArray(item.activateOnRouteNames) &&
item.activateOnRouteNames.includes(this.currentRoute.name || '')) ||
(Array.isArray(item.activateOnRoutePaths) &&
item.activateOnRoutePaths.includes(this.currentRoute.path))
);
});
this.activeTab = found ? found.id : '';
} else {
this.activeTab = this.items.length > 0 ? this.items[0].id : '';
}
this.$emit('update:modelValue', this.activeTab);
},
methods: {
onSelect(item: IMenuItem): void {
if (item && item.type === 'link' && item.properties) {

View file

@ -1,6 +1,6 @@
<template>
<div :class="['n8n-menu-item', $style.item]">
<el-sub-menu
<ElSubMenu
v-if="item.children?.length"
:id="item.id"
:class="{
@ -13,7 +13,7 @@
:popper-class="submenuPopperClass"
>
<template #title>
<n8n-icon
<N8nIcon
v-if="item.icon"
:class="$style.icon"
:icon="item.icon"
@ -26,21 +26,21 @@
:key="child.id"
:item="child"
:compact="false"
:tooltipDelay="tooltipDelay"
:popperClass="popperClass"
:tooltip-delay="tooltipDelay"
:popper-class="popperClass"
:mode="mode"
:activeTab="activeTab"
:active-tab="activeTab"
:handle-select="handleSelect"
/>
</el-sub-menu>
<n8n-tooltip
</ElSubMenu>
<N8nTooltip
v-else
placement="right"
:content="item.label"
:disabled="!compact"
:show-after="tooltipDelay"
>
<el-menu-item
<ElMenuItem
:id="item.id"
:class="{
[$style.menuItem]: true,
@ -53,14 +53,14 @@
:index="item.id"
@click="handleSelect(item)"
>
<n8n-icon
<N8nIcon
v-if="item.icon"
:class="$style.icon"
:icon="item.icon"
:size="item.customIconSize || 'large'"
/>
<span :class="$style.label">{{ item.label }}</span>
<n8n-tooltip
<N8nTooltip
v-if="item.secondaryIcon"
:class="$style.secondaryIcon"
:placement="item.secondaryIcon?.tooltip?.placement || 'right'"
@ -68,10 +68,10 @@
:disabled="compact || !item.secondaryIcon?.tooltip?.content"
:show-after="tooltipDelay"
>
<n8n-icon :icon="item.secondaryIcon.name" :size="item.secondaryIcon.size || 'small'" />
</n8n-tooltip>
</el-menu-item>
</n8n-tooltip>
<N8nIcon :icon="item.secondaryIcon.name" :size="item.secondaryIcon.size || 'small'" />
</N8nTooltip>
</ElMenuItem>
</N8nTooltip>
</div>
</template>
@ -84,7 +84,7 @@ import { defineComponent } from 'vue';
import type { IMenuItem, RouteObject } from '../../types';
export default defineComponent({
name: 'n8n-menu-item',
name: 'N8nMenuItem',
components: {
ElSubMenu,
ElMenuItem,

View file

@ -36,18 +36,18 @@ const i18n = useI18n();
</div>
<div>
<div :class="$style.details">
<span :class="$style.name" v-text="title" data-test-id="node-creator-item-name" />
<el-tag v-if="tag" :class="$style.tag" size="small" round type="success">
<span :class="$style.name" data-test-id="node-creator-item-name" v-text="title" />
<ElTag v-if="tag" :class="$style.tag" size="small" round type="success">
{{ tag }}
</el-tag>
<font-awesome-icon
icon="bolt"
</ElTag>
<FontAwesomeIcon
v-if="isTrigger"
icon="bolt"
size="xs"
:title="i18n.baseText('nodeCreator.nodeItem.triggerIconTitle')"
:class="$style.triggerIcon"
/>
<n8n-tooltip
<N8nTooltip
v-if="!!$slots.tooltip"
placement="top"
data-test-id="node-creator-item-tooltip"
@ -56,7 +56,7 @@ const i18n = useI18n();
<slot name="tooltip" />
</template>
<n8n-icon :class="$style.tooltipIcon" icon="cube" />
</n8n-tooltip>
</N8nTooltip>
</div>
<p
v-if="description"
@ -66,8 +66,8 @@ const i18n = useI18n();
/>
</div>
<slot name="dragContent" />
<button :class="$style.panelIcon" v-if="showActionArrow">
<font-awesome-icon :class="$style.panelArrow" icon="arrow-right" />
<button v-if="showActionArrow" :class="$style.panelIcon">
<FontAwesomeIcon :class="$style.panelArrow" icon="arrow-right" />
</button>
</div>
</template>

View file

@ -9,20 +9,20 @@
:style="iconStyleData"
>
<!-- ElementUI tooltip is prone to memory-leaking so we only render it if we really need it -->
<n8n-tooltip :placement="tooltipPosition" :disabled="!showTooltip" v-if="showTooltip">
<N8nTooltip v-if="showTooltip" :placement="tooltipPosition" :disabled="!showTooltip">
<template #content>{{ nodeTypeName }}</template>
<div v-if="type !== 'unknown'" :class="$style.icon">
<img v-if="type === 'file'" :src="src" :class="$style.nodeIconImage" />
<font-awesome-icon v-else :icon="name" :class="$style.iconFa" :style="fontStyleData" />
<FontAwesomeIcon v-else :icon="name" :class="$style.iconFa" :style="fontStyleData" />
</div>
<div v-else :class="$style.nodeIconPlaceholder">
{{ nodeTypeName ? nodeTypeName.charAt(0) : '?' }}
</div>
</n8n-tooltip>
</N8nTooltip>
<template v-else>
<div v-if="type !== 'unknown'" :class="$style.icon">
<img v-if="type === 'file'" :src="src" :class="$style.nodeIconImage" />
<font-awesome-icon v-else :icon="name" :style="fontStyleData" />
<FontAwesomeIcon v-else :icon="name" :style="fontStyleData" />
<div v-if="badge" :class="$style.badge" :style="badgeStyleData">
<n8n-node-icon :type="badge.type" :src="badge.src" :size="badgeSize"></n8n-node-icon>
</div>
@ -41,7 +41,7 @@ import { defineComponent, type PropType } from 'vue';
import N8nTooltip from '../N8nTooltip';
export default defineComponent({
name: 'n8n-node-icon',
name: 'N8nNodeIcon',
components: {
N8nTooltip,
FontAwesomeIcon,

View file

@ -1,16 +1,16 @@
<template>
<div :id="id" :class="classes" role="alert" @click="onClick">
<div class="notice-content">
<n8n-text size="small" :compact="true">
<N8nText size="small" :compact="true">
<slot>
<span
:class="showFullContent ? $style['expanded'] : $style['truncated']"
:id="`${id}-content`"
:class="showFullContent ? $style['expanded'] : $style['truncated']"
role="region"
v-html="sanitizeHtml(showFullContent ? fullContent : content)"
/>
</slot>
</n8n-text>
</N8nText>
</div>
</div>
</template>
@ -23,8 +23,11 @@ import Locale from '../../mixins/locale';
import { uid } from '../../utils';
export default defineComponent({
name: 'n8n-notice',
name: 'N8nNotice',
directives: {},
components: {
N8nText,
},
mixins: [Locale],
props: {
id: {
@ -44,9 +47,6 @@ export default defineComponent({
default: '',
},
},
components: {
N8nText,
},
data() {
return {
showFullContent: false,

View file

@ -2,7 +2,7 @@
exports[`components > N8nNotice > props > content > should render HTML 1`] = `
"<div id="notice" class="notice notice warning" role="alert">
<div class="notice-content"><span class="n8n-text compact size-small regular"><span class="truncated" id="notice-content" role="region"><strong>Hello world!</strong> This is a notice.</span></span></div>
<div class="notice-content"><span class="n8n-text compact size-small regular"><span id="notice-content" class="truncated" role="region"><strong>Hello world!</strong> This is a notice.</span></span></div>
</div>"
`;

View file

@ -3,15 +3,15 @@ import { ElOption } from 'element-plus';
import { defineComponent } from 'vue';
export default defineComponent({
props: {
...ElOption.props,
},
components: {
ElOption,
},
props: {
...ElOption.props,
},
});
</script>
<template>
<el-option v-bind="{ ...$props, ...$attrs }"><slot /></el-option>
<ElOption v-bind="{ ...$props, ...$attrs }"><slot /></ElOption>
</template>

View file

@ -3,17 +3,17 @@ import { defineComponent } from 'vue';
import { ElPagination } from 'element-plus';
export default defineComponent({
props: {
...ElPagination.props,
},
components: {
ElPagination,
},
props: {
...ElPagination.props,
},
});
</script>
<template>
<el-pagination
<ElPagination
class="is-background"
layout="prev, pager, next"
v-bind="{ ...$props, ...$attrs }"

View file

@ -4,22 +4,22 @@ import { defineComponent } from 'vue';
export default defineComponent({
name: 'N8nPopover',
props: {
...ElPopover.props,
},
components: {
ElPopover,
},
props: {
...ElPopover.props,
},
});
</script>
<template>
<span>
<el-popover v-bind="{ ...$props, ...$attrs }">
<ElPopover v-bind="{ ...$props, ...$attrs }">
<template #reference>
<slot name="reference" />
</template>
<slot />
</el-popover>
</ElPopover>
</span>
</template>

View file

@ -12,7 +12,7 @@
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-pulse',
name: 'N8nPulse',
});
</script>

View file

@ -27,7 +27,7 @@
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-radio-button',
name: 'N8nRadioButton',
props: {
label: {
type: String,

View file

@ -28,7 +28,10 @@ export interface RadioOption {
}
export default defineComponent({
name: 'n8n-radio-buttons',
name: 'N8nRadioButtons',
components: {
RadioButton,
},
props: {
modelValue: {
type: String,
@ -44,9 +47,6 @@ export default defineComponent({
type: Boolean,
},
},
components: {
RadioButton,
},
methods: {
onClick(option: { label: string; value: string; disabled?: boolean }) {
if (this.disabled || option.disabled) {

View file

@ -5,7 +5,7 @@ import type { PropType, ComponentPublicInstance } from 'vue';
import { computed, defineComponent, onMounted, onBeforeMount, ref, nextTick, watch } from 'vue';
export default defineComponent({
name: 'n8n-recycle-scroller',
name: 'N8nRecycleScroller',
props: {
itemSize: {
type: Number,
@ -223,16 +223,16 @@ export default defineComponent({
</script>
<template>
<div class="recycle-scroller-wrapper" ref="wrapperRef">
<div class="recycle-scroller" :style="scrollerStyles" ref="scrollerRef">
<div class="recycle-scroller-items-wrapper" :style="itemsStyles" ref="itemsRef">
<div ref="wrapperRef" class="recycle-scroller-wrapper">
<div ref="scrollerRef" class="recycle-scroller" :style="scrollerStyles">
<div ref="itemsRef" class="recycle-scroller-items-wrapper" :style="itemsStyles">
<div
v-for="item in itemsVisible"
:key="item[itemKey]"
class="recycle-scroller-item"
:ref="(element) => (itemRefs[item[itemKey]] = element)"
class="recycle-scroller-item"
>
<slot :item="item" :updateItemSize="onUpdateItemSize" />
<slot :item="item" :update-item-size="onUpdateItemSize" />
</div>
</div>
</div>

View file

@ -47,7 +47,7 @@ const directionsCursorMaps: { [key: string]: string } = {
};
export default defineComponent({
name: 'n8n-resize',
name: 'N8nResize',
props: {
isResizingEnabled: {
type: Boolean,

View file

@ -11,7 +11,7 @@
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-route',
name: 'N8nRoute',
props: {
to: {
type: String || Object,

View file

@ -9,22 +9,22 @@
<div v-if="$slots.prepend" :class="$style.prepend">
<slot name="prepend" />
</div>
<el-select
<ElSelect
v-bind="{ ...$props, ...listeners }"
:modelValue="modelValue"
ref="innerSelect"
:model-value="modelValue"
:size="computedSize"
:class="$style[classes]"
:popper-class="popperClass"
ref="innerSelect"
>
<template #prefix v-if="$slots.prefix">
<template v-if="$slots.prefix" #prefix>
<slot name="prefix" />
</template>
<template #suffix v-if="$slots.suffix">
<template v-if="$slots.suffix" #suffix>
<slot name="suffix" />
</template>
<slot></slot>
</el-select>
</ElSelect>
</div>
</template>
@ -41,7 +41,7 @@ export interface IProps {
}
export default defineComponent({
name: 'n8n-select',
name: 'N8nSelect',
components: {
ElSelect,
},

View file

@ -26,11 +26,6 @@ describe('components', () => {
it('should select an option', async () => {
const n8nSelectTestComponent = defineComponent({
template: `
<n8n-select v-model="selected">
<n8n-option v-for="o in options" :key="o" :value="o" :label="o" />
</n8n-select>
`,
setup() {
const options = ref(['1', '2', '3']);
const selected = ref('');
@ -40,6 +35,11 @@ describe('components', () => {
selected,
};
},
template: `
<n8n-select v-model="selected">
<n8n-option v-for="o in options" :key="o" :value="o" :label="o" />
</n8n-select>
`,
});
const { container } = render(n8nSelectTestComponent, {

View file

@ -6,7 +6,7 @@
<div></div>
<div></div>
</div>
<n8n-icon v-else icon="spinner" :size="size" spin />
<N8nIcon v-else icon="spinner" :size="size" spin />
</span>
</template>
@ -16,7 +16,7 @@ import N8nIcon from '../N8nIcon';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-spinner',
name: 'N8nSpinner',
components: {
N8nIcon,
},

View file

@ -9,51 +9,51 @@
:style="styles"
@keydown.prevent
>
<n8n-resize-wrapper
:isResizingEnabled="!readOnly"
<N8nResizeWrapper
:is-resizing-enabled="!readOnly"
:height="height"
:width="width"
:minHeight="minHeight"
:minWidth="minWidth"
:min-height="minHeight"
:min-width="minWidth"
:scale="scale"
:gridSize="gridSize"
:grid-size="gridSize"
@resizeend="onResizeEnd"
@resize="onResize"
@resizestart="onResizeStart"
>
<div v-show="!editMode" :class="$style.wrapper" @dblclick.stop="onDoubleClick">
<n8n-markdown
<N8nMarkdown
theme="sticky"
:content="modelValue"
:withMultiBreaks="true"
:with-multi-breaks="true"
@markdown-click="onMarkdownClick"
/>
</div>
<div
v-show="editMode"
:class="{ 'full-height': !shouldShowFooter, 'sticky-textarea': true }"
@click.stop
@mousedown.stop
@mouseup.stop
@keydown.esc="onInputBlur"
@keydown.stop
:class="{ 'full-height': !shouldShowFooter, 'sticky-textarea': true }"
>
<n8n-input
:modelValue="modelValue"
<N8nInput
ref="input"
:model-value="modelValue"
type="textarea"
:rows="5"
@blur="onInputBlur"
@update:modelValue="onUpdateModelValue"
@wheel="onInputScroll"
ref="input"
/>
</div>
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
<n8n-text size="xsmall" aligh="right">
<N8nText size="xsmall" aligh="right">
<span v-html="t('sticky.markdownHint')"></span>
</n8n-text>
</N8nText>
</div>
</n8n-resize-wrapper>
</N8nResizeWrapper>
</div>
</template>
@ -66,7 +66,13 @@ import Locale from '../../mixins/locale';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-sticky',
name: 'N8nSticky',
components: {
N8nInput,
N8nMarkdown,
N8nResizeWrapper,
N8nText,
},
mixins: [Locale],
props: {
modelValue: {
@ -116,12 +122,6 @@ export default defineComponent({
default: 1,
},
},
components: {
N8nInput,
N8nMarkdown,
N8nResizeWrapper,
N8nText,
},
data() {
return {
isResizing: false,
@ -152,6 +152,19 @@ export default defineComponent({
return this.resHeight > 100 && this.resWidth > 155;
},
},
watch: {
editMode(newMode, prevMode) {
setTimeout(() => {
if (newMode && !prevMode && this.$refs.input) {
const textarea = this.$refs.input as HTMLTextAreaElement;
if (this.defaultText === this.modelValue) {
textarea.select();
}
textarea.focus();
}
}, 100);
},
},
methods: {
onDoubleClick() {
if (!this.readOnly) {
@ -187,19 +200,6 @@ export default defineComponent({
}
},
},
watch: {
editMode(newMode, prevMode) {
setTimeout(() => {
if (newMode && !prevMode && this.$refs.input) {
const textarea = this.$refs.input as HTMLTextAreaElement;
if (this.defaultText === this.modelValue) {
textarea.select();
}
textarea.focus();
}
}, 100);
},
},
});
</script>

View file

@ -1,21 +1,21 @@
<template>
<div :class="['n8n-tabs', $style.container]">
<div :class="$style.back" v-if="scrollPosition > 0" @click="scrollLeft">
<n8n-icon icon="chevron-left" size="small" />
<div v-if="scrollPosition > 0" :class="$style.back" @click="scrollLeft">
<N8nIcon icon="chevron-left" size="small" />
</div>
<div :class="$style.next" v-if="canScrollRight" @click="scrollRight">
<n8n-icon icon="chevron-right" size="small" />
<div v-if="canScrollRight" :class="$style.next" @click="scrollRight">
<N8nIcon icon="chevron-right" size="small" />
</div>
<div ref="tabs" :class="$style.tabs">
<div
v-for="option in options"
:key="option.value"
:id="option.value"
:key="option.value"
:class="{ [$style.alignRight]: option.align === 'right' }"
>
<n8n-tooltip :disabled="!option.tooltip" placement="bottom">
<template #content>
<div v-html="option.tooltip" @click="handleTooltipClick(option.value, $event)" />
<div @click="handleTooltipClick(option.value, $event)" v-html="option.tooltip" />
</template>
<a
v-if="option.href"
@ -27,7 +27,7 @@
<div>
{{ option.label }}
<span :class="$style.external"
><n8n-icon icon="external-link-alt" size="small"
><N8nIcon icon="external-link-alt" size="small"
/></span>
</div>
</a>
@ -38,7 +38,7 @@
:data-test-id="`tab-${option.value}`"
@click="() => handleTabClick(option.value)"
>
<n8n-icon v-if="option.icon" :icon="option.icon" size="medium" />
<N8nIcon v-if="option.icon" :icon="option.icon" size="medium" />
<span v-if="option.label">{{ option.label }}</span>
</div>
</n8n-tooltip>
@ -66,6 +66,23 @@ export default defineComponent({
components: {
N8nIcon,
},
props: {
modelValue: {
type: String,
default: '',
},
options: {
type: Array as PropType<N8nTabOptions[]>,
default: (): N8nTabOptions[] => [],
},
},
data() {
return {
scrollPosition: 0,
canScrollRight: false,
resizeObserver: null as ResizeObserver | null,
};
},
mounted() {
const container = this.$refs.tabs as HTMLDivElement | undefined;
if (container) {
@ -94,23 +111,6 @@ export default defineComponent({
this.resizeObserver.disconnect();
}
},
data() {
return {
scrollPosition: 0,
canScrollRight: false,
resizeObserver: null as ResizeObserver | null,
};
},
props: {
modelValue: {
type: String,
default: '',
},
options: {
type: Array as PropType<N8nTabOptions[]>,
default: (): N8nTabOptions[] => [],
},
},
methods: {
handleTooltipClick(tab: string, event: MouseEvent) {
this.$emit('tooltipClick', tab, event);

View file

@ -8,7 +8,7 @@
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-tag',
name: 'N8nTag',
props: {
text: {
type: String,

View file

@ -1,12 +1,12 @@
<template>
<div :class="['n8n-tags', $style.tags]">
<n8n-tag
<N8nTag
v-for="tag in visibleTags"
:key="tag.id"
:text="tag.name"
@click="$emit('click:tag', tag.id, $event)"
/>
<n8n-link
<N8nLink
v-if="truncate && !showAll && hiddenTagsLength > 0"
theme="text"
underline
@ -14,7 +14,7 @@
@click.stop.prevent="onExpand"
>
{{ t('tags.showMore', `${hiddenTagsLength}`) }}
</n8n-link>
</N8nLink>
</div>
</template>
@ -31,17 +31,12 @@ export interface ITag {
}
export default defineComponent({
name: 'n8n-tags',
mixins: [Locale],
name: 'N8nTags',
components: {
N8nTag,
N8nLink,
},
data() {
return {
showAll: false,
};
},
mixins: [Locale],
props: {
tags: {
type: Array as PropType<ITag[]>,
@ -56,6 +51,11 @@ export default defineComponent({
default: 3,
},
},
data() {
return {
showAll: false,
};
},
computed: {
visibleTags(): ITag[] {
if (this.truncate && !this.showAll && this.tags.length > this.truncateAt) {

View file

@ -7,7 +7,7 @@
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-text',
name: 'N8nText',
props: {
bold: {
type: Boolean,

View file

@ -1,5 +1,5 @@
<template>
<el-tooltip v-bind="{ ...$props, ...$attrs }" :popperClass="$props.popperClass ?? 'n8n-tooltip'">
<ElTooltip v-bind="{ ...$props, ...$attrs }" :popper-class="$props.popperClass ?? 'n8n-tooltip'">
<slot />
<template #content>
<slot name="content">
@ -10,14 +10,14 @@
:class="$style.buttons"
:style="{ justifyContent: justifyButtons }"
>
<n8n-button
<N8nButton
v-for="button in buttons"
:key="button.attrs.label"
v-bind="{ ...button.attrs, ...button.listeners }"
/>
</div>
</template>
</el-tooltip>
</ElTooltip>
</template>
<script lang="ts">
@ -28,12 +28,12 @@ import type { IN8nButton } from '@/types';
import N8nButton from '../N8nButton';
export default defineComponent({
name: 'n8n-tooltip',
inheritAttrs: false,
name: 'N8nTooltip',
components: {
ElTooltip,
N8nButton,
},
inheritAttrs: false,
props: {
...ElTooltip.props,
content: {

View file

@ -1,21 +1,21 @@
<template>
<div class="n8n-tree">
<div v-for="(label, i) in Object.keys(value)" :key="i" :class="classes">
<div :class="$style.simple" v-if="isSimple(value[label])">
<slot v-if="$slots.label" name="label" v-bind:label="label" v-bind:path="getPath(label)" />
<div v-if="isSimple(value[label])" :class="$style.simple">
<slot v-if="$slots.label" name="label" :label="label" :path="getPath(label)" />
<span v-else>{{ label }}</span>
<span>:</span>
<slot v-if="$slots.value" name="value" v-bind:value="value[label]" />
<slot v-if="$slots.value" name="value" :value="value[label]" />
<span v-else>{{ value[label] }}</span>
</div>
<div v-else>
<slot v-if="$slots.label" name="label" v-bind:label="label" v-bind:path="getPath(label)" />
<slot v-if="$slots.label" name="label" :label="label" :path="getPath(label)" />
<span v-else>{{ label }}</span>
<n8n-tree
:path="getPath(label)"
:depth="depth + 1"
:value="value[label]"
:nodeClass="nodeClass"
:node-class="nodeClass"
>
<template v-for="(index, name) in $slots" #[name]="data">
<slot :name="name" v-bind="data"></slot>
@ -31,7 +31,7 @@ import type { PropType } from 'vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-tree',
name: 'N8nTree',
components: {},
props: {
value: {

View file

@ -1,25 +1,25 @@
<template>
<div :class="classes">
<div :class="$style.avatarContainer">
<n8n-avatar :firstName="firstName" :lastName="lastName" />
<N8nAvatar :first-name="firstName" :last-name="lastName" />
</div>
<div v-if="isPendingUser" :class="$style.pendingUser">
<n8n-text :bold="true">{{ email }}</n8n-text>
<span :class="$style.pendingBadge"><n8n-badge :bold="true">Pending</n8n-badge></span>
<N8nText :bold="true">{{ email }}</N8nText>
<span :class="$style.pendingBadge"><N8nBadge :bold="true">Pending</N8nBadge></span>
</div>
<div v-else :class="$style.infoContainer">
<div>
<n8n-text :bold="true" color="text-dark">
<N8nText :bold="true" color="text-dark">
{{ firstName }} {{ lastName }}
{{ isCurrentUser ? t('nds.userInfo.you') : '' }}
</n8n-text>
</N8nText>
<span v-if="disabled" :class="$style.pendingBadge">
<n8n-badge :bold="true">Disabled</n8n-badge>
<N8nBadge :bold="true">Disabled</N8nBadge>
</span>
</div>
<div>
<n8n-text data-test-id="user-email" size="small" color="text-light">{{ email }}</n8n-text>
<N8nText data-test-id="user-email" size="small" color="text-light">{{ email }}</N8nText>
</div>
</div>
</div>
@ -33,13 +33,13 @@ import Locale from '../../mixins/locale';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-users-info',
mixins: [Locale],
name: 'N8nUsersInfo',
components: {
N8nAvatar,
N8nText,
N8nBadge,
},
mixins: [Locale],
props: {
firstName: {
type: String,

View file

@ -1,23 +1,23 @@
<template>
<n8n-select
<N8nSelect
data-test-id="user-select-trigger"
v-bind="$attrs"
:modelValue="modelValue"
:model-value="modelValue"
:filterable="true"
:filterMethod="setFilter"
:filter-method="setFilter"
:placeholder="placeholder"
:default-first-option="true"
teleported
:popper-class="$style.limitPopperWidth"
:noDataText="t('nds.userSelect.noMatchingUsers')"
:no-data-text="t('nds.userSelect.noMatchingUsers')"
:size="size"
@blur="onBlur"
@focus="onFocus"
>
<template #prefix v-if="$slots.prefix">
<template v-if="$slots.prefix" #prefix>
<slot name="prefix" />
</template>
<n8n-option
<N8nOption
v-for="user in sortedUsers"
:key="user.id"
:value="user.id"
@ -25,9 +25,9 @@
:label="getLabel(user)"
:disabled="user.disabled"
>
<n8n-user-info v-bind="user" :isCurrentUser="currentUserId === user.id" />
</n8n-option>
</n8n-select>
<N8nUserInfo v-bind="user" :is-current-user="currentUserId === user.id" />
</N8nOption>
</N8nSelect>
</template>
<script lang="ts">
@ -41,13 +41,13 @@ import type { PropType } from 'vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-user-select',
mixins: [Locale],
name: 'N8nUserSelect',
components: {
N8nUserInfo,
N8nSelect,
N8nOption,
},
mixins: [Locale],
props: {
users: {
type: Array as PropType<IUser[]>,

View file

@ -76,11 +76,11 @@ const menuHeight = computed(() => {
popper-class="user-stack-popper"
>
<div :class="$style.avatars" data-test-id="user-stack-avatars">
<n8n-avatar
<N8nAvatar
v-for="user in flatUserList.slice(0, visibleAvatarCount)"
:key="user.id"
:firstName="user.firstName"
:lastName="user.lastName"
:first-name="user.firstName"
:last-name="user.lastName"
:class="$style.avatar"
:data-test-id="`user-stack-avatar-${user.id}`"
size="small"
@ -101,9 +101,9 @@ const menuHeight = computed(() => {
:data-test-id="`user-stack-info-${user.id}`"
:class="$style.userInfoContainer"
>
<n8n-user-info
<N8nUserInfo
v-bind="user"
:isCurrentUser="user.email === props.currentUserEmail"
:is-current-user="user.email === props.currentUserEmail"
/>
</el-dropdown-item>
</div>

View file

@ -6,17 +6,17 @@
:class="i === sortedUsers.length - 1 ? $style.itemContainer : $style.itemWithBorder"
:data-test-id="`user-list-item-${user.email}`"
>
<n8n-user-info
<N8nUserInfo
v-bind="user"
:isCurrentUser="currentUserId === user.id"
:isSamlLoginEnabled="isSamlLoginEnabled"
:is-current-user="currentUserId === user.id"
:is-saml-login-enabled="isSamlLoginEnabled"
/>
<div :class="$style.badgeContainer">
<n8n-badge v-if="user.isOwner" theme="tertiary" bold>
<N8nBadge v-if="user.isOwner" theme="tertiary" bold>
{{ t('nds.auth.roles.owner') }}
</n8n-badge>
</N8nBadge>
<slot v-if="!user.isOwner && !readonly" name="actions" :user="user" />
<n8n-action-toggle
<N8nActionToggle
v-if="
!user.isOwner &&
user.signInType !== 'ldap' &&
@ -44,13 +44,13 @@ import type { PropType } from 'vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'n8n-users-list',
mixins: [Locale],
name: 'N8nUsersList',
components: {
N8nActionToggle,
N8nBadge,
N8nUserInfo,
},
mixins: [Locale],
props: {
readonly: {
type: Boolean,

View file

@ -1,118 +1,12 @@
import type { Plugin } from 'vue';
import {
N8nActionBox,
N8nActionDropdown,
N8nActionToggle,
N8nAlert,
N8nAvatar,
N8nBadge,
N8nBlockUi,
N8nButton,
N8nCallout,
N8nCard,
N8nCheckbox,
N8nCircleLoader,
N8nColorPicker,
N8nDatatable,
N8nFormBox,
N8nFormInputs,
N8nFormInput,
N8nHeading,
N8nIcon,
N8nIconButton,
N8nInfoAccordion,
N8nInfoTip,
N8nInput,
N8nInputLabel,
N8nInputNumber,
N8nLink,
N8nLoading,
N8nMarkdown,
N8nMenu,
N8nMenuItem,
N8nNodeCreatorNode,
N8nNodeIcon,
N8nNotice,
N8nOption,
N8nPopover,
N8nPulse,
N8nRadioButtons,
N8nRecycleScroller,
N8nResizeWrapper,
N8nSelect,
N8nSpinner,
N8nSticky,
N8nTabs,
N8nTag,
N8nTags,
N8nText,
N8nTooltip,
N8nTree,
N8nUserInfo,
N8nUserSelect,
N8nUsersList,
N8nResizeObserver,
N8nKeyboardShortcut,
N8nUserStack,
} from './components';
import type { Component, Plugin } from 'vue';
import * as components from './components';
export interface N8nPluginOptions {}
export const N8nPlugin: Plugin<N8nPluginOptions> = {
install: (app) => {
app.component('n8n-action-box', N8nActionBox);
app.component('n8n-action-dropdown', N8nActionDropdown);
app.component('n8n-action-toggle', N8nActionToggle);
app.component('n8n-alert', N8nAlert);
app.component('n8n-avatar', N8nAvatar);
app.component('n8n-badge', N8nBadge);
app.component('n8n-block-ui', N8nBlockUi);
app.component('n8n-button', N8nButton);
app.component('n8n-callout', N8nCallout);
app.component('n8n-card', N8nCard);
app.component('n8n-checkbox', N8nCheckbox);
app.component('n8n-circle-loader', N8nCircleLoader);
app.component('n8n-color-picker', N8nColorPicker);
app.component('n8n-datatable', N8nDatatable);
app.component('n8n-form-box', N8nFormBox);
app.component('n8n-form-inputs', N8nFormInputs);
app.component('n8n-form-input', N8nFormInput);
app.component('n8n-heading', N8nHeading);
app.component('n8n-icon', N8nIcon);
app.component('n8n-icon-button', N8nIconButton);
app.component('n8n-info-accordion', N8nInfoAccordion);
app.component('n8n-info-tip', N8nInfoTip);
app.component('n8n-input', N8nInput);
app.component('n8n-input-label', N8nInputLabel);
app.component('n8n-input-number', N8nInputNumber);
app.component('n8n-link', N8nLink);
app.component('n8n-loading', N8nLoading);
app.component('n8n-markdown', N8nMarkdown);
app.component('n8n-menu', N8nMenu);
app.component('n8n-menu-item', N8nMenuItem);
app.component('n8n-node-creator-node', N8nNodeCreatorNode);
app.component('n8n-node-icon', N8nNodeIcon);
app.component('n8n-notice', N8nNotice);
app.component('n8n-option', N8nOption);
app.component('n8n-popover', N8nPopover);
app.component('n8n-pulse', N8nPulse);
app.component('n8n-radio-buttons', N8nRadioButtons);
app.component('n8n-recycle-scroller', N8nRecycleScroller);
app.component('n8n-resize-wrapper', N8nResizeWrapper);
app.component('n8n-select', N8nSelect);
app.component('n8n-spinner', N8nSpinner);
app.component('n8n-sticky', N8nSticky);
app.component('n8n-tabs', N8nTabs);
app.component('n8n-tag', N8nTag);
app.component('n8n-tags', N8nTags);
app.component('n8n-text', N8nText);
app.component('n8n-tooltip', N8nTooltip);
app.component('n8n-tree', N8nTree);
app.component('n8n-user-stack', N8nUserStack);
app.component('n8n-user-info', N8nUserInfo);
app.component('n8n-users-list', N8nUsersList);
app.component('n8n-user-select', N8nUserSelect);
app.component('n8n-resize-observer', N8nResizeObserver);
app.component('n8n-keyboard-shortcut', N8nKeyboardShortcut);
for (const [name, component] of Object.entries(components)) {
app.component(name, component as unknown as Component);
}
},
};

View file

@ -53,19 +53,19 @@ function getHex(hsl: string): string {
}
export default defineComponent({
name: 'color-circles',
data() {
return {
observer: null as null | MutationObserver,
hsl: {} as { [color: string]: string },
};
},
name: 'ColorCircles',
props: {
colors: {
type: Array as PropType<string[]>,
required: true,
},
},
data() {
return {
observer: null as null | MutationObserver,
hsl: {} as { [color: string]: string },
};
},
created() {
const setColors = () => {
this.colors.forEach((color) => {

View file

@ -22,13 +22,7 @@ import type { PropType } from 'vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'sizes',
data() {
return {
observer: null as null | MutationObserver,
sizes: {} as Record<string, { rem: string; px: number }>,
};
},
name: 'Sizes',
props: {
variables: {
type: Array as PropType<string[]>,
@ -39,6 +33,12 @@ export default defineComponent({
default: '',
},
},
data() {
return {
observer: null as null | MutationObserver,
sizes: {} as Record<string, { rem: string; px: number }>,
};
},
created() {
const setSizes = () => {
this.variables.forEach((variable: string) => {

View file

@ -20,13 +20,7 @@ import type { PropType } from 'vue';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'variable-table',
data() {
return {
observer: null as null | MutationObserver,
values: {} as Record<string, string>,
};
},
name: 'VariableTable',
props: {
variables: {
type: Array as PropType<string[]>,
@ -37,6 +31,12 @@ export default defineComponent({
default: '',
},
},
data() {
return {
observer: null as null | MutationObserver,
values: {} as Record<string, string>,
};
},
created() {
const setValues = () => {
this.variables.forEach((variable) => {

View file

@ -1,6 +1,6 @@
<template>
<div>
<div v-for="size in sizes" class="spacing-group" :key="size">
<div v-for="size in sizes" :key="size" class="spacing-group">
<div class="spacing-example" :class="`${property[0]}${side ? side[0] : ''}-${size}`">
<div class="spacing-box" />
<div class="label">{{ property[0] }}{{ side ? side[0] : '' }}-{{ size }}</div>

View file

@ -10,7 +10,7 @@
}"
>
<div id="banners" :class="$style.banners">
<banner-stack v-if="!isDemoMode" />
<BannerStack v-if="!isDemoMode" />
</div>
<div id="header" :class="$style.header">
<router-view name="header"></router-view>
@ -23,7 +23,7 @@
<keep-alive v-if="$route.meta.keepWorkflowAlive" include="NodeView" :max="1">
<component :is="Component" />
</keep-alive>
<component v-else :is="Component" />
<component :is="Component" v-else />
</router-view>
</div>
<Modals />
@ -102,12 +102,16 @@ export default defineComponent({
loading: true,
};
},
methods: {
logHiringBanner() {
if (this.settingsStore.isHiringBannerEnabled && !this.isDemoMode) {
console.log(HIRING_BANNER);
watch: {
// eslint-disable-next-line @typescript-eslint/naming-convention
async 'usersStore.currentUser'(currentValue, previousValue) {
if (currentValue && !previousValue) {
await initializeAuthenticatedFeatures();
}
},
defaultLocale(newLocale) {
void loadLanguage(newLocale);
},
},
async mounted() {
this.logHiringBanner();
@ -117,16 +121,12 @@ export default defineComponent({
void useExternalHooks().run('app.mount');
this.loading = false;
},
watch: {
// eslint-disable-next-line @typescript-eslint/naming-convention
async 'usersStore.currentUser'(currentValue, previousValue) {
if (currentValue && !previousValue) {
await initializeAuthenticatedFeatures();
methods: {
logHiringBanner() {
if (this.settingsStore.isHiringBannerEnabled && !this.isDemoMode) {
console.log(HIRING_BANNER);
}
},
defaultLocale(newLocale) {
void loadLanguage(newLocale);
},
},
});
</script>

View file

@ -2,7 +2,7 @@
<Modal
max-width="540px"
:title="$locale.baseText('about.aboutN8n')"
:eventBus="modalBus"
:event-bus="modalBus"
:name="ABOUT_MODAL_KEY"
:center="true"
>
@ -48,10 +48,10 @@
<template #footer>
<div class="action-buttons">
<n8n-button
@click="closeDialog"
float="right"
:label="$locale.baseText('about.close')"
data-test-id="close-about-modal-button"
@click="closeDialog"
/>
</div>
</template>

View file

@ -25,10 +25,10 @@
<template #footer="{ close }">
<div :class="$style.footer">
<el-checkbox :modelValue="checked" @update:modelValue="handleCheckboxChange">{{
<el-checkbox :model-value="checked" @update:modelValue="handleCheckboxChange">{{
$locale.baseText('generic.dontShowAgain')
}}</el-checkbox>
<n8n-button @click="close" :label="$locale.baseText('activationModal.gotIt')" />
<n8n-button :label="$locale.baseText('activationModal.gotIt')" @click="close" />
</div>
</template>
</Modal>

View file

@ -16,7 +16,7 @@
</div>
</div>
<slot name="button" v-if="$slots.button" />
<slot v-if="$slots.button" name="button" />
<n8n-button
v-else-if="buttonLabel"
:label="buttonLoading && buttonLoadingLabel ? buttonLoadingLabel : buttonLabel"
@ -40,11 +40,6 @@ import { defineComponent } from 'vue';
export default defineComponent({
name: 'Banner',
data() {
return {
expanded: false,
};
},
props: {
theme: {
type: String,
@ -70,6 +65,11 @@ export default defineComponent({
default: false,
},
},
data() {
return {
expanded: false,
};
},
methods: {
expand() {
this.expanded = true;

View file

@ -1,19 +1,19 @@
<template>
<div v-if="windowVisible" :class="['binary-data-window', binaryData?.fileType]">
<n8n-button
@click.stop="closeWindow"
size="small"
class="binary-data-window-back"
:title="$locale.baseText('binaryDataDisplay.backToOverviewPage')"
icon="arrow-left"
:label="$locale.baseText('binaryDataDisplay.backToList')"
@click.stop="closeWindow"
/>
<div class="binary-data-window-wrapper">
<div v-if="!binaryData">
{{ $locale.baseText('binaryDataDisplay.noDataFoundToDisplay') }}
</div>
<BinaryDataDisplayEmbed v-else :binaryData="binaryData" />
<BinaryDataDisplayEmbed v-else :binary-data="binaryData" />
</div>
</div>
</template>
@ -34,6 +34,10 @@ export default defineComponent({
components: {
BinaryDataDisplayEmbed,
},
props: [
'displayData', // IBinaryData
'windowVisible', // boolean
],
setup() {
const nodeHelpers = useNodeHelpers();
@ -41,10 +45,6 @@ export default defineComponent({
nodeHelpers,
};
},
props: [
'displayData', // IBinaryData
'windowVisible', // boolean
],
computed: {
...mapStores(useWorkflowsStore),
binaryData(): IBinaryData | null {

View file

@ -11,13 +11,13 @@
<source :src="embedSource" :type="binaryData.mimeType" />
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
</audio>
<vue-json-pretty
<VueJsonPretty
v-else-if="binaryData.fileType === 'json'"
:data="data"
:deep="3"
:showLength="true"
:show-length="true"
/>
<run-data-html v-else-if="binaryData.fileType === 'html'" :inputHtml="data" />
<RunDataHtml v-else-if="binaryData.fileType === 'html'" :input-html="data" />
<embed v-else :src="embedSource" class="binary-data" :class="embedClass()" />
</span>
</span>

View file

@ -1,6 +1,6 @@
<template>
<span>
<slot v-bind:bp="bp" v-bind:value="value" />
<slot :bp="bp" :value="value" />
</span>
</template>
@ -31,24 +31,6 @@ export default defineComponent({
width: window.innerWidth,
};
},
created() {
window.addEventListener('resize', this.onResize);
},
beforeUnmount() {
window.removeEventListener('resize', this.onResize);
},
methods: {
onResize() {
void this.callDebounced('onResizeEnd', { debounceTime: 50 });
},
async onResizeEnd() {
this.width = window.innerWidth;
await this.$nextTick();
const bannerHeight = await getBannerRowHeight();
useUIStore().updateBannersHeight(bannerHeight);
},
},
computed: {
bp(): string {
if (this.width < BREAKPOINT_SM) {
@ -94,5 +76,23 @@ export default defineComponent({
return this.valueDefault;
},
},
created() {
window.addEventListener('resize', this.onResize);
},
beforeUnmount() {
window.removeEventListener('resize', this.onResize);
},
methods: {
onResize() {
void this.callDebounced('onResizeEnd', { debounceTime: 50 });
},
async onResizeEnd() {
this.width = window.innerWidth;
await this.$nextTick();
const bannerHeight = await getBannerRowHeight();
useUIStore().updateBannersHeight(bannerHeight);
},
},
});
</script>

View file

@ -6,55 +6,55 @@
[$style.demoZoomMenu]: isDemo,
}"
>
<keyboard-shortcut-tooltip
<KeyboardShortcutTooltip
:label="$locale.baseText('nodeView.zoomToFit')"
:shortcut="{ keys: ['1'] }"
>
<n8n-icon-button
@click="zoomToFit"
type="tertiary"
size="large"
icon="expand"
data-test-id="zoom-to-fit"
@click="zoomToFit"
/>
</keyboard-shortcut-tooltip>
<keyboard-shortcut-tooltip
</KeyboardShortcutTooltip>
<KeyboardShortcutTooltip
:label="$locale.baseText('nodeView.zoomIn')"
:shortcut="{ keys: ['+'] }"
>
<n8n-icon-button
@click="zoomIn"
type="tertiary"
size="large"
icon="search-plus"
data-test-id="zoom-in-button"
@click="zoomIn"
/>
</keyboard-shortcut-tooltip>
<keyboard-shortcut-tooltip
</KeyboardShortcutTooltip>
<KeyboardShortcutTooltip
:label="$locale.baseText('nodeView.zoomOut')"
:shortcut="{ keys: ['-'] }"
>
<n8n-icon-button
@click="zoomOut"
type="tertiary"
size="large"
icon="search-minus"
data-test-id="zoom-out-button"
@click="zoomOut"
/>
</keyboard-shortcut-tooltip>
<keyboard-shortcut-tooltip
</KeyboardShortcutTooltip>
<KeyboardShortcutTooltip
:label="$locale.baseText('nodeView.resetZoom')"
:shortcut="{ keys: ['0'] }"
>
<n8n-icon-button
v-if="nodeViewScale !== 1 && !isDemo"
@click="resetZoom"
type="tertiary"
size="large"
icon="undo"
data-test-id="reset-zoom-button"
@click="resetZoom"
/>
</keyboard-shortcut-tooltip>
</KeyboardShortcutTooltip>
</div>
</template>
<script lang="ts" setup>

View file

@ -1,17 +1,17 @@
<template>
<Modal
:name="CHANGE_PASSWORD_MODAL_KEY"
@enter="onSubmit"
:title="$locale.baseText('auth.changePassword')"
:center="true"
width="460px"
:eventBus="modalBus"
:event-bus="modalBus"
@enter="onSubmit"
>
<template #content>
<n8n-form-inputs
:inputs="config"
:eventBus="formBus"
:columnView="true"
:event-bus="formBus"
:column-view="true"
@update="onInput"
@submit="onSubmit"
/>
@ -20,9 +20,9 @@
<n8n-button
:loading="loading"
:label="$locale.baseText('auth.changePassword')"
@click="onSubmitClick"
float="right"
data-test-id="change-password-button"
@click="onSubmitClick"
/>
</template>
</Modal>

View file

@ -117,19 +117,19 @@ function closeDialog() {
<Modal
max-width="960px"
:title="i18n.baseText('chatEmbed.title')"
:eventBus="modalBus"
:event-bus="modalBus"
:name="CHAT_EMBED_MODAL_KEY"
:center="true"
>
<template #content>
<div :class="$style.container">
<n8n-tabs :options="tabs" v-model="currentTab" />
<n8n-tabs v-model="currentTab" :options="tabs" />
<div v-if="currentTab !== 'cdn'">
<n8n-text>
{{ i18n.baseText('chatEmbed.install') }}
</n8n-text>
<CodeNodeEditor :modelValue="commonCode.install" isReadOnly />
<CodeNodeEditor :model-value="commonCode.install" is-read-only />
</div>
<n8n-text>
@ -139,10 +139,10 @@ function closeDialog() {
</template>
</i18n-t>
</n8n-text>
<HtmlEditor v-if="currentTab === 'cdn'" :modelValue="cdnCode" isReadOnly />
<HtmlEditor v-if="currentTab === 'vue'" :modelValue="vueCode" isReadOnly />
<CodeNodeEditor v-if="currentTab === 'react'" :modelValue="reactCode" isReadOnly />
<CodeNodeEditor v-if="currentTab === 'other'" :modelValue="otherCode" isReadOnly />
<HtmlEditor v-if="currentTab === 'cdn'" :model-value="cdnCode" is-read-only />
<HtmlEditor v-if="currentTab === 'vue'" :model-value="vueCode" is-read-only />
<CodeNodeEditor v-if="currentTab === 'react'" :model-value="reactCode" is-read-only />
<CodeNodeEditor v-if="currentTab === 'other'" :model-value="otherCode" is-read-only />
<n8n-info-tip>
{{ i18n.baseText('chatEmbed.packageInfo.description') }}
@ -155,7 +155,7 @@ function closeDialog() {
<template #footer>
<div class="action-buttons">
<n8n-button @click="closeDialog" float="right" :label="i18n.baseText('chatEmbed.close')" />
<n8n-button float="right" :label="i18n.baseText('chatEmbed.close')" @click="closeDialog" />
</div>
</template>
</Modal>

View file

@ -249,8 +249,8 @@ onMounted(() => {
<span
v-show="prompt.length > 1"
:class="$style.counter"
v-text="`${prompt.length} / ${ASK_AI_MAX_PROMPT_LENGTH}`"
data-test-id="ask-ai-prompt-counter"
v-text="`${prompt.length} / ${ASK_AI_MAX_PROMPT_LENGTH}`"
/>
<a href="https://docs.n8n.io/code-examples/ai-code" target="_blank" :class="$style.help">
<n8n-icon icon="question-circle" color="text-light" size="large" />{{
@ -260,29 +260,29 @@ onMounted(() => {
</div>
<N8nInput
v-model="prompt"
@input="onPromptInput"
:class="$style.input"
type="textarea"
:rows="6"
:maxlength="ASK_AI_MAX_PROMPT_LENGTH"
:placeholder="i18n.baseText('codeNodeEditor.askAi.placeholder')"
data-test-id="ask-ai-prompt-input"
@input="onPromptInput"
/>
</div>
<div :class="$style.controls">
<div :class="$style.loader" v-if="isLoading">
<div v-if="isLoading" :class="$style.loader">
<transition name="text-fade-in-out" mode="out-in">
<div v-text="loadingString" :key="loadingPhraseIndex" />
<div :key="loadingPhraseIndex" v-text="loadingString" />
</transition>
<n8n-circle-loader :radius="8" :progress="loaderProgress" :stroke-width="3" />
</div>
<n8n-tooltip :disabled="isSubmitEnabled" v-else>
<N8nTooltip v-else :disabled="isSubmitEnabled">
<div>
<N8nButton
:disabled="!isSubmitEnabled"
@click="onSubmit"
size="small"
data-test-id="ask-ai-cta"
@click="onSubmit"
>
{{ i18n.baseText('codeNodeEditor.askAi.generateCode') }}
</N8nButton>
@ -290,18 +290,18 @@ onMounted(() => {
<template #content>
<span
v-if="!hasExecutionData"
v-text="i18n.baseText('codeNodeEditor.askAi.noInputData')"
data-test-id="ask-ai-cta-tooltip-no-input-data"
v-text="i18n.baseText('codeNodeEditor.askAi.noInputData')"
/>
<span
v-else-if="prompt.length === 0"
v-text="i18n.baseText('codeNodeEditor.askAi.noPrompt')"
data-test-id="ask-ai-cta-tooltip-no-prompt"
v-text="i18n.baseText('codeNodeEditor.askAi.noPrompt')"
/>
<span
v-else-if="isEachItemMode"
v-text="i18n.baseText('codeNodeEditor.askAi.onlyAllItemsMode')"
data-test-id="ask-ai-cta-tooltip-only-all-items-mode"
v-text="i18n.baseText('codeNodeEditor.askAi.onlyAllItemsMode')"
/>
<span
v-else-if="prompt.length < ASK_AI_MIN_PROMPT_LENGTH"
@ -313,7 +313,7 @@ onMounted(() => {
"
/>
</template>
</n8n-tooltip>
</N8nTooltip>
</div>
</div>
</template>

View file

@ -1,15 +1,15 @@
<template>
<div
ref="codeNodeEditorContainer"
:class="['code-node-editor', $style['code-node-editor-container'], language]"
@mouseover="onMouseOver"
@mouseout="onMouseOut"
ref="codeNodeEditorContainer"
>
<el-tabs
type="card"
v-if="aiEnabled"
ref="tabs"
v-model="activeTab"
v-if="aiEnabled"
type="card"
:before-leave="onBeforeTabLeave"
>
<el-tab-pane
@ -26,9 +26,9 @@
>
<!-- Key the AskAI tab to make sure it re-mounts when changing tabs -->
<AskAI
@replaceCode="onReplaceCode"
:has-changes="hasChanges"
:key="activeTab"
:has-changes="hasChanges"
@replaceCode="onReplaceCode"
@started-loading="isLoadingAIResponse = true"
@finished-loading="isLoadingAIResponse = false"
/>
@ -73,11 +73,11 @@ import { useMessage } from '@/composables/useMessage';
import { useSettingsStore } from '@/stores/settings.store';
export default defineComponent({
name: 'code-node-editor',
mixins: [linterExtension, completerExtension, workflowHelpers],
name: 'CodeNodeEditor',
components: {
AskAI,
},
mixins: [linterExtension, completerExtension, workflowHelpers],
props: {
aiButtonEnabled: {
type: Boolean,
@ -184,6 +184,67 @@ export default defineComponent({
}
},
},
beforeUnmount() {
if (!this.isReadOnly) codeNodeEditorEventBus.off('error-line-number', this.highlightLine);
},
mounted() {
if (!this.isReadOnly) codeNodeEditorEventBus.on('error-line-number', this.highlightLine);
const { isReadOnly, language } = this;
const extensions: Extension[] = [
...readOnlyEditorExtensions,
EditorState.readOnly.of(isReadOnly),
EditorView.editable.of(!isReadOnly),
codeNodeEditorTheme({ isReadOnly, customMinHeight: this.rows }),
];
if (!isReadOnly) {
const linter = this.createLinter(language);
if (linter) {
extensions.push(this.linterCompartment.of(linter));
}
extensions.push(
...writableEditorExtensions,
EditorView.domEventHandlers({
focus: () => {
this.isEditorFocused = true;
},
blur: () => {
this.isEditorFocused = false;
},
}),
EditorView.updateListener.of((viewUpdate) => {
if (!viewUpdate.docChanged) return;
this.trackCompletion(viewUpdate);
this.$emit('update:modelValue', this.editor?.state.doc.toString());
this.hasChanges = true;
}),
);
}
const [languageSupport, ...otherExtensions] = this.languageExtensions;
extensions.push(this.languageCompartment.of(languageSupport), ...otherExtensions);
const state = EditorState.create({
doc: this.modelValue ?? this.placeholder,
extensions,
});
this.editor = new EditorView({
parent: this.$refs.codeNodeEditor as HTMLDivElement,
state,
});
// empty on first load, default param value
if (!this.modelValue) {
this.refreshPlaceholder();
this.$emit('update:modelValue', this.placeholder);
}
},
methods: {
getCurrentEditorContent() {
return this.editor?.state.doc.toString() ?? '';
@ -311,67 +372,6 @@ export default defineComponent({
} catch {}
},
},
beforeUnmount() {
if (!this.isReadOnly) codeNodeEditorEventBus.off('error-line-number', this.highlightLine);
},
mounted() {
if (!this.isReadOnly) codeNodeEditorEventBus.on('error-line-number', this.highlightLine);
const { isReadOnly, language } = this;
const extensions: Extension[] = [
...readOnlyEditorExtensions,
EditorState.readOnly.of(isReadOnly),
EditorView.editable.of(!isReadOnly),
codeNodeEditorTheme({ isReadOnly, customMinHeight: this.rows }),
];
if (!isReadOnly) {
const linter = this.createLinter(language);
if (linter) {
extensions.push(this.linterCompartment.of(linter));
}
extensions.push(
...writableEditorExtensions,
EditorView.domEventHandlers({
focus: () => {
this.isEditorFocused = true;
},
blur: () => {
this.isEditorFocused = false;
},
}),
EditorView.updateListener.of((viewUpdate) => {
if (!viewUpdate.docChanged) return;
this.trackCompletion(viewUpdate);
this.$emit('update:modelValue', this.editor?.state.doc.toString());
this.hasChanges = true;
}),
);
}
const [languageSupport, ...otherExtensions] = this.languageExtensions;
extensions.push(this.languageCompartment.of(languageSupport), ...otherExtensions);
const state = EditorState.create({
doc: this.modelValue ?? this.placeholder,
extensions,
});
this.editor = new EditorView({
parent: this.$refs.codeNodeEditor as HTMLDivElement,
state,
});
// empty on first load, default param value
if (!this.modelValue) {
this.refreshPlaceholder();
this.$emit('update:modelValue', this.placeholder);
}
},
});
</script>

View file

@ -1,18 +1,18 @@
<template>
<div @keydown.stop class="collection-parameter">
<div class="collection-parameter" @keydown.stop>
<div class="collection-parameter-wrapper">
<div v-if="getProperties.length === 0" class="no-items-exist">
<n8n-text size="small">{{ $locale.baseText('collectionParameter.noProperties') }}</n8n-text>
</div>
<Suspense>
<parameter-input-list
<ParameterInputList
:parameters="getProperties"
:nodeValues="nodeValues"
:node-values="nodeValues"
:path="path"
:hideDelete="hideDelete"
:hide-delete="hideDelete"
:indent="true"
:isReadOnly="isReadOnly"
:is-read-only="isReadOnly"
@valueChanged="valueChanged"
/>
</Suspense>
@ -22,16 +22,16 @@
v-if="(parameter.options ?? []).length === 1"
type="tertiary"
block
@click="optionSelected((parameter.options ?? [])[0].name)"
:label="getPlaceholderText"
@click="optionSelected((parameter.options ?? [])[0].name)"
/>
<div v-else class="add-option">
<n8n-select
v-model="selectedOption"
:placeholder="getPlaceholderText"
size="small"
@update:modelValue="optionSelected"
filterable
@update:modelValue="optionSelected"
>
<n8n-option
v-for="item in parameterOptions"

View file

@ -1,10 +1,10 @@
<template>
<n8n-card :class="$style.card" v-bind="$attrs">
<template #header v-if="!loading">
<span v-text="title" :class="$style.title" />
<template v-if="!loading" #header>
<span :class="$style.title" v-text="title" />
</template>
<n8n-loading :loading="loading" :rows="3" variant="p" />
<template #footer v-if="!loading">
<template v-if="!loading" #footer>
<slot name="footer" />
</template>
</n8n-card>

View file

@ -3,10 +3,10 @@
width="540px"
:name="COMMUNITY_PACKAGE_INSTALL_MODAL_KEY"
:title="$locale.baseText('settings.communityNodes.installModal.title')"
:eventBus="modalBus"
:event-bus="modalBus"
:center="true"
:beforeClose="onModalClose"
:showClose="!loading"
:before-close="onModalClose"
:show-close="!loading"
>
<template #content>
<div :class="[$style.descriptionContainer, 'p-s']">
@ -30,15 +30,15 @@
<n8n-input-label
:class="$style.labelTooltip"
:label="$locale.baseText('settings.communityNodes.installModal.packageName.label')"
:tooltipText="
:tooltip-text="
$locale.baseText('settings.communityNodes.installModal.packageName.tooltip', {
interpolate: { npmURL: NPM_KEYWORD_SEARCH_URL },
})
"
>
<n8n-input
name="packageNameInput"
v-model="packageName"
name="packageNameInput"
type="text"
:maxlength="214"
:placeholder="
@ -60,8 +60,8 @@
v-model="userAgreed"
:class="[$style.checkbox, checkboxWarning ? $style.error : '', 'mt-l']"
:disabled="loading"
@update:modelValue="onCheckboxChecked"
data-test-id="user-agreement-checkbox"
@update:modelValue="onCheckboxChecked"
>
<n8n-text>
{{ $locale.baseText('settings.communityNodes.installModal.checkbox.label') }} </n8n-text
@ -83,8 +83,8 @@
"
size="large"
float="right"
@click="onInstallClick"
data-test-id="install-community-package-button"
@click="onInstallClick"
/>
</template>
</Modal>

View file

@ -3,16 +3,16 @@
width="540px"
:name="COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY"
:title="getModalContent.title"
:eventBus="modalBus"
:event-bus="modalBus"
:center="true"
:showClose="!loading"
:beforeClose="onModalClose"
:show-close="!loading"
:before-close="onModalClose"
>
<template #content>
<n8n-text>{{ getModalContent.message }}</n8n-text>
<div
:class="$style.descriptionContainer"
v-if="mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE"
:class="$style.descriptionContainer"
>
<n8n-info-tip theme="info" type="note" :bold="false">
<span v-text="getModalContent.description"></span>

View file

@ -1,11 +1,11 @@
<template>
<Modal
:name="modalName"
:eventBus="modalBus"
:event-bus="modalBus"
:center="true"
:closeOnPressEscape="false"
:beforeClose="closeDialog"
customClass="contact-prompt-modal"
:close-on-press-escape="false"
:before-close="closeDialog"
custom-class="contact-prompt-modal"
width="460px"
>
<template #header>
@ -26,7 +26,7 @@
</template>
<template #footer>
<div :class="$style.footer">
<n8n-button label="Send" float="right" @click="send" :disabled="!isEmailValid" />
<n8n-button label="Send" float="right" :disabled="!isEmailValid" @click="send" />
</div>
</template>
</Modal>
@ -46,8 +46,8 @@ import { useToast } from '@/composables/useToast';
export default defineComponent({
name: 'ContactPromptModal',
mixins: [workflowHelpers],
components: { Modal },
mixins: [workflowHelpers],
props: ['modalName'],
setup() {
return {

View file

@ -43,19 +43,19 @@ function onVisibleChange(open: boolean) {
top: `${position[1]}px`,
}"
>
<n8n-action-dropdown
<N8nActionDropdown
ref="dropdown"
:items="actions"
placement="bottom-start"
data-test-id="context-menu"
:hideArrow="target.source !== 'node-button'"
:hide-arrow="target.source !== 'node-button'"
@select="onActionSelect"
@visibleChange="onVisibleChange"
>
<template #activator>
<div :class="$style.activator"></div>
</template>
</n8n-action-dropdown>
</N8nActionDropdown>
</div>
</Teleport>
</template>

View file

@ -8,8 +8,8 @@
[$style.collapsed]: collapse,
'ph-no-capture': redactValue,
}"
@click="copy"
data-test-id="copy-input"
@click="copy"
>
<span ref="copyInputValue">{{ value }}</span>
<div :class="$style.copyButton">

View file

@ -1,7 +1,7 @@
<template>
<n8n-card :class="$style.cardLink" @click="onClick">
<template #prepend>
<credential-icon :credential-type-name="credentialType ? credentialType.name : ''" />
<CredentialIcon :credential-type-name="credentialType ? credentialType.name : ''" />
</template>
<template #header>
<n8n-heading tag="h2" bold :class="$style.cardHeading">
@ -12,7 +12,7 @@
<n8n-text color="text-light" size="small">
<span v-if="credentialType">{{ credentialType.displayName }} | </span>
<span v-show="data"
>{{ $locale.baseText('credentials.item.updated') }} <time-ago :date="data.updatedAt" /> |
>{{ $locale.baseText('credentials.item.updated') }} <TimeAgo :date="data.updatedAt" /> |
</span>
<span v-show="data"
>{{ $locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }}
@ -20,7 +20,7 @@
</n8n-text>
</div>
<template #append>
<div :class="$style.cardActions" ref="cardActions">
<div ref="cardActions" :class="$style.cardActions">
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
<n8n-badge v-if="credentialPermissions.isOwner" class="mr-xs" theme="tertiary" bold>
{{ $locale.baseText('credentials.item.owner') }}
@ -54,16 +54,6 @@ export const CREDENTIAL_LIST_ITEM_ACTIONS = {
};
export default defineComponent({
data() {
return {
EnterpriseEditionFeature,
};
},
setup() {
return {
...useMessage(),
};
},
components: {
TimeAgo,
CredentialIcon,
@ -88,6 +78,16 @@ export default defineComponent({
default: false,
},
},
setup() {
return {
...useMessage(),
};
},
data() {
return {
EnterpriseEditionFeature,
};
},
computed: {
...mapStores(useCredentialsStore, useUIStore, useUsersStore),
currentUser(): IUser | null {

View file

@ -118,18 +118,18 @@ defineExpose({
<template>
<div v-if="filteredNodeAuthOptions.length > 0" data-test-id="node-auth-type-selector">
<div v-for="parameter in authRelatedFields" :key="parameter.name" class="mb-l">
<parameter-input-full
<ParameterInputFull
:parameter="parameter"
:value="authRelatedFieldsValues[parameter.name] || parameter.default"
:path="parameter.name"
:displayOptions="false"
:display-options="false"
@update="valueChanged"
/>
</div>
<div>
<n8n-input-label
:label="$locale.baseText('credentialEdit.credentialConfig.authTypeSelectorLabel')"
:tooltipText="$locale.baseText('credentialEdit.credentialConfig.authTypeSelectorTooltip')"
:tooltip-text="$locale.baseText('credentialEdit.credentialConfig.authTypeSelectorTooltip')"
:required="true"
/>
</div>

View file

@ -1,6 +1,6 @@
<template>
<div :class="$style.container" data-test-id="node-credentials-config-container">
<banner
<Banner
v-show="showValidationWarning"
theme="danger"
:message="
@ -13,7 +13,7 @@
"
/>
<banner
<Banner
v-if="authError && !showValidationWarning"
theme="danger"
:message="
@ -25,40 +25,40 @@
)
"
:details="authError"
:buttonLabel="$locale.baseText('credentialEdit.credentialConfig.retry')"
buttonLoadingLabel="Retrying"
:buttonTitle="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
:buttonLoading="isRetesting"
:button-label="$locale.baseText('credentialEdit.credentialConfig.retry')"
button-loading-label="Retrying"
:button-title="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
:button-loading="isRetesting"
@click="$emit('retest')"
/>
<banner
<Banner
v-show="showOAuthSuccessBanner && !showValidationWarning"
theme="success"
:message="$locale.baseText('credentialEdit.credentialConfig.accountConnected')"
:buttonLabel="$locale.baseText('credentialEdit.credentialConfig.reconnect')"
:buttonTitle="$locale.baseText('credentialEdit.credentialConfig.reconnectOAuth2Credential')"
:button-label="$locale.baseText('credentialEdit.credentialConfig.reconnect')"
:button-title="$locale.baseText('credentialEdit.credentialConfig.reconnectOAuth2Credential')"
@click="$emit('oauth')"
>
<template #button v-if="isGoogleOAuthType">
<template v-if="isGoogleOAuthType" #button>
<p
v-text="`${$locale.baseText('credentialEdit.credentialConfig.reconnect')}:`"
:class="$style.googleReconnectLabel"
v-text="`${$locale.baseText('credentialEdit.credentialConfig.reconnect')}:`"
/>
<GoogleAuthButton @click="$emit('oauth')" />
</template>
</banner>
</Banner>
<banner
<Banner
v-show="testedSuccessfully && !showValidationWarning"
theme="success"
:message="$locale.baseText('credentialEdit.credentialConfig.connectionTestedSuccessfully')"
:buttonLabel="$locale.baseText('credentialEdit.credentialConfig.retry')"
:buttonLoadingLabel="$locale.baseText('credentialEdit.credentialConfig.retrying')"
:buttonTitle="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
:buttonLoading="isRetesting"
@click="$emit('retest')"
:button-label="$locale.baseText('credentialEdit.credentialConfig.retry')"
:button-loading-label="$locale.baseText('credentialEdit.credentialConfig.retrying')"
:button-title="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
:button-loading="isRetesting"
data-test-id="credentials-config-container-test-success"
@click="$emit('retest')"
/>
<template v-if="credentialPermissions.update">
@ -73,7 +73,7 @@
<AuthTypeSelector
v-if="showAuthTypeSelector && isNewCredential"
:credentialType="credentialType"
:credential-type="credentialType"
@authTypeChanged="onAuthTypeChange"
/>
@ -81,17 +81,17 @@
v-if="isOAuthType && credentialProperties.length"
:label="$locale.baseText('credentialEdit.credentialConfig.oAuthRedirectUrl')"
:value="oAuthCallbackUrl"
:copyButtonText="$locale.baseText('credentialEdit.credentialConfig.clickToCopy')"
:copy-button-text="$locale.baseText('credentialEdit.credentialConfig.clickToCopy')"
:hint="
$locale.baseText('credentialEdit.credentialConfig.subtitle', { interpolate: { appName } })
"
:toastTitle="
:toast-title="
$locale.baseText('credentialEdit.credentialConfig.redirectUrlCopiedToClipboard')
"
:redactValue="true"
:redact-value="true"
/>
</template>
<enterprise-edition v-else :features="[EnterpriseEditionFeature.Sharing]">
<EnterpriseEdition v-else :features="[EnterpriseEditionFeature.Sharing]">
<div>
<n8n-info-tip :bold="false">
{{
@ -101,14 +101,14 @@
}}
</n8n-info-tip>
</div>
</enterprise-edition>
</EnterpriseEdition>
<CredentialInputs
v-if="credentialType && credentialPermissions.update"
:credentialData="credentialData"
:credentialProperties="credentialProperties"
:documentationUrl="documentationUrl"
:showValidationWarnings="showValidationWarning"
:credential-data="credentialData"
:credential-properties="credentialProperties"
:documentation-url="documentationUrl"
:show-validation-warnings="showValidationWarning"
@update="onDataChange"
/>
@ -119,7 +119,7 @@
!isOAuthConnected &&
credentialPermissions.isOwner
"
:isGoogleOAuthType="isGoogleOAuthType"
:is-google-o-auth-type="isGoogleOAuthType"
@click="$emit('oauth')"
/>

View file

@ -1,10 +1,10 @@
<template>
<Modal
:name="modalName"
:customClass="$style.credentialModal"
:eventBus="modalBus"
:custom-class="$style.credentialModal"
:event-bus="modalBus"
:loading="loading"
:beforeClose="beforeClose"
:before-close="beforeClose"
width="70%"
height="80%"
>
@ -12,15 +12,15 @@
<div :class="$style.header">
<div :class="$style.credInfo">
<div :class="$style.credIcon">
<CredentialIcon :credentialTypeName="defaultCredentialTypeName" />
<CredentialIcon :credential-type-name="defaultCredentialTypeName" />
</div>
<InlineNameEdit
:modelValue="credentialName"
:model-value="credentialName"
:subtitle="credentialType ? credentialType.displayName : ''"
:readonly="!credentialPermissions.update || !credentialType"
type="Credential"
@update:modelValue="onNameEdit"
data-test-id="credential-name"
@update:modelValue="onNameEdit"
/>
</div>
<div :class="$style.credActions">
@ -31,20 +31,20 @@
type="tertiary"
:disabled="isSaving"
:loading="isDeleting"
@click="deleteCredential"
data-test-id="credential-delete-button"
@click="deleteCredential"
/>
<SaveButton
v-if="(hasUnsavedChanges || credentialId) && credentialPermissions.save"
:saved="!hasUnsavedChanges && !isTesting"
:isSaving="isSaving || isTesting"
:savingLabel="
:is-saving="isSaving || isTesting"
:saving-label="
isTesting
? $locale.baseText('credentialEdit.credentialEdit.testing')
: $locale.baseText('credentialEdit.credentialEdit.saving')
"
@click="saveCredential"
data-test-id="credential-save-button"
@click="saveCredential"
/>
</div>
</div>
@ -56,28 +56,28 @@
<n8n-menu
mode="tabs"
:items="sidebarItems"
:transparentBackground="true"
:transparent-background="true"
@select="onTabSelect"
></n8n-menu>
</div>
<div v-if="activeTab === 'connection'" :class="$style.mainContent" ref="content">
<div v-if="activeTab === 'connection'" ref="content" :class="$style.mainContent">
<CredentialConfig
:credentialType="credentialType"
:credentialProperties="credentialProperties"
:credentialData="credentialData"
:credentialId="credentialId"
:showValidationWarning="showValidationWarning"
:authError="authError"
:testedSuccessfully="testedSuccessfully"
:isOAuthType="isOAuthType"
:isOAuthConnected="isOAuthConnected"
:isRetesting="isRetesting"
:parentTypes="parentTypes"
:requiredPropertiesFilled="requiredPropertiesFilled"
:credentialPermissions="credentialPermissions"
:credential-type="credentialType"
:credential-properties="credentialProperties"
:credential-data="credentialData"
:credential-id="credentialId"
:show-validation-warning="showValidationWarning"
:auth-error="authError"
:tested-successfully="testedSuccessfully"
:is-o-auth-type="isOAuthType"
:is-o-auth-connected="isOAuthConnected"
:is-retesting="isRetesting"
:parent-types="parentTypes"
:required-properties-filled="requiredPropertiesFilled"
:credential-permissions="credentialPermissions"
:mode="mode"
:selectedCredential="selectedCredential"
:showAuthTypeSelector="requiredCredentials"
:selected-credential="selectedCredential"
:show-auth-type-selector="requiredCredentials"
@update="onDataChange"
@oauth="oAuthCredentialAuthorize"
@retest="retestCredential"
@ -88,24 +88,24 @@
<div v-else-if="activeTab === 'sharing' && credentialType" :class="$style.mainContent">
<CredentialSharing
:credential="currentCredential"
:credentialData="credentialData"
:credentialId="credentialId"
:credentialPermissions="credentialPermissions"
:modalBus="modalBus"
:credential-data="credentialData"
:credential-id="credentialId"
:credential-permissions="credentialPermissions"
:modal-bus="modalBus"
@update:modelValue="onChangeSharedWith"
/>
</div>
<div v-else-if="activeTab === 'details' && credentialType" :class="$style.mainContent">
<CredentialInfo
:nodeAccess="nodeAccess"
:nodesWithAccess="nodesWithAccess"
:currentCredential="currentCredential"
:credentialPermissions="credentialPermissions"
:node-access="nodeAccess"
:nodes-with-access="nodesWithAccess"
:current-credential="currentCredential"
:credential-permissions="credentialPermissions"
@accessChange="onNodeAccessChange"
/>
</div>
<div v-else-if="activeTab.startsWith('coming-soon')" :class="$style.mainContent">
<FeatureComingSoon :featureId="activeTab.split('/')[1]"></FeatureComingSoon>
<FeatureComingSoon :feature-id="activeTab.split('/')[1]"></FeatureComingSoon>
</div>
</div>
</template>

View file

@ -16,7 +16,7 @@
fallback: node.displayName,
})
"
:modelValue="!!nodeAccess[node.name]"
:model-value="!!nodeAccess[node.name]"
@update:modelValue="(val) => onNodeAccessChange(node.name, val)"
/>
<n8n-text v-else>
@ -75,10 +75,10 @@ import type { INodeTypeDescription } from 'n8n-workflow';
export default defineComponent({
name: 'CredentialInfo',
props: ['nodesWithAccess', 'nodeAccess', 'currentCredential', 'credentialPermissions'],
components: {
TimeAgo,
},
props: ['nodesWithAccess', 'nodeAccess', 'currentCredential', 'credentialPermissions'],
methods: {
onNodeAccessChange(name: string, value: string) {
this.$emit('accessChange', {

View file

@ -1,5 +1,5 @@
<template>
<div @keydown.stop :class="$style.container" v-if="credentialProperties.length">
<div v-if="credentialProperties.length" :class="$style.container" @keydown.stop>
<form
v-for="parameter in credentialProperties"
:key="parameter.name"
@ -9,14 +9,14 @@
>
<!-- Why form? to break up inputs, to prevent Chrome autofill -->
<n8n-notice v-if="parameter.type === 'notice'" :content="parameter.displayName" />
<parameter-input-expanded
<ParameterInputExpanded
v-else
:parameter="parameter"
:value="credentialData[parameter.name]"
:documentationUrl="documentationUrl"
:showValidationWarnings="showValidationWarnings"
:documentation-url="documentationUrl"
:show-validation-warnings="showValidationWarnings"
:label="label"
eventSource="credentials"
event-source="credentials"
@update="valueChanged"
/>
</form>
@ -31,15 +31,15 @@ import ParameterInputExpanded from '../ParameterInputExpanded.vue';
export default defineComponent({
name: 'CredentialsInput',
components: {
ParameterInputExpanded,
},
props: [
'credentialProperties',
'credentialData', // ICredentialsDecryptedResponse
'documentationUrl',
'showValidationWarnings',
],
components: {
ParameterInputExpanded,
},
data(): { label: IParameterLabel } {
return {
label: {

View file

@ -12,7 +12,7 @@
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.description,
)
"
:buttonText="
:button-text="
$locale.baseText(
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.button,
)
@ -26,7 +26,7 @@
:description="
$locale.baseText('credentialEdit.credentialSharing.isDefaultUser.description')
"
:buttonText="$locale.baseText('credentialEdit.credentialSharing.isDefaultUser.button')"
:button-text="$locale.baseText('credentialEdit.credentialSharing.isDefaultUser.button')"
@click:button="goToUsersSettings"
/>
</div>
@ -61,7 +61,7 @@
class="mb-s"
size="large"
:users="usersList"
:currentUserId="usersStore.currentUser.id"
:current-user-id="usersStore.currentUser.id"
:placeholder="$locale.baseText('credentialEdit.credentialSharing.select.placeholder')"
data-test-id="credential-sharing-modal-users-select"
@update:modelValue="onAddSharee"
@ -73,7 +73,7 @@
<n8n-users-list
:actions="usersListActions"
:users="sharedWithList"
:currentUserId="usersStore.currentUser.id"
:current-user-id="usersStore.currentUser.id"
:readonly="!credentialPermissions.share"
@delete="onRemoveSharee"
/>
@ -151,6 +151,9 @@ export default defineComponent({
});
},
},
mounted() {
void this.loadUsers();
},
methods: {
async onAddSharee(userId: string) {
const sharee = { ...this.usersStore.getUserById(userId), isOwner: false };
@ -196,9 +199,6 @@ export default defineComponent({
void this.uiStore.goToUpgrade('credential_sharing', 'upgrade-credentials-sharing');
},
},
mounted() {
void this.loadUsers();
},
});
</script>

View file

@ -1,8 +1,8 @@
<template>
<div>
<img v-if="filePath" :class="$style.credIcon" :src="filePath" />
<NodeIcon v-else-if="relevantNode" :nodeType="relevantNode" :size="28" />
<span :class="$style.fallback" v-else></span>
<NodeIcon v-else-if="relevantNode" :node-type="relevantNode" :size="28" />
<span v-else :class="$style.fallback"></span>
</div>
</template>

View file

@ -104,16 +104,16 @@ listenForCredentialChanges({
[$style.invisible]: !props.selectedCredentialId,
}"
:title="i18n.baseText('nodeCredentials.updateCredential')"
@click="editCredential()"
data-test-id="credential-edit-button"
@click="editCredential()"
/>
</div>
<n8n-button
v-else
:label="`Create new ${props.appName} credential`"
@click="createNewCredential"
data-test-id="create-credential"
@click="createNewCredential"
/>
</div>
</template>

View file

@ -40,13 +40,13 @@ const onCredentialSelected = (credentialId: string) => {
<template>
<n8n-select
size="small"
:modelValue="props.selectedCredentialId"
:model-value="props.selectedCredentialId"
@update:modelValue="onCredentialSelected"
>
<n8n-option
v-for="item in props.credentialOptions"
:data-test-id="`node-credentials-select-item-${item.id}`"
:key="item.id"
:data-test-id="`node-credentials-select-item-${item.id}`"
:label="item.name"
:value="item.id"
>
@ -56,8 +56,8 @@ const onCredentialSelected = (credentialId: string) => {
</div>
</n8n-option>
<n8n-option
data-test-id="node-credentials-select-item-new"
:key="NEW_CREDENTIALS_TEXT"
data-test-id="node-credentials-select-item-new"
:value="NEW_CREDENTIALS_TEXT"
:label="NEW_CREDENTIALS_TEXT"
>

View file

@ -2,25 +2,25 @@
<div>
<div :class="$style['parameter-value-container']">
<n8n-select
ref="innerSelect"
:size="inputSize"
filterable
:modelValue="displayValue"
:model-value="displayValue"
:placeholder="
parameter.placeholder ? getPlaceholder() : $locale.baseText('parameterInput.select')
"
:title="displayTitle"
:disabled="isReadOnly"
ref="innerSelect"
data-test-id="credential-select"
@update:modelValue="(value) => $emit('update:modelValue', value)"
@keydown.stop
@focus="$emit('setFocus')"
@blur="$emit('onBlur')"
data-test-id="credential-select"
>
<n8n-option
v-for="credType in supportedCredentialTypes"
:value="credType.name"
:key="credType.name"
:value="credType.name"
:label="credType.displayName"
data-test-id="credential-select-option"
>
@ -39,15 +39,15 @@
<slot name="issues-and-options" />
</div>
<scopes-notice
<ScopesNotice
v-if="scopes.length > 0"
:activeCredentialType="activeCredentialType"
:active-credential-type="activeCredentialType"
:scopes="scopes"
/>
<div>
<node-credentials
<NodeCredentials
:node="node"
:overrideCredType="node.parameters[parameter.name]"
:override-cred-type="node.parameters[parameter.name]"
@credentialSelected="(updateInformation) => $emit('credentialSelected', updateInformation)"
/>
</div>

View file

@ -1,12 +1,12 @@
<template>
<Modal
:name="CREDENTIAL_SELECT_MODAL_KEY"
:eventBus="modalBus"
:event-bus="modalBus"
width="50%"
:center="true"
:loading="loading"
maxWidth="460px"
minHeight="250px"
max-width="460px"
min-height="250px"
>
<template #header>
<h2 :class="$style.title">
@ -19,22 +19,22 @@
{{ $locale.baseText('credentialSelectModal.selectAnAppOrServiceToConnectTo') }}
</div>
<n8n-select
ref="select"
filterable
defaultFirstOption
default-first-option
:placeholder="$locale.baseText('credentialSelectModal.searchForApp')"
size="xlarge"
ref="select"
:modelValue="selected"
@update:modelValue="onSelect"
:model-value="selected"
data-test-id="new-credential-type-select"
@update:modelValue="onSelect"
>
<template #prefix>
<font-awesome-icon icon="search" />
</template>
<n8n-option
v-for="credential in credentialsStore.allCredentialTypes"
:value="credential.name"
:key="credential.name"
:value="credential.name"
:label="credential.displayName"
filterable
data-test-id="new-credential-type-select-option"
@ -49,8 +49,8 @@
float="right"
size="large"
:disabled="!selected"
@click="openCredentialType"
data-test-id="new-credential-type-button"
@click="openCredentialType"
/>
</div>
</template>
@ -79,6 +79,14 @@ export default defineComponent({
externalHooks,
};
},
data() {
return {
modalBus: createEventBus(),
selected: '',
loading: true,
CREDENTIAL_SELECT_MODAL_KEY,
};
},
async mounted() {
try {
await this.credentialsStore.fetchCredentialTypes(false);
@ -92,14 +100,6 @@ export default defineComponent({
}
}, 0);
},
data() {
return {
modalBus: createEventBus(),
selected: '',
loading: true,
CREDENTIAL_SELECT_MODAL_KEY,
};
},
computed: {
...mapStores(useCredentialsStore, useUIStore, useWorkflowsStore),
},

View file

@ -1,11 +1,11 @@
<template>
<Modal
:name="modalName"
@enter="onSubmit"
:title="title"
:center="true"
width="460px"
:eventBus="modalBus"
:event-bus="modalBus"
@enter="onSubmit"
>
<template #content>
<div>
@ -14,14 +14,14 @@
$locale.baseText('settings.users.confirmUserDeletion')
}}</n8n-text>
</div>
<div :class="$style.content" v-else>
<div v-else :class="$style.content">
<div>
<n8n-text color="text-base">{{
$locale.baseText('settings.users.confirmDataHandlingAfterDeletion')
}}</n8n-text>
</div>
<el-radio
:modelValue="operation"
:model-value="operation"
label="transfer"
@update:modelValue="() => setOperation('transfer')"
>
@ -29,19 +29,19 @@
$locale.baseText('settings.users.transferWorkflowsAndCredentials')
}}</n8n-text>
</el-radio>
<div :class="$style.optionInput" v-if="operation === 'transfer'">
<div v-if="operation === 'transfer'" :class="$style.optionInput">
<n8n-input-label :label="$locale.baseText('settings.users.userToTransferTo')">
<n8n-user-select
:users="usersStore.allUsers"
:modelValue="transferId"
:ignoreIds="ignoreIds"
:currentUserId="usersStore.currentUserId"
:model-value="transferId"
:ignore-ids="ignoreIds"
:current-user-id="usersStore.currentUserId"
@update:modelValue="setTransferId"
/>
</n8n-input-label>
</div>
<el-radio
:modelValue="operation"
:model-value="operation"
label="delete"
@update:modelValue="() => setOperation('delete')"
>
@ -50,13 +50,13 @@
}}</n8n-text>
</el-radio>
<div
:class="$style.optionInput"
v-if="operation === 'delete'"
:class="$style.optionInput"
data-test-id="delete-data-input"
>
<n8n-input-label :label="$locale.baseText('settings.users.deleteConfirmationMessage')">
<n8n-input
:modelValue="deleteConfirmText"
:model-value="deleteConfirmText"
:placeholder="$locale.baseText('settings.users.deleteConfirmationText')"
@update:modelValue="setConfirmText"
/>
@ -70,8 +70,8 @@
:loading="loading"
:disabled="!enabled"
:label="$locale.baseText('settings.users.delete')"
@click="onSubmit"
float="right"
@click="onSubmit"
/>
</template>
</Modal>

View file

@ -1,15 +1,15 @@
<template>
<component
:is="tag"
ref="wrapper"
:class="{ [$style.dragging]: isDragging }"
@mousedown="onDragStart"
ref="wrapper"
>
<slot :isDragging="isDragging"></slot>
<slot :is-dragging="isDragging"></slot>
<Teleport to="body">
<div ref="draggable" :class="$style.draggable" :style="draggableStyle" v-show="isDragging">
<slot name="preview" :canDrop="canDrop" :el="draggingEl"></slot>
<div v-show="isDragging" ref="draggable" :class="$style.draggable" :style="draggableStyle">
<slot name="preview" :can-drop="canDrop" :el="draggingEl"></slot>
</div>
</Teleport>
</component>
@ -22,7 +22,7 @@ import { mapStores } from 'pinia';
import { defineComponent } from 'vue';
export default defineComponent({
name: 'draggable',
name: 'Draggable',
props: {
disabled: {
type: Boolean,

View file

@ -1,6 +1,6 @@
<template>
<div ref="target">
<slot :droppable="droppable" :activeDrop="activeDrop"></slot>
<slot :droppable="droppable" :active-drop="activeDrop"></slot>
</div>
</template>

View file

@ -1,46 +1,46 @@
<template>
<Modal
:name="modalName"
:eventBus="modalBus"
@enter="save"
:event-bus="modalBus"
:title="$locale.baseText('duplicateWorkflowDialog.duplicateWorkflow')"
:center="true"
width="420px"
@enter="save"
>
<template #content>
<div :class="$style.content">
<n8n-input
v-model="name"
ref="nameInput"
v-model="name"
:placeholder="$locale.baseText('duplicateWorkflowDialog.enterWorkflowName')"
:maxlength="MAX_WORKFLOW_NAME_LENGTH"
/>
<TagsDropdown
v-if="settingsStore.areTagsEnabled"
ref="dropdown"
v-model="currentTagIds"
:createEnabled="true"
:eventBus="dropdownBus"
:create-enabled="true"
:event-bus="dropdownBus"
:placeholder="$locale.baseText('duplicateWorkflowDialog.chooseOrCreateATag')"
@blur="onTagsBlur"
@esc="onTagsEsc"
:placeholder="$locale.baseText('duplicateWorkflowDialog.chooseOrCreateATag')"
ref="dropdown"
/>
</div>
</template>
<template #footer="{ close }">
<div :class="$style.footer">
<n8n-button
@click="save"
:loading="isSaving"
:label="$locale.baseText('duplicateWorkflowDialog.save')"
float="right"
@click="save"
/>
<n8n-button
type="secondary"
@click="close"
:disabled="isSaving"
:label="$locale.baseText('duplicateWorkflowDialog.cancel')"
float="right"
@click="close"
/>
</div>
</template>
@ -66,8 +66,8 @@ import { useCredentialsStore } from '@/stores/credentials.store';
export default defineComponent({
name: 'DuplicateWorkflow',
mixins: [workflowHelpers],
components: { TagsDropdown, Modal },
mixins: [workflowHelpers],
props: ['modalName', 'isActive', 'data'],
setup() {
return {

View file

@ -1,7 +1,7 @@
<template>
<div>
<slot v-if="canAccess" />
<slot name="fallback" v-else />
<slot v-else name="fallback" />
</div>
</template>

View file

@ -2,7 +2,7 @@
<div>
<div class="error-header">
<div class="error-message" v-text="getErrorMessage()" />
<div class="error-description" v-if="error.description" v-html="getErrorDescription()"></div>
<div v-if="error.description" class="error-description" v-html="getErrorDescription()"></div>
</div>
<details>
<summary class="error-details__summary">
@ -76,19 +76,19 @@
</div>
</template>
<div>
<div class="copy-button" v-if="displayCause">
<div v-if="displayCause" class="copy-button">
<n8n-icon-button
@click="copyCause"
:title="$locale.baseText('nodeErrorView.copyToClipboard')"
icon="copy"
@click="copyCause"
/>
</div>
<vue-json-pretty
<VueJsonPretty
v-if="displayCause"
:data="error.cause"
:deep="3"
:showLength="true"
selectableType="single"
:show-length="true"
selectable-type="single"
path="error"
class="json-data"
/>
@ -137,11 +137,11 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store';
export default defineComponent({
name: 'NodeErrorView',
mixins: [copyPaste],
props: ['error'],
components: {
VueJsonPretty,
},
mixins: [copyPaste],
props: ['error'],
setup() {
return {
...useToast(),

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