feat: Replace Vue.extend with defineComponent in design system (no-changelog) (#5918)

* refactor: replace new Vue() with custom event bus (no-changelog)

* fix: export types from design system main

* fix: update component types

* fix: update form inputs event bus

* refactor: replace global Vue references in design-system

* refactor: update prop types

* feat: improve types

* fix: further type improvements

* fix: further types improvements

* fix: further type improvements

* test: fix test snapshots

* test: fix snapshot

* chore: fix linting issues

* test: fix personalization modal snapshot
This commit is contained in:
Alex Grozav
2023-04-12 17:39:45 +03:00
committed by GitHub
parent 0a53c957c4
commit 430a8781e8
67 changed files with 447 additions and 375 deletions

View File

@@ -16,7 +16,7 @@
"scripts": { "scripts": {
"clean": "rimraf dist .turbo", "clean": "rimraf dist .turbo",
"build": "vite build", "build": "vite build",
"typecheck": "vue-tsc --emitDeclarationOnly", "typecheck": "vue-tsc --declaration --emitDeclarationOnly",
"test": "vitest run --coverage", "test": "vitest run --coverage",
"test:dev": "vitest", "test:dev": "vitest",
"build:storybook": "storybook build", "build:storybook": "storybook build",

View File

@@ -42,9 +42,9 @@ import N8nButton from '../N8nButton';
import N8nHeading from '../N8nHeading'; import N8nHeading from '../N8nHeading';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import N8nCallout from '../N8nCallout'; import N8nCallout from '../N8nCallout';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-action-box', name: 'n8n-action-box',
components: { components: {
N8nButton, N8nButton,

View File

@@ -18,14 +18,7 @@
:disabled="item.disabled" :disabled="item.disabled"
:divided="item.divided" :divided="item.divided"
> >
<div <div :class="getItemClasses(item)" :data-test-id="`workflow-menu-item-${item.id}`">
:class="{
[$style.itemContainer]: true,
[$style.hasCustomStyling]: item.customClass !== undefined,
[item.customClass]: item.customClass !== undefined,
}"
:data-test-id="`workflow-menu-item-${item.id}`"
>
<span v-if="item.icon" :class="$style.icon"> <span v-if="item.icon" :class="$style.icon">
<n8n-icon :icon="item.icon" :size="iconSize" /> <n8n-icon :icon="item.icon" :size="iconSize" />
</span> </span>
@@ -41,7 +34,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue, { PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import { import {
Dropdown as ElDropdown, Dropdown as ElDropdown,
DropdownMenu as ElDropdownMenu, DropdownMenu as ElDropdownMenu,
@@ -49,7 +42,7 @@ import {
} from 'element-ui'; } from 'element-ui';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
interface IActionDropdownItem { export interface IActionDropdownItem {
id: string; id: string;
label: string; label: string;
icon?: string; icon?: string;
@@ -64,7 +57,7 @@ interface IActionDropdownItem {
// by Element UI dropdown component). // by Element UI dropdown component).
// It can be used in different parts of editor UI while ActionToggle // It can be used in different parts of editor UI while ActionToggle
// is designed to be used in card components. // is designed to be used in card components.
export default Vue.extend({ export default defineComponent({
name: 'n8n-action-dropdown', name: 'n8n-action-dropdown',
components: { components: {
ElDropdown, ElDropdown,
@@ -99,6 +92,13 @@ export default Vue.extend({
}, },
}, },
methods: { methods: {
getItemClasses(item: IActionDropdownItem): Record<string, boolean> {
return {
[this.$style.itemContainer]: true,
[this.$style.hasCustomStyling]: item.customClass !== undefined,
...(item.customClass !== undefined ? { [item.customClass]: true } : {}),
};
},
onSelect(action: string): void { onSelect(action: string): void {
this.$emit('select', action); this.$emit('select', action);
}, },

View File

@@ -9,7 +9,7 @@
@visible-change="onVisibleChange" @visible-change="onVisibleChange"
> >
<span :class="{ [$style.button]: true, [$style[theme]]: !!theme }"> <span :class="{ [$style.button]: true, [$style[theme]]: !!theme }">
<component :is="$options.components.N8nIcon" icon="ellipsis-v" :size="iconSize" /> <n8n-icon icon="ellipsis-v" :size="iconSize" />
</span> </span>
<template #dropdown> <template #dropdown>
@@ -22,9 +22,8 @@
> >
{{ action.label }} {{ action.label }}
<div :class="$style.iconContainer"> <div :class="$style.iconContainer">
<component <n8n-icon
v-if="action.type === 'external-link'" v-if="action.type === 'external-link'"
:is="$options.components.N8nIcon"
icon="external-link-alt" icon="external-link-alt"
size="xsmall" size="xsmall"
color="text-base" color="text-base"
@@ -38,22 +37,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
import { import {
Dropdown as ElDropdown, Dropdown as ElDropdown,
DropdownMenu as ElDropdownMenu, DropdownMenu as ElDropdownMenu,
DropdownItem as ElDropdownItem, DropdownItem as ElDropdownItem,
} from 'element-ui'; } from 'element-ui';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
import type { UserAction } from '@/types';
interface Action { export default defineComponent({
label: string;
value: string;
disabled: boolean;
type?: 'external-link';
}
export default Vue.extend({
name: 'n8n-action-toggle', name: 'n8n-action-toggle',
components: { components: {
ElDropdown, ElDropdown,
@@ -63,7 +56,7 @@ export default Vue.extend({
}, },
props: { props: {
actions: { actions: {
type: Array<Action>, type: Array as PropType<UserAction[]>,
default: () => [], default: () => [],
}, },
placement: { placement: {

View File

@@ -21,9 +21,9 @@ const sizes: { [size: string]: number } = {
medium: 40, medium: 40,
}; };
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-avatar', name: 'n8n-avatar',
props: { props: {
firstName: { firstName: {

View File

@@ -9,9 +9,9 @@
<script lang="ts"> <script lang="ts">
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
props: { props: {
theme: { theme: {
type: String, type: String,

View File

@@ -18,11 +18,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
import N8nSpinner from '../N8nSpinner'; import N8nSpinner from '../N8nSpinner';
export default Vue.extend({ export default defineComponent({
name: 'n8n-button', name: 'n8n-button',
props: { props: {
label: { label: {
@@ -81,11 +81,11 @@ export default Vue.extend({
N8nIcon, N8nIcon,
}, },
computed: { computed: {
ariaBusy(): string { ariaBusy(): 'true' | undefined {
return this.loading ? 'true' : 'false'; return this.loading ? 'true' : undefined;
}, },
ariaDisabled(): string { ariaDisabled(): 'true' | undefined {
return this.disabled ? 'true' : 'false'; return this.disabled ? 'true' : undefined;
}, },
classes(): string { classes(): string {
return ( return (

View File

@@ -1,23 +1,23 @@
// Vitest Snapshot v1 // Vitest Snapshot v1
exports[`components > N8nButton > overrides > should render as \`secondary\` when \`text\` is given as type 1`] = `"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button secondary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`; exports[`components > N8nButton > overrides > should render as \`secondary\` when \`text\` is given as type 1`] = `"<button aria-live=\\"polite\\" class=\\"button button secondary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
exports[`components > N8nButton > overrides > should render as \`tertiary\` when \`info\` is given as type 1`] = `"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button tertiary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`; exports[`components > N8nButton > overrides > should render as \`tertiary\` when \`info\` is given as type 1`] = `"<button aria-live=\\"polite\\" class=\\"button button tertiary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
exports[`components > N8nButton > overrides > should use default (\`primary\`) type when no type is given 1`] = `"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`; exports[`components > N8nButton > overrides > should use default (\`primary\`) type when no type is given 1`] = `"<button aria-live=\\"polite\\" class=\\"button button primary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
exports[`components > N8nButton > overrides > should use given (\`secondary\`) type 1`] = `"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button secondary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`; exports[`components > N8nButton > overrides > should use given (\`secondary\`) type 1`] = `"<button aria-live=\\"polite\\" class=\\"button button secondary medium icon\\" icon=\\"plus-circle\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
exports[`components > N8nButton > props > icon > should render icon button 1`] = `"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium icon\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`; exports[`components > N8nButton > props > icon > should render icon button 1`] = `"<button aria-live=\\"polite\\" class=\\"button button primary medium icon\\"><span class=\\"icon\\"><n8n-icon-stub icon=\\"plus-circle\\" size=\\"medium\\"></n8n-icon-stub></span><span>Button</span></button>"`;
exports[`components > N8nButton > props > loading > should render loading spinner 1`] = `"<button disabled=\\"disabled\\" aria-disabled=\\"false\\" aria-busy=\\"true\\" aria-live=\\"polite\\" class=\\"button button primary medium loading icon\\"><span class=\\"icon\\"><n8n-spinner-stub size=\\"medium\\" type=\\"dots\\"></n8n-spinner-stub></span><span>Button</span></button>"`; exports[`components > N8nButton > props > loading > should render loading spinner 1`] = `"<button disabled=\\"disabled\\" aria-busy=\\"true\\" aria-live=\\"polite\\" class=\\"button button primary medium loading icon\\"><span class=\\"icon\\"><n8n-spinner-stub size=\\"medium\\" type=\\"dots\\"></n8n-spinner-stub></span><span>Button</span></button>"`;
exports[`components > N8nButton > props > square > should render square button 1`] = ` exports[`components > N8nButton > props > square > should render square button 1`] = `
"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium square\\"> "<button aria-live=\\"polite\\" class=\\"button button primary medium square\\">
<!----><span>48</span></button>" <!----><span>48</span></button>"
`; `;
exports[`components > N8nButton > should render correctly 1`] = ` exports[`components > N8nButton > should render correctly 1`] = `
"<button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\"> "<button aria-live=\\"polite\\" class=\\"button button primary medium\\">
<!----><span>Button</span></button>" <!----><span>Button</span></button>"
`; `;

View File

@@ -5,7 +5,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import N8nButton from '../Button.vue'; import N8nButton from '../Button.vue';
const classToTypeMap = { const classToTypeMap = {
@@ -13,7 +13,7 @@ const classToTypeMap = {
'el-picker-panel__link-btn': 'secondary', 'el-picker-panel__link-btn': 'secondary',
}; };
export default Vue.extend({ export default defineComponent({
components: { components: {
N8nButton, N8nButton,
}, },

View File

@@ -16,7 +16,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
@@ -27,7 +27,7 @@ const CALLOUT_DEFAULT_ICONS: { [key: string]: string } = {
danger: 'times-circle', danger: 'times-circle',
}; };
export default Vue.extend({ export default defineComponent({
name: 'n8n-callout', name: 'n8n-callout',
components: { components: {
N8nText, N8nText,

View File

@@ -21,9 +21,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-card', name: 'n8n-card',
inheritAttrs: true, inheritAttrs: true,
props: { props: {

View File

@@ -19,11 +19,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import { Checkbox as ElCheckbox } from 'element-ui'; import { Checkbox as ElCheckbox } from 'element-ui';
import N8nInputLabel from '../N8nInputLabel'; import N8nInputLabel from '../N8nInputLabel';
export default Vue.extend({ export default defineComponent({
name: 'n8n-checkbox', name: 'n8n-checkbox',
components: { components: {
ElCheckbox, ElCheckbox,

View File

@@ -112,7 +112,7 @@ export default defineComponent({
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="row in visibleRows" :key="row.id" :class="getTrClass(row)"> <tr v-for="row in visibleRows" :key="row.id" :class="getTrClass()">
<td v-for="column in columns" :key="column.id"> <td v-for="column in columns" :key="column.id">
<component v-if="column.render" :is="column.render" :row="row" :column="column" /> <component v-if="column.render" :is="column.render" :row="row" :column="column" />
<span v-else>{{ getTdValue(row, column) }}</span> <span v-else>{{ getTdValue(row, column) }}</span>

View File

@@ -16,70 +16,70 @@ exports[`components > N8nDatatable > should render correctly 1`] = `
<td><span>1</span></td> <td><span>1</span></td>
<td><span>Richard Hendricks</span></td> <td><span>Richard Hendricks</span></td>
<td><span>29</span></td> <td><span>29</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 1</span></button></td> <!----><span>Button 1</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>2</span></td> <td><span>2</span></td>
<td><span>Bertram Gilfoyle</span></td> <td><span>Bertram Gilfoyle</span></td>
<td><span>44</span></td> <td><span>44</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 2</span></button></td> <!----><span>Button 2</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>3</span></td> <td><span>3</span></td>
<td><span>Dinesh Chugtai</span></td> <td><span>Dinesh Chugtai</span></td>
<td><span>31</span></td> <td><span>31</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 3</span></button></td> <!----><span>Button 3</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>4</span></td> <td><span>4</span></td>
<td><span>Jared Dunn </span></td> <td><span>Jared Dunn </span></td>
<td><span>38</span></td> <td><span>38</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 4</span></button></td> <!----><span>Button 4</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>5</span></td> <td><span>5</span></td>
<td><span>Richard Hendricks</span></td> <td><span>Richard Hendricks</span></td>
<td><span>29</span></td> <td><span>29</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 5</span></button></td> <!----><span>Button 5</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>6</span></td> <td><span>6</span></td>
<td><span>Bertram Gilfoyle</span></td> <td><span>Bertram Gilfoyle</span></td>
<td><span>44</span></td> <td><span>44</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 6</span></button></td> <!----><span>Button 6</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>7</span></td> <td><span>7</span></td>
<td><span>Dinesh Chugtai</span></td> <td><span>Dinesh Chugtai</span></td>
<td><span>31</span></td> <td><span>31</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 7</span></button></td> <!----><span>Button 7</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>8</span></td> <td><span>8</span></td>
<td><span>Jared Dunn </span></td> <td><span>Jared Dunn </span></td>
<td><span>38</span></td> <td><span>38</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 8</span></button></td> <!----><span>Button 8</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>9</span></td> <td><span>9</span></td>
<td><span>Richard Hendricks</span></td> <td><span>Richard Hendricks</span></td>
<td><span>29</span></td> <td><span>29</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 9</span></button></td> <!----><span>Button 9</span></button></td>
</tr> </tr>
<tr class=\\"datatableRow\\"> <tr class=\\"datatableRow\\">
<td><span>10</span></td> <td><span>10</span></td>
<td><span>Bertram Gilfoyle</span></td> <td><span>Bertram Gilfoyle</span></td>
<td><span>44</span></td> <td><span>44</span></td>
<td><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\"> <td><button aria-live=\\"polite\\" class=\\"button button primary medium\\" column=\\"[object Object]\\">
<!----><span>Button 10</span></button></td> <!----><span>Button 10</span></button></td>
</tr> </tr>
</tbody> </tbody>

View File

@@ -38,14 +38,14 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import N8nFormInputs from '../N8nFormInputs'; import N8nFormInputs from '../N8nFormInputs';
import N8nHeading from '../N8nHeading'; import N8nHeading from '../N8nHeading';
import N8nLink from '../N8nLink'; import N8nLink from '../N8nLink';
import N8nButton from '../N8nButton'; import N8nButton from '../N8nButton';
import { createEventBus } from '../../utils'; import { createEventBus } from '../../utils';
export default Vue.extend({ export default defineComponent({
name: 'n8n-form-box', name: 'n8n-form-box',
components: { components: {
N8nHeading, N8nHeading,
@@ -56,12 +56,11 @@ export default Vue.extend({
props: { props: {
title: { title: {
type: String, type: String,
default: '',
}, },
inputs: { inputs: {
type: Array, type: Array,
default() { default: () => [],
return [];
},
}, },
buttonText: { buttonText: {
type: String, type: String,
@@ -75,9 +74,11 @@ export default Vue.extend({
}, },
redirectText: { redirectText: {
type: String, type: String,
default: '',
}, },
redirectLink: { redirectLink: {
type: String, type: String,
default: '',
}, },
}, },
data() { data() {

View File

@@ -92,7 +92,7 @@ import N8nSelect from '../N8nSelect';
import N8nOption from '../N8nOption'; import N8nOption from '../N8nOption';
import N8nInputLabel from '../N8nInputLabel'; import N8nInputLabel from '../N8nInputLabel';
import N8nCheckbox from '../N8nCheckbox'; import N8nCheckbox from '../N8nCheckbox';
import ElSwitch from 'element-ui/lib/switch'; import { Switch as ElSwitch } from 'element-ui';
import { getValidationError, VALIDATORS } from './validators'; import { getValidationError, VALIDATORS } from './validators';
import { Rule, RuleGroup, IValidator, Validatable, FormState } from '../../types'; import { Rule, RuleGroup, IValidator, Validatable, FormState } from '../../types';

View File

@@ -3,8 +3,7 @@ import { IValidator, RuleGroup, Validatable } from '../../types';
export const emailRegex = export const emailRegex =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = { export const requiredValidator: IValidator<{}> = {
REQUIRED: {
validate: (value: Validatable) => { validate: (value: Validatable) => {
if (typeof value === 'string' && !!value.trim()) { if (typeof value === 'string' && !!value.trim()) {
return false; return false;
@@ -18,8 +17,9 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
messageKey: 'formInput.validator.fieldRequired', messageKey: 'formInput.validator.fieldRequired',
}; };
}, },
}, };
MIN_LENGTH: {
export const minLengthValidator: IValidator<{ minimum: number }> = {
validate: (value: Validatable, config: { minimum: number }) => { validate: (value: Validatable, config: { minimum: number }) => {
if (typeof value === 'string' && value.length < config.minimum) { if (typeof value === 'string' && value.length < config.minimum) {
return { return {
@@ -30,8 +30,9 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
return false; return false;
}, },
}, };
MAX_LENGTH: {
export const maxLengthValidator: IValidator<{ maximum: number }> = {
validate: (value: Validatable, config: { maximum: number }) => { validate: (value: Validatable, config: { maximum: number }) => {
if (typeof value === 'string' && value.length > config.maximum) { if (typeof value === 'string' && value.length > config.maximum) {
return { return {
@@ -42,8 +43,9 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
return false; return false;
}, },
}, };
CONTAINS_NUMBER: {
export const containsNumberValidator: IValidator<{ minimum: number }> = {
validate: (value: Validatable, config: { minimum: number }) => { validate: (value: Validatable, config: { minimum: number }) => {
if (typeof value !== 'string') { if (typeof value !== 'string') {
return false; return false;
@@ -59,8 +61,9 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
return false; return false;
}, },
}, };
VALID_EMAIL: {
export const emailValidator: IValidator<{}> = {
validate: (value: Validatable) => { validate: (value: Validatable) => {
if (!emailRegex.test(String(value).trim().toLowerCase())) { if (!emailRegex.test(String(value).trim().toLowerCase())) {
return { return {
@@ -70,8 +73,9 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
return false; return false;
}, },
}, };
CONTAINS_UPPERCASE: {
export const containsUpperCaseValidator: IValidator<{ minimum: number }> = {
validate: (value: Validatable, config: { minimum: number }) => { validate: (value: Validatable, config: { minimum: number }) => {
if (typeof value !== 'string') { if (typeof value !== 'string') {
return false; return false;
@@ -87,8 +91,9 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
return false; return false;
}, },
}, };
DEFAULT_PASSWORD_RULES: {
export const defaultPasswordRules: RuleGroup = {
rules: [ rules: [
{ {
rules: [ rules: [
@@ -102,7 +107,16 @@ export const VALIDATORS: { [key: string]: IValidator | RuleGroup } = {
}, },
{ name: 'MAX_LENGTH', config: { maximum: 64 } }, { name: 'MAX_LENGTH', config: { maximum: 64 } },
], ],
}, };
export const VALIDATORS = {
REQUIRED: requiredValidator,
MIN_LENGTH: minLengthValidator,
MAX_LENGTH: maxLengthValidator,
CONTAINS_NUMBER: containsNumberValidator,
VALID_EMAIL: emailValidator,
CONTAINS_UPPERCASE: containsUpperCaseValidator,
DEFAULT_PASSWORD_RULES: defaultPasswordRules,
}; };
export const getValidationError = <T extends Validatable, C>( export const getValidationError = <T extends Validatable, C>(

View File

@@ -5,7 +5,7 @@
<div <div
v-for="(input, index) in filteredInputs" v-for="(input, index) in filteredInputs"
:key="input.name" :key="input.name"
:class="{ [`mt-${verticalSpacing}`]: index > 0 }" :class="{ [`mt-${verticalSpacing}`]: verticalSpacing && index > 0 }"
> >
<n8n-text <n8n-text
color="text-base" color="text-base"
@@ -37,13 +37,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue, { PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import N8nFormInput from '../N8nFormInput'; import N8nFormInput from '../N8nFormInput';
import type { IFormInput } from '../../types'; import type { IFormInput } from '../../types';
import ResizeObserver from '../ResizeObserver'; import ResizeObserver from '../ResizeObserver';
import { EventBus } from '@/utils'; import { createEventBus, EventBus } from '../../utils';
export default Vue.extend({ export default defineComponent({
name: 'n8n-form-inputs', name: 'n8n-form-inputs',
components: { components: {
N8nFormInput, N8nFormInput,
@@ -51,13 +51,12 @@ export default Vue.extend({
}, },
props: { props: {
inputs: { inputs: {
type: Array, type: Array as PropType<IFormInput[]>,
default() { default: (): IFormInput[] => [],
return [[]];
},
}, },
eventBus: { eventBus: {
type: Object as PropType<EventBus>, type: Object as PropType<EventBus>,
default: (): EventBus => createEventBus(),
}, },
columnView: { columnView: {
type: Boolean, type: Boolean,
@@ -65,8 +64,8 @@ export default Vue.extend({
}, },
verticalSpacing: { verticalSpacing: {
type: String, type: String,
required: false, default: '',
validator: (value: string): boolean => ['xs', 's', 'm', 'm', 'l', 'xl'].includes(value), validator: (value: string): boolean => ['', 'xs', 's', 'm', 'm', 'l', 'xl'].includes(value),
}, },
}, },
data() { data() {
@@ -77,19 +76,19 @@ export default Vue.extend({
}; };
}, },
mounted() { mounted() {
(this.inputs as IFormInput[]).forEach((input) => { this.inputs.forEach((input) => {
if (input.hasOwnProperty('initialValue')) { if (input.hasOwnProperty('initialValue')) {
Vue.set(this.values, input.name, input.initialValue); this.$set(this.values, input.name, input.initialValue);
} }
}); });
if (this.eventBus) { if (this.eventBus) {
this.eventBus.on('submit', this.onSubmit); // eslint-disable-line @typescript-eslint/unbound-method this.eventBus.on('submit', () => this.onSubmit());
} }
}, },
computed: { computed: {
filteredInputs(): IFormInput[] { filteredInputs(): IFormInput[] {
return (this.inputs as IFormInput[]).filter((input) => return this.inputs.filter((input) =>
typeof input.shouldDisplay === 'function' ? input.shouldDisplay(this.values) : true, typeof input.shouldDisplay === 'function' ? input.shouldDisplay(this.values) : true,
); );
}, },
@@ -107,15 +106,16 @@ export default Vue.extend({
onInput(name: string, value: unknown) { onInput(name: string, value: unknown) {
this.values = { this.values = {
...this.values, ...this.values,
[name]: value, // eslint-disable-line @typescript-eslint/no-unsafe-assignment [name]: value,
}; };
this.$emit('input', { name, value }); // eslint-disable-line @typescript-eslint/no-unsafe-assignment this.$emit('input', { name, value });
}, },
onValidate(name: string, valid: boolean) { onValidate(name: string, valid: boolean) {
Vue.set(this.validity, name, valid); this.$set(this.validity, name, valid);
}, },
onSubmit() { onSubmit() {
this.showValidationWarnings = true; this.showValidationWarnings = true;
if (this.isReadyToSubmit) { if (this.isReadyToSubmit) {
const toSubmit = this.filteredInputs.reduce<{ [key: string]: unknown }>((accu, input) => { const toSubmit = this.filteredInputs.reduce<{ [key: string]: unknown }>((accu, input) => {
if (this.values[input.name]) { if (this.values[input.name]) {

View File

@@ -5,9 +5,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-heading', name: 'n8n-heading',
props: { props: {
tag: { tag: {

View File

@@ -8,9 +8,9 @@
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-icon', name: 'n8n-icon',
components: { components: {
FontAwesomeIcon, FontAwesomeIcon,

View File

@@ -5,9 +5,9 @@
<script lang="ts"> <script lang="ts">
import N8nButton from '../N8nButton'; import N8nButton from '../N8nButton';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-icon-button', name: 'n8n-icon-button',
components: { components: {
N8nButton, N8nButton,

View File

@@ -41,15 +41,17 @@
<script lang="ts"> <script lang="ts">
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
import Vue, { PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
interface IAccordionItem { export interface IAccordionItem {
id: string; id: string;
label: string; label: string;
icon: string; icon: string;
iconColor?: string;
tooltip?: string;
} }
export default Vue.extend({ export default defineComponent({
name: 'n8n-info-accordion', name: 'n8n-info-accordion',
components: { components: {
N8nText, N8nText,
@@ -64,9 +66,7 @@ export default Vue.extend({
}, },
items: { items: {
type: Array as PropType<IAccordionItem[]>, type: Array as PropType<IAccordionItem[]>,
default() { default: () => [],
return [];
},
}, },
initiallyExpanded: { initiallyExpanded: {
type: Boolean, type: Boolean,
@@ -92,7 +92,7 @@ export default Vue.extend({
toggle() { toggle() {
this.expanded = !this.expanded; this.expanded = !this.expanded;
}, },
onClick(e) { onClick(e: MouseEvent) {
this.$emit('click', e); this.$emit('click', e);
}, },
onTooltipClick(item: string, event: MouseEvent) { onTooltipClick(item: string, event: MouseEvent) {

View File

@@ -35,9 +35,9 @@
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
import N8nTooltip from '../N8nTooltip'; import N8nTooltip from '../N8nTooltip';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-info-tip', name: 'n8n-info-tip',
components: { components: {
N8nIcon, N8nIcon,

View File

@@ -25,9 +25,9 @@
<script lang="ts"> <script lang="ts">
import { Input as ElInput } from 'element-ui'; import { Input as ElInput } from 'element-ui';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-input', name: 'n8n-input',
components: { components: {
ElInput, ElInput,

View File

@@ -47,9 +47,9 @@ import N8nIcon from '../N8nIcon';
import { addTargetBlank } from '../utils/helpers'; import { addTargetBlank } from '../utils/helpers';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-input-label', name: 'n8n-input-label',
components: { components: {
N8nText, N8nText,

View File

@@ -9,11 +9,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import N8nRoute from '../N8nRoute'; import N8nRoute from '../N8nRoute';
export default Vue.extend({ export default defineComponent({
name: 'n8n-link', name: 'n8n-link',
props: { props: {
size: { size: {

View File

@@ -38,9 +38,9 @@
<script lang="ts"> <script lang="ts">
import { Skeleton as ElSkeleton, SkeletonItem as ElSkeletonItem } from 'element-ui'; import { Skeleton as ElSkeleton, SkeletonItem as ElSkeletonItem } from 'element-ui';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-loading', name: 'n8n-loading',
components: { components: {
ElSkeleton, ElSkeleton,

View File

@@ -19,14 +19,14 @@
<script lang="ts"> <script lang="ts">
import N8nLoading from '../N8nLoading'; import N8nLoading from '../N8nLoading';
import Markdown from 'markdown-it'; import Markdown, { PluginSimple } from 'markdown-it';
import markdownLink from 'markdown-it-link-attributes'; import markdownLink from 'markdown-it-link-attributes';
import markdownEmoji from 'markdown-it-emoji'; import markdownEmoji from 'markdown-it-emoji';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import markdownTasklists from 'markdown-it-task-lists'; import markdownTasklists from 'markdown-it-task-lists';
import { defineComponent, PropType } from 'vue';
import xss, { friendlyAttrValue } from 'xss'; import xss, { friendlyAttrValue } from 'xss';
import { escapeMarkdown } from '../../utils/markdown'; import { escapeMarkdown } from '../../utils/markdown';
@@ -49,20 +49,18 @@ const DEFAULT_OPTIONS_TASKLISTS = {
labelAfter: true, labelAfter: true,
} as const; } as const;
interface IImage { export interface IImage {
id: string; id: string;
url: string; url: string;
} }
interface Options { export interface Options {
markdown: typeof DEFAULT_OPTIONS_MARKDOWN; markdown: typeof DEFAULT_OPTIONS_MARKDOWN;
linkAttributes: typeof DEFAULT_OPTIONS_LINK_ATTRIBUTES; linkAttributes: typeof DEFAULT_OPTIONS_LINK_ATTRIBUTES;
tasklists: typeof DEFAULT_OPTIONS_TASKLISTS; tasklists: typeof DEFAULT_OPTIONS_TASKLISTS;
} }
import Vue, { PropType } from 'vue'; export default defineComponent({
export default Vue.extend({
components: { components: {
N8nLoading, N8nLoading,
}, },
@@ -70,15 +68,19 @@ export default Vue.extend({
props: { props: {
content: { content: {
type: String, type: String,
default: '',
}, },
withMultiBreaks: { withMultiBreaks: {
type: Boolean, type: Boolean,
default: false,
}, },
images: { images: {
type: Array<IImage>, type: Array as PropType<IImage[]>,
default: () => [],
}, },
loading: { loading: {
type: Boolean, type: Boolean,
default: false,
}, },
loadingBlocks: { loadingBlocks: {
type: Number, type: Number,
@@ -86,7 +88,7 @@ export default Vue.extend({
}, },
loadingRows: { loadingRows: {
type: Number, type: Number,
default: () => 3, default: 3,
}, },
theme: { theme: {
type: String, type: String,
@@ -94,15 +96,21 @@ export default Vue.extend({
}, },
options: { options: {
type: Object as PropType<Options>, type: Object as PropType<Options>,
default() { default: (): Options => ({
return {
markdown: DEFAULT_OPTIONS_MARKDOWN, markdown: DEFAULT_OPTIONS_MARKDOWN,
linkAttributes: DEFAULT_OPTIONS_LINK_ATTRIBUTES, linkAttributes: DEFAULT_OPTIONS_LINK_ATTRIBUTES,
tasklists: DEFAULT_OPTIONS_TASKLISTS, tasklists: DEFAULT_OPTIONS_TASKLISTS,
}),
},
},
data(): { md: Markdown } {
return {
md: new Markdown(this.options.markdown)
.use(markdownLink, this.options.linkAttributes)
.use(markdownEmoji)
.use(markdownTasklists as PluginSimple, this.options.tasklists),
}; };
}, },
},
},
computed: { computed: {
htmlContent(): string { htmlContent(): string {
if (!this.content) { if (!this.content) {
@@ -154,14 +162,6 @@ export default Vue.extend({
return safeHtml; return safeHtml;
}, },
}, },
data() {
return {
md: new Markdown(this.options.markdown) // eslint-disable-line @typescript-eslint/no-unsafe-member-access
.use(markdownLink, this.options.linkAttributes) // eslint-disable-line @typescript-eslint/no-unsafe-member-access
.use(markdownEmoji)
.use(markdownTasklists, this.options.tasklists), // eslint-disable-line @typescript-eslint/no-unsafe-member-access
};
},
methods: { methods: {
onClick(event: MouseEvent) { onClick(event: MouseEvent) {
let clickedLink = null; let clickedLink = null;

View File

@@ -54,11 +54,10 @@
<script lang="ts"> <script lang="ts">
import { Menu as ElMenu } from 'element-ui'; import { Menu as ElMenu } from 'element-ui';
import N8nMenuItem from '../N8nMenuItem'; import N8nMenuItem from '../N8nMenuItem';
import { defineComponent, PropType } from 'vue';
import type { IMenuItem, RouteObject } from '../../types';
import Vue, { PropType } from 'vue'; export default defineComponent({
import { IMenuItem } from '../../types';
export default Vue.extend({
name: 'n8n-menu', name: 'n8n-menu',
components: { components: {
ElMenu, ElMenu,
@@ -93,6 +92,7 @@ export default Vue.extend({
}, },
items: { items: {
type: Array as PropType<IMenuItem[]>, type: Array as PropType<IMenuItem[]>,
default: (): IMenuItem[] => [],
}, },
value: { value: {
type: String, type: String,
@@ -104,9 +104,9 @@ export default Vue.extend({
const found = this.items.find((item) => { const found = this.items.find((item) => {
return ( return (
(Array.isArray(item.activateOnRouteNames) && (Array.isArray(item.activateOnRouteNames) &&
item.activateOnRouteNames.includes(this.$route.name || '')) || item.activateOnRouteNames.includes(this.currentRoute.name || '')) ||
(Array.isArray(item.activateOnRoutePaths) && (Array.isArray(item.activateOnRoutePaths) &&
item.activateOnRoutePaths.includes(this.$route.path)) item.activateOnRoutePaths.includes(this.currentRoute.path))
); );
}); });
this.activeTab = found ? found.id : ''; this.activeTab = found ? found.id : '';
@@ -127,6 +127,14 @@ export default Vue.extend({
(item: IMenuItem) => item.position === 'bottom' && item.available !== false, (item: IMenuItem) => item.position === 'bottom' && item.available !== false,
); );
}, },
currentRoute(): RouteObject {
return (
(this as typeof this & { $route: RouteObject }).$route || {
name: '',
path: '',
}
);
},
}, },
methods: { methods: {
onSelect(event: MouseEvent, option: string): void { onSelect(event: MouseEvent, option: string): void {

View File

@@ -32,7 +32,7 @@
}" }"
data-test-id="menu-item" data-test-id="menu-item"
:index="child.id" :index="child.id"
@click="onItemClick(child)" @click="onItemClick(child, $event)"
> >
<n8n-icon v-if="child.icon" :class="$style.icon" :icon="child.icon" /> <n8n-icon v-if="child.icon" :class="$style.icon" :icon="child.icon" />
<span :class="$style.label">{{ child.label }}</span> <span :class="$style.label">{{ child.label }}</span>
@@ -56,7 +56,7 @@
}" }"
data-test-id="menu-item" data-test-id="menu-item"
:index="item.id" :index="item.id"
@click="onItemClick(item)" @click="onItemClick(item, $event)"
> >
<n8n-icon <n8n-icon
v-if="item.icon" v-if="item.icon"
@@ -74,10 +74,10 @@
import { Submenu as ElSubmenu, MenuItem as ElMenuItem } from 'element-ui'; import { Submenu as ElSubmenu, MenuItem as ElMenuItem } from 'element-ui';
import N8nTooltip from '../N8nTooltip'; import N8nTooltip from '../N8nTooltip';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
import { IMenuItem } from '../../types'; import { defineComponent, PropType } from 'vue';
import Vue, { PropType } from 'vue'; import type { IMenuItem, RouteObject } from '../../types';
export default Vue.extend({ export default defineComponent({
name: 'n8n-menu-item', name: 'n8n-menu-item',
components: { components: {
ElSubmenu, ElSubmenu,
@@ -117,6 +117,14 @@ export default Vue.extend({
? this.item.children.filter((child) => child.available !== false) ? this.item.children.filter((child) => child.available !== false)
: []; : [];
}, },
currentRoute(): RouteObject {
return (
(this as typeof this & { $route: RouteObject }).$route || {
name: '',
path: '',
}
);
},
}, },
methods: { methods: {
isItemActive(item: IMenuItem): boolean { isItemActive(item: IMenuItem): boolean {
@@ -130,12 +138,12 @@ export default Vue.extend({
if (item.activateOnRoutePaths) { if (item.activateOnRoutePaths) {
return ( return (
Array.isArray(item.activateOnRoutePaths) && Array.isArray(item.activateOnRoutePaths) &&
item.activateOnRoutePaths.includes(this.$route.path) item.activateOnRoutePaths.includes(this.currentRoute.path)
); );
} else if (item.activateOnRouteNames) { } else if (item.activateOnRouteNames) {
return ( return (
Array.isArray(item.activateOnRouteNames) && Array.isArray(item.activateOnRouteNames) &&
item.activateOnRouteNames.includes(this.$route.name || '') item.activateOnRouteNames.includes(this.currentRoute.name || '')
); );
} }
return false; return false;

View File

@@ -36,11 +36,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import N8nTooltip from '../N8nTooltip'; import N8nTooltip from '../N8nTooltip';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
export default Vue.extend({ export default defineComponent({
name: 'n8n-node-icon', name: 'n8n-node-icon',
components: { components: {
N8nTooltip, N8nTooltip,
@@ -78,12 +78,13 @@ export default Vue.extend({
}, },
}, },
computed: { computed: {
iconStyleData(): object { iconStyleData(): Record<string, string> {
if (!this.size) { if (!this.size) {
return { return {
color: this.color || '', color: this.color || '',
}; };
} }
return { return {
color: this.color || '', color: this.color || '',
width: `${this.size}px`, width: `${this.size}px`,
@@ -92,7 +93,11 @@ export default Vue.extend({
'line-height': `${this.size}px`, 'line-height': `${this.size}px`,
}; };
}, },
fontStyleData(): object { fontStyleData(): Record<string, string> {
if (!this.size) {
return {};
}
return { return {
'max-width': `${this.size}px`, 'max-width': `${this.size}px`,
}; };

View File

@@ -16,13 +16,13 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
import sanitizeHtml from 'sanitize-html'; import sanitizeHtml from 'sanitize-html';
import N8nText from '../../components/N8nText'; import N8nText from '../../components/N8nText';
import Locale from '../../mixins/locale'; import Locale from '../../mixins/locale';
import { uid } from '../../utils'; import { uid } from '../../utils';
export default Vue.extend({ export default defineComponent({
name: 'n8n-notice', name: 'n8n-notice',
directives: {}, directives: {},
mixins: [Locale], mixins: [Locale],

View File

@@ -9,9 +9,9 @@
</template> </template>
<script> <script>
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-pulse', name: 'n8n-pulse',
}); });
</script> </script>

View File

@@ -5,7 +5,7 @@
:class="{ :class="{
'n8n-radio-button': true, 'n8n-radio-button': true,
[$style.container]: true, [$style.container]: true,
[$style.hoverable]: !this.disabled, [$style.hoverable]: !disabled,
}" }"
aria-checked="true" aria-checked="true"
> >
@@ -26,9 +26,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-radio-button', name: 'n8n-radio-button',
props: { props: {
label: { label: {

View File

@@ -10,7 +10,7 @@
:active="value === option.value" :active="value === option.value"
:size="size" :size="size"
:disabled="disabled || option.disabled" :disabled="disabled || option.disabled"
@click="(e) => onClick(option, e)" @click="() => onClick(option)"
/> />
</div> </div>
</template> </template>
@@ -18,15 +18,24 @@
<script lang="ts"> <script lang="ts">
import RadioButton from './RadioButton.vue'; import RadioButton from './RadioButton.vue';
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
export default Vue.extend({ export interface RadioOption {
label: string;
value: string;
disabled?: boolean;
}
export default defineComponent({
name: 'n8n-radio-buttons', name: 'n8n-radio-buttons',
props: { props: {
value: { value: {
type: String, type: String,
}, },
options: {}, options: {
type: Array as PropType<RadioOption[]>,
default: (): RadioOption[] => [],
},
size: { size: {
type: String, type: String,
}, },

View File

@@ -10,6 +10,7 @@ import {
PropType, PropType,
nextTick, nextTick,
watch, watch,
ComponentPublicInstance,
} from 'vue'; } from 'vue';
export default defineComponent({ export default defineComponent({
@@ -36,7 +37,7 @@ export default defineComponent({
const wrapperRef = ref<HTMLElement | null>(null); const wrapperRef = ref<HTMLElement | null>(null);
const scrollerRef = ref<HTMLElement | null>(null); const scrollerRef = ref<HTMLElement | null>(null);
const itemsRef = ref<HTMLElement | null>(null); const itemsRef = ref<HTMLElement | null>(null);
const itemRefs = ref<Record<string, HTMLElement | null>>({}); const itemRefs = ref<Record<string, Element | ComponentPublicInstance | null>>({});
const scrollTop = ref(0); const scrollTop = ref(0);
const wrapperHeight = ref(0); const wrapperHeight = ref(0);
@@ -174,7 +175,7 @@ export default defineComponent({
function onUpdateItemSize(item: { [key: string]: string }) { function onUpdateItemSize(item: { [key: string]: string }) {
nextTick(() => { nextTick(() => {
const itemId = item[props.itemKey]; const itemId = item[props.itemKey];
const itemRef = itemRefs.value[itemId]; const itemRef = itemRefs.value[itemId] as HTMLElement;
const previousSize = itemSizeCache.value[itemId]; const previousSize = itemSizeCache.value[itemId];
const size = itemRef ? itemRef.offsetHeight : props.itemSize; const size = itemRef ? itemRef.offsetHeight : props.itemSize;
const difference = size - previousSize; const difference = size - previousSize;

View File

@@ -4,7 +4,7 @@
v-for="direction in enabledDirections" v-for="direction in enabledDirections"
:key="direction" :key="direction"
:data-dir="direction" :data-dir="direction"
:class="[$style.resizer, $style[direction]]" :class="{ [$style.resizer]: true, [$style[direction]]: true }"
@mousedown="resizerMove" @mousedown="resizerMove"
/> />
<slot></slot> <slot></slot>
@@ -13,7 +13,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/unbound-method */
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
function closestNumber(value: number, divisor: number): number { function closestNumber(value: number, divisor: number): number {
const q = value / divisor; const q = value / divisor;
@@ -46,7 +46,7 @@ const directionsCursorMaps: { [key: string]: string } = {
bottomRight: 'se-resize', bottomRight: 'se-resize',
}; };
export default Vue.extend({ export default defineComponent({
name: 'n8n-resize', name: 'n8n-resize',
props: { props: {
isResizingEnabled: { isResizingEnabled: {
@@ -55,15 +55,19 @@ export default Vue.extend({
}, },
height: { height: {
type: Number, type: Number,
default: 0,
}, },
width: { width: {
type: Number, type: Number,
default: 0,
}, },
minHeight: { minHeight: {
type: Number, type: Number,
default: 0,
}, },
minWidth: { minWidth: {
type: Number, type: Number,
default: 0,
}, },
scale: { scale: {
type: Number, type: Number,
@@ -71,10 +75,11 @@ export default Vue.extend({
}, },
gridSize: { gridSize: {
type: Number, type: Number,
default: 20,
}, },
supportedDirections: { supportedDirections: {
type: Array, type: Array as PropType<string[]>,
default: () => [], default: (): string[] => [],
}, },
}, },
data() { data() {
@@ -90,7 +95,7 @@ export default Vue.extend({
}; };
}, },
computed: { computed: {
enabledDirections() { enabledDirections(): string[] {
const availableDirections = Object.keys(directionsCursorMaps); const availableDirections = Object.keys(directionsCursorMaps);
if (!this.isResizingEnabled) return []; if (!this.isResizingEnabled) return [];

View File

@@ -10,9 +10,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-route', name: 'n8n-route',
props: { props: {
to: { to: {
@@ -25,12 +25,13 @@ export default Vue.extend({
}, },
computed: { computed: {
useRouterLink() { useRouterLink() {
if (this.newWindow === true) { if (this.newWindow) {
// router-link does not support click events and opening in new window // router-link does not support click events and opening in new window
return false; return false;
} }
if (typeof this.to === 'string') { if (typeof this.to === 'string') {
return (this.to as string).startsWith('/'); return this.to.startsWith('/');
} }
return this.to !== undefined; return this.to !== undefined;
@@ -39,8 +40,9 @@ export default Vue.extend({
if (this.newWindow !== undefined) { if (this.newWindow !== undefined) {
return this.newWindow; return this.newWindow;
} }
if (typeof this.to === 'string') { if (typeof this.to === 'string') {
return !(this.to as string).startsWith('/'); return !this.to.startsWith('/');
} }
return true; return true;
}, },

View File

@@ -33,7 +33,7 @@
<script lang="ts"> <script lang="ts">
import { Select as ElSelect } from 'element-ui'; import { Select as ElSelect } from 'element-ui';
import Vue from 'vue'; import { defineComponent } from 'vue';
export interface IProps { export interface IProps {
size?: string; size?: string;
@@ -41,7 +41,7 @@ export interface IProps {
popperClass?: string; popperClass?: string;
} }
export default Vue.extend({ export default defineComponent({
name: 'n8n-select', name: 'n8n-select',
components: { components: {
ElSelect, ElSelect,

View File

@@ -13,9 +13,9 @@
<script lang="ts"> <script lang="ts">
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-spinner', name: 'n8n-spinner',
components: { components: {
N8nIcon, N8nIcon,

View File

@@ -66,10 +66,11 @@ import N8nMarkdown from '../N8nMarkdown';
import N8nResizeWrapper from '../N8nResizeWrapper'; import N8nResizeWrapper from '../N8nResizeWrapper';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import Locale from '../../mixins/locale'; import Locale from '../../mixins/locale';
import mixins from 'vue-typed-mixins'; import { defineComponent } from 'vue';
export default mixins(Locale).extend({ export default defineComponent({
name: 'n8n-sticky', name: 'n8n-sticky',
mixins: [Locale],
props: { props: {
content: { content: {
type: String, type: String,

View File

@@ -47,10 +47,19 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
import N8nIcon from '../N8nIcon'; import N8nIcon from '../N8nIcon';
export default Vue.extend({ export interface N8nTabOptions {
value: string;
label?: string;
icon?: string;
href?: string;
tooltip?: string;
align?: 'left' | 'right';
}
export default defineComponent({
name: 'N8nTabs', name: 'N8nTabs',
components: { components: {
N8nIcon, N8nIcon,
@@ -92,7 +101,10 @@ export default Vue.extend({
}, },
props: { props: {
value: {}, value: {},
options: {}, options: {
type: Array as PropType<N8nTabOptions[]>,
default: (): N8nTabOptions[] => [],
},
}, },
methods: { methods: {
handleTooltipClick(tab: string, event: MouseEvent) { handleTooltipClick(tab: string, event: MouseEvent) {

View File

@@ -5,9 +5,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-tag', name: 'n8n-tag',
props: { props: {
text: { text: {

View File

@@ -13,7 +13,7 @@
size="small" size="small"
@click.stop.prevent="onExpand" @click.stop.prevent="onExpand"
> >
{{ t('tags.showMore', hiddenTagsLength) }} {{ t('tags.showMore', `${hiddenTagsLength}`) }}
</n8n-link> </n8n-link>
</div> </div>
</template> </template>
@@ -22,16 +22,16 @@
import N8nTag from '../N8nTag'; import N8nTag from '../N8nTag';
import N8nLink from '../N8nLink'; import N8nLink from '../N8nLink';
import Locale from '../../mixins/locale'; import Locale from '../../mixins/locale';
import { PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import mixins from 'vue-typed-mixins';
interface ITag { export interface ITag {
id: string; id: string;
name: string; name: string;
} }
export default mixins(Locale).extend({ export default defineComponent({
name: 'n8n-tags', name: 'n8n-tags',
mixins: [Locale],
components: { components: {
N8nTag, N8nTag,
N8nLink, N8nLink,

View File

@@ -5,8 +5,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-text', name: 'n8n-text',
props: { props: {
bold: { bold: {

View File

@@ -20,12 +20,12 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue, { PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import { Tooltip as ElTooltip } from 'element-ui'; import { Tooltip as ElTooltip } from 'element-ui';
import type { IN8nButton } from '@/types'; import type { IN8nButton } from '@/types';
import N8nButton from '../N8nButton'; import N8nButton from '../N8nButton';
export default Vue.extend({ export default defineComponent({
name: 'n8n-tooltip', name: 'n8n-tooltip',
inheritAttrs: false, inheritAttrs: false,
components: { components: {

View File

@@ -1,10 +1,6 @@
<template> <template>
<div class="n8n-tree"> <div class="n8n-tree">
<div <div v-for="(label, i) in Object.keys(value)" :key="i" :class="classes">
v-for="(label, i) in Object.keys(value || {})"
:key="i"
:class="{ [nodeClass]: !!nodeClass, [$style.indent]: depth > 0 }"
>
<div :class="$style.simple" v-if="isSimple(value[label])"> <div :class="$style.simple" v-if="isSimple(value[label])">
<slot <slot
v-if="$scopedSlots.label" v-if="$scopedSlots.label"
@@ -41,15 +37,18 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'n8n-tree', name: 'n8n-tree',
components: {}, components: {},
props: { props: {
value: {}, value: {
type: Object as PropType<Record<string, unknown>>,
default: () => ({}),
},
path: { path: {
type: Array, type: Array as PropType<string[]>,
default: () => [], default: () => [],
}, },
depth: { depth: {
@@ -58,6 +57,12 @@ export default Vue.extend({
}, },
nodeClass: { nodeClass: {
type: String, type: String,
default: '',
},
},
computed: {
classes(): Record<string, boolean> {
return { [this.nodeClass]: !!this.nodeClass, [this.$style.indent]: this.depth > 0 };
}, },
}, },
methods: { methods: {

View File

@@ -12,7 +12,7 @@
<div> <div>
<n8n-text :bold="true" color="text-dark"> <n8n-text :bold="true" color="text-dark">
{{ firstName }} {{ lastName }} {{ firstName }} {{ lastName }}
{{ isCurrentUser ? this.t('nds.userInfo.you') : '' }} {{ isCurrentUser ? t('nds.userInfo.you') : '' }}
</n8n-text> </n8n-text>
<span v-if="disabled" :class="$style.pendingBadge"> <span v-if="disabled" :class="$style.pendingBadge">
<n8n-badge :bold="true">Disabled</n8n-badge> <n8n-badge :bold="true">Disabled</n8n-badge>
@@ -31,15 +31,15 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import 'vue';
import N8nText from '../N8nText'; import N8nText from '../N8nText';
import N8nAvatar from '../N8nAvatar'; import N8nAvatar from '../N8nAvatar';
import N8nBadge from '../N8nBadge'; import N8nBadge from '../N8nBadge';
import Locale from '../../mixins/locale'; import Locale from '../../mixins/locale';
import mixins from 'vue-typed-mixins'; import { defineComponent } from 'vue';
export default mixins(Locale).extend({ export default defineComponent({
name: 'n8n-users-info', name: 'n8n-users-info',
mixins: [Locale],
components: { components: {
N8nAvatar, N8nAvatar,
N8nText, N8nText,

View File

@@ -30,41 +30,39 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import 'vue';
import mixins from 'vue-typed-mixins';
import { Select as ElSelect, Option as ElOption } from 'element-ui';
import N8nUserInfo from '../N8nUserInfo'; import N8nUserInfo from '../N8nUserInfo';
import N8nSelect from '../N8nSelect';
import N8nOption from '../N8nOption';
import { IUser } from '../../types'; import { IUser } from '../../types';
import Locale from '../../mixins/locale'; import Locale from '../../mixins/locale';
import { t } from '../../locale'; import { t } from '../../locale';
import { defineComponent, PropType } from 'vue';
export default mixins(Locale).extend({ export default defineComponent({
name: 'n8n-user-select', name: 'n8n-user-select',
mixins: [Locale],
components: { components: {
N8nUserInfo, N8nUserInfo,
ElSelect, N8nSelect,
ElOption, N8nOption,
}, },
props: { props: {
users: { users: {
type: Array, type: Array as PropType<IUser[]>,
default() { default: () => [],
return [];
},
}, },
value: { value: {
type: String, type: String,
default: '', default: '',
}, },
ignoreIds: { ignoreIds: {
type: Array, type: Array as PropType<string[]>,
default() { default: () => [],
return [];
},
validator: (ids: string[]) => !ids.find((id) => typeof id !== 'string'), validator: (ids: string[]) => !ids.find((id) => typeof id !== 'string'),
}, },
currentUserId: { currentUserId: {
type: String, type: String,
default: '',
}, },
placeholder: { placeholder: {
type: String, type: String,
@@ -72,6 +70,7 @@ export default mixins(Locale).extend({
}, },
size: { size: {
type: String, type: String,
default: '',
validator: (value: string): boolean => ['mini', 'small', 'medium', 'large'].includes(value), validator: (value: string): boolean => ['mini', 'small', 'medium', 'large'].includes(value),
}, },
}, },
@@ -82,12 +81,12 @@ export default mixins(Locale).extend({
}, },
computed: { computed: {
filteredUsers(): IUser[] { filteredUsers(): IUser[] {
return (this.users as IUser[]).filter((user) => { return this.users.filter((user) => {
if (user.isPendingUser || !user.email) { if (user.isPendingUser || !user.email) {
return false; return false;
} }
if (this.ignoreIds && this.ignoreIds.includes(user.id)) { if (this.ignoreIds.includes(user.id)) {
return false; return false;
} }

View File

@@ -32,16 +32,16 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { IUser, IUserListAction } from '../../types'; import type { IUser, UserAction } from '../../types';
import N8nActionToggle from '../N8nActionToggle'; import N8nActionToggle from '../N8nActionToggle';
import N8nBadge from '../N8nBadge'; import N8nBadge from '../N8nBadge';
import N8nUserInfo from '../N8nUserInfo'; import N8nUserInfo from '../N8nUserInfo';
import Locale from '../../mixins/locale'; import Locale from '../../mixins/locale';
import mixins from 'vue-typed-mixins'; import { defineComponent, PropType } from 'vue';
import { PropType } from 'vue';
export default mixins(Locale).extend({ export default defineComponent({
name: 'n8n-users-list', name: 'n8n-users-list',
mixins: [Locale],
components: { components: {
N8nActionToggle, N8nActionToggle,
N8nBadge, N8nBadge,
@@ -63,7 +63,7 @@ export default mixins(Locale).extend({
type: String, type: String,
}, },
actions: { actions: {
type: Array as PropType<IUserListAction[]>, type: Array as PropType<UserAction[]>,
default: () => [], default: () => [],
}, },
}, },
@@ -107,7 +107,7 @@ export default mixins(Locale).extend({
}, },
}, },
methods: { methods: {
getActions(user: IUser): IUserListAction[] { getActions(user: IUser): UserAction[] {
if (user.isOwner) { if (user.isOwner) {
return []; return [];
} }

View File

@@ -5,9 +5,9 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'ResizeObserver', name: 'ResizeObserver',
props: { props: {
enabled: { enabled: {
@@ -62,7 +62,7 @@ export default Vue.extend({
}); });
}); });
this.$data.observer = observer; this.observer = observer;
if (this.$refs.root) { if (this.$refs.root) {
observer.observe(this.$refs.root as HTMLDivElement); observer.observe(this.$refs.root as HTMLDivElement);
@@ -70,7 +70,7 @@ export default Vue.extend({
}, },
beforeDestroy() { beforeDestroy() {
if (this.enabled) { if (this.enabled) {
this.$data.observer.disconnect(); // eslint-disable-line this.observer?.disconnect(); // eslint-disable-line
} }
}, },
}); });

View File

@@ -11,7 +11,7 @@ const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g;
* https://github.com/ElemeFE/element/blob/dev/src/locale/format.js * https://github.com/ElemeFE/element/blob/dev/src/locale/format.js
* https://github.com/Matt-Esch/string-template/index.js * https://github.com/Matt-Esch/string-template/index.js
*/ */
export default function (Vue) { export default function () {
/** /**
* template * template
* *

View File

@@ -1,12 +1,11 @@
import defaultLang from '../locale/lang/en'; import defaultLang from '../locale/lang/en';
import Vue from 'vue';
import Format from './format'; import Format from './format';
import ElementLocale from 'element-ui/lib/locale'; import ElementLocale from 'element-ui/lib/locale';
import ElementLang from 'element-ui/lib/locale/lang/en'; import ElementLang from 'element-ui/lib/locale/lang/en';
ElementLocale.use(ElementLang); ElementLocale.use(ElementLang);
const format = Format(Vue); const format = Format();
let lang = defaultLang; let lang = defaultLang;
let i18nHandler; let i18nHandler;

View File

@@ -1,4 +1,4 @@
import Vue from 'vue'; import type { PluginObject } from 'vue';
import N8nActionBox from '../components/N8nActionBox'; import N8nActionBox from '../components/N8nActionBox';
import N8nActionDropdown from '../components/N8nActionDropdown'; import N8nActionDropdown from '../components/N8nActionDropdown';
import N8nActionToggle from '../components/N8nActionToggle'; import N8nActionToggle from '../components/N8nActionToggle';
@@ -47,8 +47,8 @@ import N8nUsersList from '../components/N8nUsersList';
import N8nResizeWrapper from '../components/N8nResizeWrapper'; import N8nResizeWrapper from '../components/N8nResizeWrapper';
import N8nRecycleScroller from '../components/N8nRecycleScroller'; import N8nRecycleScroller from '../components/N8nRecycleScroller';
export default { const n8nComponentsPlugin: PluginObject<{}> = {
install: (app: typeof Vue) => { install: (app) => {
app.component('n8n-info-accordion', N8nInfoAccordion); app.component('n8n-info-accordion', N8nInfoAccordion);
app.component('n8n-action-box', N8nActionBox); app.component('n8n-action-box', N8nActionBox);
app.component('n8n-action-dropdown', N8nActionDropdown); app.component('n8n-action-dropdown', N8nActionDropdown);
@@ -98,3 +98,5 @@ export default {
app.component('n8n-recycle-scroller', N8nRecycleScroller); app.component('n8n-recycle-scroller', N8nRecycleScroller);
}, },
}; };
export default n8nComponentsPlugin;

View File

@@ -0,0 +1,4 @@
declare module 'markdown-it-task-lists' {
import type { PluginSimple } from 'markdown-it';
export default plugin as PluginSimple<{}>;
}

View File

@@ -10,7 +10,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
function hslToHex(h: number, s: number, l: number): string { function hslToHex(h: number, s: number, l: number): string {
l /= 100; l /= 100;
@@ -36,7 +36,7 @@ function getHex(hsl: string): string {
return hslToHex(colors[0], colors[1], colors[2]); return hslToHex(colors[0], colors[1], colors[2]);
} }
export default Vue.extend({ export default defineComponent({
name: 'color-circles', name: 'color-circles',
data() { data() {
return { return {
@@ -46,16 +46,16 @@ export default Vue.extend({
}, },
props: { props: {
colors: { colors: {
type: Array, type: Array as PropType<string[]>,
required: true, required: true,
}, },
}, },
created() { created() {
const setColors = () => { const setColors = () => {
(this.colors as string[]).forEach((color: string) => { this.colors.forEach((color) => {
const style = getComputedStyle(document.body); const style = getComputedStyle(document.body);
Vue.set(this.hsl, color, style.getPropertyValue(color)); this.$set(this.hsl, color, style.getPropertyValue(color));
}); });
}; };

View File

@@ -18,33 +18,34 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'sizes', name: 'sizes',
data() { data() {
return { return {
observer: null as null | MutationObserver, observer: null as null | MutationObserver,
sizes: {}, sizes: {} as Record<string, { rem: string; px: number }>,
}; };
}, },
props: { props: {
variables: { variables: {
type: Array, type: Array as PropType<string[]>,
required: true, required: true,
}, },
attr: { attr: {
type: String, type: String,
default: '',
}, },
}, },
created() { created() {
const setSizes = () => { const setSizes = () => {
(this.variables as string[]).forEach((variable: string) => { this.variables.forEach((variable: string) => {
const style = getComputedStyle(document.body); const style = getComputedStyle(document.body);
const rem = style.getPropertyValue(variable); const rem = style.getPropertyValue(variable);
const px = parseFloat(rem.replace('rem', '')) * 16; const px = parseFloat(rem.replace('rem', '')) * 16;
Vue.set(this.sizes, variable, { rem, px }); this.$set(this.sizes, variable, { rem, px });
}); });
}; };

View File

@@ -16,32 +16,33 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import { defineComponent, PropType } from 'vue';
export default Vue.extend({ export default defineComponent({
name: 'variable-table', name: 'variable-table',
data() { data() {
return { return {
observer: null as null | MutationObserver, observer: null as null | MutationObserver,
values: {}, values: {} as Record<string, string>,
}; };
}, },
props: { props: {
variables: { variables: {
type: Array, type: Array as PropType<string[]>,
required: true, required: true,
}, },
attr: { attr: {
type: String, type: String,
default: '',
}, },
}, },
created() { created() {
const setValues = () => { const setValues = () => {
(this.variables as string[]).forEach((variable: string) => { this.variables.forEach((variable) => {
const style = getComputedStyle(document.body); const style = getComputedStyle(document.body);
const value = style.getPropertyValue(variable); const value = style.getPropertyValue(variable);
Vue.set(this.values, variable, value); this.$set(this.values, variable, value);
}); });
}; };

View File

@@ -8,10 +8,11 @@
</div> </div>
</div> </div>
</template> </template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({ <script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'SpacingPreview', name: 'SpacingPreview',
props: { props: {
property: { property: {

View File

@@ -7,10 +7,10 @@ export type RuleGroup = {
export type Validatable = string | number | boolean | null | undefined; export type Validatable = string | number | boolean | null | undefined;
export type IValidator = { export type IValidator<T = unknown> = {
validate: ( validate: (
value: Validatable, value: Validatable,
config: unknown, config: T,
) => false | { messageKey: string; options?: unknown } | null; ) => false | { messageKey: string; options?: unknown } | null;
}; };

View File

@@ -1,4 +1,5 @@
export * from './button'; export * from './button';
export * from './form'; export * from './form';
export * from './user';
export * from './menu'; export * from './menu';
export * from './router';
export * from './user';

View File

@@ -0,0 +1,4 @@
export interface RouteObject {
name: string;
path: string;
}

View File

@@ -11,14 +11,10 @@ export interface IUser {
signInType: string; signInType: string;
} }
export interface IUserListAction { export interface UserAction {
label: string;
value: string;
guard?: (user: IUser) => boolean;
}
export interface IUserListAction {
label: string; label: string;
value: string; value: string;
disabled: boolean;
type?: 'external-link';
guard?: (user: IUser) => boolean; guard?: (user: IUser) => boolean;
} }

View File

@@ -2,7 +2,13 @@
export type CallbackFn = Function; export type CallbackFn = Function;
export type UnregisterFn = () => void; export type UnregisterFn = () => void;
export function createEventBus() { export interface EventBus {
on: (eventName: string, fn: CallbackFn) => UnregisterFn;
off: (eventName: string, fn: CallbackFn) => void;
emit: <T = Event>(eventName: string, event?: T) => void;
}
export function createEventBus(): EventBus {
const handlers = new Map<string, CallbackFn[]>(); const handlers = new Map<string, CallbackFn[]>();
function off(eventName: string, fn: CallbackFn) { function off(eventName: string, fn: CallbackFn) {
@@ -43,5 +49,3 @@ export function createEventBus() {
emit, emit,
}; };
} }
export type EventBus = ReturnType<typeof createEventBus>;

View File

@@ -22,9 +22,7 @@ export default Vue.extend({
}, },
stickyOffset: { stickyOffset: {
type: Array as PropType<number[]>, type: Array as PropType<number[]>,
default() { default: () => [0, 0],
return [0, 0];
},
}, },
}, },
data() { data() {

View File

@@ -195,15 +195,13 @@ export default mixins(debounceHelper, workflowHelpers, nodeHelpers).extend({
inputSize: { inputSize: {
type: String, type: String,
default: 'small', default: 'small',
validator: (size) => { validator: (size: string) => {
return ['mini', 'small', 'medium', 'large', 'xlarge'].includes(size); return ['mini', 'small', 'medium', 'large', 'xlarge'].includes(size);
}, },
}, },
parameterIssues: { parameterIssues: {
type: Array as PropType<string[]>, type: Array as PropType<string[]>,
default() { default: () => [],
return [];
},
}, },
displayTitle: { displayTitle: {
type: String, type: String,

View File

@@ -70,7 +70,7 @@ exports[`PersonalizationModal.vue > should render correctly 1`] = `
<!----> <!---->
</div> </div>
</div> </div>
<div class=\\"mt-undefined\\"> <div class=\\"\\">
<div class=\\"container\\" data-test-id=\\"role\\"><label for=\\"role\\" class=\\"inputLabel heading medium\\"> <div class=\\"container\\" data-test-id=\\"role\\"><label for=\\"role\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> Which role best describes you? <!----></span></div> <div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> Which role best describes you? <!----></span></div>
<!----> <!---->
@@ -122,7 +122,7 @@ exports[`PersonalizationModal.vue > should render correctly 1`] = `
<!----> <!---->
</div> </div>
</div> </div>
<div class=\\"mt-undefined\\"> <div class=\\"\\">
<div class=\\"container\\" data-test-id=\\"automationBeneficiary\\"><label for=\\"automationBeneficiary\\" class=\\"inputLabel heading medium\\"> <div class=\\"container\\" data-test-id=\\"automationBeneficiary\\"><label for=\\"automationBeneficiary\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> Who will your automations mainly be for? <!----></span></div> <div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> Who will your automations mainly be for? <!----></span></div>
<!----> <!---->
@@ -168,7 +168,7 @@ exports[`PersonalizationModal.vue > should render correctly 1`] = `
<!----> <!---->
</div> </div>
</div> </div>
<div class=\\"mt-undefined\\"> <div class=\\"\\">
<div class=\\"container\\" data-test-id=\\"companySize\\"><label for=\\"companySize\\" class=\\"inputLabel heading medium\\"> <div class=\\"container\\" data-test-id=\\"companySize\\"><label for=\\"companySize\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> How big is your company? <!----></span></div> <div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> How big is your company? <!----></span></div>
<!----> <!---->
@@ -217,7 +217,7 @@ exports[`PersonalizationModal.vue > should render correctly 1`] = `
<!----> <!---->
</div> </div>
</div> </div>
<div class=\\"mt-undefined\\"> <div class=\\"\\">
<div class=\\"container\\" data-test-id=\\"reportedSource\\"><label for=\\"reportedSource\\" class=\\"inputLabel heading medium\\"> <div class=\\"container\\" data-test-id=\\"reportedSource\\"><label for=\\"reportedSource\\" class=\\"inputLabel heading medium\\">
<div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> How did you hear about n8n? <!----></span></div> <div class=\\"title\\"><span class=\\"n8n-text size-medium bold\\"> How did you hear about n8n? <!----></span></div>
<!----> <!---->
@@ -273,7 +273,7 @@ exports[`PersonalizationModal.vue > should render correctly 1`] = `
</div> </div>
</div> </div>
<div class=\\"footer\\"> <div class=\\"footer\\">
<div><button aria-disabled=\\"false\\" aria-busy=\\"false\\" aria-live=\\"polite\\" class=\\"button button primary medium float-right\\"> <div><button aria-live=\\"polite\\" class=\\"button button primary medium float-right\\">
<!----><span>Get started</span></button></div> <!----><span>Get started</span></button></div>
</div> </div>
</div> </div>