refactor(design-system): replace functional components (#3802)

* update creator item

* update warning tooltip

* update badge and trigger icon

* update action box

* update avatar component

* update badge

* update heading component

* update icon component

* update link component

* update menu

* update route component

* fix avatar bug

* fix avatar bug

* update input component

* update select

* update input

* update tags component

* update spinner

* update square button

* update tag component

* update text component

* add danger color

* add vue.extend

* add human readable names

* add human readable name

* revert button changes

* update name

* revert name

* update classes

* delete unused component

* redo name change

* rename

* rename back

* rename back

* update snapshots
This commit is contained in:
Mutasem Aldmour 2022-08-05 15:03:24 +02:00 committed by GitHub
parent f151a8ad4a
commit 60da5bb7ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 486 additions and 307 deletions

View file

@ -1,29 +1,28 @@
<template functional>
<div :class="$style.container">
<div :class="$style.heading" v-if="props.heading">
<component :is="$options.components.N8nHeading" size="xlarge" align="center">{{ props.heading }}</component>
<template>
<div :class="['n8n-action-box', $style.container]">
<div :class="$style.heading" v-if="heading">
<n8n-heading size="xlarge" align="center">{{ heading }}</n8n-heading>
</div>
<div :class="$style.description" @click="(e) => listeners.descriptionClick && listeners.descriptionClick(e)">
<component :is="$options.components.N8nText" color="text-base">
<span v-html="props.description"></span>
</component>
<div :class="$style.description" @click="$emit('descriptionClick', $event)">
<n8n-text color="text-base">
<span v-html="description"></span>
</n8n-text>
</div>
<component v-if="props.buttonText" :is="$options.components.N8nButton" :label="props.buttonText" size="large"
@click="(e) => listeners.click && listeners.click(e)"
<n8n-button v-if="buttonText" :label="buttonText" size="large"
@click="$emit('click', $event)"
/>
<component
v-if="props.calloutText"
:is="$options.components.N8nCallout"
:theme="props.calloutTheme"
:icon="props.calloutIcon"
<n8n-callout
v-if="calloutText"
:theme="calloutTheme"
:icon="calloutIcon"
:class="$style.callout"
>
<template>
<component :is="$options.components.N8nText" color="text-base">
<span size="small" v-html="props.calloutText"></span>
</component>
<n8n-text color="text-base">
<span size="small" v-html="calloutText"></span>
</n8n-text>
</template>
</component>
</n8n-callout>
</div>
</template>
@ -32,8 +31,9 @@ import N8nButton from '../N8nButton';
import N8nHeading from '../N8nHeading';
import N8nText from '../N8nText';
import N8nCallout from '../N8nCallout';
import Vue from 'vue';
export default {
export default Vue.extend({
name: 'n8n-action-box',
components: {
N8nButton,
@ -62,7 +62,7 @@ export default {
type: String,
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,19 +1,18 @@
<template functional>
<span :class="$style.container">
<component
v-if="props.firstName"
:is="$options.components.Avatar"
:size="$options.methods.getSize(props.size)"
:name="props.firstName + ' ' + props.lastName"
<template>
<span :class="['n8n-avatar', $style.container]" v-on="$listeners">
<avatar
v-if="firstName"
:size="getSize(size)"
:name="firstName + ' ' + lastName"
variant="marble"
:colors="$options.methods.getColors(props.colors)"
:colors="getColors(colors)"
/>
<div
v-else
:class="$style.empty"
:style="$options.methods.getBlankStyles(props.size)">
:class="[$style.empty, $style[size]]"
>
</div>
<span v-if="props.firstName" :class="$style.initials">{{$options.methods.getInitials(props)}}</span>
<span v-if="firstName" :class="$style.initials">{{initials}}</span>
</span>
</template>
@ -26,7 +25,9 @@ const sizes: {[size: string]: number} = {
medium: 40,
};
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-avatar',
props: {
firstName: {
@ -46,14 +47,12 @@ export default {
components: {
Avatar,
},
computed: {
initials() {
return (this.firstName ? this.firstName.charAt(0): '') + (this.lastName? this.lastName.charAt(0): '');
},
},
methods: {
getInitials({firstName, lastName}) {
return firstName.charAt(0) + lastName.charAt(0);
},
getBlankStyles(size): {height: string, width: string} {
const px = sizes[size];
return { height: `${px}px`, width: `${px}px` };
},
getColors(colors): string[] {
const style = getComputedStyle(document.body);
return colors.map((color: string) => style.getPropertyValue(color));
@ -62,7 +61,7 @@ export default {
return sizes[size];
},
},
};
});
</script>
<style lang="scss" module>
@ -86,4 +85,19 @@ export default {
color: var(--color-text-xlight);
text-shadow: 0px 1px 6px rgba(25, 11, 9, 0.3);
}
.small {
height: 28px;
width: 28px;
}
.medium {
height: 40px;
width: 40px;
}
.large {
height: 48px;
width: 48px;
}
</style>

View file

@ -1,17 +1,19 @@
<template functional>
<template>
<span
:class="$style[props.theme]"
:class="['n8n-badge', $style[theme]]"
>
<component :is="$options.components.N8nText" :size="props.size" :bold="props.bold" :compact="true">
<n8n-text :size="size" :bold="bold" :compact="true">
<slot></slot>
</component>
</n8n-text>
</span>
</template>
<script lang="ts">
import N8nText from '../N8nText';
export default {
import Vue from 'vue';
export default Vue.extend({
props: {
theme: {
type: String,
@ -30,7 +32,7 @@ export default {
components: {
N8nText,
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,7 +1,7 @@
// Vitest Snapshot v1
exports[`components > N8nBadge > props > should render default theme correctly 1`] = `"<span class=\\"_default_13dw2_9 _badge_13dw2_1\\"><span class=\\"_size-large_9dlpz_14 _bold_9dlpz_1\\" style=\\"line-height: 1;\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">Default badge</n8n-text-stub></span></span>"`;
exports[`components > N8nBadge > props > should render default theme correctly 1`] = `"<span class=\\"n8n-badge _default_13dw2_9 _badge_13dw2_1\\"><n8n-text-stub bold=\\"true\\" size=\\"large\\" compact=\\"true\\" tag=\\"span\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">Default badge</n8n-text-stub></n8n-text-stub></span>"`;
exports[`components > N8nBadge > props > should render secondary theme correctly 1`] = `"<span class=\\"_secondary_13dw2_16 _badge_13dw2_1\\"><span class=\\"_size-medium_9dlpz_19 _regular_9dlpz_5\\" style=\\"line-height: 1;\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">Secondary badge</n8n-text-stub></span></span>"`;
exports[`components > N8nBadge > props > should render secondary theme correctly 1`] = `"<span class=\\"n8n-badge _secondary_13dw2_16 _badge_13dw2_1\\"><n8n-text-stub size=\\"medium\\" compact=\\"true\\" tag=\\"span\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">Secondary badge</n8n-text-stub></n8n-text-stub></span>"`;
exports[`components > N8nBadge > props > should render with default values correctly 1`] = `"<span class=\\"_default_13dw2_9 _badge_13dw2_1\\"><span class=\\"_size-small_9dlpz_24 _regular_9dlpz_5\\" style=\\"line-height: 1;\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">A Badge</n8n-text-stub></span></span>"`;
exports[`components > N8nBadge > props > should render with default values correctly 1`] = `"<span class=\\"n8n-badge _default_13dw2_9 _badge_13dw2_1\\"><n8n-text-stub size=\\"small\\" compact=\\"true\\" tag=\\"span\\"><n8n-text-stub size=\\"medium\\" tag=\\"span\\">A Badge</n8n-text-stub></n8n-text-stub></span>"`;

View file

@ -49,6 +49,7 @@ export default Vue.extend({
computed: {
classes(): string[] {
return [
'n8n-callout',
this.$style['callout'],
this.$style[this.theme],
];

View file

@ -1,7 +1,7 @@
// Vitest Snapshot v1
exports[`components > N8nCallout > should render additional slots correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _custom_p74de_16\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _custom_p74de_16\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"code-branch\\" size=\\"large\\"></n8n-icon-stub>
@ -13,7 +13,7 @@ exports[`components > N8nCallout > should render additional slots correctly 1`]
`;
exports[`components > N8nCallout > should render custom theme correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _custom_p74de_16\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _custom_p74de_16\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"code-branch\\" size=\\"large\\"></n8n-icon-stub>
@ -24,7 +24,7 @@ exports[`components > N8nCallout > should render custom theme correctly 1`] = `
`;
exports[`components > N8nCallout > should render danger theme correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _danger_p74de_34\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _danger_p74de_34\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"times-circle\\" size=\\"large\\"></n8n-icon-stub>
@ -35,7 +35,7 @@ exports[`components > N8nCallout > should render danger theme correctly 1`] = `
`;
exports[`components > N8nCallout > should render info theme correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _info_p74de_16\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _info_p74de_16\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"info-circle\\" size=\\"large\\"></n8n-icon-stub>
@ -46,7 +46,7 @@ exports[`components > N8nCallout > should render info theme correctly 1`] = `
`;
exports[`components > N8nCallout > should render secondary theme correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _secondary_p74de_44\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _secondary_p74de_44\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"info-circle\\" size=\\"medium\\"></n8n-icon-stub>
@ -57,7 +57,7 @@ exports[`components > N8nCallout > should render secondary theme correctly 1`] =
`;
exports[`components > N8nCallout > should render success theme correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _success_p74de_28\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _success_p74de_28\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"check-circle\\" size=\\"large\\"></n8n-icon-stub>
@ -68,7 +68,7 @@ exports[`components > N8nCallout > should render success theme correctly 1`] = `
`;
exports[`components > N8nCallout > should render warning theme correctly 1`] = `
"<div role=\\"alert\\" class=\\"_callout_p74de_1 _warning_p74de_22\\">
"<div role=\\"alert\\" class=\\"n8n-callout _callout_p74de_1 _warning_p74de_22\\">
<div class=\\"_message-section_p74de_12\\">
<div class=\\"_icon_p74de_40\\">
<n8n-icon-stub icon=\\"exclamation-triangle\\" size=\\"large\\"></n8n-icon-stub>

View file

@ -1,7 +1,7 @@
<template>
<el-checkbox
v-bind="$props"
:class="$style.n8nCheckbox"
:class="['n8n-checkbox', $style.n8nCheckbox]"
:disabled="disabled"
:indeterminate="indeterminate"
:value="value"

View file

@ -1,6 +1,6 @@
<template>
<div
:class="$style.container"
:class="['n8n-form-box', $style.container]"
>
<div
v-if="title"

View file

@ -1,11 +1,13 @@
<template functional>
<component :is="props.tag" :class="$options.methods.getClasses(props, $style)" :style="$options.methods.getStyles(props)">
<template>
<component :is="tag" :class="['n8n-heading', ...classes]" v-on="$listeners">
<slot></slot>
</component>
</template>
<script lang="ts">
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-heading',
props: {
tag: {
@ -23,29 +25,31 @@ export default {
},
color: {
type: String,
validator: (value: string): boolean => ['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight'].includes(value),
validator: (value: string): boolean => ['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight', 'danger'].includes(value),
},
align: {
type: String,
validator: (value: string): boolean => ['right', 'left', 'center'].includes(value),
},
},
methods: {
getClasses(props: {size: string, bold: boolean}, $style: any) {
return {[$style[`size-${props.size}`]]: true, [$style.bold]: props.bold, [$style.regular]: !props.bold};
},
getStyles(props: {color: string}) {
const styles = {} as any;
if (props.color) {
styles.color = `var(--color-${props.color})`;
computed: {
classes() {
const applied = [];
if (this.align) {
applied.push(`align-${this.align}`);
}
if (props.align) {
styles['text-align'] = props.align;
if (this.color) {
applied.push(this.color);
}
return styles;
},
applied.push(`size-${this.size}`);
applied.push(this.bold? 'bold': 'regular');
return applied.map((c) => this.$style[c]);
}
},
};
});
</script>
<style lang="scss" module>
@ -82,4 +86,40 @@ export default {
line-height: var(--font-line-height-regular);
}
.primary {
color: var(--color-primary);
}
.text-dark {
color: var(--color-text-dark);
}
.text-base {
color: var(--color-text-base);
}
.text-light {
color: var(--color-text-light);
}
.text-xlight {
color: var(--color-text-xlight);
}
.danger {
color: var(--color-danger);
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.align-center {
text-align: center;
}
</style>

View file

@ -1,24 +1,25 @@
<template functional>
<component
:is="$options.components.N8nText"
:size="props.size"
:color="props.color"
<template>
<n8n-text
:size="size"
:color="color"
:compact="true"
class="n8n-icon"
>
<component
:is="$options.components.FontAwesomeIcon"
:icon="props.icon"
:spin="props.spin"
:class="$style[props.size]"
<font-awesome-icon
:icon="icon"
:spin="spin"
:class="$style[size]"
/>
</component>
</n8n-text>
</template>
<script lang="ts">
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import N8nText from '../N8nText';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-icon',
components: {
FontAwesomeIcon,
@ -40,7 +41,7 @@ export default {
type: String,
},
},
};
});
</script>

View file

@ -9,7 +9,9 @@
<script lang="ts">
import N8nButton from '../N8nButton';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-icon-button',
components: {
N8nButton,
@ -61,7 +63,7 @@ export default {
default: true,
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -21,7 +21,9 @@
import N8nText from '../N8nText';
import N8nIcon from '../N8nIcon';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-info-accordion',
components: {
N8nText,
@ -53,7 +55,7 @@ export default {
this.$emit('click', e);
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,5 +1,5 @@
<template>
<div :class="{[$style[theme]]: true, [$style[type]]: true, [$style.bold]: bold}">
<div :class="{'n8n-info-tip': true, [$style[theme]]: true, [$style[type]]: true, [$style.bold]: bold}">
<n8n-tooltip :placement="tooltipPlacement" :popper-class="$style.tooltipPopper" :disabled="type !== 'tooltip'">
<span :class="$style.iconText">
<n8n-icon :icon="theme.startsWith('info') ? 'info-circle': 'exclamation-triangle'" />
@ -14,7 +14,9 @@
import N8nIcon from '../N8nIcon';
import N8nTooltip from '../N8nTooltip';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-info-tip',
components: {
N8nIcon,
@ -42,7 +44,7 @@ export default {
default: 'top',
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,32 +1,33 @@
<template functional>
<component
:is="$options.components.ElInput"
v-bind="props"
:size="$options.methods.getSize(props.size)"
:class="$style[$options.methods.getClass(props)]"
:ref="data.ref"
:autoComplete="props.autocomplete"
v-on="listeners"
<template>
<el-input
v-bind="$props"
:size="computedSize"
:class="['n8n-input', ...classes]"
:autoComplete="autocomplete"
ref="innerInput"
v-on="$listeners"
>
<template v-slot:prepend>
<template #prepend>
<slot name="prepend" />
</template>
<template v-slot:append>
<template #append>
<slot name="append" />
</template>
<template v-slot:prefix>
<template #prefix>
<slot name="prefix" />
</template>
<template v-slot:suffix>
<template #suffix>
<slot name="suffix" />
</template>
</component>
</el-input>
</template>
<script lang="ts">
import ElInput from 'element-ui/lib/input';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-input',
components: {
ElInput,
@ -68,23 +69,43 @@ export default {
default: 'off',
},
},
methods: {
getSize(size: string): string | undefined {
if (size === 'xlarge') {
computed: {
computedSize(): string | undefined {
if (this.size === 'xlarge') {
return undefined;
}
return size;
return this.size;
},
getClass(props: { size: string }): string {
if (props.size === 'xlarge') {
return 'xlarge';
classes(): string[] {
if (this.size === 'xlarge') {
return ['xlarge'];
}
return '';
return [];
},
},
};
methods: {
focus() {
if (this.$refs.innerInput.$el) {
// @ts-ignore
(this.$refs.innerInput.$el.querySelector(this.type === 'textarea' ? 'textarea' : 'input') as HTMLInputElement).focus();
}
},
blur() {
if (this.$refs.innerInput.$el) {
// @ts-ignore
(this.$refs.innerInput.$el.querySelector(this.type === 'textarea' ? 'textarea' : 'input') as HTMLInputElement).blur();
}
},
select() {
if (this.$refs.innerInput.$el) {
// @ts-ignore
(this.$refs.innerInput.$el.querySelector(this.type === 'textarea' ? 'textarea' : 'input') as HTMLInputElement).select();
}
},
},
});
</script>
<style lang="scss" module>

View file

@ -1,6 +1,7 @@
<template>
<div :class="$style.container">
<div v-if="label || $slots.options" :class="{
'n8n-input-label': true,
[this.$style.heading]: !!this.label,
[this.$style.underline]: this.underline,
[this.$style[this.size]]: true,
@ -34,7 +35,9 @@ import N8nIcon from '../N8nIcon';
import { addTargetBlank } from '../utils/helpers';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-input-label',
components: {
N8nText,
@ -74,7 +77,7 @@ export default {
methods: {
addTargetBlank,
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,15 +1,16 @@
<template functional>
<component :is="$options.components.N8nRoute" :to="props.to" :newWindow="props.newWindow"
@click="listeners.click"
<template>
<n8n-route :to="to" :newWindow="newWindow"
v-on="$listeners"
class="n8n-link"
>
<span
:class="$style[`${props.underline ? `${props.theme}-underline` : props.theme}`]"
:class="$style[`${underline ? `${theme}-underline` : theme}`]"
>
<component :is="$options.components.N8nText" :size="props.size" :bold="props.bold">
<n8n-text :size="size" :bold="bold">
<slot></slot>
</component>
</n8n-text>
</span>
</component>
</n8n-route>
</template>
<script lang="ts">
@ -17,7 +18,9 @@ import Vue from 'vue';
import N8nText from '../N8nText';
import N8nRoute from '../N8nRoute';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-link',
props: {
size: {
@ -49,7 +52,7 @@ export default {
N8nText,
N8nRoute,
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,5 +1,5 @@
<template>
<el-skeleton :loading="loading" :animated="animated">
<el-skeleton :loading="loading" :animated="animated" class="n8n-loading">
<template slot="template">
<el-skeleton-item
v-if="variant === 'button'"
@ -43,7 +43,9 @@
import ElSkeleton from 'element-ui/lib/skeleton';
import ElSkeletonItem from 'element-ui/lib/skeleton-item';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-loading',
components: {
ElSkeleton,
@ -72,7 +74,7 @@ export default {
validator: (value: string): boolean => ['p', 'h1', 'button', 'image'].includes(value),
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,5 +1,5 @@
<template>
<div>
<div class="n8n-markdown">
<div
v-if="!loading"
ref="editor"
@ -55,7 +55,9 @@ interface IImage {
url: string;
}
export default {
import Vue from 'vue';
export default Vue.extend({
components: {
N8nLoading,
},
@ -175,7 +177,7 @@ export default {
this.$emit('markdown-click', clickedLink, event);
}
}
};
});
</script>
<style lang="scss" module>

View file

@ -1,20 +1,21 @@
<template functional>
<component
:is="$options.components.ElMenu"
:defaultActive="props.defaultActive"
:collapse="props.collapse"
:router="props.router"
:class="$style[props.type + (props.light ? '-light' : '')]"
@select="(e) => listeners.select && listeners.select(e)"
<template>
<el-menu
:defaultActive="defaultActive"
:collapse="collapse"
:router="router"
:class="['n8n-menu', $style[type + (light ? '-light' : '')]]"
v-on="$listeners"
>
<slot></slot>
</component>
</el-menu>
</template>
<script lang="ts">
import ElMenu from 'element-ui/lib/menu';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-menu',
props: {
type: {
@ -38,7 +39,7 @@ export default {
components: {
ElMenu,
},
};
});
</script>
<style lang="scss" module>

View file

@ -9,9 +9,11 @@
</template>
<script>
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-pulse',
};
});
</script>
<style lang="scss" module>

View file

@ -1,12 +1,14 @@
<template>
<label role="radio" tabindex="-1" :class="{[$style.container]: true, [$style.hoverable]: !this.disabled}" aria-checked="true">
<label role="radio" tabindex="-1" :class="{'n8n-radio-button': true, [$style.container]: true, [$style.hoverable]: !this.disabled}" aria-checked="true">
<input type="radio" tabindex="-1" autocomplete="off" :class="$style.input" :value="value">
<div :class="{[$style.button]: true, [$style.active]: active, [$style[size]]: true, [$style.disabled]: disabled}" @click="$emit('click')">{{ label }}</div>
</label>
</template>
<script lang="ts">
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-radio-button',
props: {
label: {
@ -31,7 +33,7 @@ export default {
type: Boolean,
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,5 +1,5 @@
<template>
<div role="radiogroup" :class="{[$style.radioGroup]: true, [$style.disabled]: disabled}">
<div role="radiogroup" :class="{'n8n-radio-buttons': true, [$style.radioGroup]: true, [$style.disabled]: disabled}">
<RadioButton
v-for="option in options"
:key="option.value"
@ -15,7 +15,9 @@
<script lang="ts">
import RadioButton from './RadioButton.vue';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-radio-buttons',
props: {
value: {
@ -41,7 +43,7 @@ export default {
this.$emit('input', value);
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,17 +1,17 @@
<template functional>
<template>
<span>
<router-link
v-if="$options.methods.useRouterLink(props)"
:to="props.to"
@click="(e) => listeners.click && listeners.click(e)"
v-if="useRouterLink"
:to="to"
v-on="$listeners"
>
<slot></slot>
</router-link>
<a
v-else
:href="props.to"
@click="(e) => listeners.click && listeners.click(e)"
:target="$options.methods.openNewWindow(props) ? '_blank': '_self'"
:href="to"
:target="openNewWindow ? '_blank': '_self'"
v-on="$listeners"
>
<slot></slot>
</a>
@ -21,7 +21,7 @@
<script lang="ts">
import Vue from 'vue';
export default {
export default Vue.extend({
name: 'n8n-route',
props: {
to: {
@ -32,28 +32,28 @@ export default {
default: undefined,
},
},
methods: {
useRouterLink(props: {to: object | string, newWindow: boolean | undefined}) {
if (props.newWindow === true) {
computed: {
useRouterLink() {
if (this.newWindow === true) {
// router-link does not support click events and opening in new window
return false;
}
if (typeof props.to === 'string') {
return props.to.startsWith('/');
if (typeof this.to === 'string') {
return this.to.startsWith('/');
}
return props.to !== undefined;
return this.to !== undefined;
},
openNewWindow(props: {to: string, newWindow: boolean | undefined}) {
if (props.newWindow !== undefined) {
return props.newWindow;
openNewWindow() {
if (this.newWindow !== undefined) {
return this.newWindow;
}
if (typeof props.to === 'string') {
return !props.to.startsWith('/');
if (typeof this.to === 'string') {
return !this.to.startsWith('/');
}
return true;
},
},
};
});
</script>

View file

@ -1,33 +1,33 @@
<template functional>
<div :class="{[$style.container]: true, [$style.withPrepend]: !!$slots.prepend}">
<template>
<div :class="{'n8n-select': true, [$style.container]: true, [$style.withPrepend]: !!$slots.prepend}">
<div v-if="$slots.prepend" :class="$style.prepend">
<slot name="prepend" />
</div>
<component
:is="$options.components.ElSelect"
v-bind="props"
:value="props.value"
:size="$options.methods.getSize(props.size)"
:class="$style[$options.methods.getClass(props)]"
:popper-class="$options.methods.getPopperClass(props, $style)"
v-on="listeners"
:ref="data.ref"
<el-select
v-bind="$props"
:value="value"
:size="computedSize"
:class="$style[classes]"
:popper-class="popperClass"
v-on="$listeners"
ref="innerSelect"
>
<template v-slot:prefix>
<template #prefix>
<slot name="prefix" />
</template>
<template v-slot:suffix>
<template #suffix>
<slot name="suffix" />
</template>
<template v-slot:default>
<template #default>
<slot></slot>
</template>
</component>
</el-select>
</div>
</template>
<script lang="ts">
import ElSelect from 'element-ui/lib/select';
import Vue from 'vue';
interface IProps {
size?: string;
@ -35,7 +35,7 @@ interface IProps {
popperClass?: string;
}
export default {
export default Vue.extend({
name: 'n8n-select',
components: {
ElSelect,
@ -86,31 +86,54 @@ export default {
type: String,
},
},
methods: {
getSize(size: string): string | undefined {
if (size === 'xlarge') {
computed: {
computedSize(): string | undefined {
if (this.size === 'xlarge') {
return undefined;
}
return size;
return this.size;
},
getClass(props: IProps): string {
if (props.size === 'xlarge') {
classes(): string {
if (this.size === 'xlarge') {
return 'xlarge';
}
return '';
},
getPopperClass(props: IProps, $style: any): string {
let classes = props.popperClass || '';
if (props.limitPopperWidth) {
classes = `${classes} ${$style.limitPopperWidth}`;
popperClasses(): string {
let classes = this.popperClass || '';
if (this.limitPopperWidth) {
classes = `${classes} ${this.$style.limitPopperWidth}`;
}
return classes;
},
},
};
methods: {
focus() {
const input = this.$refs.innerSelect;
if (input) {
input.focus();
}
},
blur() {
const input = this.$refs.innerSelect;
if (input) {
input.blur();
}
},
focusOnInput() {
const select = (this.$refs.innerSelect) as (Vue | undefined);
if (select) {
const input = select.$refs.input;
if (input) {
input.focus();
}
}
},
},
});
</script>
<style lang="scss" module>

View file

@ -1,11 +1,10 @@
<template functional>
<span>
<div v-if="props.type === 'ring'" class="lds-ring"><div></div><div></div><div></div><div></div></div>
<component
<template>
<span class="n8n-spinner">
<div v-if="type === 'ring'" class="lds-ring"><div></div><div></div><div></div><div></div></div>
<n8n-icon
v-else
:is="$options.components.N8nIcon"
icon="spinner"
:size="props.size"
:size="size"
spin
/>
</span>
@ -14,7 +13,9 @@
<script lang="ts">
import N8nIcon from '../N8nIcon';
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-spinner',
components: {
N8nIcon,
@ -34,7 +35,7 @@ export default {
default: 'dots',
},
},
};
});
</script>
<style lang="scss">

View file

@ -1,11 +1,12 @@
<template functional>
<button :class="$style.button" @click="(e) => listeners.click && listeners.click(e)">
<span :class="$style.text" v-text="props.label" />
<template>
<button :class="['n8n-square-button', $style.button]" v-on="$listeners">
<span :class="$style.text">{{label}}</span>
</button>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-square-button',
props: {

View file

@ -46,7 +46,9 @@ function getSize(delta, min, virtual, gridSize): number {
return min;
};
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-resize',
props: {
isResizingEnabled: {
@ -152,7 +154,7 @@ export default {
this.dir = '';
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,6 +1,6 @@
<template>
<div
:class="{[$style.sticky]: true, [$style.clickable]: !isResizing}"
:class="{'n8n-sticky': true, [$style.sticky]: true, [$style.clickable]: !isResizing}"
:style="styles"
@keydown.prevent
>
@ -183,8 +183,11 @@ export default mixins(Locale).extend({
watch: {
editMode(newMode, prevMode) {
setTimeout(() => {
if (newMode && !prevMode && this.$refs.input && this.$refs.input.$refs && this.$refs.input.$refs.textarea) {
const textarea = this.$refs.input.$refs.textarea;
if (newMode &&
!prevMode &&
this.$refs.input
) {
const textarea = this.$refs.input;
if (this.defaultText === this.content) {
textarea.select();
}

View file

@ -1,5 +1,5 @@
<template>
<div :class="$style.container">
<div :class="['n8n-tabs', $style.container]">
<div :class="$style.back" v-if="scrollPosition > 0" @click="scrollLeft">
<n8n-icon icon="chevron-left" size="small" />
</div>

View file

@ -1,16 +1,20 @@
<template functional>
<span :class="$style.tag" v-text="props.text" @click="(e) => listeners.click && listeners.click(e)" />
<template>
<span :class="['n8n-tag', $style.tag]" v-on="$listeners">
{{ text }}
</span>
</template>
<script lang="ts">
export default {
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-tag',
props: {
text: {
type: String,
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,13 +1,14 @@
<template functional>
<div :class="$style.tags">
<component :is="$options.components.N8nTag" v-for="tag in props.tags" :key="tag.id" :text="tag.name" @click="(e) => listeners.click && listeners.click(tag.id, e)"/>
<template>
<div :class="['n8n-tags', $style.tags]">
<n8n-tag v-for="tag in tags" :key="tag.id" :text="tag.name" @click="$emit('click', tag.id, $event)"/>
</div>
</template>
<script lang="ts">
import N8nTag from '../N8nTag';
import Vue from 'vue';
export default {
export default Vue.extend({
name: 'n8n-tags',
components: {
N8nTag,
@ -17,7 +18,7 @@ export default {
type: Array,
},
},
};
});
</script>
<style lang="scss" module>

View file

@ -1,11 +1,12 @@
<template functional>
<component :is="props.tag" :class="$options.methods.getClasses(props, $style, data)" :style="$options.methods.getStyles(props)" v-on="listeners">
<template>
<component :is="tag" :class="['n8n-text', ...classes]" v-on="$listeners">
<slot></slot>
</component>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
name: 'n8n-text',
props: {
@ -20,7 +21,7 @@ export default Vue.extend({
},
color: {
type: String,
validator: (value: string): boolean => ['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight'].includes(value),
validator: (value: string): boolean => ['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight', 'danger'].includes(value),
},
align: {
type: String,
@ -35,28 +36,26 @@ export default Vue.extend({
default: 'span',
},
},
methods: {
getClasses(props: {size: string, bold: boolean}, $style: any, data: any) {
const classes = {[$style[`size-${props.size}`]]: true, [$style.bold]: props.bold, [$style.regular]: !props.bold};
if (data.staticClass) {
classes[data.staticClass] = true;
computed: {
classes() {
const applied = [];
if (this.align) {
applied.push(`align-${this.align}`);
}
if (this.color) {
applied.push(this.color);
}
return classes;
},
getStyles(props: {color: string, align: string, compact: false}) {
const styles = {} as any;
if (props.color) {
styles.color = `var(--color-${props.color})`;
if (this.compact) {
applied.push('compact');
}
if (props.compact) {
styles['line-height'] = 1;
}
if (props.align) {
styles['text-align'] = props.align;
}
return styles;
},
applied.push(`size-${this.size}`);
applied.push(this.bold? 'bold': 'regular');
return applied.map((c) => this.$style[c]);
}
},
});
</script>
@ -95,4 +94,44 @@ export default Vue.extend({
line-height: var(--font-line-height-compact);
}
.compact {
line-height: 1;
}
.primary {
color: var(--color-primary);
}
.text-dark {
color: var(--color-text-dark);
}
.text-base {
color: var(--color-text-base);
}
.text-light {
color: var(--color-text-light);
}
.text-xlight {
color: var(--color-text-xlight);
}
.danger {
color: var(--color-danger);
}
.align-left {
text-align: left;
}
.align-right {
text-align: right;
}
.align-center {
text-align: center;
}
</style>

View file

@ -1,19 +1,19 @@
<template functional>
<template>
<fragment>
<el-tag
v-if="props.type === 'danger'"
v-if="type === 'danger'"
type="danger"
size="small"
:class="$style['danger']"
>
{{ props.text }}
{{ text }}
</el-tag>
<el-tag
v-else-if="props.type === 'warning'"
v-else-if="type === 'warning'"
size="small"
:class="$style['warning']"
>
{{ props.text }}
{{ text }}
</el-tag>
</fragment>
</template>
@ -48,4 +48,4 @@ export default {
color: $--badge-warning-color;
border: none;
}
</style>
</style>

View file

@ -1,28 +1,28 @@
<template functional>
<template>
<div
:class="{
container: true,
clickable: props.clickable,
active: props.active,
clickable: clickable,
active: active,
}"
@click="listeners.click"
v-on="$listeners"
>
<CategoryItem
v-if="props.item.type === 'category'"
:item="props.item"
v-if="item.type === 'category'"
:item="item"
/>
<SubcategoryItem
v-else-if="props.item.type === 'subcategory'"
:item="props.item"
v-else-if="item.type === 'subcategory'"
:item="item"
/>
<NodeItem
v-else-if="props.item.type === 'node'"
:nodeType="props.item.properties.nodeType"
:bordered="!props.lastNode"
@dragstart="listeners.dragstart"
@dragend="listeners.dragend"
v-else-if="item.type === 'node'"
:nodeType="item.properties.nodeType"
:bordered="!lastNode"
@dragstart="$listeners.dragstart"
@dragend="$listeners.dragend"
/>
</div>
</template>
@ -33,13 +33,15 @@ import NodeItem from './NodeItem.vue';
import CategoryItem from './CategoryItem.vue';
import SubcategoryItem from './SubcategoryItem.vue';
Vue.component('CategoryItem', CategoryItem);
Vue.component('SubcategoryItem', SubcategoryItem);
Vue.component('NodeItem', NodeItem);
export default {
export default Vue.extend({
name: 'CreatorItem',
components: {
CategoryItem,
SubcategoryItem,
NodeItem,
},
props: ['item', 'active', 'clickable', 'lastNode'],
};
});
</script>
<style lang="scss" scoped>

View file

@ -8,7 +8,7 @@
@keydown.stop
@keydown.esc="editName = false"
>
<n8n-text :class="$style.renameText" :bold="true" color="text-base" tag="div"
<n8n-text :bold="true" color="text-base" tag="div"
>{{ $locale.baseText('ndv.title.renameNode') }}</n8n-text>
<n8n-input ref="input" size="small" v-model="newName" />
<div :class="$style.editButtons">

View file

@ -845,9 +845,9 @@ export default mixins(
// Set focus on field
setTimeout(() => {
// @ts-ignore
if (this.$refs.inputField.$el) {
if (this.$refs.inputField) {
// @ts-ignore
(this.$refs.inputField.$el.querySelector(this.getStringInputType === 'textarea' ? 'textarea' : 'input') as HTMLInputElement).focus();
this.$refs.inputField.focus();
}
});

View file

@ -77,7 +77,8 @@ export default mixins(showMessage).extend({
};
},
mounted() {
const select = this.$refs.select as (Vue | undefined);
// @ts-ignore
const select = (this.$refs.select && this.$refs.select.$refs && this.$refs.select.$refs.innerSelect) as (Vue | undefined);
if (select) {
const input = select.$refs.input as (Element | undefined);
if (input) {
@ -200,10 +201,10 @@ export default mixins(showMessage).extend({
}
},
focusOnInput() {
const select = this.$refs.select as Vue;
const input = select && select.$refs.input as HTMLElement;
if (input && input.focus) {
input.focus();
const select = (this.$refs.select) as (Vue | undefined);
if (select) {
// @ts-ignore
select.focusOnInput();
this.focused = true;
}
},

View file

@ -1,4 +1,4 @@
<template functional>
<template>
<span :class="$style.trigger">
<svg width="36px" height="36px" viewBox="0 0 36 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Trigger node</title>

View file

@ -1,4 +1,4 @@
<template functional>
<template>
<n8n-tooltip content=" " placement="top" >
<div slot="content"><slot /></div>
<font-awesome-icon :class="$style['icon']" icon="exclamation-triangle"></font-awesome-icon>

View file

@ -86,10 +86,10 @@ import { ElMessageBoxOptions } from "element-ui/types/message-box";
Vue.use(Fragment.Plugin);
// n8n design system
Vue.use(N8nInfoAccordion);
Vue.use(N8nActionBox);
Vue.use(N8nActionToggle);
Vue.use(N8nAvatar);
Vue.component('n8n-info-accordion', N8nInfoAccordion);
Vue.component('n8n-action-box', N8nActionBox);
Vue.component('n8n-action-toggle', N8nActionToggle);
Vue.component('n8n-avatar', N8nAvatar);
Vue.component('n8n-button', N8nButton);
Vue.component('el-button', N8nElButton);
Vue.component('n8n-callout', N8nCallout);
@ -97,31 +97,31 @@ Vue.component('n8n-card', N8nCard);
Vue.component('n8n-form-box', N8nFormBox);
Vue.component('n8n-form-inputs', N8nFormInputs);
Vue.component('n8n-icon', N8nIcon);
Vue.use(N8nIconButton);
Vue.use(N8nInfoTip);
Vue.use(N8nInput);
Vue.use(N8nInputLabel);
Vue.use(N8nInputNumber);
Vue.component('n8n-icon-button', N8nIconButton);
Vue.component('n8n-info-tip', N8nInfoTip);
Vue.component('n8n-input', N8nInput);
Vue.component('n8n-input-label', N8nInputLabel);
Vue.component('n8n-input-number', N8nInputNumber);
Vue.component('n8n-loading', N8nLoading);
Vue.use(N8nHeading);
Vue.use(N8nLink);
Vue.component('n8n-heading', N8nHeading);
Vue.component('n8n-link', N8nLink);
Vue.component('n8n-markdown', N8nMarkdown);
Vue.use(N8nMenu);
Vue.use(N8nMenuItem);
Vue.component('n8n-menu', N8nMenu);
Vue.component('n8n-menu-item', N8nMenuItem);
Vue.component('n8n-node-icon', N8nNodeIcon);
Vue.component('n8n-notice', N8nNotice);
Vue.use(N8nOption);
Vue.use(N8nPulse);
Vue.use(N8nSelect);
Vue.use(N8nSpinner);
Vue.component('n8n-option', N8nOption);
Vue.component('n8n-pulse', N8nPulse);
Vue.component('n8n-select', N8nSelect);
Vue.component('n8n-spinner', N8nSpinner);
Vue.component('n8n-sticky', N8nSticky);
Vue.use(N8nRadioButtons);
Vue.component('n8n-radio-buttons', N8nRadioButtons);
Vue.component('n8n-square-button', N8nSquareButton);
Vue.use(N8nTags);
Vue.component('n8n-tags', N8nTags);
Vue.component('n8n-tabs', N8nTabs);
Vue.use(N8nTag);
Vue.component('n8n-tag', N8nTag);
Vue.component('n8n-text', N8nText);
Vue.use(N8nTooltip);
Vue.component('n8n-tooltip', N8nTooltip);
// element io
Vue.use(Dialog);