mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
refactor(editor): Standardize components sections order (no-changelog) (#10540)
This commit is contained in:
@@ -66,6 +66,12 @@ module.exports = {
|
|||||||
useAttrs: 'attrs',
|
useAttrs: 'attrs',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'vue/block-order': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
order: ['script', 'template', 'style'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
// TODO: fix these
|
// TODO: fix these
|
||||||
'@typescript-eslint/no-unsafe-call': 'off',
|
'@typescript-eslint/no-unsafe-call': 'off',
|
||||||
|
|||||||
@@ -1,3 +1,29 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import N8nButton from '../N8nButton';
|
||||||
|
import N8nHeading from '../N8nHeading';
|
||||||
|
import N8nText from '../N8nText';
|
||||||
|
import N8nCallout, { type CalloutTheme } from '../N8nCallout';
|
||||||
|
import type { ButtonType } from 'n8n-design-system/types/button';
|
||||||
|
import N8nTooltip from 'n8n-design-system/components/N8nTooltip/Tooltip.vue';
|
||||||
|
|
||||||
|
interface ActionBoxProps {
|
||||||
|
emoji: string;
|
||||||
|
heading: string;
|
||||||
|
buttonText: string;
|
||||||
|
buttonType: ButtonType;
|
||||||
|
buttonDisabled?: boolean;
|
||||||
|
description: string;
|
||||||
|
calloutText?: string;
|
||||||
|
calloutTheme?: CalloutTheme;
|
||||||
|
calloutIcon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({ name: 'N8nActionBox' });
|
||||||
|
withDefaults(defineProps<ActionBoxProps>(), {
|
||||||
|
calloutTheme: 'info',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="['n8n-action-box', $style.container]" data-test-id="action-box">
|
<div :class="['n8n-action-box', $style.container]" data-test-id="action-box">
|
||||||
<div v-if="emoji" :class="$style.emoji">
|
<div v-if="emoji" :class="$style.emoji">
|
||||||
@@ -41,32 +67,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import N8nButton from '../N8nButton';
|
|
||||||
import N8nHeading from '../N8nHeading';
|
|
||||||
import N8nText from '../N8nText';
|
|
||||||
import N8nCallout, { type CalloutTheme } from '../N8nCallout';
|
|
||||||
import type { ButtonType } from 'n8n-design-system/types/button';
|
|
||||||
import N8nTooltip from 'n8n-design-system/components/N8nTooltip/Tooltip.vue';
|
|
||||||
|
|
||||||
interface ActionBoxProps {
|
|
||||||
emoji: string;
|
|
||||||
heading: string;
|
|
||||||
buttonText: string;
|
|
||||||
buttonType: ButtonType;
|
|
||||||
buttonDisabled?: boolean;
|
|
||||||
description: string;
|
|
||||||
calloutText?: string;
|
|
||||||
calloutTheme?: CalloutTheme;
|
|
||||||
calloutIcon?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({ name: 'N8nActionBox' });
|
|
||||||
withDefaults(defineProps<ActionBoxProps>(), {
|
|
||||||
calloutTheme: 'info',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
border: 2px dashed var(--color-foreground-base);
|
border: 2px dashed var(--color-foreground-base);
|
||||||
|
|||||||
@@ -1,57 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="['action-dropdown-container', $style.actionDropdownContainer]">
|
|
||||||
<ElDropdown
|
|
||||||
ref="elementDropdown"
|
|
||||||
:placement="placement"
|
|
||||||
:trigger="trigger"
|
|
||||||
:popper-class="popperClass"
|
|
||||||
:teleported="teleported"
|
|
||||||
:disabled="disabled"
|
|
||||||
@command="onSelect"
|
|
||||||
@visible-change="onVisibleChange"
|
|
||||||
>
|
|
||||||
<slot v-if="$slots.activator" name="activator" />
|
|
||||||
<n8n-icon-button
|
|
||||||
v-else
|
|
||||||
type="tertiary"
|
|
||||||
text
|
|
||||||
:class="$style.activator"
|
|
||||||
:size="activatorSize"
|
|
||||||
:icon="activatorIcon"
|
|
||||||
@blur="onButtonBlur"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template #dropdown>
|
|
||||||
<ElDropdownMenu :class="$style.userActionsMenu">
|
|
||||||
<ElDropdownItem
|
|
||||||
v-for="item in items"
|
|
||||||
:key="item.id"
|
|
||||||
:command="item.id"
|
|
||||||
:disabled="item.disabled"
|
|
||||||
:divided="item.divided"
|
|
||||||
:class="$style.elementItem"
|
|
||||||
>
|
|
||||||
<div :class="getItemClasses(item)" :data-test-id="`${testIdPrefix}-item-${item.id}`">
|
|
||||||
<span v-if="item.icon" :class="$style.icon">
|
|
||||||
<N8nIcon :icon="item.icon" :size="iconSize" />
|
|
||||||
</span>
|
|
||||||
<span :class="$style.label">
|
|
||||||
{{ item.label }}
|
|
||||||
</span>
|
|
||||||
<N8nKeyboardShortcut
|
|
||||||
v-if="item.shortcut"
|
|
||||||
v-bind="item.shortcut"
|
|
||||||
:class="$style.shortcut"
|
|
||||||
>
|
|
||||||
</N8nKeyboardShortcut>
|
|
||||||
</div>
|
|
||||||
</ElDropdownItem>
|
|
||||||
</ElDropdownMenu>
|
|
||||||
</template>
|
|
||||||
</ElDropdown>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// This component is visually similar to the ActionToggle component
|
// This component is visually similar to the ActionToggle component
|
||||||
// but it offers more options when it comes to dropdown items styling
|
// but it offers more options when it comes to dropdown items styling
|
||||||
@@ -129,6 +75,60 @@ const close = () => elementDropdown.value?.handleClose();
|
|||||||
defineExpose({ open, close });
|
defineExpose({ open, close });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['action-dropdown-container', $style.actionDropdownContainer]">
|
||||||
|
<ElDropdown
|
||||||
|
ref="elementDropdown"
|
||||||
|
:placement="placement"
|
||||||
|
:trigger="trigger"
|
||||||
|
:popper-class="popperClass"
|
||||||
|
:teleported="teleported"
|
||||||
|
:disabled="disabled"
|
||||||
|
@command="onSelect"
|
||||||
|
@visible-change="onVisibleChange"
|
||||||
|
>
|
||||||
|
<slot v-if="$slots.activator" name="activator" />
|
||||||
|
<n8n-icon-button
|
||||||
|
v-else
|
||||||
|
type="tertiary"
|
||||||
|
text
|
||||||
|
:class="$style.activator"
|
||||||
|
:size="activatorSize"
|
||||||
|
:icon="activatorIcon"
|
||||||
|
@blur="onButtonBlur"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template #dropdown>
|
||||||
|
<ElDropdownMenu :class="$style.userActionsMenu">
|
||||||
|
<ElDropdownItem
|
||||||
|
v-for="item in items"
|
||||||
|
:key="item.id"
|
||||||
|
:command="item.id"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:divided="item.divided"
|
||||||
|
:class="$style.elementItem"
|
||||||
|
>
|
||||||
|
<div :class="getItemClasses(item)" :data-test-id="`${testIdPrefix}-item-${item.id}`">
|
||||||
|
<span v-if="item.icon" :class="$style.icon">
|
||||||
|
<N8nIcon :icon="item.icon" :size="iconSize" />
|
||||||
|
</span>
|
||||||
|
<span :class="$style.label">
|
||||||
|
{{ item.label }}
|
||||||
|
</span>
|
||||||
|
<N8nKeyboardShortcut
|
||||||
|
v-if="item.shortcut"
|
||||||
|
v-bind="item.shortcut"
|
||||||
|
:class="$style.shortcut"
|
||||||
|
>
|
||||||
|
</N8nKeyboardShortcut>
|
||||||
|
</div>
|
||||||
|
</ElDropdownItem>
|
||||||
|
</ElDropdownMenu>
|
||||||
|
</template>
|
||||||
|
</ElDropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
:global(.el-dropdown__list) {
|
:global(.el-dropdown__list) {
|
||||||
.userActionsMenu {
|
.userActionsMenu {
|
||||||
|
|||||||
@@ -1,3 +1,38 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElDropdown, ElDropdownMenu, ElDropdownItem, type Placement } from 'element-plus';
|
||||||
|
import type { UserAction } from 'n8n-design-system/types';
|
||||||
|
import N8nIcon from '../N8nIcon';
|
||||||
|
import type { IconOrientation, IconSize } from 'n8n-design-system/types/icon';
|
||||||
|
|
||||||
|
const SIZE = ['mini', 'small', 'medium'] as const;
|
||||||
|
const THEME = ['default', 'dark'] as const;
|
||||||
|
|
||||||
|
interface ActionToggleProps {
|
||||||
|
actions?: UserAction[];
|
||||||
|
placement?: Placement;
|
||||||
|
size?: (typeof SIZE)[number];
|
||||||
|
iconSize?: IconSize;
|
||||||
|
theme?: (typeof THEME)[number];
|
||||||
|
iconOrientation?: IconOrientation;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({ name: 'N8nActionToggle' });
|
||||||
|
withDefaults(defineProps<ActionToggleProps>(), {
|
||||||
|
actions: () => [],
|
||||||
|
placement: 'bottom',
|
||||||
|
size: 'medium',
|
||||||
|
theme: 'default',
|
||||||
|
iconOrientation: 'vertical',
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
action: [value: string];
|
||||||
|
'visible-change': [value: boolean];
|
||||||
|
}>();
|
||||||
|
const onCommand = (value: string) => emit('action', value);
|
||||||
|
const onVisibleChange = (value: boolean) => emit('visible-change', value);
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span :class="$style.container" data-test-id="action-toggle" @click.stop.prevent>
|
<span :class="$style.container" data-test-id="action-toggle" @click.stop.prevent>
|
||||||
<ElDropdown
|
<ElDropdown
|
||||||
@@ -41,41 +76,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem, type Placement } from 'element-plus';
|
|
||||||
import type { UserAction } from 'n8n-design-system/types';
|
|
||||||
import N8nIcon from '../N8nIcon';
|
|
||||||
import type { IconOrientation, IconSize } from 'n8n-design-system/types/icon';
|
|
||||||
|
|
||||||
const SIZE = ['mini', 'small', 'medium'] as const;
|
|
||||||
const THEME = ['default', 'dark'] as const;
|
|
||||||
|
|
||||||
interface ActionToggleProps {
|
|
||||||
actions?: UserAction[];
|
|
||||||
placement?: Placement;
|
|
||||||
size?: (typeof SIZE)[number];
|
|
||||||
iconSize?: IconSize;
|
|
||||||
theme?: (typeof THEME)[number];
|
|
||||||
iconOrientation?: IconOrientation;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({ name: 'N8nActionToggle' });
|
|
||||||
withDefaults(defineProps<ActionToggleProps>(), {
|
|
||||||
actions: () => [],
|
|
||||||
placement: 'bottom',
|
|
||||||
size: 'medium',
|
|
||||||
theme: 'default',
|
|
||||||
iconOrientation: 'vertical',
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
action: [value: string];
|
|
||||||
'visible-change': [value: boolean];
|
|
||||||
}>();
|
|
||||||
const onCommand = (value: string) => emit('action', value);
|
|
||||||
const onVisibleChange = (value: boolean) => emit('visible-change', value);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container > * {
|
.container > * {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -1,28 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="alertBoxClassNames" role="alert">
|
|
||||||
<div :class="$style.content">
|
|
||||||
<span v-if="showIcon || $slots.icon" :class="$style.icon">
|
|
||||||
<N8nIcon v-if="showIcon" :icon="icon" />
|
|
||||||
<slot v-else-if="$slots.icon" name="icon" />
|
|
||||||
</span>
|
|
||||||
<div :class="$style.text">
|
|
||||||
<div v-if="$slots.title || title" :class="$style.title">
|
|
||||||
<slot name="title">{{ title }}</slot>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="$slots.default || description"
|
|
||||||
:class="{ [$style.description]: true, [$style.hasTitle]: $slots.title || title }"
|
|
||||||
>
|
|
||||||
<slot>{{ description }}</slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="$slots.aside" :class="$style.aside">
|
|
||||||
<slot name="aside" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useCssModule } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
@@ -76,6 +51,31 @@ const alertBoxClassNames = computed(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="alertBoxClassNames" role="alert">
|
||||||
|
<div :class="$style.content">
|
||||||
|
<span v-if="showIcon || $slots.icon" :class="$style.icon">
|
||||||
|
<N8nIcon v-if="showIcon" :icon="icon" />
|
||||||
|
<slot v-else-if="$slots.icon" name="icon" />
|
||||||
|
</span>
|
||||||
|
<div :class="$style.text">
|
||||||
|
<div v-if="$slots.title || title" :class="$style.title">
|
||||||
|
<slot name="title">{{ title }}</slot>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="$slots.default || description"
|
||||||
|
:class="{ [$style.description]: true, [$style.hasTitle]: $slots.title || title }"
|
||||||
|
>
|
||||||
|
<slot>{{ description }}</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="$slots.aside" :class="$style.aside">
|
||||||
|
<slot name="aside" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
@import '../../css/common/var.scss';
|
@import '../../css/common/var.scss';
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span :class="['n8n-avatar', $style.container]" v-bind="$attrs">
|
|
||||||
<Avatar
|
|
||||||
v-if="name"
|
|
||||||
:size="getSize(size)"
|
|
||||||
:name="name"
|
|
||||||
variant="marble"
|
|
||||||
:colors="getColors(colors)"
|
|
||||||
/>
|
|
||||||
<div v-else :class="[$style.empty, $style[size]]"></div>
|
|
||||||
<span v-if="firstName || lastName" :class="[$style.initials, $style[`text-${size}`]]">
|
|
||||||
{{ initials }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import Avatar from 'vue-boring-avatars';
|
import Avatar from 'vue-boring-avatars';
|
||||||
@@ -57,6 +41,22 @@ const sizes: { [size: string]: number } = {
|
|||||||
const getSize = (size: string): number => sizes[size];
|
const getSize = (size: string): number => sizes[size];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span :class="['n8n-avatar', $style.container]" v-bind="$attrs">
|
||||||
|
<Avatar
|
||||||
|
v-if="name"
|
||||||
|
:size="getSize(size)"
|
||||||
|
:name="name"
|
||||||
|
variant="marble"
|
||||||
|
:colors="getColors(colors)"
|
||||||
|
/>
|
||||||
|
<div v-else :class="[$style.empty, $style[size]]"></div>
|
||||||
|
<span v-if="firstName || lastName" :class="[$style.initials, $style[`text-${size}`]]">
|
||||||
|
{{ initials }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -1,11 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span :class="['n8n-badge', $style[theme]]">
|
|
||||||
<N8nText :size="size" :bold="bold" :compact="true">
|
|
||||||
<slot></slot>
|
|
||||||
</N8nText>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TextSize } from 'n8n-design-system/types/text';
|
import type { TextSize } from 'n8n-design-system/types/text';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
@@ -34,6 +26,14 @@ withDefaults(defineProps<BadgeProps>(), {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span :class="['n8n-badge', $style[theme]]">
|
||||||
|
<N8nText :size="size" :bold="bold" :compact="true">
|
||||||
|
<slot></slot>
|
||||||
|
</N8nText>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.badge {
|
.badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
type BlockUiProps = {
|
||||||
|
show: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
withDefaults(defineProps<BlockUiProps>(), {
|
||||||
|
show: false,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div
|
<div
|
||||||
@@ -9,16 +19,6 @@
|
|||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
type BlockUiProps = {
|
|
||||||
show: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
withDefaults(defineProps<BlockUiProps>(), {
|
|
||||||
show: false,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.uiBlocker {
|
.uiBlocker {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,27 +1,3 @@
|
|||||||
<template>
|
|
||||||
<component
|
|
||||||
:is="element"
|
|
||||||
:class="classes"
|
|
||||||
:disabled="isDisabled"
|
|
||||||
:aria-disabled="ariaDisabled"
|
|
||||||
:aria-busy="ariaBusy"
|
|
||||||
:href="href"
|
|
||||||
aria-live="polite"
|
|
||||||
v-bind="{
|
|
||||||
...attrs,
|
|
||||||
...(props.nativeType ? { type: props.nativeType } : {}),
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span v-if="loading || icon" :class="$style.icon">
|
|
||||||
<N8nSpinner v-if="loading" :size="iconSize" />
|
|
||||||
<N8nIcon v-else-if="icon" :icon="icon" :size="iconSize" />
|
|
||||||
</span>
|
|
||||||
<span v-if="label || $slots.default">
|
|
||||||
<slot>{{ label }}</slot>
|
|
||||||
</span>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCssModule, computed, useAttrs, watchEffect } from 'vue';
|
import { useCssModule, computed, useAttrs, watchEffect } from 'vue';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
@@ -75,6 +51,30 @@ const classes = computed(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="element"
|
||||||
|
:class="classes"
|
||||||
|
:disabled="isDisabled"
|
||||||
|
:aria-disabled="ariaDisabled"
|
||||||
|
:aria-busy="ariaBusy"
|
||||||
|
:href="href"
|
||||||
|
aria-live="polite"
|
||||||
|
v-bind="{
|
||||||
|
...attrs,
|
||||||
|
...(props.nativeType ? { type: props.nativeType } : {}),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span v-if="loading || icon" :class="$style.icon">
|
||||||
|
<N8nSpinner v-if="loading" :size="iconSize" />
|
||||||
|
<N8nIcon v-else-if="icon" :icon="icon" :size="iconSize" />
|
||||||
|
</span>
|
||||||
|
<span v-if="label || $slots.default">
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</span>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import './Button';
|
@import './Button';
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="classes" role="alert">
|
|
||||||
<div :class="$style.messageSection">
|
|
||||||
<div v-if="!iconless" :class="$style.icon">
|
|
||||||
<N8nIcon :icon="getIcon" :size="getIconSize" />
|
|
||||||
</div>
|
|
||||||
<N8nText size="small">
|
|
||||||
<slot />
|
|
||||||
</N8nText>
|
|
||||||
|
|
||||||
<slot name="actions" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<slot name="trailingContent" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useCssModule } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
@@ -70,6 +53,23 @@ const getIconSize = computed<IconSize>(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="classes" role="alert">
|
||||||
|
<div :class="$style.messageSection">
|
||||||
|
<div v-if="!iconless" :class="$style.icon">
|
||||||
|
<N8nIcon :icon="getIcon" :size="getIconSize" />
|
||||||
|
</div>
|
||||||
|
<N8nText size="small">
|
||||||
|
<slot />
|
||||||
|
</N8nText>
|
||||||
|
|
||||||
|
<slot name="actions" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<slot name="trailingContent" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.callout {
|
.callout {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, useCssModule } from 'vue';
|
||||||
|
|
||||||
|
interface CardProps {
|
||||||
|
hoverable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({ name: 'N8nCard' });
|
||||||
|
const props = withDefaults(defineProps<CardProps>(), {
|
||||||
|
hoverable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
const classes = computed(() => ({
|
||||||
|
card: true,
|
||||||
|
[$style.card]: true,
|
||||||
|
[$style.hoverable]: props.hoverable,
|
||||||
|
}));
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="classes" v-bind="$attrs">
|
<div :class="classes" v-bind="$attrs">
|
||||||
<div v-if="$slots.prepend" :class="$style.icon">
|
<div v-if="$slots.prepend" :class="$style.icon">
|
||||||
@@ -20,26 +40,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, useCssModule } from 'vue';
|
|
||||||
|
|
||||||
interface CardProps {
|
|
||||||
hoverable?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({ name: 'N8nCard' });
|
|
||||||
const props = withDefaults(defineProps<CardProps>(), {
|
|
||||||
hoverable: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const $style = useCssModule();
|
|
||||||
const classes = computed(() => ({
|
|
||||||
card: true,
|
|
||||||
[$style.card]: true,
|
|
||||||
[$style.hoverable]: props.hoverable,
|
|
||||||
}));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.card {
|
.card {
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
|
|||||||
@@ -1,25 +1,3 @@
|
|||||||
<template>
|
|
||||||
<ElCheckbox
|
|
||||||
v-bind="$props"
|
|
||||||
ref="checkbox"
|
|
||||||
:class="['n8n-checkbox', $style.n8nCheckbox]"
|
|
||||||
:disabled="disabled"
|
|
||||||
:indeterminate="indeterminate"
|
|
||||||
:model-value="modelValue"
|
|
||||||
@update:model-value="onUpdateModelValue"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
<N8nInputLabel
|
|
||||||
v-if="label"
|
|
||||||
:label="label"
|
|
||||||
:tooltip-text="tooltipText"
|
|
||||||
:bold="false"
|
|
||||||
:size="labelSize"
|
|
||||||
@click.prevent="onLabelClick"
|
|
||||||
/>
|
|
||||||
</ElCheckbox>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { ElCheckbox } from 'element-plus';
|
import { ElCheckbox } from 'element-plus';
|
||||||
@@ -58,6 +36,28 @@ const onLabelClick = () => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElCheckbox
|
||||||
|
v-bind="$props"
|
||||||
|
ref="checkbox"
|
||||||
|
:class="['n8n-checkbox', $style.n8nCheckbox]"
|
||||||
|
:disabled="disabled"
|
||||||
|
:indeterminate="indeterminate"
|
||||||
|
:model-value="modelValue"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
<N8nInputLabel
|
||||||
|
v-if="label"
|
||||||
|
:label="label"
|
||||||
|
:tooltip-text="tooltipText"
|
||||||
|
:bold="false"
|
||||||
|
:size="labelSize"
|
||||||
|
@click.prevent="onLabelClick"
|
||||||
|
/>
|
||||||
|
</ElCheckbox>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.n8nCheckbox {
|
.n8nCheckbox {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
|
|||||||
@@ -1,26 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div class="progress-circle">
|
|
||||||
<svg class="progress-ring" :width="diameter" :height="diameter">
|
|
||||||
<circle
|
|
||||||
:class="$style.progressRingCircle"
|
|
||||||
:stroke-width="strokeWidth"
|
|
||||||
stroke="#DCDFE6"
|
|
||||||
fill="transparent"
|
|
||||||
:r="radius"
|
|
||||||
v-bind="{ cx, cy }"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
:class="$style.progressRingCircle"
|
|
||||||
stroke="#5C4EC2"
|
|
||||||
:stroke-width="strokeWidth"
|
|
||||||
fill="transparent"
|
|
||||||
:r="radius"
|
|
||||||
v-bind="{ cx, cy, style }"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
@@ -50,6 +27,29 @@ const style = computed(() => ({
|
|||||||
}));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="progress-circle">
|
||||||
|
<svg class="progress-ring" :width="diameter" :height="diameter">
|
||||||
|
<circle
|
||||||
|
:class="$style.progressRingCircle"
|
||||||
|
:stroke-width="strokeWidth"
|
||||||
|
stroke="#DCDFE6"
|
||||||
|
fill="transparent"
|
||||||
|
:r="radius"
|
||||||
|
v-bind="{ cx, cy }"
|
||||||
|
/>
|
||||||
|
<circle
|
||||||
|
:class="$style.progressRingCircle"
|
||||||
|
stroke="#5C4EC2"
|
||||||
|
:stroke-width="strokeWidth"
|
||||||
|
fill="transparent"
|
||||||
|
:r="radius"
|
||||||
|
v-bind="{ cx, cy, style }"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
.progressRingCircle {
|
.progressRingCircle {
|
||||||
transition: stroke-dashoffset 0.35s linear;
|
transition: stroke-dashoffset 0.35s linear;
|
||||||
|
|||||||
@@ -1,65 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="classes" v-bind="$attrs">
|
|
||||||
<table :class="$style.datatable">
|
|
||||||
<thead :class="$style.datatableHeader">
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
v-for="column in columns"
|
|
||||||
:key="column.id"
|
|
||||||
:class="column.classes"
|
|
||||||
:style="getThStyle(column)"
|
|
||||||
>
|
|
||||||
{{ column.label }}
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<template v-for="row in visibleRows">
|
|
||||||
<slot name="row" :columns="columns" :row="row" :get-td-value="getTdValue">
|
|
||||||
<tr :key="row.id">
|
|
||||||
<td v-for="column in columns" :key="column.id" :class="column.classes">
|
|
||||||
<component :is="column.render" v-if="column.render" :row="row" :column="column" />
|
|
||||||
<span v-else>{{ getTdValue(row, column) }}</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</slot>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div :class="$style.pagination">
|
|
||||||
<N8nPagination
|
|
||||||
v-if="totalPages > 1"
|
|
||||||
background
|
|
||||||
:pager-count="5"
|
|
||||||
:page-size="rowsPerPage"
|
|
||||||
layout="prev, pager, next"
|
|
||||||
:total="totalRows"
|
|
||||||
:current-page="currentPage"
|
|
||||||
@update:current-page="onUpdateCurrentPage"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div :class="$style.pageSizeSelector">
|
|
||||||
<N8nSelect
|
|
||||||
size="mini"
|
|
||||||
:model-value="rowsPerPage"
|
|
||||||
teleported
|
|
||||||
@update:model-value="onRowsPerPageChange"
|
|
||||||
>
|
|
||||||
<template #prepend>{{ t('datatable.pageSize') }}</template>
|
|
||||||
<N8nOption
|
|
||||||
v-for="size in rowsPerPageOptions"
|
|
||||||
:key="size"
|
|
||||||
:label="`${size}`"
|
|
||||||
:value="size"
|
|
||||||
/>
|
|
||||||
<N8nOption :label="`All`" value="*"> </N8nOption>
|
|
||||||
</N8nSelect>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, useCssModule } from 'vue';
|
import { computed, ref, useCssModule } from 'vue';
|
||||||
import N8nSelect from '../N8nSelect';
|
import N8nSelect from '../N8nSelect';
|
||||||
@@ -138,6 +76,68 @@ function getThStyle(column: DatatableColumn) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="classes" v-bind="$attrs">
|
||||||
|
<table :class="$style.datatable">
|
||||||
|
<thead :class="$style.datatableHeader">
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
v-for="column in columns"
|
||||||
|
:key="column.id"
|
||||||
|
:class="column.classes"
|
||||||
|
:style="getThStyle(column)"
|
||||||
|
>
|
||||||
|
{{ column.label }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<template v-for="row in visibleRows">
|
||||||
|
<slot name="row" :columns="columns" :row="row" :get-td-value="getTdValue">
|
||||||
|
<tr :key="row.id">
|
||||||
|
<td v-for="column in columns" :key="column.id" :class="column.classes">
|
||||||
|
<component :is="column.render" v-if="column.render" :row="row" :column="column" />
|
||||||
|
<span v-else>{{ getTdValue(row, column) }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</slot>
|
||||||
|
</template>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div :class="$style.pagination">
|
||||||
|
<N8nPagination
|
||||||
|
v-if="totalPages > 1"
|
||||||
|
background
|
||||||
|
:pager-count="5"
|
||||||
|
:page-size="rowsPerPage"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
:total="totalRows"
|
||||||
|
:current-page="currentPage"
|
||||||
|
@update:current-page="onUpdateCurrentPage"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div :class="$style.pageSizeSelector">
|
||||||
|
<N8nSelect
|
||||||
|
size="mini"
|
||||||
|
:model-value="rowsPerPage"
|
||||||
|
teleported
|
||||||
|
@update:model-value="onRowsPerPageChange"
|
||||||
|
>
|
||||||
|
<template #prepend>{{ t('datatable.pageSize') }}</template>
|
||||||
|
<N8nOption
|
||||||
|
v-for="size in rowsPerPageOptions"
|
||||||
|
:key="size"
|
||||||
|
:label="`${size}`"
|
||||||
|
:value="size"
|
||||||
|
/>
|
||||||
|
<N8nOption :label="`All`" value="*"> </N8nOption>
|
||||||
|
</N8nSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.datatableWrapper {
|
.datatableWrapper {
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -1,43 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="['n8n-form-box', $style.container]">
|
|
||||||
<div v-if="title" :class="$style.heading">
|
|
||||||
<N8nHeading size="xlarge">
|
|
||||||
{{ title }}
|
|
||||||
</N8nHeading>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.inputsContainer">
|
|
||||||
<N8nFormInputs
|
|
||||||
:inputs="inputs"
|
|
||||||
:event-bus="formBus"
|
|
||||||
:column-view="true"
|
|
||||||
@update="onUpdateModelValue"
|
|
||||||
@submit="onSubmit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="secondaryButtonText || buttonText" :class="$style.buttonsContainer">
|
|
||||||
<span v-if="secondaryButtonText" :class="$style.secondaryButtonContainer">
|
|
||||||
<N8nLink size="medium" theme="text" @click="onSecondaryButtonClick">
|
|
||||||
{{ secondaryButtonText }}
|
|
||||||
</N8nLink>
|
|
||||||
</span>
|
|
||||||
<N8nButton
|
|
||||||
v-if="buttonText"
|
|
||||||
:label="buttonText"
|
|
||||||
:loading="buttonLoading"
|
|
||||||
data-test-id="form-submit-button"
|
|
||||||
size="large"
|
|
||||||
@click="onButtonClick"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.actionContainer">
|
|
||||||
<N8nLink v-if="redirectText && redirectLink" :to="redirectLink">
|
|
||||||
{{ redirectText }}
|
|
||||||
</N8nLink>
|
|
||||||
</div>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import N8nFormInputs from '../N8nFormInputs';
|
import N8nFormInputs from '../N8nFormInputs';
|
||||||
import N8nHeading from '../N8nHeading';
|
import N8nHeading from '../N8nHeading';
|
||||||
@@ -80,6 +40,46 @@ const onButtonClick = () => formBus.emit('submit');
|
|||||||
const onSecondaryButtonClick = (event: Event) => emit('secondaryClick', event);
|
const onSecondaryButtonClick = (event: Event) => emit('secondaryClick', event);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['n8n-form-box', $style.container]">
|
||||||
|
<div v-if="title" :class="$style.heading">
|
||||||
|
<N8nHeading size="xlarge">
|
||||||
|
{{ title }}
|
||||||
|
</N8nHeading>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.inputsContainer">
|
||||||
|
<N8nFormInputs
|
||||||
|
:inputs="inputs"
|
||||||
|
:event-bus="formBus"
|
||||||
|
:column-view="true"
|
||||||
|
@update="onUpdateModelValue"
|
||||||
|
@submit="onSubmit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="secondaryButtonText || buttonText" :class="$style.buttonsContainer">
|
||||||
|
<span v-if="secondaryButtonText" :class="$style.secondaryButtonContainer">
|
||||||
|
<N8nLink size="medium" theme="text" @click="onSecondaryButtonClick">
|
||||||
|
{{ secondaryButtonText }}
|
||||||
|
</N8nLink>
|
||||||
|
</span>
|
||||||
|
<N8nButton
|
||||||
|
v-if="buttonText"
|
||||||
|
:label="buttonText"
|
||||||
|
:loading="buttonLoading"
|
||||||
|
data-test-id="form-submit-button"
|
||||||
|
size="large"
|
||||||
|
@click="onButtonClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.actionContainer">
|
||||||
|
<N8nLink v-if="redirectText && redirectLink" :to="redirectLink">
|
||||||
|
{{ redirectText }}
|
||||||
|
</N8nLink>
|
||||||
|
</div>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.heading {
|
.heading {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,96 +1,3 @@
|
|||||||
<template>
|
|
||||||
<N8nCheckbox
|
|
||||||
v-if="type === 'checkbox'"
|
|
||||||
ref="inputRef"
|
|
||||||
:label="label"
|
|
||||||
:disabled="disabled"
|
|
||||||
:label-size="labelSize as CheckboxLabelSizePropType"
|
|
||||||
:model-value="modelValue as CheckboxModelValuePropType"
|
|
||||||
@update:model-value="onUpdateModelValue"
|
|
||||||
@focus="onFocus"
|
|
||||||
/>
|
|
||||||
<N8nInputLabel
|
|
||||||
v-else-if="type === 'toggle'"
|
|
||||||
:input-name="name"
|
|
||||||
:label="label"
|
|
||||||
:tooltip-text="tooltipText"
|
|
||||||
:required="required && showRequiredAsterisk"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
{{ tooltipText }}
|
|
||||||
</template>
|
|
||||||
<ElSwitch
|
|
||||||
:model-value="modelValue as SwitchModelValuePropType"
|
|
||||||
:active-color="activeColor"
|
|
||||||
:inactive-color="inactiveColor"
|
|
||||||
@update:model-value="onUpdateModelValue"
|
|
||||||
></ElSwitch>
|
|
||||||
</N8nInputLabel>
|
|
||||||
<N8nInputLabel
|
|
||||||
v-else
|
|
||||||
:input-name="name"
|
|
||||||
:label="label"
|
|
||||||
:tooltip-text="tooltipText"
|
|
||||||
:required="required && showRequiredAsterisk"
|
|
||||||
>
|
|
||||||
<div :class="showErrors ? $style.errorInput : ''" @keydown.stop @keydown.enter="onEnter">
|
|
||||||
<slot v-if="hasDefaultSlot" />
|
|
||||||
<N8nSelect
|
|
||||||
v-else-if="type === 'select' || type === 'multi-select'"
|
|
||||||
ref="inputRef"
|
|
||||||
:class="{ [$style.multiSelectSmallTags]: tagSize === 'small' }"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:multiple="type === 'multi-select'"
|
|
||||||
:disabled="disabled"
|
|
||||||
:name="name"
|
|
||||||
:teleported="teleported"
|
|
||||||
@update:model-value="onUpdateModelValue"
|
|
||||||
@focus="onFocus"
|
|
||||||
@blur="onBlur"
|
|
||||||
>
|
|
||||||
<N8nOption
|
|
||||||
v-for="option in options || []"
|
|
||||||
:key="option.value"
|
|
||||||
:value="option.value"
|
|
||||||
:label="option.label"
|
|
||||||
:disabled="!!option.disabled"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</N8nSelect>
|
|
||||||
<N8nInput
|
|
||||||
v-else
|
|
||||||
ref="inputRef"
|
|
||||||
:name="name"
|
|
||||||
:type="type as InputTypePropType"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:model-value="modelValue as InputModelValuePropType"
|
|
||||||
:maxlength="maxlength"
|
|
||||||
:autocomplete="autocomplete"
|
|
||||||
:disabled="disabled"
|
|
||||||
@update:model-value="onUpdateModelValue"
|
|
||||||
@blur="onBlur"
|
|
||||||
@focus="onFocus"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="showErrors" :class="$style.errors">
|
|
||||||
<span v-text="validationError" />
|
|
||||||
<n8n-link
|
|
||||||
v-if="documentationUrl && documentationText"
|
|
||||||
:to="documentationUrl"
|
|
||||||
:new-window="true"
|
|
||||||
size="small"
|
|
||||||
theme="danger"
|
|
||||||
>
|
|
||||||
{{ documentationText }}
|
|
||||||
</n8n-link>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="infoText" :class="$style.infoText">
|
|
||||||
<span size="small" v-text="infoText" />
|
|
||||||
</div>
|
|
||||||
</N8nInputLabel>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, reactive, onMounted, ref, watch, useSlots } from 'vue';
|
import { computed, reactive, onMounted, ref, watch, useSlots } from 'vue';
|
||||||
|
|
||||||
@@ -271,6 +178,99 @@ watch(
|
|||||||
defineExpose({ inputRef });
|
defineExpose({ inputRef });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nCheckbox
|
||||||
|
v-if="type === 'checkbox'"
|
||||||
|
ref="inputRef"
|
||||||
|
:label="label"
|
||||||
|
:disabled="disabled"
|
||||||
|
:label-size="labelSize as CheckboxLabelSizePropType"
|
||||||
|
:model-value="modelValue as CheckboxModelValuePropType"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
@focus="onFocus"
|
||||||
|
/>
|
||||||
|
<N8nInputLabel
|
||||||
|
v-else-if="type === 'toggle'"
|
||||||
|
:input-name="name"
|
||||||
|
:label="label"
|
||||||
|
:tooltip-text="tooltipText"
|
||||||
|
:required="required && showRequiredAsterisk"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
{{ tooltipText }}
|
||||||
|
</template>
|
||||||
|
<ElSwitch
|
||||||
|
:model-value="modelValue as SwitchModelValuePropType"
|
||||||
|
:active-color="activeColor"
|
||||||
|
:inactive-color="inactiveColor"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
></ElSwitch>
|
||||||
|
</N8nInputLabel>
|
||||||
|
<N8nInputLabel
|
||||||
|
v-else
|
||||||
|
:input-name="name"
|
||||||
|
:label="label"
|
||||||
|
:tooltip-text="tooltipText"
|
||||||
|
:required="required && showRequiredAsterisk"
|
||||||
|
>
|
||||||
|
<div :class="showErrors ? $style.errorInput : ''" @keydown.stop @keydown.enter="onEnter">
|
||||||
|
<slot v-if="hasDefaultSlot" />
|
||||||
|
<N8nSelect
|
||||||
|
v-else-if="type === 'select' || type === 'multi-select'"
|
||||||
|
ref="inputRef"
|
||||||
|
:class="{ [$style.multiSelectSmallTags]: tagSize === 'small' }"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:multiple="type === 'multi-select'"
|
||||||
|
:disabled="disabled"
|
||||||
|
:name="name"
|
||||||
|
:teleported="teleported"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
@focus="onFocus"
|
||||||
|
@blur="onBlur"
|
||||||
|
>
|
||||||
|
<N8nOption
|
||||||
|
v-for="option in options || []"
|
||||||
|
:key="option.value"
|
||||||
|
:value="option.value"
|
||||||
|
:label="option.label"
|
||||||
|
:disabled="!!option.disabled"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</N8nSelect>
|
||||||
|
<N8nInput
|
||||||
|
v-else
|
||||||
|
ref="inputRef"
|
||||||
|
:name="name"
|
||||||
|
:type="type as InputTypePropType"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:model-value="modelValue as InputModelValuePropType"
|
||||||
|
:maxlength="maxlength"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
:disabled="disabled"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
@blur="onBlur"
|
||||||
|
@focus="onFocus"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="showErrors" :class="$style.errors">
|
||||||
|
<span v-text="validationError" />
|
||||||
|
<n8n-link
|
||||||
|
v-if="documentationUrl && documentationText"
|
||||||
|
:to="documentationUrl"
|
||||||
|
:new-window="true"
|
||||||
|
size="small"
|
||||||
|
theme="danger"
|
||||||
|
>
|
||||||
|
{{ documentationText }}
|
||||||
|
</n8n-link>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="infoText" :class="$style.infoText">
|
||||||
|
<span size="small" v-text="infoText" />
|
||||||
|
</div>
|
||||||
|
</N8nInputLabel>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.infoText {
|
.infoText {
|
||||||
margin-top: var(--spacing-2xs);
|
margin-top: var(--spacing-2xs);
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<component :is="tag" :class="['n8n-heading', ...classes]" v-bind="$attrs">
|
|
||||||
<slot></slot>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useCssModule } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
|
|
||||||
@@ -50,6 +44,12 @@ const classes = computed(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="tag" :class="['n8n-heading', ...classes]" v-bind="$attrs">
|
||||||
|
<slot></slot>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.bold {
|
.bold {
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<N8nText :size="size" :color="color" :compact="true" class="n8n-icon" v-bind="$attrs">
|
|
||||||
<FontAwesomeIcon :icon="icon" :spin="spin" :class="$style[size]" />
|
|
||||||
</N8nText>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { FontAwesomeIconProps } from '@fortawesome/vue-fontawesome';
|
import type { FontAwesomeIconProps } from '@fortawesome/vue-fontawesome';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
@@ -24,6 +18,12 @@ withDefaults(defineProps<IconProps>(), {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nText :size="size" :color="color" :compact="true" class="n8n-icon" v-bind="$attrs">
|
||||||
|
<FontAwesomeIcon :icon="icon" :spin="spin" :class="$style[size]" />
|
||||||
|
</N8nText>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.xlarge {
|
.xlarge {
|
||||||
width: var(--font-size-xl) !important;
|
width: var(--font-size-xl) !important;
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
<template>
|
|
||||||
<N8nButton square v-bind="{ ...$attrs, ...$props }" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IconButtonProps } from 'n8n-design-system/types/button';
|
import type { IconButtonProps } from 'n8n-design-system/types/button';
|
||||||
import N8nButton from '../N8nButton';
|
import N8nButton from '../N8nButton';
|
||||||
@@ -17,3 +13,7 @@ withDefaults(defineProps<IconButtonProps>(), {
|
|||||||
active: false,
|
active: false,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nButton square v-bind="{ ...$attrs, ...$props }" />
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,43 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="['accordion', $style.container]">
|
|
||||||
<div :class="{ [$style.header]: true, [$style.expanded]: expanded }" @click="toggle">
|
|
||||||
<N8nIcon
|
|
||||||
v-if="headerIcon"
|
|
||||||
:icon="headerIcon.icon"
|
|
||||||
:color="headerIcon.color"
|
|
||||||
size="small"
|
|
||||||
class="mr-2xs"
|
|
||||||
/>
|
|
||||||
<N8nText :class="$style.headerText" color="text-base" size="small" align="left" bold>{{
|
|
||||||
title
|
|
||||||
}}</N8nText>
|
|
||||||
<N8nIcon :icon="expanded ? 'chevron-up' : 'chevron-down'" bold />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="expanded"
|
|
||||||
:class="{ [$style.description]: true, [$style.collapsed]: !expanded }"
|
|
||||||
@click="onClick"
|
|
||||||
>
|
|
||||||
<!-- Info accordion can display list of items with icons or just a HTML description -->
|
|
||||||
<div v-if="items.length > 0" :class="$style.accordionItems">
|
|
||||||
<div v-for="item in items" :key="item.id" :class="$style.accordionItem">
|
|
||||||
<n8n-tooltip :disabled="!item.tooltip">
|
|
||||||
<template #content>
|
|
||||||
<div @click="onTooltipClick(item.id, $event)" v-html="item.tooltip"></div>
|
|
||||||
</template>
|
|
||||||
<N8nIcon :icon="item.icon" :color="item.iconColor" size="small" class="mr-2xs" />
|
|
||||||
</n8n-tooltip>
|
|
||||||
<N8nText size="small" color="text-base">{{ item.label }}</N8nText>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<N8nText color="text-base" size="small" align="left">
|
|
||||||
<span v-html="description"></span>
|
|
||||||
</N8nText>
|
|
||||||
<slot name="customContent"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
@@ -90,6 +50,46 @@ const onClick = (e: MouseEvent) => emit('click:body', e);
|
|||||||
const onTooltipClick = (item: string, event: MouseEvent) => emit('tooltipClick', item, event);
|
const onTooltipClick = (item: string, event: MouseEvent) => emit('tooltipClick', item, event);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['accordion', $style.container]">
|
||||||
|
<div :class="{ [$style.header]: true, [$style.expanded]: expanded }" @click="toggle">
|
||||||
|
<N8nIcon
|
||||||
|
v-if="headerIcon"
|
||||||
|
:icon="headerIcon.icon"
|
||||||
|
:color="headerIcon.color"
|
||||||
|
size="small"
|
||||||
|
class="mr-2xs"
|
||||||
|
/>
|
||||||
|
<N8nText :class="$style.headerText" color="text-base" size="small" align="left" bold>{{
|
||||||
|
title
|
||||||
|
}}</N8nText>
|
||||||
|
<N8nIcon :icon="expanded ? 'chevron-up' : 'chevron-down'" bold />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="expanded"
|
||||||
|
:class="{ [$style.description]: true, [$style.collapsed]: !expanded }"
|
||||||
|
@click="onClick"
|
||||||
|
>
|
||||||
|
<!-- Info accordion can display list of items with icons or just a HTML description -->
|
||||||
|
<div v-if="items.length > 0" :class="$style.accordionItems">
|
||||||
|
<div v-for="item in items" :key="item.id" :class="$style.accordionItem">
|
||||||
|
<n8n-tooltip :disabled="!item.tooltip">
|
||||||
|
<template #content>
|
||||||
|
<div @click="onTooltipClick(item.id, $event)" v-html="item.tooltip"></div>
|
||||||
|
</template>
|
||||||
|
<N8nIcon :icon="item.icon" :color="item.iconColor" size="small" class="mr-2xs" />
|
||||||
|
</n8n-tooltip>
|
||||||
|
<N8nText size="small" color="text-base">{{ item.label }}</N8nText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<N8nText color="text-base" size="small" align="left">
|
||||||
|
<span v-html="description"></span>
|
||||||
|
</N8nText>
|
||||||
|
<slot name="customContent"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
background-color: var(--color-background-base);
|
background-color: var(--color-background-base);
|
||||||
|
|||||||
@@ -1,37 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
'n8n-info-tip': true,
|
|
||||||
[$style.infoTip]: true,
|
|
||||||
[$style[theme]]: true,
|
|
||||||
[$style[type]]: true,
|
|
||||||
[$style.bold]: bold,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<N8nTooltip
|
|
||||||
v-if="type === 'tooltip'"
|
|
||||||
:placement="tooltipPlacement"
|
|
||||||
:popper-class="$style.tooltipPopper"
|
|
||||||
:disabled="type !== 'tooltip'"
|
|
||||||
>
|
|
||||||
<span :class="$style.iconText" :style="{ color: iconData.color }">
|
|
||||||
<N8nIcon :icon="iconData.icon" />
|
|
||||||
</span>
|
|
||||||
<template #content>
|
|
||||||
<span>
|
|
||||||
<slot />
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</N8nTooltip>
|
|
||||||
<span v-else :class="$style.iconText">
|
|
||||||
<N8nIcon :icon="iconData.icon" />
|
|
||||||
<span>
|
|
||||||
<slot />
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { Placement } from 'element-plus';
|
import type { Placement } from 'element-plus';
|
||||||
@@ -92,6 +58,40 @@ const iconData = computed((): { icon: string; color: string } => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'n8n-info-tip': true,
|
||||||
|
[$style.infoTip]: true,
|
||||||
|
[$style[theme]]: true,
|
||||||
|
[$style[type]]: true,
|
||||||
|
[$style.bold]: bold,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<N8nTooltip
|
||||||
|
v-if="type === 'tooltip'"
|
||||||
|
:placement="tooltipPlacement"
|
||||||
|
:popper-class="$style.tooltipPopper"
|
||||||
|
:disabled="type !== 'tooltip'"
|
||||||
|
>
|
||||||
|
<span :class="$style.iconText" :style="{ color: iconData.color }">
|
||||||
|
<N8nIcon :icon="iconData.icon" />
|
||||||
|
</span>
|
||||||
|
<template #content>
|
||||||
|
<span>
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</N8nTooltip>
|
||||||
|
<span v-else :class="$style.iconText">
|
||||||
|
<N8nIcon :icon="iconData.icon" />
|
||||||
|
<span>
|
||||||
|
<slot />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.infoTip {
|
.infoTip {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,35 +1,3 @@
|
|||||||
<template>
|
|
||||||
<ElInput
|
|
||||||
ref="innerInput"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:type="type"
|
|
||||||
:size="resolvedSize"
|
|
||||||
:class="['n8n-input', ...classes]"
|
|
||||||
:autocomplete="autocomplete"
|
|
||||||
:name="name"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:disabled="disabled"
|
|
||||||
:readonly="readonly"
|
|
||||||
:clearable="clearable"
|
|
||||||
:rows="rows"
|
|
||||||
:title="title"
|
|
||||||
v-bind="$attrs"
|
|
||||||
>
|
|
||||||
<template v-if="$slots.prepend" #prepend>
|
|
||||||
<slot name="prepend" />
|
|
||||||
</template>
|
|
||||||
<template v-if="$slots.append" #append>
|
|
||||||
<slot name="append" />
|
|
||||||
</template>
|
|
||||||
<template v-if="$slots.prefix" #prefix>
|
|
||||||
<slot name="prefix" />
|
|
||||||
</template>
|
|
||||||
<template v-if="$slots.suffix" #suffix>
|
|
||||||
<slot name="suffix" />
|
|
||||||
</template>
|
|
||||||
</ElInput>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { ElInput } from 'element-plus';
|
import { ElInput } from 'element-plus';
|
||||||
@@ -96,6 +64,38 @@ const select = () => inputElement.value?.select();
|
|||||||
defineExpose({ focus, blur, select });
|
defineExpose({ focus, blur, select });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElInput
|
||||||
|
ref="innerInput"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:type="type"
|
||||||
|
:size="resolvedSize"
|
||||||
|
:class="['n8n-input', ...classes]"
|
||||||
|
:autocomplete="autocomplete"
|
||||||
|
:name="name"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
:readonly="readonly"
|
||||||
|
:clearable="clearable"
|
||||||
|
:rows="rows"
|
||||||
|
:title="title"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<template v-if="$slots.prepend" #prepend>
|
||||||
|
<slot name="prepend" />
|
||||||
|
</template>
|
||||||
|
<template v-if="$slots.append" #append>
|
||||||
|
<slot name="append" />
|
||||||
|
</template>
|
||||||
|
<template v-if="$slots.prefix" #prefix>
|
||||||
|
<slot name="prefix" />
|
||||||
|
</template>
|
||||||
|
<template v-if="$slots.suffix" #suffix>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</template>
|
||||||
|
</ElInput>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.xlarge {
|
.xlarge {
|
||||||
--input-font-size: var(--font-size-m);
|
--input-font-size: var(--font-size-m);
|
||||||
|
|||||||
@@ -1,3 +1,36 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import N8nText from '../N8nText';
|
||||||
|
import N8nIcon from '../N8nIcon';
|
||||||
|
import N8nTooltip from '../N8nTooltip';
|
||||||
|
import type { TextColor } from 'n8n-design-system/types/text';
|
||||||
|
|
||||||
|
const SIZE = ['small', 'medium'] as const;
|
||||||
|
|
||||||
|
interface InputLabelProps {
|
||||||
|
compact?: boolean;
|
||||||
|
color?: TextColor;
|
||||||
|
label?: string;
|
||||||
|
tooltipText?: string;
|
||||||
|
inputName?: string;
|
||||||
|
required?: boolean;
|
||||||
|
bold?: boolean;
|
||||||
|
size?: (typeof SIZE)[number];
|
||||||
|
underline?: boolean;
|
||||||
|
showTooltip?: boolean;
|
||||||
|
showOptions?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({ name: 'N8nInputLabel' });
|
||||||
|
withDefaults(defineProps<InputLabelProps>(), {
|
||||||
|
compact: false,
|
||||||
|
bold: true,
|
||||||
|
size: 'medium',
|
||||||
|
});
|
||||||
|
|
||||||
|
const addTargetBlank = (html: string) =>
|
||||||
|
html && html.includes('href=') ? html.replace(/href=/g, 'target="_blank" href=') : html;
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.container" v-bind="$attrs" data-test-id="input-label">
|
<div :class="$style.container" v-bind="$attrs" data-test-id="input-label">
|
||||||
<label
|
<label
|
||||||
@@ -45,39 +78,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import N8nText from '../N8nText';
|
|
||||||
import N8nIcon from '../N8nIcon';
|
|
||||||
import N8nTooltip from '../N8nTooltip';
|
|
||||||
import type { TextColor } from 'n8n-design-system/types/text';
|
|
||||||
|
|
||||||
const SIZE = ['small', 'medium'] as const;
|
|
||||||
|
|
||||||
interface InputLabelProps {
|
|
||||||
compact?: boolean;
|
|
||||||
color?: TextColor;
|
|
||||||
label?: string;
|
|
||||||
tooltipText?: string;
|
|
||||||
inputName?: string;
|
|
||||||
required?: boolean;
|
|
||||||
bold?: boolean;
|
|
||||||
size?: (typeof SIZE)[number];
|
|
||||||
underline?: boolean;
|
|
||||||
showTooltip?: boolean;
|
|
||||||
showOptions?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineOptions({ name: 'N8nInputLabel' });
|
|
||||||
withDefaults(defineProps<InputLabelProps>(), {
|
|
||||||
compact: false,
|
|
||||||
bold: true,
|
|
||||||
size: 'medium',
|
|
||||||
});
|
|
||||||
|
|
||||||
const addTargetBlank = (html: string) =>
|
|
||||||
html && html.includes('href=') ? html.replace(/href=/g, 'target="_blank" href=') : html;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,13 +1,3 @@
|
|||||||
<template>
|
|
||||||
<N8nRoute :to="to" :new-window="newWindow" v-bind="$attrs" class="n8n-link">
|
|
||||||
<span :class="$style[`${underline ? `${theme}-underline` : theme}`]">
|
|
||||||
<N8nText :size="size" :bold="bold">
|
|
||||||
<slot></slot>
|
|
||||||
</N8nText>
|
|
||||||
</span>
|
|
||||||
</N8nRoute>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { RouteLocationRaw } from 'vue-router';
|
import type { RouteLocationRaw } from 'vue-router';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
@@ -35,6 +25,16 @@ withDefaults(defineProps<LinkProps>(), {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nRoute :to="to" :new-window="newWindow" v-bind="$attrs" class="n8n-link">
|
||||||
|
<span :class="$style[`${underline ? `${theme}-underline` : theme}`]">
|
||||||
|
<N8nText :size="size" :bold="bold">
|
||||||
|
<slot></slot>
|
||||||
|
</N8nText>
|
||||||
|
</span>
|
||||||
|
</N8nRoute>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
@import '../../utils';
|
@import '../../utils';
|
||||||
@import '../../css/common/var';
|
@import '../../css/common/var';
|
||||||
|
|||||||
@@ -1,3 +1,37 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElSkeleton, ElSkeletonItem } from 'element-plus';
|
||||||
|
|
||||||
|
const VARIANT = [
|
||||||
|
'custom',
|
||||||
|
'p',
|
||||||
|
'text',
|
||||||
|
'h1',
|
||||||
|
'h3',
|
||||||
|
'text',
|
||||||
|
'caption',
|
||||||
|
'button',
|
||||||
|
'image',
|
||||||
|
'circle',
|
||||||
|
'rect',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
interface LoadingProps {
|
||||||
|
animated?: boolean;
|
||||||
|
loading?: boolean;
|
||||||
|
rows?: number;
|
||||||
|
shrinkLast?: boolean;
|
||||||
|
variant?: (typeof VARIANT)[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
withDefaults(defineProps<LoadingProps>(), {
|
||||||
|
animated: true,
|
||||||
|
loading: true,
|
||||||
|
rows: 1,
|
||||||
|
shrinkLast: true,
|
||||||
|
variant: 'p',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElSkeleton
|
<ElSkeleton
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@@ -35,40 +69,6 @@
|
|||||||
</ElSkeleton>
|
</ElSkeleton>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ElSkeleton, ElSkeletonItem } from 'element-plus';
|
|
||||||
|
|
||||||
const VARIANT = [
|
|
||||||
'custom',
|
|
||||||
'p',
|
|
||||||
'text',
|
|
||||||
'h1',
|
|
||||||
'h3',
|
|
||||||
'text',
|
|
||||||
'caption',
|
|
||||||
'button',
|
|
||||||
'image',
|
|
||||||
'circle',
|
|
||||||
'rect',
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
interface LoadingProps {
|
|
||||||
animated?: boolean;
|
|
||||||
loading?: boolean;
|
|
||||||
rows?: number;
|
|
||||||
shrinkLast?: boolean;
|
|
||||||
variant?: (typeof VARIANT)[number];
|
|
||||||
}
|
|
||||||
|
|
||||||
withDefaults(defineProps<LoadingProps>(), {
|
|
||||||
animated: true,
|
|
||||||
loading: true,
|
|
||||||
rows: 1,
|
|
||||||
shrinkLast: true,
|
|
||||||
variant: 'p',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.h1Last {
|
.h1Last {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
|
|||||||
@@ -1,23 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div class="n8n-markdown">
|
|
||||||
<div
|
|
||||||
v-if="!loading"
|
|
||||||
ref="editor"
|
|
||||||
:class="$style[theme]"
|
|
||||||
@click="onClick"
|
|
||||||
@mousedown="onMouseDown"
|
|
||||||
@change="onChange"
|
|
||||||
v-html="htmlContent"
|
|
||||||
/>
|
|
||||||
<div v-else :class="$style.markdown">
|
|
||||||
<div v-for="(_, index) in loadingBlocks" :key="index">
|
|
||||||
<N8nLoading :loading="loading" :rows="loadingRows" animated variant="p" />
|
|
||||||
<div :class="$style.spacer" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import type { Options as MarkdownOptions } from 'markdown-it';
|
import type { Options as MarkdownOptions } from 'markdown-it';
|
||||||
@@ -213,6 +193,26 @@ const onCheckboxChange = (index: number) => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="n8n-markdown">
|
||||||
|
<div
|
||||||
|
v-if="!loading"
|
||||||
|
ref="editor"
|
||||||
|
:class="$style[theme]"
|
||||||
|
@click="onClick"
|
||||||
|
@mousedown="onMouseDown"
|
||||||
|
@change="onChange"
|
||||||
|
v-html="htmlContent"
|
||||||
|
/>
|
||||||
|
<div v-else :class="$style.markdown">
|
||||||
|
<div v-for="(_, index) in loadingBlocks" :key="index">
|
||||||
|
<N8nLoading :loading="loading" :rows="loadingRows" animated variant="p" />
|
||||||
|
<div :class="$style.spacer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.markdown {
|
.markdown {
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
|
|||||||
@@ -1,58 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
['menu-container']: true,
|
|
||||||
[$style.container]: true,
|
|
||||||
[$style.menuCollapsed]: collapsed,
|
|
||||||
[$style.transparentBackground]: transparentBackground,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div v-if="$slots.header" :class="$style.menuHeader">
|
|
||||||
<slot name="header"></slot>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.menuContent">
|
|
||||||
<div :class="{ [$style.upperContent]: true, ['pt-xs']: $slots.menuPrefix }">
|
|
||||||
<div v-if="$slots.menuPrefix" :class="$style.menuPrefix">
|
|
||||||
<slot name="menuPrefix"></slot>
|
|
||||||
</div>
|
|
||||||
<ElMenu :default-active="defaultActive" :collapse="collapsed">
|
|
||||||
<N8nMenuItem
|
|
||||||
v-for="item in upperMenuItems"
|
|
||||||
:key="item.id"
|
|
||||||
:item="item"
|
|
||||||
:compact="collapsed"
|
|
||||||
:tooltip-delay="tooltipDelay"
|
|
||||||
:mode="mode"
|
|
||||||
:active-tab="activeTab"
|
|
||||||
:handle-select="onSelect"
|
|
||||||
/>
|
|
||||||
</ElMenu>
|
|
||||||
</div>
|
|
||||||
<div :class="[$style.lowerContent, 'pb-2xs']">
|
|
||||||
<slot name="beforeLowerMenu"></slot>
|
|
||||||
<ElMenu :default-active="defaultActive" :collapse="collapsed">
|
|
||||||
<N8nMenuItem
|
|
||||||
v-for="item in lowerMenuItems"
|
|
||||||
:key="item.id"
|
|
||||||
:item="item"
|
|
||||||
:compact="collapsed"
|
|
||||||
:tooltip-delay="tooltipDelay"
|
|
||||||
:mode="mode"
|
|
||||||
:active-tab="activeTab"
|
|
||||||
:handle-select="onSelect"
|
|
||||||
/>
|
|
||||||
</ElMenu>
|
|
||||||
<div v-if="$slots.menuSuffix" :class="$style.menuSuffix">
|
|
||||||
<slot name="menuSuffix"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="$slots.footer" :class="$style.menuFooter">
|
|
||||||
<slot name="footer"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
@@ -125,6 +70,61 @@ const onSelect = (item: IMenuItem): void => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
['menu-container']: true,
|
||||||
|
[$style.container]: true,
|
||||||
|
[$style.menuCollapsed]: collapsed,
|
||||||
|
[$style.transparentBackground]: transparentBackground,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div v-if="$slots.header" :class="$style.menuHeader">
|
||||||
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.menuContent">
|
||||||
|
<div :class="{ [$style.upperContent]: true, ['pt-xs']: $slots.menuPrefix }">
|
||||||
|
<div v-if="$slots.menuPrefix" :class="$style.menuPrefix">
|
||||||
|
<slot name="menuPrefix"></slot>
|
||||||
|
</div>
|
||||||
|
<ElMenu :default-active="defaultActive" :collapse="collapsed">
|
||||||
|
<N8nMenuItem
|
||||||
|
v-for="item in upperMenuItems"
|
||||||
|
:key="item.id"
|
||||||
|
:item="item"
|
||||||
|
:compact="collapsed"
|
||||||
|
:tooltip-delay="tooltipDelay"
|
||||||
|
:mode="mode"
|
||||||
|
:active-tab="activeTab"
|
||||||
|
:handle-select="onSelect"
|
||||||
|
/>
|
||||||
|
</ElMenu>
|
||||||
|
</div>
|
||||||
|
<div :class="[$style.lowerContent, 'pb-2xs']">
|
||||||
|
<slot name="beforeLowerMenu"></slot>
|
||||||
|
<ElMenu :default-active="defaultActive" :collapse="collapsed">
|
||||||
|
<N8nMenuItem
|
||||||
|
v-for="item in lowerMenuItems"
|
||||||
|
:key="item.id"
|
||||||
|
:item="item"
|
||||||
|
:compact="collapsed"
|
||||||
|
:tooltip-delay="tooltipDelay"
|
||||||
|
:mode="mode"
|
||||||
|
:active-tab="activeTab"
|
||||||
|
:handle-select="onSelect"
|
||||||
|
/>
|
||||||
|
</ElMenu>
|
||||||
|
<div v-if="$slots.menuSuffix" :class="$style.menuSuffix">
|
||||||
|
<slot name="menuSuffix"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="$slots.footer" :class="$style.menuFooter">
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,3 +1,67 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, useCssModule } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { ElSubMenu, ElMenuItem } from 'element-plus';
|
||||||
|
import N8nTooltip from '../N8nTooltip';
|
||||||
|
import N8nIcon from '../N8nIcon';
|
||||||
|
import ConditionalRouterLink from '../ConditionalRouterLink';
|
||||||
|
import type { IMenuItem } from '../../types';
|
||||||
|
import { doesMenuItemMatchCurrentRoute } from './routerUtil';
|
||||||
|
import { getInitials } from '../../utils/labelUtil';
|
||||||
|
|
||||||
|
interface MenuItemProps {
|
||||||
|
item: IMenuItem;
|
||||||
|
compact?: boolean;
|
||||||
|
tooltipDelay?: number;
|
||||||
|
popperClass?: string;
|
||||||
|
mode?: 'router' | 'tabs';
|
||||||
|
activeTab?: string;
|
||||||
|
handleSelect?: (item: IMenuItem) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<MenuItemProps>(), {
|
||||||
|
compact: false,
|
||||||
|
tooltipDelay: 300,
|
||||||
|
popperClass: '',
|
||||||
|
mode: 'router',
|
||||||
|
});
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
const $route = useRoute();
|
||||||
|
|
||||||
|
const availableChildren = computed((): IMenuItem[] =>
|
||||||
|
Array.isArray(props.item.children)
|
||||||
|
? props.item.children.filter((child) => child.available !== false)
|
||||||
|
: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRoute = computed(() => {
|
||||||
|
return $route ?? { name: '', path: '' };
|
||||||
|
});
|
||||||
|
|
||||||
|
const submenuPopperClass = computed((): string => {
|
||||||
|
const popperClass = [$style.submenuPopper, props.popperClass];
|
||||||
|
if (props.compact) {
|
||||||
|
popperClass.push($style.compact);
|
||||||
|
}
|
||||||
|
return popperClass.join(' ');
|
||||||
|
});
|
||||||
|
|
||||||
|
const isActive = (item: IMenuItem): boolean => {
|
||||||
|
if (props.mode === 'router') {
|
||||||
|
return doesMenuItemMatchCurrentRoute(item, currentRoute.value);
|
||||||
|
} else {
|
||||||
|
return item.id === props.activeTab;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isItemActive = (item: IMenuItem): boolean => {
|
||||||
|
const hasActiveChild =
|
||||||
|
Array.isArray(item.children) && item.children.some((child) => isActive(child));
|
||||||
|
return isActive(item) || hasActiveChild;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="['n8n-menu-item', $style.item]">
|
<div :class="['n8n-menu-item', $style.item]">
|
||||||
<ElSubMenu
|
<ElSubMenu
|
||||||
@@ -88,70 +152,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, useCssModule } from 'vue';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import { ElSubMenu, ElMenuItem } from 'element-plus';
|
|
||||||
import N8nTooltip from '../N8nTooltip';
|
|
||||||
import N8nIcon from '../N8nIcon';
|
|
||||||
import ConditionalRouterLink from '../ConditionalRouterLink';
|
|
||||||
import type { IMenuItem } from '../../types';
|
|
||||||
import { doesMenuItemMatchCurrentRoute } from './routerUtil';
|
|
||||||
import { getInitials } from '../../utils/labelUtil';
|
|
||||||
|
|
||||||
interface MenuItemProps {
|
|
||||||
item: IMenuItem;
|
|
||||||
compact?: boolean;
|
|
||||||
tooltipDelay?: number;
|
|
||||||
popperClass?: string;
|
|
||||||
mode?: 'router' | 'tabs';
|
|
||||||
activeTab?: string;
|
|
||||||
handleSelect?: (item: IMenuItem) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<MenuItemProps>(), {
|
|
||||||
compact: false,
|
|
||||||
tooltipDelay: 300,
|
|
||||||
popperClass: '',
|
|
||||||
mode: 'router',
|
|
||||||
});
|
|
||||||
|
|
||||||
const $style = useCssModule();
|
|
||||||
const $route = useRoute();
|
|
||||||
|
|
||||||
const availableChildren = computed((): IMenuItem[] =>
|
|
||||||
Array.isArray(props.item.children)
|
|
||||||
? props.item.children.filter((child) => child.available !== false)
|
|
||||||
: [],
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentRoute = computed(() => {
|
|
||||||
return $route ?? { name: '', path: '' };
|
|
||||||
});
|
|
||||||
|
|
||||||
const submenuPopperClass = computed((): string => {
|
|
||||||
const popperClass = [$style.submenuPopper, props.popperClass];
|
|
||||||
if (props.compact) {
|
|
||||||
popperClass.push($style.compact);
|
|
||||||
}
|
|
||||||
return popperClass.join(' ');
|
|
||||||
});
|
|
||||||
|
|
||||||
const isActive = (item: IMenuItem): boolean => {
|
|
||||||
if (props.mode === 'router') {
|
|
||||||
return doesMenuItemMatchCurrentRoute(item, currentRoute.value);
|
|
||||||
} else {
|
|
||||||
return item.id === props.activeTab;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const isItemActive = (item: IMenuItem): boolean => {
|
|
||||||
const hasActiveChild =
|
|
||||||
Array.isArray(item.children) && item.children.some((child) => isActive(child));
|
|
||||||
return isActive(item) || hasActiveChild;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
// Element menu-item overrides
|
// Element menu-item overrides
|
||||||
:global(.el-menu-item),
|
:global(.el-menu-item),
|
||||||
|
|||||||
@@ -1,40 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div class="n8n-node-icon" v-bind="$attrs">
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
[$style.nodeIconWrapper]: true,
|
|
||||||
[$style.circle]: circle,
|
|
||||||
[$style.disabled]: disabled,
|
|
||||||
}"
|
|
||||||
:style="iconStyleData"
|
|
||||||
>
|
|
||||||
<!-- ElementUI tooltip is prone to memory-leaking so we only render it if we really need it -->
|
|
||||||
<N8nTooltip v-if="showTooltip" :placement="tooltipPosition" :disabled="!showTooltip">
|
|
||||||
<template #content>{{ nodeTypeName }}</template>
|
|
||||||
<div v-if="type !== 'unknown'" :class="$style.icon">
|
|
||||||
<img v-if="type === 'file'" :src="src" :class="$style.nodeIconImage" />
|
|
||||||
<FontAwesomeIcon v-else :icon="`${name}`" :class="$style.iconFa" :style="fontStyleData" />
|
|
||||||
</div>
|
|
||||||
<div v-else :class="$style.nodeIconPlaceholder">
|
|
||||||
{{ nodeTypeName ? nodeTypeName.charAt(0) : '?' }}
|
|
||||||
</div>
|
|
||||||
</N8nTooltip>
|
|
||||||
<template v-else>
|
|
||||||
<div v-if="type !== 'unknown'" :class="$style.icon">
|
|
||||||
<img v-if="type === 'file'" :src="src" :class="$style.nodeIconImage" />
|
|
||||||
<FontAwesomeIcon v-else :icon="`${name}`" :style="fontStyleData" />
|
|
||||||
<div v-if="badge" :class="$style.badge" :style="badgeStyleData">
|
|
||||||
<n8n-node-icon :type="badge.type" :src="badge.src" :size="badgeSize"></n8n-node-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else :class="$style.nodeIconPlaceholder">
|
|
||||||
{{ nodeTypeName ? nodeTypeName.charAt(0) : '?' }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
@@ -107,6 +70,43 @@ const badgeStyleData = computed((): Record<string, string> => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="n8n-node-icon" v-bind="$attrs">
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
[$style.nodeIconWrapper]: true,
|
||||||
|
[$style.circle]: circle,
|
||||||
|
[$style.disabled]: disabled,
|
||||||
|
}"
|
||||||
|
:style="iconStyleData"
|
||||||
|
>
|
||||||
|
<!-- ElementUI tooltip is prone to memory-leaking so we only render it if we really need it -->
|
||||||
|
<N8nTooltip v-if="showTooltip" :placement="tooltipPosition" :disabled="!showTooltip">
|
||||||
|
<template #content>{{ nodeTypeName }}</template>
|
||||||
|
<div v-if="type !== 'unknown'" :class="$style.icon">
|
||||||
|
<img v-if="type === 'file'" :src="src" :class="$style.nodeIconImage" />
|
||||||
|
<FontAwesomeIcon v-else :icon="`${name}`" :class="$style.iconFa" :style="fontStyleData" />
|
||||||
|
</div>
|
||||||
|
<div v-else :class="$style.nodeIconPlaceholder">
|
||||||
|
{{ nodeTypeName ? nodeTypeName.charAt(0) : '?' }}
|
||||||
|
</div>
|
||||||
|
</N8nTooltip>
|
||||||
|
<template v-else>
|
||||||
|
<div v-if="type !== 'unknown'" :class="$style.icon">
|
||||||
|
<img v-if="type === 'file'" :src="src" :class="$style.nodeIconImage" />
|
||||||
|
<FontAwesomeIcon v-else :icon="`${name}`" :style="fontStyleData" />
|
||||||
|
<div v-if="badge" :class="$style.badge" :style="badgeStyleData">
|
||||||
|
<n8n-node-icon :type="badge.type" :src="badge.src" :size="badgeSize"></n8n-node-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else :class="$style.nodeIconPlaceholder">
|
||||||
|
{{ nodeTypeName ? nodeTypeName.charAt(0) : '?' }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.nodeIconWrapper {
|
.nodeIconWrapper {
|
||||||
width: var(--node-icon-size, 26px);
|
width: var(--node-icon-size, 26px);
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :id="id" :class="classes" role="alert" @click="onClick">
|
|
||||||
<div class="notice-content">
|
|
||||||
<N8nText size="small" :compact="true">
|
|
||||||
<slot>
|
|
||||||
<span
|
|
||||||
:id="`${id}-content`"
|
|
||||||
:class="showFullContent ? $style['expanded'] : $style['truncated']"
|
|
||||||
role="region"
|
|
||||||
v-html="displayContent"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</N8nText>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, useCssModule } from 'vue';
|
import { computed, ref, useCssModule } from 'vue';
|
||||||
import sanitize from 'sanitize-html';
|
import sanitize from 'sanitize-html';
|
||||||
@@ -81,6 +64,23 @@ const onClick = (event: MouseEvent) => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :id="id" :class="classes" role="alert" @click="onClick">
|
||||||
|
<div class="notice-content">
|
||||||
|
<N8nText size="small" :compact="true">
|
||||||
|
<slot>
|
||||||
|
<span
|
||||||
|
:id="`${id}-content`"
|
||||||
|
:class="showFullContent ? $style['expanded'] : $style['truncated']"
|
||||||
|
role="region"
|
||||||
|
v-html="displayContent"
|
||||||
|
/>
|
||||||
|
</slot>
|
||||||
|
</N8nText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.notice {
|
.notice {
|
||||||
font-size: var(--font-size-2xs);
|
font-size: var(--font-size-2xs);
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({ name: 'N8nPulse' });
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="['pulse', $style.pulseContainer]">
|
<div :class="['pulse', $style.pulseContainer]">
|
||||||
<div :class="$style.pulse">
|
<div :class="$style.pulse">
|
||||||
@@ -8,10 +12,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
defineOptions({ name: 'N8nPulse' });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
$--light-pulse-color: hsla(
|
$--light-pulse-color: hsla(
|
||||||
var(--color-primary-h),
|
var(--color-primary-h),
|
||||||
|
|||||||
@@ -1,3 +1,19 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
interface RadioButtonProps {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
active?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
size?: 'small' | 'medium';
|
||||||
|
}
|
||||||
|
|
||||||
|
withDefaults(defineProps<RadioButtonProps>(), {
|
||||||
|
active: false,
|
||||||
|
disabled: false,
|
||||||
|
size: 'medium',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
role="radio"
|
role="radio"
|
||||||
@@ -23,22 +39,6 @@
|
|||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
interface RadioButtonProps {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
active?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
size?: 'small' | 'medium';
|
|
||||||
}
|
|
||||||
|
|
||||||
withDefaults(defineProps<RadioButtonProps>(), {
|
|
||||||
active: false,
|
|
||||||
disabled: false,
|
|
||||||
size: 'medium',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
role="radiogroup"
|
|
||||||
:class="{ 'n8n-radio-buttons': true, [$style.radioGroup]: true, [$style.disabled]: disabled }"
|
|
||||||
>
|
|
||||||
<RadioButton
|
|
||||||
v-for="option in options"
|
|
||||||
:key="option.value"
|
|
||||||
v-bind="option"
|
|
||||||
:active="modelValue === option.value"
|
|
||||||
:size="size"
|
|
||||||
:disabled="disabled || option.disabled"
|
|
||||||
@click.prevent.stop="onClick(option, $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import RadioButton from './RadioButton.vue';
|
import RadioButton from './RadioButton.vue';
|
||||||
|
|
||||||
@@ -53,6 +36,23 @@ const onClick = (
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
role="radiogroup"
|
||||||
|
:class="{ 'n8n-radio-buttons': true, [$style.radioGroup]: true, [$style.disabled]: disabled }"
|
||||||
|
>
|
||||||
|
<RadioButton
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.value"
|
||||||
|
v-bind="option"
|
||||||
|
:active="modelValue === option.value"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled || option.disabled"
|
||||||
|
@click.prevent.stop="onClick(option, $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.radioGroup {
|
.radioGroup {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -1,16 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.resize">
|
|
||||||
<div
|
|
||||||
v-for="direction in enabledDirections"
|
|
||||||
:key="direction"
|
|
||||||
:data-dir="direction"
|
|
||||||
:class="{ [$style.resizer]: true, [$style[direction]]: true }"
|
|
||||||
@mousedown="resizerMove"
|
|
||||||
/>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
@@ -180,6 +167,19 @@ const resizerMove = (event: MouseEvent) => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.resize">
|
||||||
|
<div
|
||||||
|
v-for="direction in enabledDirections"
|
||||||
|
:key="direction"
|
||||||
|
:data-dir="direction"
|
||||||
|
:class="{ [$style.resizer]: true, [$style[direction]]: true }"
|
||||||
|
@mousedown="resizerMove"
|
||||||
|
/>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.resize {
|
.resize {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<template>
|
|
||||||
<N8nResizeWrapper
|
|
||||||
:is-resizing-enabled="!readOnly"
|
|
||||||
:height="height"
|
|
||||||
:width="width"
|
|
||||||
:min-height="minHeight"
|
|
||||||
:min-width="minWidth"
|
|
||||||
:scale="scale"
|
|
||||||
:grid-size="gridSize"
|
|
||||||
@resizeend="onResizeEnd"
|
|
||||||
@resize="onResize"
|
|
||||||
@resizestart="onResizeStart"
|
|
||||||
>
|
|
||||||
<N8nSticky v-bind="stickyBindings" />
|
|
||||||
</N8nResizeWrapper>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, useAttrs } from 'vue';
|
import { computed, ref, useAttrs } from 'vue';
|
||||||
import N8nResizeWrapper, { type ResizeData } from '../N8nResizeWrapper/ResizeWrapper.vue';
|
import N8nResizeWrapper, { type ResizeData } from '../N8nResizeWrapper/ResizeWrapper.vue';
|
||||||
@@ -59,3 +42,20 @@ const onResizeEnd = () => {
|
|||||||
emit('resizeend');
|
emit('resizeend');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nResizeWrapper
|
||||||
|
:is-resizing-enabled="!readOnly"
|
||||||
|
:height="height"
|
||||||
|
:width="width"
|
||||||
|
:min-height="minHeight"
|
||||||
|
:min-width="minWidth"
|
||||||
|
:scale="scale"
|
||||||
|
:grid-size="gridSize"
|
||||||
|
@resizeend="onResizeEnd"
|
||||||
|
@resize="onResize"
|
||||||
|
@resizestart="onResizeStart"
|
||||||
|
>
|
||||||
|
<N8nSticky v-bind="stickyBindings" />
|
||||||
|
</N8nResizeWrapper>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,17 +1,3 @@
|
|||||||
<template>
|
|
||||||
<router-link v-if="useRouterLink && to" :to="to" v-bind="$attrs">
|
|
||||||
<slot></slot>
|
|
||||||
</router-link>
|
|
||||||
<a
|
|
||||||
v-else
|
|
||||||
:href="to ? `${to}` : undefined"
|
|
||||||
:target="openNewWindow ? '_blank' : '_self'"
|
|
||||||
v-bind="$attrs"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { type RouteLocationRaw } from 'vue-router';
|
import { type RouteLocationRaw } from 'vue-router';
|
||||||
@@ -39,3 +25,17 @@ const useRouterLink = computed(() => {
|
|||||||
|
|
||||||
const openNewWindow = computed(() => !useRouterLink.value);
|
const openNewWindow = computed(() => !useRouterLink.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-link v-if="useRouterLink && to" :to="to" v-bind="$attrs">
|
||||||
|
<slot></slot>
|
||||||
|
</router-link>
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
:href="to ? `${to}` : undefined"
|
||||||
|
:target="openNewWindow ? '_blank' : '_self'"
|
||||||
|
v-bind="$attrs"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span class="n8n-spinner">
|
|
||||||
<div v-if="type === 'ring'" class="lds-ring">
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
<N8nIcon v-else icon="spinner" :size="size" spin />
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { TextSize } from 'n8n-design-system/types/text';
|
import type { TextSize } from 'n8n-design-system/types/text';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
@@ -28,6 +16,18 @@ withDefaults(defineProps<SpinnerProps>(), {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span class="n8n-spinner">
|
||||||
|
<div v-if="type === 'ring'" class="lds-ring">
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<N8nIcon v-else icon="spinner" :size="size" spin />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.lds-ring {
|
.lds-ring {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@@ -1,51 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
'n8n-sticky': true,
|
|
||||||
[$style.sticky]: true,
|
|
||||||
[$style.clickable]: !isResizing,
|
|
||||||
[$style[`color-${backgroundColor}`]]: true,
|
|
||||||
}"
|
|
||||||
:style="styles"
|
|
||||||
@keydown.prevent
|
|
||||||
>
|
|
||||||
<div v-show="!editMode" :class="$style.wrapper" @dblclick.stop="onDoubleClick">
|
|
||||||
<N8nMarkdown
|
|
||||||
theme="sticky"
|
|
||||||
:content="modelValue"
|
|
||||||
:with-multi-breaks="true"
|
|
||||||
@markdown-click="onMarkdownClick"
|
|
||||||
@update-content="onUpdateModelValue"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-show="editMode"
|
|
||||||
:class="{ 'full-height': !shouldShowFooter, 'sticky-textarea': true }"
|
|
||||||
@click.stop
|
|
||||||
@mousedown.stop
|
|
||||||
@mouseup.stop
|
|
||||||
@keydown.esc="onInputBlur"
|
|
||||||
@keydown.stop
|
|
||||||
>
|
|
||||||
<N8nInput
|
|
||||||
ref="input"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:name="inputName"
|
|
||||||
type="textarea"
|
|
||||||
:rows="5"
|
|
||||||
@blur="onInputBlur"
|
|
||||||
@update:model-value="onUpdateModelValue"
|
|
||||||
@wheel="onInputScroll"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
|
||||||
<N8nText size="xsmall" align="right">
|
|
||||||
<span v-html="t('sticky.markdownHint')"></span>
|
|
||||||
</N8nText>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import N8nInput from '../N8nInput';
|
import N8nInput from '../N8nInput';
|
||||||
@@ -122,6 +74,54 @@ const onInputScroll = (event: WheelEvent) => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
'n8n-sticky': true,
|
||||||
|
[$style.sticky]: true,
|
||||||
|
[$style.clickable]: !isResizing,
|
||||||
|
[$style[`color-${backgroundColor}`]]: true,
|
||||||
|
}"
|
||||||
|
:style="styles"
|
||||||
|
@keydown.prevent
|
||||||
|
>
|
||||||
|
<div v-show="!editMode" :class="$style.wrapper" @dblclick.stop="onDoubleClick">
|
||||||
|
<N8nMarkdown
|
||||||
|
theme="sticky"
|
||||||
|
:content="modelValue"
|
||||||
|
:with-multi-breaks="true"
|
||||||
|
@markdown-click="onMarkdownClick"
|
||||||
|
@update-content="onUpdateModelValue"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="editMode"
|
||||||
|
:class="{ 'full-height': !shouldShowFooter, 'sticky-textarea': true }"
|
||||||
|
@click.stop
|
||||||
|
@mousedown.stop
|
||||||
|
@mouseup.stop
|
||||||
|
@keydown.esc="onInputBlur"
|
||||||
|
@keydown.stop
|
||||||
|
>
|
||||||
|
<N8nInput
|
||||||
|
ref="input"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:name="inputName"
|
||||||
|
type="textarea"
|
||||||
|
:rows="5"
|
||||||
|
@blur="onInputBlur"
|
||||||
|
@update:model-value="onUpdateModelValue"
|
||||||
|
@wheel="onInputScroll"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
||||||
|
<N8nText size="xsmall" align="right">
|
||||||
|
<span v-html="t('sticky.markdownHint')"></span>
|
||||||
|
</N8nText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.sticky {
|
.sticky {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,59 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="['n8n-tabs', $style.container]">
|
|
||||||
<div v-if="scrollPosition > 0" :class="$style.back" @click="scrollLeft">
|
|
||||||
<N8nIcon icon="chevron-left" size="small" />
|
|
||||||
</div>
|
|
||||||
<div v-if="canScrollRight" :class="$style.next" @click="scrollRight">
|
|
||||||
<N8nIcon icon="chevron-right" size="small" />
|
|
||||||
</div>
|
|
||||||
<div ref="tabs" :class="$style.tabs">
|
|
||||||
<div
|
|
||||||
v-for="option in options"
|
|
||||||
:id="option.value"
|
|
||||||
:key="option.value"
|
|
||||||
:class="{ [$style.alignRight]: option.align === 'right' }"
|
|
||||||
>
|
|
||||||
<N8nTooltip :disabled="!option.tooltip" placement="bottom">
|
|
||||||
<template #content>
|
|
||||||
<div @click="handleTooltipClick(option.value, $event)" v-html="option.tooltip" />
|
|
||||||
</template>
|
|
||||||
<a
|
|
||||||
v-if="option.href"
|
|
||||||
target="_blank"
|
|
||||||
:href="option.href"
|
|
||||||
:class="[$style.link, $style.tab]"
|
|
||||||
@click="() => handleTabClick(option.value)"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
{{ option.label }}
|
|
||||||
<span :class="$style.external">
|
|
||||||
<N8nIcon icon="external-link-alt" size="xsmall" />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<router-link
|
|
||||||
v-else-if="option.to"
|
|
||||||
:to="option.to"
|
|
||||||
:class="[$style.tab, { [$style.activeTab]: modelValue === option.value }]"
|
|
||||||
>
|
|
||||||
<N8nIcon v-if="option.icon" :icon="option.icon" size="medium" />
|
|
||||||
<span v-if="option.label">{{ option.label }}</span>
|
|
||||||
</router-link>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
:class="{ [$style.tab]: true, [$style.activeTab]: modelValue === option.value }"
|
|
||||||
:data-test-id="`tab-${option.value}`"
|
|
||||||
@click="() => handleTabClick(option.value)"
|
|
||||||
>
|
|
||||||
<N8nIcon v-if="option.icon" :icon="option.icon" size="small" />
|
|
||||||
<span v-if="option.label">{{ option.label }}</span>
|
|
||||||
</div>
|
|
||||||
</N8nTooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref } from 'vue';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
@@ -128,6 +72,62 @@ const scrollLeft = () => scroll(-50);
|
|||||||
const scrollRight = () => scroll(50);
|
const scrollRight = () => scroll(50);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['n8n-tabs', $style.container]">
|
||||||
|
<div v-if="scrollPosition > 0" :class="$style.back" @click="scrollLeft">
|
||||||
|
<N8nIcon icon="chevron-left" size="small" />
|
||||||
|
</div>
|
||||||
|
<div v-if="canScrollRight" :class="$style.next" @click="scrollRight">
|
||||||
|
<N8nIcon icon="chevron-right" size="small" />
|
||||||
|
</div>
|
||||||
|
<div ref="tabs" :class="$style.tabs">
|
||||||
|
<div
|
||||||
|
v-for="option in options"
|
||||||
|
:id="option.value"
|
||||||
|
:key="option.value"
|
||||||
|
:class="{ [$style.alignRight]: option.align === 'right' }"
|
||||||
|
>
|
||||||
|
<N8nTooltip :disabled="!option.tooltip" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
<div @click="handleTooltipClick(option.value, $event)" v-html="option.tooltip" />
|
||||||
|
</template>
|
||||||
|
<a
|
||||||
|
v-if="option.href"
|
||||||
|
target="_blank"
|
||||||
|
:href="option.href"
|
||||||
|
:class="[$style.link, $style.tab]"
|
||||||
|
@click="() => handleTabClick(option.value)"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ option.label }}
|
||||||
|
<span :class="$style.external">
|
||||||
|
<N8nIcon icon="external-link-alt" size="xsmall" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<router-link
|
||||||
|
v-else-if="option.to"
|
||||||
|
:to="option.to"
|
||||||
|
:class="[$style.tab, { [$style.activeTab]: modelValue === option.value }]"
|
||||||
|
>
|
||||||
|
<N8nIcon v-if="option.icon" :icon="option.icon" size="medium" />
|
||||||
|
<span v-if="option.label">{{ option.label }}</span>
|
||||||
|
</router-link>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
:class="{ [$style.tab]: true, [$style.activeTab]: modelValue === option.value }"
|
||||||
|
:data-test-id="`tab-${option.value}`"
|
||||||
|
@click="() => handleTabClick(option.value)"
|
||||||
|
>
|
||||||
|
<N8nIcon v-if="option.icon" :icon="option.icon" size="small" />
|
||||||
|
<span v-if="option.label">{{ option.label }}</span>
|
||||||
|
</div>
|
||||||
|
</N8nTooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span :class="['n8n-tag', $style.tag]" v-bind="$attrs">
|
|
||||||
{{ text }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
interface TagProps {
|
interface TagProps {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -12,6 +6,12 @@ defineOptions({ name: 'N8nTag' });
|
|||||||
defineProps<TagProps>();
|
defineProps<TagProps>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span :class="['n8n-tag', $style.tag]" v-bind="$attrs">
|
||||||
|
{{ text }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.tag {
|
.tag {
|
||||||
min-width: max-content;
|
min-width: max-content;
|
||||||
|
|||||||
@@ -1,23 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="['n8n-tags', $style.tags]">
|
|
||||||
<N8nTag
|
|
||||||
v-for="tag in visibleTags"
|
|
||||||
:key="tag.id"
|
|
||||||
:text="tag.name"
|
|
||||||
@click="emit('click:tag', tag.id, $event)"
|
|
||||||
/>
|
|
||||||
<N8nLink
|
|
||||||
v-if="truncate && !showAll && hiddenTagsLength > 0"
|
|
||||||
theme="text"
|
|
||||||
underline
|
|
||||||
size="small"
|
|
||||||
@click.stop.prevent="onExpand"
|
|
||||||
>
|
|
||||||
{{ t('tags.showMore', [`${hiddenTagsLength}`]) }}
|
|
||||||
</N8nLink>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import N8nTag from '../N8nTag';
|
import N8nTag from '../N8nTag';
|
||||||
@@ -67,6 +47,26 @@ const onExpand = () => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['n8n-tags', $style.tags]">
|
||||||
|
<N8nTag
|
||||||
|
v-for="tag in visibleTags"
|
||||||
|
:key="tag.id"
|
||||||
|
:text="tag.name"
|
||||||
|
@click="emit('click:tag', tag.id, $event)"
|
||||||
|
/>
|
||||||
|
<N8nLink
|
||||||
|
v-if="truncate && !showAll && hiddenTagsLength > 0"
|
||||||
|
theme="text"
|
||||||
|
underline
|
||||||
|
size="small"
|
||||||
|
@click.stop.prevent="onExpand"
|
||||||
|
>
|
||||||
|
{{ t('tags.showMore', [`${hiddenTagsLength}`]) }}
|
||||||
|
</N8nLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.tags {
|
.tags {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<component :is="tag" :class="['n8n-text', ...classes]" v-bind="$attrs">
|
|
||||||
<slot></slot>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useCssModule } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
import type { TextSize, TextColor, TextAlign } from 'n8n-design-system/types/text';
|
import type { TextSize, TextColor, TextAlign } from 'n8n-design-system/types/text';
|
||||||
@@ -46,6 +40,12 @@ const classes = computed(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component :is="tag" :class="['n8n-text', ...classes]" v-bind="$attrs">
|
||||||
|
<slot></slot>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.bold {
|
.bold {
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
|
|||||||
@@ -1,25 +1,3 @@
|
|||||||
<template>
|
|
||||||
<ElTooltip v-bind="{ ...$props, ...$attrs }" :popper-class="$props.popperClass ?? 'n8n-tooltip'">
|
|
||||||
<slot />
|
|
||||||
<template #content>
|
|
||||||
<slot name="content">
|
|
||||||
<div v-html="content"></div>
|
|
||||||
</slot>
|
|
||||||
<div
|
|
||||||
v-if="buttons.length"
|
|
||||||
:class="$style.buttons"
|
|
||||||
:style="{ justifyContent: justifyButtons }"
|
|
||||||
>
|
|
||||||
<N8nButton
|
|
||||||
v-for="button in buttons"
|
|
||||||
:key="button.attrs.label"
|
|
||||||
v-bind="{ ...button.attrs, ...button.listeners }"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</ElTooltip>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
@@ -65,6 +43,28 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ElTooltip v-bind="{ ...$props, ...$attrs }" :popper-class="$props.popperClass ?? 'n8n-tooltip'">
|
||||||
|
<slot />
|
||||||
|
<template #content>
|
||||||
|
<slot name="content">
|
||||||
|
<div v-html="content"></div>
|
||||||
|
</slot>
|
||||||
|
<div
|
||||||
|
v-if="buttons.length"
|
||||||
|
:class="$style.buttons"
|
||||||
|
:style="{ justifyContent: justifyButtons }"
|
||||||
|
>
|
||||||
|
<N8nButton
|
||||||
|
v-for="button in buttons"
|
||||||
|
:key="button.attrs.label"
|
||||||
|
v-bind="{ ...button.attrs, ...button.listeners }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ElTooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.buttons {
|
.buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,31 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div v-if="isObject(value)" class="n8n-tree">
|
|
||||||
<div v-for="(label, i) in Object.keys(value)" :key="i" :class="classes">
|
|
||||||
<div v-if="isSimple(value[label])" :class="$style.simple">
|
|
||||||
<slot v-if="$slots.label" name="label" :label="label" :path="getPath(label)" />
|
|
||||||
<span v-else>{{ label }}</span>
|
|
||||||
<span>:</span>
|
|
||||||
<slot v-if="$slots.value" name="value" :value="value[label]" />
|
|
||||||
<span v-else>{{ value[label] }}</span>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<slot v-if="$slots.label" name="label" :label="label" :path="getPath(label)" />
|
|
||||||
<span v-else>{{ label }}</span>
|
|
||||||
<n8n-tree
|
|
||||||
:path="getPath(label)"
|
|
||||||
:depth="depth + 1"
|
|
||||||
:value="value[label] as Record<string, unknown>"
|
|
||||||
:node-class="nodeClass"
|
|
||||||
>
|
|
||||||
<template v-for="(_, name) in $slots" #[name]="data">
|
|
||||||
<slot :name="name" v-bind="data"></slot>
|
|
||||||
</template>
|
|
||||||
</n8n-tree>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useCssModule } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
|
|
||||||
@@ -85,6 +57,34 @@ const getPath = (key: string): Array<string | number> => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="isObject(value)" class="n8n-tree">
|
||||||
|
<div v-for="(label, i) in Object.keys(value)" :key="i" :class="classes">
|
||||||
|
<div v-if="isSimple(value[label])" :class="$style.simple">
|
||||||
|
<slot v-if="$slots.label" name="label" :label="label" :path="getPath(label)" />
|
||||||
|
<span v-else>{{ label }}</span>
|
||||||
|
<span>:</span>
|
||||||
|
<slot v-if="$slots.value" name="value" :value="value[label]" />
|
||||||
|
<span v-else>{{ value[label] }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<slot v-if="$slots.label" name="label" :label="label" :path="getPath(label)" />
|
||||||
|
<span v-else>{{ label }}</span>
|
||||||
|
<n8n-tree
|
||||||
|
:path="getPath(label)"
|
||||||
|
:depth="depth + 1"
|
||||||
|
:value="value[label] as Record<string, unknown>"
|
||||||
|
:node-class="nodeClass"
|
||||||
|
>
|
||||||
|
<template v-for="(_, name) in $slots" #[name]="data">
|
||||||
|
<slot :name="name" v-bind="data"></slot>
|
||||||
|
</template>
|
||||||
|
</n8n-tree>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
$--spacing: var(--spacing-s);
|
$--spacing: var(--spacing-s);
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="classes">
|
|
||||||
<div :class="$style.avatarContainer">
|
|
||||||
<N8nAvatar :first-name="firstName" :last-name="lastName" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isPendingUser" :class="$style.pendingUser">
|
|
||||||
<N8nText :bold="true">{{ email }}</N8nText>
|
|
||||||
<span :class="$style.pendingBadge"><N8nBadge :bold="true">Pending</N8nBadge></span>
|
|
||||||
</div>
|
|
||||||
<div v-else :class="$style.infoContainer">
|
|
||||||
<div>
|
|
||||||
<N8nText :bold="true" color="text-dark">
|
|
||||||
{{ firstName }} {{ lastName }}
|
|
||||||
{{ isCurrentUser ? t('nds.userInfo.you') : '' }}
|
|
||||||
</N8nText>
|
|
||||||
<span v-if="disabled" :class="$style.pendingBadge">
|
|
||||||
<N8nBadge :bold="true">Disabled</N8nBadge>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<N8nText data-test-id="user-email" size="small" color="text-light">{{ email }}</N8nText>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useCssModule } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
@@ -59,6 +32,33 @@ const classes = computed(
|
|||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="classes">
|
||||||
|
<div :class="$style.avatarContainer">
|
||||||
|
<N8nAvatar :first-name="firstName" :last-name="lastName" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isPendingUser" :class="$style.pendingUser">
|
||||||
|
<N8nText :bold="true">{{ email }}</N8nText>
|
||||||
|
<span :class="$style.pendingBadge"><N8nBadge :bold="true">Pending</N8nBadge></span>
|
||||||
|
</div>
|
||||||
|
<div v-else :class="$style.infoContainer">
|
||||||
|
<div>
|
||||||
|
<N8nText :bold="true" color="text-dark">
|
||||||
|
{{ firstName }} {{ lastName }}
|
||||||
|
{{ isCurrentUser ? t('nds.userInfo.you') : '' }}
|
||||||
|
</N8nText>
|
||||||
|
<span v-if="disabled" :class="$style.pendingBadge">
|
||||||
|
<N8nBadge :bold="true">Disabled</N8nBadge>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<N8nText data-test-id="user-email" size="small" color="text-light">{{ email }}</N8nText>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -1,35 +1,3 @@
|
|||||||
<template>
|
|
||||||
<N8nSelect
|
|
||||||
data-test-id="user-select-trigger"
|
|
||||||
v-bind="$attrs"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:filterable="true"
|
|
||||||
:filter-method="setFilter"
|
|
||||||
:placeholder="placeholder || t('nds.userSelect.selectUser')"
|
|
||||||
:default-first-option="true"
|
|
||||||
teleported
|
|
||||||
:popper-class="$style.limitPopperWidth"
|
|
||||||
:no-data-text="t('nds.userSelect.noMatchingUsers')"
|
|
||||||
:size="size"
|
|
||||||
@blur="onBlur"
|
|
||||||
@focus="onFocus"
|
|
||||||
>
|
|
||||||
<template v-if="$slots.prefix" #prefix>
|
|
||||||
<slot name="prefix" />
|
|
||||||
</template>
|
|
||||||
<N8nOption
|
|
||||||
v-for="user in sortedUsers"
|
|
||||||
:key="user.id"
|
|
||||||
:value="user.id"
|
|
||||||
:class="$style.itemContainer"
|
|
||||||
:label="getLabel(user)"
|
|
||||||
:disabled="user.disabled"
|
|
||||||
>
|
|
||||||
<N8nUserInfo v-bind="user" :is-current-user="currentUserId === user.id" />
|
|
||||||
</N8nOption>
|
|
||||||
</N8nSelect>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import N8nUserInfo from '../N8nUserInfo';
|
import N8nUserInfo from '../N8nUserInfo';
|
||||||
@@ -112,6 +80,38 @@ const getLabel = (user: IUser) =>
|
|||||||
!user.fullName ? user.email : `${user.fullName} (${user.email})`;
|
!user.fullName ? user.email : `${user.fullName} (${user.email})`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<N8nSelect
|
||||||
|
data-test-id="user-select-trigger"
|
||||||
|
v-bind="$attrs"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:filterable="true"
|
||||||
|
:filter-method="setFilter"
|
||||||
|
:placeholder="placeholder || t('nds.userSelect.selectUser')"
|
||||||
|
:default-first-option="true"
|
||||||
|
teleported
|
||||||
|
:popper-class="$style.limitPopperWidth"
|
||||||
|
:no-data-text="t('nds.userSelect.noMatchingUsers')"
|
||||||
|
:size="size"
|
||||||
|
@blur="onBlur"
|
||||||
|
@focus="onFocus"
|
||||||
|
>
|
||||||
|
<template v-if="$slots.prefix" #prefix>
|
||||||
|
<slot name="prefix" />
|
||||||
|
</template>
|
||||||
|
<N8nOption
|
||||||
|
v-for="user in sortedUsers"
|
||||||
|
:key="user.id"
|
||||||
|
:value="user.id"
|
||||||
|
:class="$style.itemContainer"
|
||||||
|
:label="getLabel(user)"
|
||||||
|
:disabled="user.disabled"
|
||||||
|
>
|
||||||
|
<N8nUserInfo v-bind="user" :is-current-user="currentUserId === user.id" />
|
||||||
|
</N8nOption>
|
||||||
|
</N8nSelect>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.itemContainer {
|
.itemContainer {
|
||||||
--select-option-padding: var(--spacing-2xs) var(--spacing-s);
|
--select-option-padding: var(--spacing-2xs) var(--spacing-s);
|
||||||
|
|||||||
@@ -1,39 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
v-for="(user, i) in sortedUsers"
|
|
||||||
:key="user.id"
|
|
||||||
:class="i === sortedUsers.length - 1 ? $style.itemContainer : $style.itemWithBorder"
|
|
||||||
:data-test-id="`user-list-item-${user.email}`"
|
|
||||||
>
|
|
||||||
<N8nUserInfo
|
|
||||||
v-bind="user"
|
|
||||||
:is-current-user="currentUserId === user.id"
|
|
||||||
:is-saml-login-enabled="isSamlLoginEnabled"
|
|
||||||
/>
|
|
||||||
<div :class="$style.badgeContainer">
|
|
||||||
<N8nBadge v-if="user.isOwner" theme="tertiary" bold>
|
|
||||||
{{ t('nds.auth.roles.owner') }}
|
|
||||||
</N8nBadge>
|
|
||||||
<slot v-if="!user.isOwner && !readonly" name="actions" :user="user" />
|
|
||||||
<N8nActionToggle
|
|
||||||
v-if="
|
|
||||||
!user.isOwner &&
|
|
||||||
user.signInType !== 'ldap' &&
|
|
||||||
!readonly &&
|
|
||||||
getActions(user).length > 0 &&
|
|
||||||
actions.length > 0
|
|
||||||
"
|
|
||||||
placement="bottom"
|
|
||||||
:actions="getActions(user)"
|
|
||||||
theme="dark"
|
|
||||||
@action="(action: string) => onUserAction(user, action)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import N8nActionToggle from '../N8nActionToggle';
|
import N8nActionToggle from '../N8nActionToggle';
|
||||||
@@ -115,6 +79,42 @@ const onUserAction = (user: IUser, action: string) =>
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="(user, i) in sortedUsers"
|
||||||
|
:key="user.id"
|
||||||
|
:class="i === sortedUsers.length - 1 ? $style.itemContainer : $style.itemWithBorder"
|
||||||
|
:data-test-id="`user-list-item-${user.email}`"
|
||||||
|
>
|
||||||
|
<N8nUserInfo
|
||||||
|
v-bind="user"
|
||||||
|
:is-current-user="currentUserId === user.id"
|
||||||
|
:is-saml-login-enabled="isSamlLoginEnabled"
|
||||||
|
/>
|
||||||
|
<div :class="$style.badgeContainer">
|
||||||
|
<N8nBadge v-if="user.isOwner" theme="tertiary" bold>
|
||||||
|
{{ t('nds.auth.roles.owner') }}
|
||||||
|
</N8nBadge>
|
||||||
|
<slot v-if="!user.isOwner && !readonly" name="actions" :user="user" />
|
||||||
|
<N8nActionToggle
|
||||||
|
v-if="
|
||||||
|
!user.isOwner &&
|
||||||
|
user.signInType !== 'ldap' &&
|
||||||
|
!readonly &&
|
||||||
|
getActions(user).length > 0 &&
|
||||||
|
actions.length > 0
|
||||||
|
"
|
||||||
|
placement="bottom"
|
||||||
|
:actions="getActions(user)"
|
||||||
|
theme="dark"
|
||||||
|
@action="(action: string) => onUserAction(user, action)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.itemContainer {
|
.itemContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<template>
|
|
||||||
<table :class="$style.table">
|
|
||||||
<tr>
|
|
||||||
<th :class="$style.row">Name</th>
|
|
||||||
<th :class="$style.row">Value</th>
|
|
||||||
</tr>
|
|
||||||
<tr
|
|
||||||
v-for="variable in variables"
|
|
||||||
:key="variable"
|
|
||||||
:style="attr ? { [attr]: `var(${variable})` } : {}"
|
|
||||||
>
|
|
||||||
<td>{{ variable }}</td>
|
|
||||||
<td>{{ values[variable] }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted } from 'vue';
|
import { onMounted, onUnmounted } from 'vue';
|
||||||
|
|
||||||
@@ -64,6 +47,23 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<table :class="$style.table">
|
||||||
|
<tr>
|
||||||
|
<th :class="$style.row">Name</th>
|
||||||
|
<th :class="$style.row">Value</th>
|
||||||
|
</tr>
|
||||||
|
<tr
|
||||||
|
v-for="variable in variables"
|
||||||
|
:key="variable"
|
||||||
|
:style="attr ? { [attr]: `var(${variable})` } : {}"
|
||||||
|
>
|
||||||
|
<td>{{ variable }}</td>
|
||||||
|
<td>{{ values[variable] }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.table {
|
.table {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,14 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div v-for="size in sizes" :key="size" class="spacing-group">
|
|
||||||
<div class="spacing-example" :class="`${property[0]}${side ? side[0] : ''}-${size}`">
|
|
||||||
<div class="spacing-box" />
|
|
||||||
<div class="label">{{ property[0] }}{{ side ? side[0] : '' }}-{{ size }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
@@ -42,6 +31,17 @@ const props = withDefaults(defineProps<SpacingPreviewProps>(), {
|
|||||||
const sizes = computed(() => [...SIZES, ...(props.property === 'margin' ? ['auto'] : [])]);
|
const sizes = computed(() => [...SIZES, ...(props.property === 'margin' ? ['auto'] : [])]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-for="size in sizes" :key="size" class="spacing-group">
|
||||||
|
<div class="spacing-example" :class="`${property[0]}${side ? side[0] : ''}-${size}`">
|
||||||
|
<div class="spacing-box" />
|
||||||
|
<div class="label">{{ property[0] }}{{ side ? side[0] : '' }}-{{ size }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
$box-size: 64px;
|
$box-size: 64px;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,46 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
|
import Modal from './Modal.vue';
|
||||||
|
import { ABOUT_MODAL_KEY } from '../constants';
|
||||||
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
|
import { useRootStore } from '@/stores/root.store';
|
||||||
|
import { useToast } from '@/composables/useToast';
|
||||||
|
import { useClipboard } from '@/composables/useClipboard';
|
||||||
|
import { useDebugInfo } from '@/composables/useDebugInfo';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'About',
|
||||||
|
components: {
|
||||||
|
Modal,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ABOUT_MODAL_KEY,
|
||||||
|
modalBus: createEventBus(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapStores(useRootStore, useSettingsStore),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closeDialog() {
|
||||||
|
this.modalBus.emit('close');
|
||||||
|
},
|
||||||
|
async copyDebugInfoToClipboard() {
|
||||||
|
useToast().showToast({
|
||||||
|
title: this.$locale.baseText('about.debug.toast.title'),
|
||||||
|
message: this.$locale.baseText('about.debug.toast.message'),
|
||||||
|
type: 'info',
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
await useClipboard().copy(useDebugInfo().generateDebugInfo());
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal
|
<Modal
|
||||||
max-width="540px"
|
max-width="540px"
|
||||||
@@ -68,49 +111,6 @@
|
|||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { mapStores } from 'pinia';
|
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
|
||||||
import Modal from './Modal.vue';
|
|
||||||
import { ABOUT_MODAL_KEY } from '../constants';
|
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
|
||||||
import { useRootStore } from '@/stores/root.store';
|
|
||||||
import { useToast } from '@/composables/useToast';
|
|
||||||
import { useClipboard } from '@/composables/useClipboard';
|
|
||||||
import { useDebugInfo } from '@/composables/useDebugInfo';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'About',
|
|
||||||
components: {
|
|
||||||
Modal,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
ABOUT_MODAL_KEY,
|
|
||||||
modalBus: createEventBus(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapStores(useRootStore, useSettingsStore),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
closeDialog() {
|
|
||||||
this.modalBus.emit('close');
|
|
||||||
},
|
|
||||||
async copyDebugInfoToClipboard() {
|
|
||||||
useToast().showToast({
|
|
||||||
title: this.$locale.baseText('about.debug.toast.title'),
|
|
||||||
message: this.$locale.baseText('about.debug.toast.message'),
|
|
||||||
type: 'info',
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
await useClipboard().copy(useDebugInfo().generateDebugInfo());
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.container > * {
|
.container > * {
|
||||||
margin-bottom: var(--spacing-s);
|
margin-bottom: var(--spacing-s);
|
||||||
|
|||||||
@@ -1,39 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="WORKFLOW_ACTIVE_MODAL_KEY"
|
|
||||||
:title="$locale.baseText('activationModal.workflowActivated')"
|
|
||||||
width="460px"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div>
|
|
||||||
<n8n-text>{{ triggerContent }}</n8n-text>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.spaced">
|
|
||||||
<n8n-text>
|
|
||||||
<n8n-text :bold="true">
|
|
||||||
{{ $locale.baseText('activationModal.theseExecutionsWillNotShowUp') }}
|
|
||||||
</n8n-text>
|
|
||||||
{{ $locale.baseText('activationModal.butYouCanSeeThem') }}
|
|
||||||
<a @click="showExecutionsList">
|
|
||||||
{{ $locale.baseText('activationModal.executionList') }}
|
|
||||||
</a>
|
|
||||||
{{ $locale.baseText('activationModal.ifYouChooseTo') }}
|
|
||||||
<a @click="showSettings">{{ $locale.baseText('activationModal.saveExecutions') }}</a>
|
|
||||||
</n8n-text>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #footer="{ close }">
|
|
||||||
<div :class="$style.footer">
|
|
||||||
<el-checkbox :model-value="checked" @update:model-value="handleCheckboxChange">{{
|
|
||||||
$locale.baseText('generic.dontShowAgain')
|
|
||||||
}}</el-checkbox>
|
|
||||||
<n8n-button :label="$locale.baseText('activationModal.gotIt')" @click="close" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
@@ -134,6 +98,42 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="WORKFLOW_ACTIVE_MODAL_KEY"
|
||||||
|
:title="$locale.baseText('activationModal.workflowActivated')"
|
||||||
|
width="460px"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
<n8n-text>{{ triggerContent }}</n8n-text>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.spaced">
|
||||||
|
<n8n-text>
|
||||||
|
<n8n-text :bold="true">
|
||||||
|
{{ $locale.baseText('activationModal.theseExecutionsWillNotShowUp') }}
|
||||||
|
</n8n-text>
|
||||||
|
{{ $locale.baseText('activationModal.butYouCanSeeThem') }}
|
||||||
|
<a @click="showExecutionsList">
|
||||||
|
{{ $locale.baseText('activationModal.executionList') }}
|
||||||
|
</a>
|
||||||
|
{{ $locale.baseText('activationModal.ifYouChooseTo') }}
|
||||||
|
<a @click="showSettings">{{ $locale.baseText('activationModal.saveExecutions') }}</a>
|
||||||
|
</n8n-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer="{ close }">
|
||||||
|
<div :class="$style.footer">
|
||||||
|
<el-checkbox :model-value="checked" @update:model-value="handleCheckboxChange">{{
|
||||||
|
$locale.baseText('generic.dontShowAgain')
|
||||||
|
}}</el-checkbox>
|
||||||
|
<n8n-button :label="$locale.baseText('activationModal.gotIt')" @click="close" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.spaced {
|
.spaced {
|
||||||
margin-top: var(--spacing-2xs);
|
margin-top: var(--spacing-2xs);
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
props: ['text', 'type'],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-tag
|
<el-tag
|
||||||
v-if="type === 'danger'"
|
v-if="type === 'danger'"
|
||||||
@@ -18,12 +24,6 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
props: ['text', 'type'],
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|||||||
@@ -1,40 +1,3 @@
|
|||||||
<template>
|
|
||||||
<el-tag :type="theme" :disable-transitions="true" :class="$style.container">
|
|
||||||
<font-awesome-icon
|
|
||||||
:icon="theme === 'success' ? 'check-circle' : 'exclamation-triangle'"
|
|
||||||
:class="theme === 'success' ? $style.icon : $style.dangerIcon"
|
|
||||||
/>
|
|
||||||
<div :class="$style.banner">
|
|
||||||
<div :class="$style.content">
|
|
||||||
<div>
|
|
||||||
<span :class="theme === 'success' ? $style.message : $style.dangerMessage">
|
|
||||||
{{ message }}
|
|
||||||
</span>
|
|
||||||
<n8n-link v-if="details && !expanded" :bold="true" size="small" @click="expand">
|
|
||||||
<span :class="$style.moreDetails">More details</span>
|
|
||||||
</n8n-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<slot v-if="$slots.button" name="button" />
|
|
||||||
<n8n-button
|
|
||||||
v-else-if="buttonLabel"
|
|
||||||
:label="buttonLoading && buttonLoadingLabel ? buttonLoadingLabel : buttonLabel"
|
|
||||||
:title="buttonTitle"
|
|
||||||
:type="theme"
|
|
||||||
:loading="buttonLoading"
|
|
||||||
size="small"
|
|
||||||
outline
|
|
||||||
@click.stop="onClick"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="expanded" :class="$style.details">
|
|
||||||
{{ details }}
|
|
||||||
</div>
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
@@ -72,6 +35,43 @@ const onClick = () => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-tag :type="theme" :disable-transitions="true" :class="$style.container">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="theme === 'success' ? 'check-circle' : 'exclamation-triangle'"
|
||||||
|
:class="theme === 'success' ? $style.icon : $style.dangerIcon"
|
||||||
|
/>
|
||||||
|
<div :class="$style.banner">
|
||||||
|
<div :class="$style.content">
|
||||||
|
<div>
|
||||||
|
<span :class="theme === 'success' ? $style.message : $style.dangerMessage">
|
||||||
|
{{ message }}
|
||||||
|
</span>
|
||||||
|
<n8n-link v-if="details && !expanded" :bold="true" size="small" @click="expand">
|
||||||
|
<span :class="$style.moreDetails">More details</span>
|
||||||
|
</n8n-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<slot v-if="$slots.button" name="button" />
|
||||||
|
<n8n-button
|
||||||
|
v-else-if="buttonLabel"
|
||||||
|
:label="buttonLoading && buttonLoadingLabel ? buttonLoadingLabel : buttonLabel"
|
||||||
|
:title="buttonTitle"
|
||||||
|
:type="theme"
|
||||||
|
:loading="buttonLoading"
|
||||||
|
size="small"
|
||||||
|
outline
|
||||||
|
@click.stop="onClick"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="expanded" :class="$style.details">
|
||||||
|
{{ details }}
|
||||||
|
</div>
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.icon {
|
.icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,23 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div v-if="windowVisible" :class="['binary-data-window', binaryData?.fileType]">
|
|
||||||
<n8n-button
|
|
||||||
size="small"
|
|
||||||
class="binary-data-window-back"
|
|
||||||
:title="$locale.baseText('binaryDataDisplay.backToOverviewPage')"
|
|
||||||
icon="arrow-left"
|
|
||||||
:label="$locale.baseText('binaryDataDisplay.backToList')"
|
|
||||||
@click.stop="closeWindow"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="binary-data-window-wrapper">
|
|
||||||
<div v-if="!binaryData">
|
|
||||||
{{ $locale.baseText('binaryDataDisplay.noDataFoundToDisplay') }}
|
|
||||||
</div>
|
|
||||||
<BinaryDataDisplayEmbed v-else :binary-data="binaryData" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { IBinaryData, IRunData } from 'n8n-workflow';
|
import type { IBinaryData, IRunData } from 'n8n-workflow';
|
||||||
@@ -89,6 +69,26 @@ function closeWindow() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="windowVisible" :class="['binary-data-window', binaryData?.fileType]">
|
||||||
|
<n8n-button
|
||||||
|
size="small"
|
||||||
|
class="binary-data-window-back"
|
||||||
|
:title="$locale.baseText('binaryDataDisplay.backToOverviewPage')"
|
||||||
|
icon="arrow-left"
|
||||||
|
:label="$locale.baseText('binaryDataDisplay.backToList')"
|
||||||
|
@click.stop="closeWindow"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="binary-data-window-wrapper">
|
||||||
|
<div v-if="!binaryData">
|
||||||
|
{{ $locale.baseText('binaryDataDisplay.noDataFoundToDisplay') }}
|
||||||
|
</div>
|
||||||
|
<BinaryDataDisplayEmbed v-else :binary-data="binaryData" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.binary-data-window {
|
.binary-data-window {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,28 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span>
|
|
||||||
<div v-if="isLoading">Loading binary data...</div>
|
|
||||||
<div v-else-if="error">Error loading binary data</div>
|
|
||||||
<span v-else>
|
|
||||||
<video v-if="binaryData.fileType === 'video'" controls autoplay>
|
|
||||||
<source :src="embedSource" :type="binaryData.mimeType" />
|
|
||||||
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
|
||||||
</video>
|
|
||||||
<audio v-else-if="binaryData.fileType === 'audio'" controls autoplay>
|
|
||||||
<source :src="embedSource" :type="binaryData.mimeType" />
|
|
||||||
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
|
||||||
</audio>
|
|
||||||
<VueJsonPretty
|
|
||||||
v-else-if="binaryData.fileType === 'json'"
|
|
||||||
:data="data"
|
|
||||||
:deep="3"
|
|
||||||
:show-length="true"
|
|
||||||
/>
|
|
||||||
<RunDataHtml v-else-if="binaryData.fileType === 'html'" :input-html="data" />
|
|
||||||
<embed v-else :src="embedSource" class="binary-data" :class="embedClass" />
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import { ref, onMounted, computed } from 'vue';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
@@ -75,6 +50,31 @@ onMounted(async () => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span>
|
||||||
|
<div v-if="isLoading">Loading binary data...</div>
|
||||||
|
<div v-else-if="error">Error loading binary data</div>
|
||||||
|
<span v-else>
|
||||||
|
<video v-if="binaryData.fileType === 'video'" controls autoplay>
|
||||||
|
<source :src="embedSource" :type="binaryData.mimeType" />
|
||||||
|
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
||||||
|
</video>
|
||||||
|
<audio v-else-if="binaryData.fileType === 'audio'" controls autoplay>
|
||||||
|
<source :src="embedSource" :type="binaryData.mimeType" />
|
||||||
|
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
||||||
|
</audio>
|
||||||
|
<VueJsonPretty
|
||||||
|
v-else-if="binaryData.fileType === 'json'"
|
||||||
|
:data="data"
|
||||||
|
:deep="3"
|
||||||
|
:show-length="true"
|
||||||
|
/>
|
||||||
|
<RunDataHtml v-else-if="binaryData.fileType === 'html'" :input-html="data" />
|
||||||
|
<embed v-else :src="embedSource" class="binary-data" :class="embedClass" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.binary-data {
|
.binary-data {
|
||||||
background-color: var(--color-foreground-xlight);
|
background-color: var(--color-foreground-xlight);
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span>
|
|
||||||
<slot :bp="bp" :value="value" />
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||||
import { BREAKPOINT_SM, BREAKPOINT_MD, BREAKPOINT_LG, BREAKPOINT_XL } from '@/constants';
|
import { BREAKPOINT_SM, BREAKPOINT_MD, BREAKPOINT_LG, BREAKPOINT_XL } from '@/constants';
|
||||||
@@ -90,3 +84,9 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('resize', onResize);
|
window.removeEventListener('resize', onResize);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span>
|
||||||
|
<slot :bp="bp" :value="value" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,3 +1,37 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { onBeforeMount, onBeforeUnmount } from 'vue';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useCanvasStore } from '@/stores/canvas.store';
|
||||||
|
import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue';
|
||||||
|
import { useDeviceSupport } from 'n8n-design-system';
|
||||||
|
|
||||||
|
const canvasStore = useCanvasStore();
|
||||||
|
const { zoomToFit, zoomIn, zoomOut, resetZoom } = canvasStore;
|
||||||
|
const { nodeViewScale, isDemo } = storeToRefs(canvasStore);
|
||||||
|
const deviceSupport = useDeviceSupport();
|
||||||
|
|
||||||
|
const keyDown = (e: KeyboardEvent) => {
|
||||||
|
const isCtrlKeyPressed = deviceSupport.isCtrlKeyPressed(e);
|
||||||
|
if ((e.key === '=' || e.key === '+') && !isCtrlKeyPressed) {
|
||||||
|
zoomIn();
|
||||||
|
} else if ((e.key === '_' || e.key === '-') && !isCtrlKeyPressed) {
|
||||||
|
zoomOut();
|
||||||
|
} else if (e.key === '0' && !isCtrlKeyPressed) {
|
||||||
|
resetZoom();
|
||||||
|
} else if (e.key === '1' && !isCtrlKeyPressed) {
|
||||||
|
zoomToFit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
document.addEventListener('keydown', keyDown);
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('keydown', keyDown);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="{
|
:class="{
|
||||||
@@ -57,39 +91,6 @@
|
|||||||
</KeyboardShortcutTooltip>
|
</KeyboardShortcutTooltip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onBeforeMount, onBeforeUnmount } from 'vue';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { useCanvasStore } from '@/stores/canvas.store';
|
|
||||||
import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue';
|
|
||||||
import { useDeviceSupport } from 'n8n-design-system';
|
|
||||||
|
|
||||||
const canvasStore = useCanvasStore();
|
|
||||||
const { zoomToFit, zoomIn, zoomOut, resetZoom } = canvasStore;
|
|
||||||
const { nodeViewScale, isDemo } = storeToRefs(canvasStore);
|
|
||||||
const deviceSupport = useDeviceSupport();
|
|
||||||
|
|
||||||
const keyDown = (e: KeyboardEvent) => {
|
|
||||||
const isCtrlKeyPressed = deviceSupport.isCtrlKeyPressed(e);
|
|
||||||
if ((e.key === '=' || e.key === '+') && !isCtrlKeyPressed) {
|
|
||||||
zoomIn();
|
|
||||||
} else if ((e.key === '_' || e.key === '-') && !isCtrlKeyPressed) {
|
|
||||||
zoomOut();
|
|
||||||
} else if (e.key === '0' && !isCtrlKeyPressed) {
|
|
||||||
resetZoom();
|
|
||||||
} else if (e.key === '1' && !isCtrlKeyPressed) {
|
|
||||||
zoomToFit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
document.addEventListener('keydown', keyDown);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
document.removeEventListener('keydown', keyDown);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.zoomMenu {
|
.zoomMenu {
|
||||||
|
|||||||
@@ -1,33 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="CHANGE_PASSWORD_MODAL_KEY"
|
|
||||||
:title="i18n.baseText('auth.changePassword')"
|
|
||||||
:center="true"
|
|
||||||
width="460px"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
@enter="onSubmit"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<n8n-form-inputs
|
|
||||||
:inputs="config"
|
|
||||||
:event-bus="formBus"
|
|
||||||
:column-view="true"
|
|
||||||
@update="onInput"
|
|
||||||
@submit="onSubmit"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<n8n-button
|
|
||||||
:loading="loading"
|
|
||||||
:label="i18n.baseText('auth.changePassword')"
|
|
||||||
float="right"
|
|
||||||
data-test-id="change-password-button"
|
|
||||||
@click="onSubmitClick"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useToast } from '@/composables/useToast';
|
import { useToast } from '@/composables/useToast';
|
||||||
@@ -160,3 +130,33 @@ onMounted(() => {
|
|||||||
config.value = form;
|
config.value = form;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="CHANGE_PASSWORD_MODAL_KEY"
|
||||||
|
:title="i18n.baseText('auth.changePassword')"
|
||||||
|
:center="true"
|
||||||
|
width="460px"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
@enter="onSubmit"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<n8n-form-inputs
|
||||||
|
:inputs="config"
|
||||||
|
:event-bus="formBus"
|
||||||
|
:column-view="true"
|
||||||
|
@update="onInput"
|
||||||
|
@submit="onSubmit"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<n8n-button
|
||||||
|
:loading="loading"
|
||||||
|
:label="i18n.baseText('auth.changePassword')"
|
||||||
|
float="right"
|
||||||
|
data-test-id="change-password-button"
|
||||||
|
@click="onSubmitClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,51 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
ref="codeNodeEditorContainerRef"
|
|
||||||
:class="['code-node-editor', $style['code-node-editor-container'], language]"
|
|
||||||
@mouseover="onMouseOver"
|
|
||||||
@mouseout="onMouseOut"
|
|
||||||
>
|
|
||||||
<el-tabs
|
|
||||||
v-if="aiEnabled"
|
|
||||||
ref="tabs"
|
|
||||||
v-model="activeTab"
|
|
||||||
type="card"
|
|
||||||
:before-leave="onBeforeTabLeave"
|
|
||||||
>
|
|
||||||
<el-tab-pane
|
|
||||||
:label="$locale.baseText('codeNodeEditor.tabs.code')"
|
|
||||||
name="code"
|
|
||||||
data-test-id="code-node-tab-code"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref="codeNodeEditorRef"
|
|
||||||
:class="['ph-no-capture', 'code-editor-tabs', $style.editorInput]"
|
|
||||||
/>
|
|
||||||
<slot name="suffix" />
|
|
||||||
</el-tab-pane>
|
|
||||||
<el-tab-pane
|
|
||||||
:label="$locale.baseText('codeNodeEditor.tabs.askAi')"
|
|
||||||
name="ask-ai"
|
|
||||||
data-test-id="code-node-tab-ai"
|
|
||||||
>
|
|
||||||
<!-- Key the AskAI tab to make sure it re-mounts when changing tabs -->
|
|
||||||
<AskAI
|
|
||||||
:key="activeTab"
|
|
||||||
:has-changes="hasChanges"
|
|
||||||
@replace-code="onReplaceCode"
|
|
||||||
@started-loading="onAiLoadStart"
|
|
||||||
@finished-loading="onAiLoadEnd"
|
|
||||||
/>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
<!-- If AskAi not enabled, there's no point in rendering tabs -->
|
|
||||||
<div v-else :class="$style.fillHeight">
|
|
||||||
<div ref="codeNodeEditorRef" :class="['ph-no-capture', $style.fillHeight]" />
|
|
||||||
<slot name="suffix" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
import { python } from '@codemirror/lang-python';
|
import { python } from '@codemirror/lang-python';
|
||||||
@@ -411,6 +363,54 @@ function onAiLoadEnd() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="codeNodeEditorContainerRef"
|
||||||
|
:class="['code-node-editor', $style['code-node-editor-container'], language]"
|
||||||
|
@mouseover="onMouseOver"
|
||||||
|
@mouseout="onMouseOut"
|
||||||
|
>
|
||||||
|
<el-tabs
|
||||||
|
v-if="aiEnabled"
|
||||||
|
ref="tabs"
|
||||||
|
v-model="activeTab"
|
||||||
|
type="card"
|
||||||
|
:before-leave="onBeforeTabLeave"
|
||||||
|
>
|
||||||
|
<el-tab-pane
|
||||||
|
:label="$locale.baseText('codeNodeEditor.tabs.code')"
|
||||||
|
name="code"
|
||||||
|
data-test-id="code-node-tab-code"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref="codeNodeEditorRef"
|
||||||
|
:class="['ph-no-capture', 'code-editor-tabs', $style.editorInput]"
|
||||||
|
/>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane
|
||||||
|
:label="$locale.baseText('codeNodeEditor.tabs.askAi')"
|
||||||
|
name="ask-ai"
|
||||||
|
data-test-id="code-node-tab-ai"
|
||||||
|
>
|
||||||
|
<!-- Key the AskAI tab to make sure it re-mounts when changing tabs -->
|
||||||
|
<AskAI
|
||||||
|
:key="activeTab"
|
||||||
|
:has-changes="hasChanges"
|
||||||
|
@replace-code="onReplaceCode"
|
||||||
|
@started-loading="onAiLoadStart"
|
||||||
|
@finished-loading="onAiLoadEnd"
|
||||||
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
<!-- If AskAi not enabled, there's no point in rendering tabs -->
|
||||||
|
<div v-else :class="$style.fillHeight">
|
||||||
|
<div ref="codeNodeEditorRef" :class="['ph-no-capture', $style.fillHeight]" />
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
:deep(.el-tabs) {
|
:deep(.el-tabs) {
|
||||||
.code-editor-tabs .cm-editor {
|
.code-editor-tabs .cm-editor {
|
||||||
|
|||||||
@@ -1,53 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div class="collection-parameter" @keydown.stop>
|
|
||||||
<div class="collection-parameter-wrapper">
|
|
||||||
<div v-if="getProperties.length === 0" class="no-items-exist">
|
|
||||||
<n8n-text size="small">{{ $locale.baseText('collectionParameter.noProperties') }}</n8n-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Suspense>
|
|
||||||
<ParameterInputList
|
|
||||||
:parameters="getProperties"
|
|
||||||
:node-values="nodeValues"
|
|
||||||
:path="path"
|
|
||||||
:hide-delete="hideDelete"
|
|
||||||
:indent="true"
|
|
||||||
:is-read-only="isReadOnly"
|
|
||||||
@value-changed="valueChanged"
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
|
|
||||||
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="param-options">
|
|
||||||
<n8n-button
|
|
||||||
v-if="(parameter.options ?? []).length === 1"
|
|
||||||
type="tertiary"
|
|
||||||
block
|
|
||||||
:label="getPlaceholderText"
|
|
||||||
@click="optionSelected((parameter.options ?? [])[0].name)"
|
|
||||||
/>
|
|
||||||
<div v-else class="add-option">
|
|
||||||
<n8n-select
|
|
||||||
v-model="selectedOption"
|
|
||||||
:placeholder="getPlaceholderText"
|
|
||||||
size="small"
|
|
||||||
filterable
|
|
||||||
@update:model-value="optionSelected"
|
|
||||||
>
|
|
||||||
<n8n-option
|
|
||||||
v-for="item in parameterOptions"
|
|
||||||
:key="item.name"
|
|
||||||
:label="getParameterOptionLabel(item)"
|
|
||||||
:value="item.name"
|
|
||||||
data-test-id="collection-parameter-option"
|
|
||||||
>
|
|
||||||
</n8n-option>
|
|
||||||
</n8n-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import type { IUpdateInformation } from '@/Interface';
|
import type { IUpdateInformation } from '@/Interface';
|
||||||
@@ -210,6 +160,56 @@ function valueChanged(parameterData: IUpdateInformation) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="collection-parameter" @keydown.stop>
|
||||||
|
<div class="collection-parameter-wrapper">
|
||||||
|
<div v-if="getProperties.length === 0" class="no-items-exist">
|
||||||
|
<n8n-text size="small">{{ $locale.baseText('collectionParameter.noProperties') }}</n8n-text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Suspense>
|
||||||
|
<ParameterInputList
|
||||||
|
:parameters="getProperties"
|
||||||
|
:node-values="nodeValues"
|
||||||
|
:path="path"
|
||||||
|
:hide-delete="hideDelete"
|
||||||
|
:indent="true"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
|
@value-changed="valueChanged"
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
|
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="param-options">
|
||||||
|
<n8n-button
|
||||||
|
v-if="(parameter.options ?? []).length === 1"
|
||||||
|
type="tertiary"
|
||||||
|
block
|
||||||
|
:label="getPlaceholderText"
|
||||||
|
@click="optionSelected((parameter.options ?? [])[0].name)"
|
||||||
|
/>
|
||||||
|
<div v-else class="add-option">
|
||||||
|
<n8n-select
|
||||||
|
v-model="selectedOption"
|
||||||
|
:placeholder="getPlaceholderText"
|
||||||
|
size="small"
|
||||||
|
filterable
|
||||||
|
@update:model-value="optionSelected"
|
||||||
|
>
|
||||||
|
<n8n-option
|
||||||
|
v-for="item in parameterOptions"
|
||||||
|
:key="item.name"
|
||||||
|
:label="getParameterOptionLabel(item)"
|
||||||
|
:value="item.name"
|
||||||
|
data-test-id="collection-parameter-option"
|
||||||
|
>
|
||||||
|
</n8n-option>
|
||||||
|
</n8n-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.collection-parameter {
|
.collection-parameter {
|
||||||
padding-left: var(--spacing-s);
|
padding-left: var(--spacing-s);
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps<{
|
||||||
|
loading: boolean;
|
||||||
|
title?: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n8n-card :class="$style.card" v-bind="$attrs">
|
<n8n-card :class="$style.card" v-bind="$attrs">
|
||||||
<template v-if="!loading && title" #header>
|
<template v-if="!loading && title" #header>
|
||||||
@@ -10,13 +17,6 @@
|
|||||||
</n8n-card>
|
</n8n-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
defineProps<{
|
|
||||||
loading: boolean;
|
|
||||||
title?: string;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.card {
|
.card {
|
||||||
min-width: 235px;
|
min-width: 235px;
|
||||||
|
|||||||
@@ -1,66 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.cardContainer" data-test-id="community-package-card">
|
|
||||||
<div v-if="loading" :class="$style.cardSkeleton">
|
|
||||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
|
||||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
|
||||||
</div>
|
|
||||||
<div v-else-if="communityPackage" :class="$style.packageCard">
|
|
||||||
<div :class="$style.cardInfoContainer">
|
|
||||||
<div :class="$style.cardTitle">
|
|
||||||
<n8n-text :bold="true" size="large">{{ communityPackage.packageName }}</n8n-text>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.cardSubtitle">
|
|
||||||
<n8n-text :bold="true" size="small" color="text-light">
|
|
||||||
{{
|
|
||||||
$locale.baseText('settings.communityNodes.packageNodes.label', {
|
|
||||||
adjustToNumber: communityPackage.installedNodes.length,
|
|
||||||
})
|
|
||||||
}}:
|
|
||||||
</n8n-text>
|
|
||||||
<n8n-text size="small" color="text-light">
|
|
||||||
<span v-for="(node, index) in communityPackage.installedNodes" :key="node.name">
|
|
||||||
{{ node.name
|
|
||||||
}}<span v-if="index != communityPackage.installedNodes.length - 1">,</span>
|
|
||||||
</span>
|
|
||||||
</n8n-text>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.cardControlsContainer">
|
|
||||||
<n8n-text :bold="true" size="large" color="text-light">
|
|
||||||
v{{ communityPackage.installedVersion }}
|
|
||||||
</n8n-text>
|
|
||||||
<n8n-tooltip v-if="communityPackage.failedLoading === true" placement="top">
|
|
||||||
<template #content>
|
|
||||||
<div>
|
|
||||||
{{ $locale.baseText('settings.communityNodes.failedToLoad.tooltip') }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<n8n-icon icon="exclamation-triangle" color="danger" size="large" />
|
|
||||||
</n8n-tooltip>
|
|
||||||
<n8n-tooltip v-else-if="communityPackage.updateAvailable" placement="top">
|
|
||||||
<template #content>
|
|
||||||
<div>
|
|
||||||
{{ $locale.baseText('settings.communityNodes.updateAvailable.tooltip') }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<n8n-button outline label="Update" @click="onUpdateClick" />
|
|
||||||
</n8n-tooltip>
|
|
||||||
<n8n-tooltip v-else placement="top">
|
|
||||||
<template #content>
|
|
||||||
<div>
|
|
||||||
{{ $locale.baseText('settings.communityNodes.upToDate.tooltip') }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<n8n-icon icon="check-circle" color="text-light" size="large" />
|
|
||||||
</n8n-tooltip>
|
|
||||||
<div :class="$style.cardActions">
|
|
||||||
<n8n-action-toggle :actions="packageActions" @action="onAction"></n8n-action-toggle>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import type { PublicInstalledPackage } from 'n8n-workflow';
|
import type { PublicInstalledPackage } from 'n8n-workflow';
|
||||||
@@ -125,6 +62,69 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.cardContainer" data-test-id="community-package-card">
|
||||||
|
<div v-if="loading" :class="$style.cardSkeleton">
|
||||||
|
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||||
|
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="communityPackage" :class="$style.packageCard">
|
||||||
|
<div :class="$style.cardInfoContainer">
|
||||||
|
<div :class="$style.cardTitle">
|
||||||
|
<n8n-text :bold="true" size="large">{{ communityPackage.packageName }}</n8n-text>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.cardSubtitle">
|
||||||
|
<n8n-text :bold="true" size="small" color="text-light">
|
||||||
|
{{
|
||||||
|
$locale.baseText('settings.communityNodes.packageNodes.label', {
|
||||||
|
adjustToNumber: communityPackage.installedNodes.length,
|
||||||
|
})
|
||||||
|
}}:
|
||||||
|
</n8n-text>
|
||||||
|
<n8n-text size="small" color="text-light">
|
||||||
|
<span v-for="(node, index) in communityPackage.installedNodes" :key="node.name">
|
||||||
|
{{ node.name
|
||||||
|
}}<span v-if="index != communityPackage.installedNodes.length - 1">,</span>
|
||||||
|
</span>
|
||||||
|
</n8n-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.cardControlsContainer">
|
||||||
|
<n8n-text :bold="true" size="large" color="text-light">
|
||||||
|
v{{ communityPackage.installedVersion }}
|
||||||
|
</n8n-text>
|
||||||
|
<n8n-tooltip v-if="communityPackage.failedLoading === true" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
{{ $locale.baseText('settings.communityNodes.failedToLoad.tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n8n-icon icon="exclamation-triangle" color="danger" size="large" />
|
||||||
|
</n8n-tooltip>
|
||||||
|
<n8n-tooltip v-else-if="communityPackage.updateAvailable" placement="top">
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
{{ $locale.baseText('settings.communityNodes.updateAvailable.tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n8n-button outline label="Update" @click="onUpdateClick" />
|
||||||
|
</n8n-tooltip>
|
||||||
|
<n8n-tooltip v-else placement="top">
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
{{ $locale.baseText('settings.communityNodes.upToDate.tooltip') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<n8n-icon icon="check-circle" color="text-light" size="large" />
|
||||||
|
</n8n-tooltip>
|
||||||
|
<div :class="$style.cardActions">
|
||||||
|
<n8n-action-toggle :actions="packageActions" @action="onAction"></n8n-action-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.cardContainer {
|
.cardContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,95 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
width="540px"
|
|
||||||
:name="COMMUNITY_PACKAGE_INSTALL_MODAL_KEY"
|
|
||||||
:title="$locale.baseText('settings.communityNodes.installModal.title')"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
:center="true"
|
|
||||||
:before-close="onModalClose"
|
|
||||||
:show-close="!loading"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div :class="[$style.descriptionContainer, 'p-s']">
|
|
||||||
<div>
|
|
||||||
<n8n-text>
|
|
||||||
{{ $locale.baseText('settings.communityNodes.installModal.description') }}
|
|
||||||
</n8n-text>
|
|
||||||
{{ ' ' }}
|
|
||||||
<n8n-link :to="COMMUNITY_NODES_INSTALLATION_DOCS_URL" @click="onMoreInfoTopClick">
|
|
||||||
{{ $locale.baseText('generic.moreInfo') }}
|
|
||||||
</n8n-link>
|
|
||||||
</div>
|
|
||||||
<n8n-button
|
|
||||||
:label="$locale.baseText('settings.communityNodes.browseButton.label')"
|
|
||||||
icon="external-link-alt"
|
|
||||||
:class="$style.browseButton"
|
|
||||||
@click="openNPMPage"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div :class="[$style.formContainer, 'mt-m']">
|
|
||||||
<n8n-input-label
|
|
||||||
:class="$style.labelTooltip"
|
|
||||||
:label="$locale.baseText('settings.communityNodes.installModal.packageName.label')"
|
|
||||||
:tooltip-text="
|
|
||||||
$locale.baseText('settings.communityNodes.installModal.packageName.tooltip', {
|
|
||||||
interpolate: { npmURL: NPM_KEYWORD_SEARCH_URL },
|
|
||||||
})
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<n8n-input
|
|
||||||
v-model="packageName"
|
|
||||||
name="packageNameInput"
|
|
||||||
type="text"
|
|
||||||
:maxlength="214"
|
|
||||||
:placeholder="
|
|
||||||
$locale.baseText('settings.communityNodes.installModal.packageName.placeholder')
|
|
||||||
"
|
|
||||||
:required="true"
|
|
||||||
:disabled="loading"
|
|
||||||
@blur="onInputBlur"
|
|
||||||
/>
|
|
||||||
</n8n-input-label>
|
|
||||||
<div :class="[$style.infoText, 'mt-4xs']">
|
|
||||||
<span
|
|
||||||
size="small"
|
|
||||||
:class="[$style.infoText, infoTextErrorMessage ? $style.error : '']"
|
|
||||||
v-text="infoTextErrorMessage"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<el-checkbox
|
|
||||||
v-model="userAgreed"
|
|
||||||
:class="[$style.checkbox, checkboxWarning ? $style.error : '', 'mt-l']"
|
|
||||||
:disabled="loading"
|
|
||||||
data-test-id="user-agreement-checkbox"
|
|
||||||
@update:model-value="onCheckboxChecked"
|
|
||||||
>
|
|
||||||
<n8n-text>
|
|
||||||
{{ $locale.baseText('settings.communityNodes.installModal.checkbox.label') }} </n8n-text
|
|
||||||
><br />
|
|
||||||
<n8n-link :to="COMMUNITY_NODES_RISKS_DOCS_URL" @click="onLearnMoreLinkClick">{{
|
|
||||||
$locale.baseText('generic.moreInfo')
|
|
||||||
}}</n8n-link>
|
|
||||||
</el-checkbox>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<n8n-button
|
|
||||||
:loading="loading"
|
|
||||||
:disabled="!userAgreed || packageName === '' || loading"
|
|
||||||
:label="
|
|
||||||
loading
|
|
||||||
? $locale.baseText('settings.communityNodes.installModal.installButton.label.loading')
|
|
||||||
: $locale.baseText('settings.communityNodes.installModal.installButton.label')
|
|
||||||
"
|
|
||||||
size="large"
|
|
||||||
float="right"
|
|
||||||
data-test-id="install-community-package-button"
|
|
||||||
@click="onInstallClick"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
@@ -189,6 +97,98 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
width="540px"
|
||||||
|
:name="COMMUNITY_PACKAGE_INSTALL_MODAL_KEY"
|
||||||
|
:title="$locale.baseText('settings.communityNodes.installModal.title')"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
:center="true"
|
||||||
|
:before-close="onModalClose"
|
||||||
|
:show-close="!loading"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div :class="[$style.descriptionContainer, 'p-s']">
|
||||||
|
<div>
|
||||||
|
<n8n-text>
|
||||||
|
{{ $locale.baseText('settings.communityNodes.installModal.description') }}
|
||||||
|
</n8n-text>
|
||||||
|
{{ ' ' }}
|
||||||
|
<n8n-link :to="COMMUNITY_NODES_INSTALLATION_DOCS_URL" @click="onMoreInfoTopClick">
|
||||||
|
{{ $locale.baseText('generic.moreInfo') }}
|
||||||
|
</n8n-link>
|
||||||
|
</div>
|
||||||
|
<n8n-button
|
||||||
|
:label="$locale.baseText('settings.communityNodes.browseButton.label')"
|
||||||
|
icon="external-link-alt"
|
||||||
|
:class="$style.browseButton"
|
||||||
|
@click="openNPMPage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div :class="[$style.formContainer, 'mt-m']">
|
||||||
|
<n8n-input-label
|
||||||
|
:class="$style.labelTooltip"
|
||||||
|
:label="$locale.baseText('settings.communityNodes.installModal.packageName.label')"
|
||||||
|
:tooltip-text="
|
||||||
|
$locale.baseText('settings.communityNodes.installModal.packageName.tooltip', {
|
||||||
|
interpolate: { npmURL: NPM_KEYWORD_SEARCH_URL },
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<n8n-input
|
||||||
|
v-model="packageName"
|
||||||
|
name="packageNameInput"
|
||||||
|
type="text"
|
||||||
|
:maxlength="214"
|
||||||
|
:placeholder="
|
||||||
|
$locale.baseText('settings.communityNodes.installModal.packageName.placeholder')
|
||||||
|
"
|
||||||
|
:required="true"
|
||||||
|
:disabled="loading"
|
||||||
|
@blur="onInputBlur"
|
||||||
|
/>
|
||||||
|
</n8n-input-label>
|
||||||
|
<div :class="[$style.infoText, 'mt-4xs']">
|
||||||
|
<span
|
||||||
|
size="small"
|
||||||
|
:class="[$style.infoText, infoTextErrorMessage ? $style.error : '']"
|
||||||
|
v-text="infoTextErrorMessage"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
<el-checkbox
|
||||||
|
v-model="userAgreed"
|
||||||
|
:class="[$style.checkbox, checkboxWarning ? $style.error : '', 'mt-l']"
|
||||||
|
:disabled="loading"
|
||||||
|
data-test-id="user-agreement-checkbox"
|
||||||
|
@update:model-value="onCheckboxChecked"
|
||||||
|
>
|
||||||
|
<n8n-text>
|
||||||
|
{{ $locale.baseText('settings.communityNodes.installModal.checkbox.label') }} </n8n-text
|
||||||
|
><br />
|
||||||
|
<n8n-link :to="COMMUNITY_NODES_RISKS_DOCS_URL" @click="onLearnMoreLinkClick">{{
|
||||||
|
$locale.baseText('generic.moreInfo')
|
||||||
|
}}</n8n-link>
|
||||||
|
</el-checkbox>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<n8n-button
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="!userAgreed || packageName === '' || loading"
|
||||||
|
:label="
|
||||||
|
loading
|
||||||
|
? $locale.baseText('settings.communityNodes.installModal.installButton.label.loading')
|
||||||
|
: $locale.baseText('settings.communityNodes.installModal.installButton.label')
|
||||||
|
"
|
||||||
|
size="large"
|
||||||
|
float="right"
|
||||||
|
data-test-id="install-community-package-button"
|
||||||
|
@click="onInstallClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.descriptionContainer {
|
.descriptionContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,37 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
width="540px"
|
|
||||||
:name="COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY"
|
|
||||||
:title="getModalContent.title"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
:center="true"
|
|
||||||
:show-close="!loading"
|
|
||||||
:before-close="onModalClose"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<n8n-text>{{ getModalContent.message }}</n8n-text>
|
|
||||||
<div
|
|
||||||
v-if="mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE"
|
|
||||||
:class="$style.descriptionContainer"
|
|
||||||
>
|
|
||||||
<n8n-info-tip theme="info" type="note" :bold="false">
|
|
||||||
<span v-text="getModalContent.description"></span>
|
|
||||||
</n8n-info-tip>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<n8n-button
|
|
||||||
:loading="loading"
|
|
||||||
:disabled="loading"
|
|
||||||
:label="loading ? getModalContent.buttonLoadingLabel : getModalContent.buttonLabel"
|
|
||||||
size="large"
|
|
||||||
float="right"
|
|
||||||
@click="onConfirmButtonClick"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import Modal from '@/components/Modal.vue';
|
import Modal from '@/components/Modal.vue';
|
||||||
@@ -194,6 +160,40 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
width="540px"
|
||||||
|
:name="COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY"
|
||||||
|
:title="getModalContent.title"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
:center="true"
|
||||||
|
:show-close="!loading"
|
||||||
|
:before-close="onModalClose"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<n8n-text>{{ getModalContent.message }}</n8n-text>
|
||||||
|
<div
|
||||||
|
v-if="mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE"
|
||||||
|
:class="$style.descriptionContainer"
|
||||||
|
>
|
||||||
|
<n8n-info-tip theme="info" type="note" :bold="false">
|
||||||
|
<span v-text="getModalContent.description"></span>
|
||||||
|
</n8n-info-tip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<n8n-button
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="loading"
|
||||||
|
:label="loading ? getModalContent.buttonLoadingLabel : getModalContent.buttonLabel"
|
||||||
|
size="large"
|
||||||
|
float="right"
|
||||||
|
@click="onConfirmButtonClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.descriptionContainer {
|
.descriptionContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,37 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="modalName"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
:center="true"
|
|
||||||
:close-on-press-escape="false"
|
|
||||||
:before-close="closeDialog"
|
|
||||||
custom-class="contact-prompt-modal"
|
|
||||||
width="460px"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<n8n-heading tag="h2" size="xlarge" color="text-dark">{{ title }}</n8n-heading>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<div :class="$style.description">
|
|
||||||
<n8n-text size="medium" color="text-base">{{ description }}</n8n-text>
|
|
||||||
</div>
|
|
||||||
<div @keyup.enter="send">
|
|
||||||
<n8n-input v-model="email" placeholder="Your email address" />
|
|
||||||
</div>
|
|
||||||
<div :class="$style.disclaimer">
|
|
||||||
<n8n-text size="small" color="text-base"
|
|
||||||
>David from our product team will get in touch personally</n8n-text
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<div :class="$style.footer">
|
|
||||||
<n8n-button label="Send" float="right" :disabled="!isEmailValid" @click="send" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
@@ -118,6 +84,40 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="modalName"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
:center="true"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
custom-class="contact-prompt-modal"
|
||||||
|
width="460px"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<n8n-heading tag="h2" size="xlarge" color="text-dark">{{ title }}</n8n-heading>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div :class="$style.description">
|
||||||
|
<n8n-text size="medium" color="text-base">{{ description }}</n8n-text>
|
||||||
|
</div>
|
||||||
|
<div @keyup.enter="send">
|
||||||
|
<n8n-input v-model="email" placeholder="Your email address" />
|
||||||
|
</div>
|
||||||
|
<div :class="$style.disclaimer">
|
||||||
|
<n8n-text size="small" color="text-base"
|
||||||
|
>David from our product team will get in touch personally</n8n-text
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div :class="$style.footer">
|
||||||
|
<n8n-button label="Send" float="right" :disabled="!isEmailValid" @click="send" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.description {
|
.description {
|
||||||
margin-bottom: var(--spacing-s);
|
margin-bottom: var(--spacing-s);
|
||||||
|
|||||||
@@ -1,27 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<n8n-input-label :label="label">
|
|
||||||
<div
|
|
||||||
:class="{
|
|
||||||
[$style.copyText]: true,
|
|
||||||
[$style[size]]: true,
|
|
||||||
[$style.collapsed]: collapse,
|
|
||||||
[$style.noHover]: disableCopy,
|
|
||||||
'ph-no-capture': redactValue,
|
|
||||||
}"
|
|
||||||
data-test-id="copy-input"
|
|
||||||
@click="copy"
|
|
||||||
>
|
|
||||||
<span ref="copyInputValue">{{ value }}</span>
|
|
||||||
<div v-if="!disableCopy" :class="$style.copyButton">
|
|
||||||
<span>{{ copyButtonText }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</n8n-input-label>
|
|
||||||
<div v-if="hint" :class="$style.hint">{{ hint }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useClipboard } from '@/composables/useClipboard';
|
import { useClipboard } from '@/composables/useClipboard';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
@@ -71,6 +47,30 @@ function copy() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<n8n-input-label :label="label">
|
||||||
|
<div
|
||||||
|
:class="{
|
||||||
|
[$style.copyText]: true,
|
||||||
|
[$style[size]]: true,
|
||||||
|
[$style.collapsed]: collapse,
|
||||||
|
[$style.noHover]: disableCopy,
|
||||||
|
'ph-no-capture': redactValue,
|
||||||
|
}"
|
||||||
|
data-test-id="copy-input"
|
||||||
|
@click="copy"
|
||||||
|
>
|
||||||
|
<span ref="copyInputValue">{{ value }}</span>
|
||||||
|
<div v-if="!disableCopy" :class="$style.copyButton">
|
||||||
|
<span>{{ copyButtonText }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n8n-input-label>
|
||||||
|
<div v-if="hint" :class="$style.hint">{{ hint }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.copyText {
|
.copyText {
|
||||||
span {
|
span {
|
||||||
|
|||||||
@@ -1,161 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div :class="$style.config" data-test-id="node-credentials-config-container">
|
|
||||||
<Banner
|
|
||||||
v-show="showValidationWarning"
|
|
||||||
theme="danger"
|
|
||||||
:message="
|
|
||||||
$locale.baseText(
|
|
||||||
`credentialEdit.credentialConfig.pleaseCheckTheErrorsBelow${
|
|
||||||
credentialPermissions.update ? '' : '.sharee'
|
|
||||||
}`,
|
|
||||||
{ interpolate: { owner: credentialOwnerName } },
|
|
||||||
)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Banner
|
|
||||||
v-if="authError && !showValidationWarning"
|
|
||||||
theme="danger"
|
|
||||||
:message="
|
|
||||||
$locale.baseText(
|
|
||||||
`credentialEdit.credentialConfig.couldntConnectWithTheseSettings${
|
|
||||||
credentialPermissions.update ? '' : '.sharee'
|
|
||||||
}`,
|
|
||||||
{ interpolate: { owner: credentialOwnerName } },
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:details="authError"
|
|
||||||
:button-label="$locale.baseText('credentialEdit.credentialConfig.retry')"
|
|
||||||
button-loading-label="Retrying"
|
|
||||||
:button-title="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
|
|
||||||
:button-loading="isRetesting"
|
|
||||||
@click="$emit('retest')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Banner
|
|
||||||
v-show="showOAuthSuccessBanner && !showValidationWarning"
|
|
||||||
theme="success"
|
|
||||||
:message="$locale.baseText('credentialEdit.credentialConfig.accountConnected')"
|
|
||||||
:button-label="$locale.baseText('credentialEdit.credentialConfig.reconnect')"
|
|
||||||
:button-title="
|
|
||||||
$locale.baseText('credentialEdit.credentialConfig.reconnectOAuth2Credential')
|
|
||||||
"
|
|
||||||
data-test-id="oauth-connect-success-banner"
|
|
||||||
@click="$emit('oauth')"
|
|
||||||
>
|
|
||||||
<template v-if="isGoogleOAuthType" #button>
|
|
||||||
<p
|
|
||||||
:class="$style.googleReconnectLabel"
|
|
||||||
v-text="`${$locale.baseText('credentialEdit.credentialConfig.reconnect')}:`"
|
|
||||||
/>
|
|
||||||
<GoogleAuthButton @click="$emit('oauth')" />
|
|
||||||
</template>
|
|
||||||
</Banner>
|
|
||||||
|
|
||||||
<Banner
|
|
||||||
v-show="testedSuccessfully && !showValidationWarning"
|
|
||||||
theme="success"
|
|
||||||
:message="$locale.baseText('credentialEdit.credentialConfig.connectionTestedSuccessfully')"
|
|
||||||
:button-label="$locale.baseText('credentialEdit.credentialConfig.retry')"
|
|
||||||
:button-loading-label="$locale.baseText('credentialEdit.credentialConfig.retrying')"
|
|
||||||
:button-title="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
|
|
||||||
:button-loading="isRetesting"
|
|
||||||
data-test-id="credentials-config-container-test-success"
|
|
||||||
@click="$emit('retest')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<template v-if="credentialPermissions.update">
|
|
||||||
<n8n-notice v-if="documentationUrl && credentialProperties.length && !docs" theme="warning">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialConfig.needHelpFillingOutTheseFields') }}
|
|
||||||
<span class="ml-4xs">
|
|
||||||
<n8n-link :to="documentationUrl" size="small" bold @click="onDocumentationUrlClick">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialConfig.openDocs') }}
|
|
||||||
</n8n-link>
|
|
||||||
</span>
|
|
||||||
</n8n-notice>
|
|
||||||
|
|
||||||
<AuthTypeSelector
|
|
||||||
v-if="showAuthTypeSelector && isNewCredential"
|
|
||||||
:credential-type="credentialType"
|
|
||||||
@auth-type-changed="onAuthTypeChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<CopyInput
|
|
||||||
v-if="isOAuthType && !allOAuth2BasePropertiesOverridden"
|
|
||||||
:label="$locale.baseText('credentialEdit.credentialConfig.oAuthRedirectUrl')"
|
|
||||||
:value="oAuthCallbackUrl"
|
|
||||||
:copy-button-text="$locale.baseText('credentialEdit.credentialConfig.clickToCopy')"
|
|
||||||
:hint="
|
|
||||||
$locale.baseText('credentialEdit.credentialConfig.subtitle', {
|
|
||||||
interpolate: { appName },
|
|
||||||
})
|
|
||||||
"
|
|
||||||
:toast-title="
|
|
||||||
$locale.baseText('credentialEdit.credentialConfig.redirectUrlCopiedToClipboard')
|
|
||||||
"
|
|
||||||
:redact-value="true"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<EnterpriseEdition v-else :features="[EnterpriseEditionFeature.Sharing]">
|
|
||||||
<div>
|
|
||||||
<n8n-info-tip :bold="false">
|
|
||||||
{{
|
|
||||||
$locale.baseText('credentialEdit.credentialEdit.info.sharee', {
|
|
||||||
interpolate: { credentialOwnerName },
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</n8n-info-tip>
|
|
||||||
</div>
|
|
||||||
</EnterpriseEdition>
|
|
||||||
|
|
||||||
<CredentialInputs
|
|
||||||
v-if="credentialType && credentialPermissions.update"
|
|
||||||
:credential-data="credentialData"
|
|
||||||
:credential-properties="credentialProperties"
|
|
||||||
:documentation-url="documentationUrl"
|
|
||||||
:show-validation-warnings="showValidationWarning"
|
|
||||||
@update="onDataChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<OauthButton
|
|
||||||
v-if="
|
|
||||||
isOAuthType &&
|
|
||||||
requiredPropertiesFilled &&
|
|
||||||
!isOAuthConnected &&
|
|
||||||
credentialPermissions.update
|
|
||||||
"
|
|
||||||
:is-google-o-auth-type="isGoogleOAuthType"
|
|
||||||
data-test-id="oauth-connect-button"
|
|
||||||
@click="$emit('oauth')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<n8n-text v-if="isMissingCredentials" color="text-base" size="medium">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialConfig.missingCredentialType') }}
|
|
||||||
</n8n-text>
|
|
||||||
|
|
||||||
<EnterpriseEdition :features="[EnterpriseEditionFeature.ExternalSecrets]">
|
|
||||||
<template #fallback>
|
|
||||||
<n8n-info-tip class="mt-s">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialConfig.externalSecrets') }}
|
|
||||||
<n8n-link bold :to="$locale.baseText('settings.externalSecrets.docs')" size="small">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialConfig.externalSecrets.moreInfo') }}
|
|
||||||
</n8n-link>
|
|
||||||
</n8n-info-tip>
|
|
||||||
</template>
|
|
||||||
</EnterpriseEdition>
|
|
||||||
</div>
|
|
||||||
<CredentialDocs
|
|
||||||
v-if="docs"
|
|
||||||
:credential-type="credentialType"
|
|
||||||
:documentation-url="documentationUrl"
|
|
||||||
:docs="docs"
|
|
||||||
:class="$style.docs"
|
|
||||||
>
|
|
||||||
</CredentialDocs>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeMount, watch } from 'vue';
|
import { computed, onBeforeMount, watch } from 'vue';
|
||||||
|
|
||||||
@@ -345,6 +187,164 @@ watch(showOAuthSuccessBanner, (newValue, oldValue) => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div :class="$style.config" data-test-id="node-credentials-config-container">
|
||||||
|
<Banner
|
||||||
|
v-show="showValidationWarning"
|
||||||
|
theme="danger"
|
||||||
|
:message="
|
||||||
|
$locale.baseText(
|
||||||
|
`credentialEdit.credentialConfig.pleaseCheckTheErrorsBelow${
|
||||||
|
credentialPermissions.update ? '' : '.sharee'
|
||||||
|
}`,
|
||||||
|
{ interpolate: { owner: credentialOwnerName } },
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Banner
|
||||||
|
v-if="authError && !showValidationWarning"
|
||||||
|
theme="danger"
|
||||||
|
:message="
|
||||||
|
$locale.baseText(
|
||||||
|
`credentialEdit.credentialConfig.couldntConnectWithTheseSettings${
|
||||||
|
credentialPermissions.update ? '' : '.sharee'
|
||||||
|
}`,
|
||||||
|
{ interpolate: { owner: credentialOwnerName } },
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:details="authError"
|
||||||
|
:button-label="$locale.baseText('credentialEdit.credentialConfig.retry')"
|
||||||
|
button-loading-label="Retrying"
|
||||||
|
:button-title="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
|
||||||
|
:button-loading="isRetesting"
|
||||||
|
@click="$emit('retest')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Banner
|
||||||
|
v-show="showOAuthSuccessBanner && !showValidationWarning"
|
||||||
|
theme="success"
|
||||||
|
:message="$locale.baseText('credentialEdit.credentialConfig.accountConnected')"
|
||||||
|
:button-label="$locale.baseText('credentialEdit.credentialConfig.reconnect')"
|
||||||
|
:button-title="
|
||||||
|
$locale.baseText('credentialEdit.credentialConfig.reconnectOAuth2Credential')
|
||||||
|
"
|
||||||
|
data-test-id="oauth-connect-success-banner"
|
||||||
|
@click="$emit('oauth')"
|
||||||
|
>
|
||||||
|
<template v-if="isGoogleOAuthType" #button>
|
||||||
|
<p
|
||||||
|
:class="$style.googleReconnectLabel"
|
||||||
|
v-text="`${$locale.baseText('credentialEdit.credentialConfig.reconnect')}:`"
|
||||||
|
/>
|
||||||
|
<GoogleAuthButton @click="$emit('oauth')" />
|
||||||
|
</template>
|
||||||
|
</Banner>
|
||||||
|
|
||||||
|
<Banner
|
||||||
|
v-show="testedSuccessfully && !showValidationWarning"
|
||||||
|
theme="success"
|
||||||
|
:message="$locale.baseText('credentialEdit.credentialConfig.connectionTestedSuccessfully')"
|
||||||
|
:button-label="$locale.baseText('credentialEdit.credentialConfig.retry')"
|
||||||
|
:button-loading-label="$locale.baseText('credentialEdit.credentialConfig.retrying')"
|
||||||
|
:button-title="$locale.baseText('credentialEdit.credentialConfig.retryCredentialTest')"
|
||||||
|
:button-loading="isRetesting"
|
||||||
|
data-test-id="credentials-config-container-test-success"
|
||||||
|
@click="$emit('retest')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<template v-if="credentialPermissions.update">
|
||||||
|
<n8n-notice v-if="documentationUrl && credentialProperties.length && !docs" theme="warning">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialConfig.needHelpFillingOutTheseFields') }}
|
||||||
|
<span class="ml-4xs">
|
||||||
|
<n8n-link :to="documentationUrl" size="small" bold @click="onDocumentationUrlClick">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialConfig.openDocs') }}
|
||||||
|
</n8n-link>
|
||||||
|
</span>
|
||||||
|
</n8n-notice>
|
||||||
|
|
||||||
|
<AuthTypeSelector
|
||||||
|
v-if="showAuthTypeSelector && isNewCredential"
|
||||||
|
:credential-type="credentialType"
|
||||||
|
@auth-type-changed="onAuthTypeChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CopyInput
|
||||||
|
v-if="isOAuthType && !allOAuth2BasePropertiesOverridden"
|
||||||
|
:label="$locale.baseText('credentialEdit.credentialConfig.oAuthRedirectUrl')"
|
||||||
|
:value="oAuthCallbackUrl"
|
||||||
|
:copy-button-text="$locale.baseText('credentialEdit.credentialConfig.clickToCopy')"
|
||||||
|
:hint="
|
||||||
|
$locale.baseText('credentialEdit.credentialConfig.subtitle', {
|
||||||
|
interpolate: { appName },
|
||||||
|
})
|
||||||
|
"
|
||||||
|
:toast-title="
|
||||||
|
$locale.baseText('credentialEdit.credentialConfig.redirectUrlCopiedToClipboard')
|
||||||
|
"
|
||||||
|
:redact-value="true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<EnterpriseEdition v-else :features="[EnterpriseEditionFeature.Sharing]">
|
||||||
|
<div>
|
||||||
|
<n8n-info-tip :bold="false">
|
||||||
|
{{
|
||||||
|
$locale.baseText('credentialEdit.credentialEdit.info.sharee', {
|
||||||
|
interpolate: { credentialOwnerName },
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</n8n-info-tip>
|
||||||
|
</div>
|
||||||
|
</EnterpriseEdition>
|
||||||
|
|
||||||
|
<CredentialInputs
|
||||||
|
v-if="credentialType && credentialPermissions.update"
|
||||||
|
:credential-data="credentialData"
|
||||||
|
:credential-properties="credentialProperties"
|
||||||
|
:documentation-url="documentationUrl"
|
||||||
|
:show-validation-warnings="showValidationWarning"
|
||||||
|
@update="onDataChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<OauthButton
|
||||||
|
v-if="
|
||||||
|
isOAuthType &&
|
||||||
|
requiredPropertiesFilled &&
|
||||||
|
!isOAuthConnected &&
|
||||||
|
credentialPermissions.update
|
||||||
|
"
|
||||||
|
:is-google-o-auth-type="isGoogleOAuthType"
|
||||||
|
data-test-id="oauth-connect-button"
|
||||||
|
@click="$emit('oauth')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<n8n-text v-if="isMissingCredentials" color="text-base" size="medium">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialConfig.missingCredentialType') }}
|
||||||
|
</n8n-text>
|
||||||
|
|
||||||
|
<EnterpriseEdition :features="[EnterpriseEditionFeature.ExternalSecrets]">
|
||||||
|
<template #fallback>
|
||||||
|
<n8n-info-tip class="mt-s">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialConfig.externalSecrets') }}
|
||||||
|
<n8n-link bold :to="$locale.baseText('settings.externalSecrets.docs')" size="small">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialConfig.externalSecrets.moreInfo') }}
|
||||||
|
</n8n-link>
|
||||||
|
</n8n-info-tip>
|
||||||
|
</template>
|
||||||
|
</EnterpriseEdition>
|
||||||
|
</div>
|
||||||
|
<CredentialDocs
|
||||||
|
v-if="docs"
|
||||||
|
:credential-type="credentialType"
|
||||||
|
:documentation-url="documentationUrl"
|
||||||
|
:docs="docs"
|
||||||
|
:class="$style.docs"
|
||||||
|
>
|
||||||
|
</CredentialDocs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.config {
|
.config {
|
||||||
--notice-margin: 0;
|
--notice-margin: 0;
|
||||||
|
|||||||
@@ -1,27 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.docs">
|
|
||||||
<div :class="$style.header">
|
|
||||||
<p :class="$style.title">{{ i18n.baseText('credentialEdit.credentialEdit.setupGuide') }}</p>
|
|
||||||
<n8n-link
|
|
||||||
:class="$style.docsLink"
|
|
||||||
theme="text"
|
|
||||||
new-window
|
|
||||||
:to="documentationUrl"
|
|
||||||
@click="onDocumentationUrlClick"
|
|
||||||
>
|
|
||||||
{{ i18n.baseText('credentialEdit.credentialEdit.docs') }}
|
|
||||||
<n8n-icon icon="external-link-alt" size="small" :class="$style.externalIcon" />
|
|
||||||
</n8n-link>
|
|
||||||
</div>
|
|
||||||
<VueMarkdown :source="docs" :options="{ html: true }" :class="$style.markdown" />
|
|
||||||
<Feedback
|
|
||||||
:class="$style.feedback"
|
|
||||||
:model-value="submittedFeedback"
|
|
||||||
@update:model-value="onFeedback"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Feedback from '@/components/Feedback.vue';
|
import Feedback from '@/components/Feedback.vue';
|
||||||
import { useI18n } from '@/composables/useI18n';
|
import { useI18n } from '@/composables/useI18n';
|
||||||
@@ -66,6 +42,30 @@ function onDocumentationUrlClick(): void {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.docs">
|
||||||
|
<div :class="$style.header">
|
||||||
|
<p :class="$style.title">{{ i18n.baseText('credentialEdit.credentialEdit.setupGuide') }}</p>
|
||||||
|
<n8n-link
|
||||||
|
:class="$style.docsLink"
|
||||||
|
theme="text"
|
||||||
|
new-window
|
||||||
|
:to="documentationUrl"
|
||||||
|
@click="onDocumentationUrlClick"
|
||||||
|
>
|
||||||
|
{{ i18n.baseText('credentialEdit.credentialEdit.docs') }}
|
||||||
|
<n8n-icon icon="external-link-alt" size="small" :class="$style.externalIcon" />
|
||||||
|
</n8n-link>
|
||||||
|
</div>
|
||||||
|
<VueMarkdown :source="docs" :options="{ html: true }" :class="$style.markdown" />
|
||||||
|
<Feedback
|
||||||
|
:class="$style.feedback"
|
||||||
|
:model-value="submittedFeedback"
|
||||||
|
@update:model-value="onFeedback"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.docs {
|
.docs {
|
||||||
background-color: var(--color-background-light);
|
background-color: var(--color-background-light);
|
||||||
|
|||||||
@@ -1,118 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="modalName"
|
|
||||||
:custom-class="$style.credentialModal"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
:loading="loading"
|
|
||||||
:before-close="beforeClose"
|
|
||||||
width="70%"
|
|
||||||
height="80%"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<div :class="$style.header">
|
|
||||||
<div :class="$style.credInfo">
|
|
||||||
<div :class="$style.credIcon">
|
|
||||||
<CredentialIcon :credential-type-name="defaultCredentialTypeName" />
|
|
||||||
</div>
|
|
||||||
<InlineNameEdit
|
|
||||||
:model-value="credentialName"
|
|
||||||
:subtitle="credentialType ? credentialType.displayName : ''"
|
|
||||||
:readonly="!credentialPermissions.update || !credentialType"
|
|
||||||
type="Credential"
|
|
||||||
data-test-id="credential-name"
|
|
||||||
@update:model-value="onNameEdit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.credActions">
|
|
||||||
<n8n-icon-button
|
|
||||||
v-if="currentCredential && credentialPermissions.delete"
|
|
||||||
:title="$locale.baseText('credentialEdit.credentialEdit.delete')"
|
|
||||||
icon="trash"
|
|
||||||
type="tertiary"
|
|
||||||
:disabled="isSaving"
|
|
||||||
:loading="isDeleting"
|
|
||||||
data-test-id="credential-delete-button"
|
|
||||||
@click="deleteCredential"
|
|
||||||
/>
|
|
||||||
<SaveButton
|
|
||||||
v-if="showSaveButton"
|
|
||||||
:saved="!hasUnsavedChanges && !isTesting"
|
|
||||||
:is-saving="isSaving || isTesting"
|
|
||||||
:saving-label="
|
|
||||||
isTesting
|
|
||||||
? $locale.baseText('credentialEdit.credentialEdit.testing')
|
|
||||||
: $locale.baseText('credentialEdit.credentialEdit.saving')
|
|
||||||
"
|
|
||||||
data-test-id="credential-save-button"
|
|
||||||
@click="saveCredential"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<div :class="$style.container" data-test-id="credential-edit-dialog">
|
|
||||||
<div :class="$style.sidebar">
|
|
||||||
<n8n-menu
|
|
||||||
mode="tabs"
|
|
||||||
:items="sidebarItems"
|
|
||||||
:transparent-background="true"
|
|
||||||
@select="onTabSelect"
|
|
||||||
></n8n-menu>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="activeTab === 'connection' && credentialType"
|
|
||||||
ref="contentRef"
|
|
||||||
:class="$style.mainContent"
|
|
||||||
>
|
|
||||||
<CredentialConfig
|
|
||||||
:credential-type="credentialType"
|
|
||||||
:credential-properties="credentialProperties"
|
|
||||||
:credential-data="credentialData"
|
|
||||||
:credential-id="credentialId"
|
|
||||||
:show-validation-warning="showValidationWarning"
|
|
||||||
:auth-error="authError"
|
|
||||||
:tested-successfully="testedSuccessfully"
|
|
||||||
:is-o-auth-type="isOAuthType"
|
|
||||||
:is-o-auth-connected="isOAuthConnected"
|
|
||||||
:is-retesting="isRetesting"
|
|
||||||
:parent-types="parentTypes"
|
|
||||||
:required-properties-filled="requiredPropertiesFilled"
|
|
||||||
:credential-permissions="credentialPermissions"
|
|
||||||
:all-o-auth2-base-properties-overridden="allOAuth2BasePropertiesOverridden"
|
|
||||||
:mode="mode"
|
|
||||||
:selected-credential="selectedCredential"
|
|
||||||
:show-auth-type-selector="requiredCredentials"
|
|
||||||
@update="onDataChange"
|
|
||||||
@oauth="oAuthCredentialAuthorize"
|
|
||||||
@retest="retestCredential"
|
|
||||||
@scroll-to-top="scrollToTop"
|
|
||||||
@auth-type-changed="onAuthTypeChanged"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="showSharingContent" :class="$style.mainContent">
|
|
||||||
<CredentialSharing
|
|
||||||
:credential="currentCredential"
|
|
||||||
:credential-data="credentialData"
|
|
||||||
:credential-id="credentialId"
|
|
||||||
:credential-permissions="credentialPermissions"
|
|
||||||
:modal-bus="modalBus"
|
|
||||||
@update:model-value="onChangeSharedWith"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="activeTab === 'details' && credentialType" :class="$style.mainContent">
|
|
||||||
<CredentialInfo
|
|
||||||
:current-credential="currentCredential"
|
|
||||||
:credential-permissions="credentialPermissions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="activeTab.startsWith('coming-soon')" :class="$style.mainContent">
|
|
||||||
<FeatureComingSoon :feature-id="activeTab.split('/')[1]"></FeatureComingSoon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
|
||||||
@@ -1143,6 +1028,121 @@ function resetCredentialData(): void {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="modalName"
|
||||||
|
:custom-class="$style.credentialModal"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
:loading="loading"
|
||||||
|
:before-close="beforeClose"
|
||||||
|
width="70%"
|
||||||
|
height="80%"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div :class="$style.header">
|
||||||
|
<div :class="$style.credInfo">
|
||||||
|
<div :class="$style.credIcon">
|
||||||
|
<CredentialIcon :credential-type-name="defaultCredentialTypeName" />
|
||||||
|
</div>
|
||||||
|
<InlineNameEdit
|
||||||
|
:model-value="credentialName"
|
||||||
|
:subtitle="credentialType ? credentialType.displayName : ''"
|
||||||
|
:readonly="!credentialPermissions.update || !credentialType"
|
||||||
|
type="Credential"
|
||||||
|
data-test-id="credential-name"
|
||||||
|
@update:model-value="onNameEdit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.credActions">
|
||||||
|
<n8n-icon-button
|
||||||
|
v-if="currentCredential && credentialPermissions.delete"
|
||||||
|
:title="$locale.baseText('credentialEdit.credentialEdit.delete')"
|
||||||
|
icon="trash"
|
||||||
|
type="tertiary"
|
||||||
|
:disabled="isSaving"
|
||||||
|
:loading="isDeleting"
|
||||||
|
data-test-id="credential-delete-button"
|
||||||
|
@click="deleteCredential"
|
||||||
|
/>
|
||||||
|
<SaveButton
|
||||||
|
v-if="showSaveButton"
|
||||||
|
:saved="!hasUnsavedChanges && !isTesting"
|
||||||
|
:is-saving="isSaving || isTesting"
|
||||||
|
:saving-label="
|
||||||
|
isTesting
|
||||||
|
? $locale.baseText('credentialEdit.credentialEdit.testing')
|
||||||
|
: $locale.baseText('credentialEdit.credentialEdit.saving')
|
||||||
|
"
|
||||||
|
data-test-id="credential-save-button"
|
||||||
|
@click="saveCredential"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div :class="$style.container" data-test-id="credential-edit-dialog">
|
||||||
|
<div :class="$style.sidebar">
|
||||||
|
<n8n-menu
|
||||||
|
mode="tabs"
|
||||||
|
:items="sidebarItems"
|
||||||
|
:transparent-background="true"
|
||||||
|
@select="onTabSelect"
|
||||||
|
></n8n-menu>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="activeTab === 'connection' && credentialType"
|
||||||
|
ref="contentRef"
|
||||||
|
:class="$style.mainContent"
|
||||||
|
>
|
||||||
|
<CredentialConfig
|
||||||
|
:credential-type="credentialType"
|
||||||
|
:credential-properties="credentialProperties"
|
||||||
|
:credential-data="credentialData"
|
||||||
|
:credential-id="credentialId"
|
||||||
|
:show-validation-warning="showValidationWarning"
|
||||||
|
:auth-error="authError"
|
||||||
|
:tested-successfully="testedSuccessfully"
|
||||||
|
:is-o-auth-type="isOAuthType"
|
||||||
|
:is-o-auth-connected="isOAuthConnected"
|
||||||
|
:is-retesting="isRetesting"
|
||||||
|
:parent-types="parentTypes"
|
||||||
|
:required-properties-filled="requiredPropertiesFilled"
|
||||||
|
:credential-permissions="credentialPermissions"
|
||||||
|
:all-o-auth2-base-properties-overridden="allOAuth2BasePropertiesOverridden"
|
||||||
|
:mode="mode"
|
||||||
|
:selected-credential="selectedCredential"
|
||||||
|
:show-auth-type-selector="requiredCredentials"
|
||||||
|
@update="onDataChange"
|
||||||
|
@oauth="oAuthCredentialAuthorize"
|
||||||
|
@retest="retestCredential"
|
||||||
|
@scroll-to-top="scrollToTop"
|
||||||
|
@auth-type-changed="onAuthTypeChanged"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="showSharingContent" :class="$style.mainContent">
|
||||||
|
<CredentialSharing
|
||||||
|
:credential="currentCredential"
|
||||||
|
:credential-data="credentialData"
|
||||||
|
:credential-id="credentialId"
|
||||||
|
:credential-permissions="credentialPermissions"
|
||||||
|
:modal-bus="modalBus"
|
||||||
|
@update:model-value="onChangeSharedWith"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="activeTab === 'details' && credentialType" :class="$style.mainContent">
|
||||||
|
<CredentialInfo
|
||||||
|
:current-credential="currentCredential"
|
||||||
|
:credential-permissions="credentialPermissions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="activeTab.startsWith('coming-soon')" :class="$style.mainContent">
|
||||||
|
<FeatureComingSoon :feature-id="activeTab.split('/')[1]"></FeatureComingSoon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.credentialModal {
|
.credentialModal {
|
||||||
--dialog-max-width: 1200px;
|
--dialog-max-width: 1200px;
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
import TimeAgo from '../TimeAgo.vue';
|
||||||
|
import type { INodeTypeDescription } from 'n8n-workflow';
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'CredentialInfo',
|
||||||
|
components: {
|
||||||
|
TimeAgo,
|
||||||
|
},
|
||||||
|
props: ['currentCredential', 'credentialPermissions'],
|
||||||
|
methods: {
|
||||||
|
shortNodeType(nodeType: INodeTypeDescription) {
|
||||||
|
return this.$locale.shortNodeType(nodeType.name);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<el-row v-if="currentCredential">
|
<el-row v-if="currentCredential">
|
||||||
@@ -37,26 +57,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import TimeAgo from '../TimeAgo.vue';
|
|
||||||
import type { INodeTypeDescription } from 'n8n-workflow';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'CredentialInfo',
|
|
||||||
components: {
|
|
||||||
TimeAgo,
|
|
||||||
},
|
|
||||||
props: ['currentCredential', 'credentialPermissions'],
|
|
||||||
methods: {
|
|
||||||
shortNodeType(nodeType: INodeTypeDescription) {
|
|
||||||
return this.$locale.shortNodeType(nodeType.name);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
> * {
|
> * {
|
||||||
|
|||||||
@@ -1,28 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div v-if="credentialProperties.length" :class="$style.container" @keydown.stop>
|
|
||||||
<form
|
|
||||||
v-for="parameter in credentialProperties"
|
|
||||||
:key="parameter.name"
|
|
||||||
autocomplete="off"
|
|
||||||
data-test-id="credential-connection-parameter"
|
|
||||||
@submit.prevent
|
|
||||||
>
|
|
||||||
<!-- Why form? to break up inputs, to prevent Chrome autofill -->
|
|
||||||
<n8n-notice v-if="parameter.type === 'notice'" :content="parameter.displayName" />
|
|
||||||
<ParameterInputExpanded
|
|
||||||
v-else
|
|
||||||
:parameter="parameter"
|
|
||||||
:value="credentialDataValues[parameter.name]"
|
|
||||||
:documentation-url="documentationUrl"
|
|
||||||
:show-validation-warnings="showValidationWarnings"
|
|
||||||
:label="{ size: 'medium' }"
|
|
||||||
event-source="credentials"
|
|
||||||
@update="valueChanged"
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type {
|
import type {
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
@@ -60,6 +35,31 @@ function valueChanged(parameterData: IUpdateInformation) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="credentialProperties.length" :class="$style.container" @keydown.stop>
|
||||||
|
<form
|
||||||
|
v-for="parameter in credentialProperties"
|
||||||
|
:key="parameter.name"
|
||||||
|
autocomplete="off"
|
||||||
|
data-test-id="credential-connection-parameter"
|
||||||
|
@submit.prevent
|
||||||
|
>
|
||||||
|
<!-- Why form? to break up inputs, to prevent Chrome autofill -->
|
||||||
|
<n8n-notice v-if="parameter.type === 'notice'" :content="parameter.displayName" />
|
||||||
|
<ParameterInputExpanded
|
||||||
|
v-else
|
||||||
|
:parameter="parameter"
|
||||||
|
:value="credentialDataValues[parameter.name]"
|
||||||
|
:documentation-url="documentationUrl"
|
||||||
|
:show-validation-warnings="showValidationWarnings"
|
||||||
|
:label="{ size: 'medium' }"
|
||||||
|
event-source="credentials"
|
||||||
|
@update="valueChanged"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
> * {
|
> * {
|
||||||
|
|||||||
@@ -1,52 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.container">
|
|
||||||
<div v-if="!isSharingEnabled">
|
|
||||||
<N8nActionBox
|
|
||||||
:heading="
|
|
||||||
$locale.baseText(
|
|
||||||
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.title,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:description="
|
|
||||||
$locale.baseText(
|
|
||||||
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.description,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:button-text="
|
|
||||||
$locale.baseText(
|
|
||||||
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.button,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click:button="goToUpgrade"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<N8nInfoTip v-if="credentialPermissions.share" :bold="false" class="mb-s">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialSharing.info.owner') }}
|
|
||||||
</N8nInfoTip>
|
|
||||||
<N8nInfoTip v-else-if="isHomeTeamProject" :bold="false" class="mb-s">
|
|
||||||
{{ $locale.baseText('credentialEdit.credentialSharing.info.sharee.team') }}
|
|
||||||
</N8nInfoTip>
|
|
||||||
<N8nInfoTip v-else :bold="false" class="mb-s">
|
|
||||||
{{
|
|
||||||
$locale.baseText('credentialEdit.credentialSharing.info.sharee.personal', {
|
|
||||||
interpolate: { credentialOwnerName },
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</N8nInfoTip>
|
|
||||||
<ProjectSharing
|
|
||||||
v-model="sharedWithProjects"
|
|
||||||
:projects="projects"
|
|
||||||
:roles="credentialRoles"
|
|
||||||
:home-project="homeProject"
|
|
||||||
:readonly="!credentialPermissions.share"
|
|
||||||
:static="!credentialPermissions.share"
|
|
||||||
:placeholder="sharingSelectPlaceholder"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type {
|
import type {
|
||||||
ICredentialsResponse,
|
ICredentialsResponse,
|
||||||
@@ -208,6 +159,55 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.container">
|
||||||
|
<div v-if="!isSharingEnabled">
|
||||||
|
<N8nActionBox
|
||||||
|
:heading="
|
||||||
|
$locale.baseText(
|
||||||
|
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.title,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:description="
|
||||||
|
$locale.baseText(
|
||||||
|
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.description,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:button-text="
|
||||||
|
$locale.baseText(
|
||||||
|
uiStore.contextBasedTranslationKeys.credentials.sharing.unavailable.button,
|
||||||
|
)
|
||||||
|
"
|
||||||
|
@click:button="goToUpgrade"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<N8nInfoTip v-if="credentialPermissions.share" :bold="false" class="mb-s">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialSharing.info.owner') }}
|
||||||
|
</N8nInfoTip>
|
||||||
|
<N8nInfoTip v-else-if="isHomeTeamProject" :bold="false" class="mb-s">
|
||||||
|
{{ $locale.baseText('credentialEdit.credentialSharing.info.sharee.team') }}
|
||||||
|
</N8nInfoTip>
|
||||||
|
<N8nInfoTip v-else :bold="false" class="mb-s">
|
||||||
|
{{
|
||||||
|
$locale.baseText('credentialEdit.credentialSharing.info.sharee.personal', {
|
||||||
|
interpolate: { credentialOwnerName },
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</N8nInfoTip>
|
||||||
|
<ProjectSharing
|
||||||
|
v-model="sharedWithProjects"
|
||||||
|
:projects="projects"
|
||||||
|
:roles="credentialRoles"
|
||||||
|
:home-project="homeProject"
|
||||||
|
:readonly="!credentialPermissions.share"
|
||||||
|
:static="!credentialPermissions.share"
|
||||||
|
:placeholder="sharingSelectPlaceholder"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.container {
|
.container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,11 +1,3 @@
|
|||||||
<template>
|
|
||||||
<button
|
|
||||||
:class="$style.googleAuthBtn"
|
|
||||||
:title="$locale.baseText('credentialEdit.oAuthButton.signInWithGoogle')"
|
|
||||||
:style="googleAuthButtons"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useRootStore } from '@/stores/root.store';
|
import { useRootStore } from '@/stores/root.store';
|
||||||
@@ -20,6 +12,14 @@ const googleAuthButtons = {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
:class="$style.googleAuthBtn"
|
||||||
|
:title="$locale.baseText('credentialEdit.oAuthButton.signInWithGoogle')"
|
||||||
|
:style="googleAuthButtons"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.googleAuthBtn {
|
.googleAuthBtn {
|
||||||
--google-auth-btn-height: 46px;
|
--google-auth-btn-height: 46px;
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import GoogleAuthButton from './GoogleAuthButton.vue';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
isGoogleOAuthType: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<GoogleAuthButton v-if="isGoogleOAuthType" />
|
<GoogleAuthButton v-if="isGoogleOAuthType" />
|
||||||
@@ -9,14 +17,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import GoogleAuthButton from './GoogleAuthButton.vue';
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
isGoogleOAuthType: boolean;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.container {
|
.container {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@@ -1,53 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div :class="$style['parameter-value-container']">
|
|
||||||
<n8n-select
|
|
||||||
ref="innerSelect"
|
|
||||||
:size="inputSize"
|
|
||||||
filterable
|
|
||||||
:model-value="displayValue"
|
|
||||||
:placeholder="$locale.baseText('parameterInput.select')"
|
|
||||||
:title="displayTitle"
|
|
||||||
:disabled="isReadOnly"
|
|
||||||
data-test-id="credential-select"
|
|
||||||
@update:model-value="(value: string) => $emit('update:modelValue', value)"
|
|
||||||
@keydown.stop
|
|
||||||
@focus="$emit('setFocus')"
|
|
||||||
@blur="$emit('onBlur')"
|
|
||||||
>
|
|
||||||
<n8n-option
|
|
||||||
v-for="credType in supportedCredentialTypes"
|
|
||||||
:key="credType.name"
|
|
||||||
:value="credType.name"
|
|
||||||
:label="credType.displayName"
|
|
||||||
data-test-id="credential-select-option"
|
|
||||||
>
|
|
||||||
<div class="list-option">
|
|
||||||
<div class="option-headline">
|
|
||||||
{{ credType.displayName }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</n8n-option>
|
|
||||||
</n8n-select>
|
|
||||||
<slot name="issues-and-options" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ScopesNotice
|
|
||||||
v-if="scopes.length > 0"
|
|
||||||
:active-credential-type="activeCredentialType"
|
|
||||||
:scopes="scopes"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<NodeCredentials
|
|
||||||
:node="node"
|
|
||||||
:readonly="isReadOnly"
|
|
||||||
:override-cred-type="node.parameters[parameter.name]"
|
|
||||||
@credential-selected="(updateInformation) => $emit('credentialSelected', updateInformation)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ICredentialType } from 'n8n-workflow';
|
import type { ICredentialType } from 'n8n-workflow';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
@@ -156,6 +106,56 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div :class="$style['parameter-value-container']">
|
||||||
|
<n8n-select
|
||||||
|
ref="innerSelect"
|
||||||
|
:size="inputSize"
|
||||||
|
filterable
|
||||||
|
:model-value="displayValue"
|
||||||
|
:placeholder="$locale.baseText('parameterInput.select')"
|
||||||
|
:title="displayTitle"
|
||||||
|
:disabled="isReadOnly"
|
||||||
|
data-test-id="credential-select"
|
||||||
|
@update:model-value="(value: string) => $emit('update:modelValue', value)"
|
||||||
|
@keydown.stop
|
||||||
|
@focus="$emit('setFocus')"
|
||||||
|
@blur="$emit('onBlur')"
|
||||||
|
>
|
||||||
|
<n8n-option
|
||||||
|
v-for="credType in supportedCredentialTypes"
|
||||||
|
:key="credType.name"
|
||||||
|
:value="credType.name"
|
||||||
|
:label="credType.displayName"
|
||||||
|
data-test-id="credential-select-option"
|
||||||
|
>
|
||||||
|
<div class="list-option">
|
||||||
|
<div class="option-headline">
|
||||||
|
{{ credType.displayName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n8n-option>
|
||||||
|
</n8n-select>
|
||||||
|
<slot name="issues-and-options" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ScopesNotice
|
||||||
|
v-if="scopes.length > 0"
|
||||||
|
:active-credential-type="activeCredentialType"
|
||||||
|
:scopes="scopes"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<NodeCredentials
|
||||||
|
:node="node"
|
||||||
|
:readonly="isReadOnly"
|
||||||
|
:override-cred-type="node.parameters[parameter.name]"
|
||||||
|
@credential-selected="(updateInformation) => $emit('credentialSelected', updateInformation)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.parameter-value-container {
|
.parameter-value-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,62 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="CREDENTIAL_SELECT_MODAL_KEY"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
width="50%"
|
|
||||||
:center="true"
|
|
||||||
:loading="loading"
|
|
||||||
max-width="460px"
|
|
||||||
min-height="250px"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<h2 :class="$style.title">
|
|
||||||
{{ $locale.baseText('credentialSelectModal.addNewCredential') }}
|
|
||||||
</h2>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<div>
|
|
||||||
<div :class="$style.subtitle">
|
|
||||||
{{ $locale.baseText('credentialSelectModal.selectAnAppOrServiceToConnectTo') }}
|
|
||||||
</div>
|
|
||||||
<n8n-select
|
|
||||||
ref="select"
|
|
||||||
filterable
|
|
||||||
default-first-option
|
|
||||||
:placeholder="$locale.baseText('credentialSelectModal.searchForApp')"
|
|
||||||
size="xlarge"
|
|
||||||
:model-value="selected"
|
|
||||||
data-test-id="new-credential-type-select"
|
|
||||||
@update:model-value="onSelect"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<font-awesome-icon icon="search" />
|
|
||||||
</template>
|
|
||||||
<n8n-option
|
|
||||||
v-for="credential in credentialsStore.allCredentialTypes"
|
|
||||||
:key="credential.name"
|
|
||||||
:value="credential.name"
|
|
||||||
:label="credential.displayName"
|
|
||||||
filterable
|
|
||||||
data-test-id="new-credential-type-select-option"
|
|
||||||
/>
|
|
||||||
</n8n-select>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<div :class="$style.footer">
|
|
||||||
<n8n-button
|
|
||||||
:label="$locale.baseText('credentialSelectModal.continue')"
|
|
||||||
float="right"
|
|
||||||
size="large"
|
|
||||||
:disabled="!selected"
|
|
||||||
data-test-id="new-credential-type-button"
|
|
||||||
@click="openCredentialType"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import Modal from './Modal.vue';
|
import Modal from './Modal.vue';
|
||||||
@@ -125,6 +66,65 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="CREDENTIAL_SELECT_MODAL_KEY"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
width="50%"
|
||||||
|
:center="true"
|
||||||
|
:loading="loading"
|
||||||
|
max-width="460px"
|
||||||
|
min-height="250px"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h2 :class="$style.title">
|
||||||
|
{{ $locale.baseText('credentialSelectModal.addNewCredential') }}
|
||||||
|
</h2>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<div>
|
||||||
|
<div :class="$style.subtitle">
|
||||||
|
{{ $locale.baseText('credentialSelectModal.selectAnAppOrServiceToConnectTo') }}
|
||||||
|
</div>
|
||||||
|
<n8n-select
|
||||||
|
ref="select"
|
||||||
|
filterable
|
||||||
|
default-first-option
|
||||||
|
:placeholder="$locale.baseText('credentialSelectModal.searchForApp')"
|
||||||
|
size="xlarge"
|
||||||
|
:model-value="selected"
|
||||||
|
data-test-id="new-credential-type-select"
|
||||||
|
@update:model-value="onSelect"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<font-awesome-icon icon="search" />
|
||||||
|
</template>
|
||||||
|
<n8n-option
|
||||||
|
v-for="credential in credentialsStore.allCredentialTypes"
|
||||||
|
:key="credential.name"
|
||||||
|
:value="credential.name"
|
||||||
|
:label="credential.displayName"
|
||||||
|
filterable
|
||||||
|
data-test-id="new-credential-type-select-option"
|
||||||
|
/>
|
||||||
|
</n8n-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div :class="$style.footer">
|
||||||
|
<n8n-button
|
||||||
|
:label="$locale.baseText('credentialSelectModal.continue')"
|
||||||
|
float="right"
|
||||||
|
size="large"
|
||||||
|
:disabled="!selected"
|
||||||
|
data-test-id="new-credential-type-button"
|
||||||
|
@click="openCredentialType"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.title {
|
.title {
|
||||||
font-size: var(--font-size-xl);
|
font-size: var(--font-size-xl);
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<template>
|
|
||||||
<component
|
|
||||||
:is="tag"
|
|
||||||
ref="wrapper"
|
|
||||||
:class="{ [$style.dragging]: isDragging }"
|
|
||||||
@mousedown="onDragStart"
|
|
||||||
>
|
|
||||||
<slot :is-dragging="isDragging"></slot>
|
|
||||||
|
|
||||||
<Teleport to="body">
|
|
||||||
<div v-show="isDragging" ref="draggable" :class="$style.draggable" :style="draggableStyle">
|
|
||||||
<slot name="preview" :can-drop="canDrop" :el="draggingElement"></slot>
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { XYPosition } from '@/Interface';
|
import type { XYPosition } from '@/Interface';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
@@ -137,6 +120,23 @@ const onDragEnd = () => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="tag"
|
||||||
|
ref="wrapper"
|
||||||
|
:class="{ [$style.dragging]: isDragging }"
|
||||||
|
@mousedown="onDragStart"
|
||||||
|
>
|
||||||
|
<slot :is-dragging="isDragging"></slot>
|
||||||
|
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-show="isDragging" ref="draggable" :class="$style.draggable" :style="draggableStyle">
|
||||||
|
<slot name="preview" :can-drop="canDrop" :el="draggingElement"></slot>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.dragging {
|
.dragging {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div ref="targetRef" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" @mouseup="onMouseUp">
|
|
||||||
<slot :droppable="droppable" :active-drop="activeDrop"></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { XYPosition } from '@/Interface';
|
import type { XYPosition } from '@/Interface';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
@@ -84,3 +78,9 @@ function getStickyPosition(): XYPosition | null {
|
|||||||
return [left + props.stickyOffset[0], top + props.stickyOffset[1]];
|
return [left + props.stickyOffset[0], top + props.stickyOffset[1]];
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="targetRef" @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" @mouseup="onMouseUp">
|
||||||
|
<slot :droppable="droppable" :active-drop="activeDrop"></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,52 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="modalName"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
:title="$locale.baseText('duplicateWorkflowDialog.duplicateWorkflow')"
|
|
||||||
:center="true"
|
|
||||||
width="420px"
|
|
||||||
@enter="save"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div :class="$style.content">
|
|
||||||
<n8n-input
|
|
||||||
ref="nameInput"
|
|
||||||
v-model="name"
|
|
||||||
:placeholder="$locale.baseText('duplicateWorkflowDialog.enterWorkflowName')"
|
|
||||||
:maxlength="MAX_WORKFLOW_NAME_LENGTH"
|
|
||||||
/>
|
|
||||||
<TagsDropdown
|
|
||||||
v-if="settingsStore.areTagsEnabled"
|
|
||||||
ref="dropdown"
|
|
||||||
v-model="currentTagIds"
|
|
||||||
:create-enabled="true"
|
|
||||||
:event-bus="dropdownBus"
|
|
||||||
:placeholder="$locale.baseText('duplicateWorkflowDialog.chooseOrCreateATag')"
|
|
||||||
@blur="onTagsBlur"
|
|
||||||
@esc="onTagsEsc"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer="{ close }">
|
|
||||||
<div :class="$style.footer">
|
|
||||||
<n8n-button
|
|
||||||
:loading="isSaving"
|
|
||||||
:label="$locale.baseText('duplicateWorkflowDialog.save')"
|
|
||||||
float="right"
|
|
||||||
@click="save"
|
|
||||||
/>
|
|
||||||
<n8n-button
|
|
||||||
type="secondary"
|
|
||||||
:disabled="isSaving"
|
|
||||||
:label="$locale.baseText('duplicateWorkflowDialog.cancel')"
|
|
||||||
float="right"
|
|
||||||
@click="close"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
@@ -199,6 +150,55 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="modalName"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
:title="$locale.baseText('duplicateWorkflowDialog.duplicateWorkflow')"
|
||||||
|
:center="true"
|
||||||
|
width="420px"
|
||||||
|
@enter="save"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div :class="$style.content">
|
||||||
|
<n8n-input
|
||||||
|
ref="nameInput"
|
||||||
|
v-model="name"
|
||||||
|
:placeholder="$locale.baseText('duplicateWorkflowDialog.enterWorkflowName')"
|
||||||
|
:maxlength="MAX_WORKFLOW_NAME_LENGTH"
|
||||||
|
/>
|
||||||
|
<TagsDropdown
|
||||||
|
v-if="settingsStore.areTagsEnabled"
|
||||||
|
ref="dropdown"
|
||||||
|
v-model="currentTagIds"
|
||||||
|
:create-enabled="true"
|
||||||
|
:event-bus="dropdownBus"
|
||||||
|
:placeholder="$locale.baseText('duplicateWorkflowDialog.chooseOrCreateATag')"
|
||||||
|
@blur="onTagsBlur"
|
||||||
|
@esc="onTagsEsc"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer="{ close }">
|
||||||
|
<div :class="$style.footer">
|
||||||
|
<n8n-button
|
||||||
|
:loading="isSaving"
|
||||||
|
:label="$locale.baseText('duplicateWorkflowDialog.save')"
|
||||||
|
float="right"
|
||||||
|
@click="save"
|
||||||
|
/>
|
||||||
|
<n8n-button
|
||||||
|
type="secondary"
|
||||||
|
:disabled="isSaving"
|
||||||
|
:label="$locale.baseText('duplicateWorkflowDialog.cancel')"
|
||||||
|
float="right"
|
||||||
|
@click="close"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.content {
|
.content {
|
||||||
> *:not(:last-child) {
|
> *:not(:last-child) {
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<slot v-if="canAccess" />
|
|
||||||
<slot v-else name="fallback" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type PropType, defineComponent } from 'vue';
|
import { type PropType, defineComponent } from 'vue';
|
||||||
import type { EnterpriseEditionFeatureValue } from '@/Interface';
|
import type { EnterpriseEditionFeatureValue } from '@/Interface';
|
||||||
@@ -29,3 +22,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<slot v-if="canAccess" />
|
||||||
|
<slot v-else name="fallback" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
<template>
|
|
||||||
<!-- mock el-input element to apply styles -->
|
|
||||||
<div :class="{ 'el-input': true, 'static-size': staticSize }" :data-value="hiddenValue">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
@@ -26,6 +19,13 @@ const hiddenValue = computed(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- mock el-input element to apply styles -->
|
||||||
|
<div :class="{ 'el-input': true, 'static-size': staticSize }" :data-value="hiddenValue">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
$--horiz-padding: 15px;
|
$--horiz-padding: 15px;
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,3 @@
|
|||||||
<template>
|
|
||||||
<ExpandableInputBase :model-value="modelValue" :placeholder="placeholder">
|
|
||||||
<input
|
|
||||||
ref="inputRef"
|
|
||||||
class="el-input__inner"
|
|
||||||
:value="modelValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:maxlength="maxlength"
|
|
||||||
size="4"
|
|
||||||
@input="onInput"
|
|
||||||
@keydown.enter="onEnter"
|
|
||||||
@keydown.esc="onEscape"
|
|
||||||
/>
|
|
||||||
</ExpandableInputBase>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { EventBus } from 'n8n-design-system';
|
import type { EventBus } from 'n8n-design-system';
|
||||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||||
@@ -78,3 +62,19 @@ function onEscape() {
|
|||||||
emit('esc');
|
emit('esc');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ExpandableInputBase :model-value="modelValue" :placeholder="placeholder">
|
||||||
|
<input
|
||||||
|
ref="inputRef"
|
||||||
|
class="el-input__inner"
|
||||||
|
:value="modelValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:maxlength="maxlength"
|
||||||
|
size="4"
|
||||||
|
@input="onInput"
|
||||||
|
@keydown.enter="onEnter"
|
||||||
|
@keydown.esc="onEscape"
|
||||||
|
/>
|
||||||
|
</ExpandableInputBase>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import ExpandableInputBase from './ExpandableInputBase.vue';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
modelValue: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineProps<Props>();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ExpandableInputBase :model-value="modelValue" :static-size="true">
|
<ExpandableInputBase :model-value="modelValue" :static-size="true">
|
||||||
<input
|
<input
|
||||||
@@ -9,16 +19,6 @@
|
|||||||
</ExpandableInputBase>
|
</ExpandableInputBase>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import ExpandableInputBase from './ExpandableInputBase.vue';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
modelValue: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
defineProps<Props>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
input,
|
input,
|
||||||
input:hover {
|
input:hover {
|
||||||
|
|||||||
@@ -1,94 +1,3 @@
|
|||||||
<template>
|
|
||||||
<el-dialog
|
|
||||||
width="calc(100vw - var(--spacing-3xl))"
|
|
||||||
append-to-body
|
|
||||||
:class="$style.modal"
|
|
||||||
:model-value="dialogVisible"
|
|
||||||
:before-close="closeDialog"
|
|
||||||
>
|
|
||||||
<button :class="$style.close" @click="closeDialog">
|
|
||||||
<Close height="18" width="18" />
|
|
||||||
</button>
|
|
||||||
<div :class="$style.container">
|
|
||||||
<div :class="$style.sidebar">
|
|
||||||
<N8nInput
|
|
||||||
v-model="search"
|
|
||||||
size="small"
|
|
||||||
:class="$style.search"
|
|
||||||
:placeholder="i18n.baseText('ndv.search.placeholder.input.schema')"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<N8nIcon :class="$style.ioSearchIcon" icon="search" />
|
|
||||||
</template>
|
|
||||||
</N8nInput>
|
|
||||||
|
|
||||||
<RunDataSchema
|
|
||||||
:class="$style.schema"
|
|
||||||
:search="appliedSearch"
|
|
||||||
:nodes="parentNodes"
|
|
||||||
mapping-enabled
|
|
||||||
pane-type="input"
|
|
||||||
connection-type="main"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :class="$style.io">
|
|
||||||
<div :class="$style.input">
|
|
||||||
<div :class="$style.header">
|
|
||||||
<N8nText bold size="large">
|
|
||||||
{{ i18n.baseText('expressionEdit.expression') }}
|
|
||||||
</N8nText>
|
|
||||||
<N8nText
|
|
||||||
:class="$style.tip"
|
|
||||||
size="small"
|
|
||||||
v-html="i18n.baseText('expressionTip.javascript')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DraggableTarget :class="$style.editorContainer" type="mapping" @drop="onDrop">
|
|
||||||
<template #default>
|
|
||||||
<ExpressionEditorModalInput
|
|
||||||
ref="expressionInputRef"
|
|
||||||
:model-value="modelValue"
|
|
||||||
:is-read-only="isReadOnly"
|
|
||||||
:path="path"
|
|
||||||
:class="[
|
|
||||||
$style.editor,
|
|
||||||
{
|
|
||||||
'ph-no-capture': redactValues,
|
|
||||||
},
|
|
||||||
]"
|
|
||||||
data-test-id="expression-modal-input"
|
|
||||||
@change="valueChanged"
|
|
||||||
@close="closeDialog"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</DraggableTarget>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :class="$style.output">
|
|
||||||
<div :class="$style.header">
|
|
||||||
<N8nText bold size="large">
|
|
||||||
{{ i18n.baseText('parameterInput.result') }}
|
|
||||||
</N8nText>
|
|
||||||
<OutputItemSelect />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :class="[$style.editorContainer, { 'ph-no-capture': redactValues }]">
|
|
||||||
<ExpressionOutput
|
|
||||||
ref="expressionResultRef"
|
|
||||||
:class="$style.editor"
|
|
||||||
:segments="segments"
|
|
||||||
:extensions="theme"
|
|
||||||
data-test-id="expression-modal-output"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ExpressionEditorModalInput from '@/components/ExpressionEditorModal/ExpressionEditorModalInput.vue';
|
import ExpressionEditorModalInput from '@/components/ExpressionEditorModal/ExpressionEditorModalInput.vue';
|
||||||
import { computed, ref, toRaw, watch } from 'vue';
|
import { computed, ref, toRaw, watch } from 'vue';
|
||||||
@@ -211,6 +120,97 @@ async function onDrop(expression: string, event: MouseEvent) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
width="calc(100vw - var(--spacing-3xl))"
|
||||||
|
append-to-body
|
||||||
|
:class="$style.modal"
|
||||||
|
:model-value="dialogVisible"
|
||||||
|
:before-close="closeDialog"
|
||||||
|
>
|
||||||
|
<button :class="$style.close" @click="closeDialog">
|
||||||
|
<Close height="18" width="18" />
|
||||||
|
</button>
|
||||||
|
<div :class="$style.container">
|
||||||
|
<div :class="$style.sidebar">
|
||||||
|
<N8nInput
|
||||||
|
v-model="search"
|
||||||
|
size="small"
|
||||||
|
:class="$style.search"
|
||||||
|
:placeholder="i18n.baseText('ndv.search.placeholder.input.schema')"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<N8nIcon :class="$style.ioSearchIcon" icon="search" />
|
||||||
|
</template>
|
||||||
|
</N8nInput>
|
||||||
|
|
||||||
|
<RunDataSchema
|
||||||
|
:class="$style.schema"
|
||||||
|
:search="appliedSearch"
|
||||||
|
:nodes="parentNodes"
|
||||||
|
mapping-enabled
|
||||||
|
pane-type="input"
|
||||||
|
connection-type="main"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="$style.io">
|
||||||
|
<div :class="$style.input">
|
||||||
|
<div :class="$style.header">
|
||||||
|
<N8nText bold size="large">
|
||||||
|
{{ i18n.baseText('expressionEdit.expression') }}
|
||||||
|
</N8nText>
|
||||||
|
<N8nText
|
||||||
|
:class="$style.tip"
|
||||||
|
size="small"
|
||||||
|
v-html="i18n.baseText('expressionTip.javascript')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DraggableTarget :class="$style.editorContainer" type="mapping" @drop="onDrop">
|
||||||
|
<template #default>
|
||||||
|
<ExpressionEditorModalInput
|
||||||
|
ref="expressionInputRef"
|
||||||
|
:model-value="modelValue"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
|
:path="path"
|
||||||
|
:class="[
|
||||||
|
$style.editor,
|
||||||
|
{
|
||||||
|
'ph-no-capture': redactValues,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
data-test-id="expression-modal-input"
|
||||||
|
@change="valueChanged"
|
||||||
|
@close="closeDialog"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</DraggableTarget>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="$style.output">
|
||||||
|
<div :class="$style.header">
|
||||||
|
<N8nText bold size="large">
|
||||||
|
{{ i18n.baseText('parameterInput.result') }}
|
||||||
|
</N8nText>
|
||||||
|
<OutputItemSelect />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="[$style.editorContainer, { 'ph-no-capture': redactValues }]">
|
||||||
|
<ExpressionOutput
|
||||||
|
ref="expressionResultRef"
|
||||||
|
:class="$style.editor"
|
||||||
|
:segments="segments"
|
||||||
|
:extensions="theme"
|
||||||
|
data-test-id="expression-modal-output"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.modal {
|
.modal {
|
||||||
--dialog-close-top: var(--spacing-m);
|
--dialog-close-top: var(--spacing-m);
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div ref="root" :class="$style.editor" @keydown.stop></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { history } from '@codemirror/commands';
|
import { history } from '@codemirror/commands';
|
||||||
import { Prec } from '@codemirror/state';
|
import { Prec } from '@codemirror/state';
|
||||||
@@ -109,6 +105,10 @@ onMounted(() => {
|
|||||||
defineExpose({ editor });
|
defineExpose({ editor });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="root" :class="$style.editor" @keydown.stop></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
:global(.cm-content) {
|
:global(.cm-content) {
|
||||||
border-radius: var(--border-radius-base);
|
border-radius: var(--border-radius-base);
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'ExpressionFunctionIcon',
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
@@ -7,10 +13,4 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: 'ExpressionFunctionIcon',
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss"></style>
|
<style lang="scss"></style>
|
||||||
|
|||||||
@@ -1,31 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div v-if="featureInfo" :class="[$style.container]">
|
|
||||||
<div v-if="showTitle" class="mb-2xl">
|
|
||||||
<n8n-heading size="2xlarge">
|
|
||||||
{{ $locale.baseText(featureInfo.featureName) }}
|
|
||||||
</n8n-heading>
|
|
||||||
</div>
|
|
||||||
<div v-if="featureInfo.infoText" class="mb-l">
|
|
||||||
<n8n-info-tip theme="info" type="note">
|
|
||||||
<span v-html="$locale.baseText(featureInfo.infoText)"></span>
|
|
||||||
</n8n-info-tip>
|
|
||||||
</div>
|
|
||||||
<div :class="$style.actionBoxContainer">
|
|
||||||
<n8n-action-box
|
|
||||||
:description="$locale.baseText(featureInfo.actionBoxDescription)"
|
|
||||||
:button-text="
|
|
||||||
$locale.baseText(featureInfo.actionBoxButtonLabel || 'fakeDoor.actionBox.button.label')
|
|
||||||
"
|
|
||||||
@click:button="openLinkPage"
|
|
||||||
>
|
|
||||||
<template #heading>
|
|
||||||
<span v-html="$locale.baseText(featureInfo.actionBoxTitle)" />
|
|
||||||
</template>
|
|
||||||
</n8n-action-box>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
@@ -75,6 +47,34 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="featureInfo" :class="[$style.container]">
|
||||||
|
<div v-if="showTitle" class="mb-2xl">
|
||||||
|
<n8n-heading size="2xlarge">
|
||||||
|
{{ $locale.baseText(featureInfo.featureName) }}
|
||||||
|
</n8n-heading>
|
||||||
|
</div>
|
||||||
|
<div v-if="featureInfo.infoText" class="mb-l">
|
||||||
|
<n8n-info-tip theme="info" type="note">
|
||||||
|
<span v-html="$locale.baseText(featureInfo.infoText)"></span>
|
||||||
|
</n8n-info-tip>
|
||||||
|
</div>
|
||||||
|
<div :class="$style.actionBoxContainer">
|
||||||
|
<n8n-action-box
|
||||||
|
:description="$locale.baseText(featureInfo.actionBoxDescription)"
|
||||||
|
:button-text="
|
||||||
|
$locale.baseText(featureInfo.actionBoxButtonLabel || 'fakeDoor.actionBox.button.label')
|
||||||
|
"
|
||||||
|
@click:button="openLinkPage"
|
||||||
|
>
|
||||||
|
<template #heading>
|
||||||
|
<span v-html="$locale.baseText(featureInfo.actionBoxTitle)" />
|
||||||
|
</template>
|
||||||
|
</n8n-action-box>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.actionBoxContainer {
|
.actionBoxContainer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,131 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
class="fixed-collection-parameter"
|
|
||||||
:data-test-id="`fixed-collection-${parameter.name}`"
|
|
||||||
@keydown.stop
|
|
||||||
>
|
|
||||||
<div v-if="getProperties.length === 0" class="no-items-exist">
|
|
||||||
<n8n-text size="small">{{
|
|
||||||
$locale.baseText('fixedCollectionParameter.currentlyNoItemsExist')
|
|
||||||
}}</n8n-text>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-for="property in getProperties"
|
|
||||||
:key="property.name"
|
|
||||||
class="fixed-collection-parameter-property"
|
|
||||||
>
|
|
||||||
<n8n-input-label
|
|
||||||
v-if="property.displayName !== '' && parameter.options && parameter.options.length !== 1"
|
|
||||||
:label="$locale.nodeText().inputLabelDisplayName(property, path)"
|
|
||||||
:underline="true"
|
|
||||||
size="small"
|
|
||||||
color="text-dark"
|
|
||||||
/>
|
|
||||||
<div v-if="multipleValues">
|
|
||||||
<div
|
|
||||||
v-for="(_, index) in mutableValues[property.name]"
|
|
||||||
:key="property.name + index"
|
|
||||||
class="parameter-item"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
:class="index ? 'border-top-dashed parameter-item-wrapper ' : 'parameter-item-wrapper'"
|
|
||||||
>
|
|
||||||
<div v-if="!isReadOnly" class="delete-option">
|
|
||||||
<n8n-icon-button
|
|
||||||
type="tertiary"
|
|
||||||
text
|
|
||||||
size="mini"
|
|
||||||
icon="trash"
|
|
||||||
:title="$locale.baseText('fixedCollectionParameter.deleteItem')"
|
|
||||||
@click="deleteOption(property.name, index)"
|
|
||||||
></n8n-icon-button>
|
|
||||||
<n8n-icon-button
|
|
||||||
v-if="sortable && index !== 0"
|
|
||||||
type="tertiary"
|
|
||||||
text
|
|
||||||
size="mini"
|
|
||||||
icon="angle-up"
|
|
||||||
:title="$locale.baseText('fixedCollectionParameter.moveUp')"
|
|
||||||
@click="moveOptionUp(property.name, index)"
|
|
||||||
></n8n-icon-button>
|
|
||||||
<n8n-icon-button
|
|
||||||
v-if="sortable && index !== mutableValues[property.name].length - 1"
|
|
||||||
type="tertiary"
|
|
||||||
text
|
|
||||||
size="mini"
|
|
||||||
icon="angle-down"
|
|
||||||
:title="$locale.baseText('fixedCollectionParameter.moveDown')"
|
|
||||||
@click="moveOptionDown(property.name, index)"
|
|
||||||
></n8n-icon-button>
|
|
||||||
</div>
|
|
||||||
<Suspense>
|
|
||||||
<ParameterInputList
|
|
||||||
:parameters="property.values"
|
|
||||||
:node-values="nodeValues"
|
|
||||||
:path="getPropertyPath(property.name, index)"
|
|
||||||
:hide-delete="true"
|
|
||||||
:is-read-only="isReadOnly"
|
|
||||||
@value-changed="valueChanged"
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="parameter-item">
|
|
||||||
<div class="parameter-item-wrapper">
|
|
||||||
<div v-if="!isReadOnly" class="delete-option">
|
|
||||||
<n8n-icon-button
|
|
||||||
type="tertiary"
|
|
||||||
text
|
|
||||||
size="mini"
|
|
||||||
icon="trash"
|
|
||||||
:title="$locale.baseText('fixedCollectionParameter.deleteItem')"
|
|
||||||
@click="deleteOption(property.name)"
|
|
||||||
></n8n-icon-button>
|
|
||||||
</div>
|
|
||||||
<ParameterInputList
|
|
||||||
:parameters="property.values"
|
|
||||||
:node-values="nodeValues"
|
|
||||||
:path="getPropertyPath(property.name)"
|
|
||||||
:is-read-only="isReadOnly"
|
|
||||||
class="parameter-item"
|
|
||||||
:hide-delete="true"
|
|
||||||
@value-changed="valueChanged"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="controls">
|
|
||||||
<n8n-button
|
|
||||||
v-if="parameter.options && parameter.options.length === 1"
|
|
||||||
type="tertiary"
|
|
||||||
block
|
|
||||||
data-test-id="fixed-collection-add"
|
|
||||||
:label="getPlaceholderText"
|
|
||||||
@click="optionSelected(parameter.options[0].name)"
|
|
||||||
/>
|
|
||||||
<div v-else class="add-option">
|
|
||||||
<n8n-select
|
|
||||||
v-model="selectedOption"
|
|
||||||
:placeholder="getPlaceholderText"
|
|
||||||
size="small"
|
|
||||||
filterable
|
|
||||||
@update:model-value="optionSelected"
|
|
||||||
>
|
|
||||||
<n8n-option
|
|
||||||
v-for="item in parameterOptions"
|
|
||||||
:key="item.name"
|
|
||||||
:label="$locale.nodeText().collectionOptionDisplayName(parameter, item, path)"
|
|
||||||
:value="item.name"
|
|
||||||
></n8n-option>
|
|
||||||
</n8n-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
@@ -339,6 +211,134 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="fixed-collection-parameter"
|
||||||
|
:data-test-id="`fixed-collection-${parameter.name}`"
|
||||||
|
@keydown.stop
|
||||||
|
>
|
||||||
|
<div v-if="getProperties.length === 0" class="no-items-exist">
|
||||||
|
<n8n-text size="small">{{
|
||||||
|
$locale.baseText('fixedCollectionParameter.currentlyNoItemsExist')
|
||||||
|
}}</n8n-text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="property in getProperties"
|
||||||
|
:key="property.name"
|
||||||
|
class="fixed-collection-parameter-property"
|
||||||
|
>
|
||||||
|
<n8n-input-label
|
||||||
|
v-if="property.displayName !== '' && parameter.options && parameter.options.length !== 1"
|
||||||
|
:label="$locale.nodeText().inputLabelDisplayName(property, path)"
|
||||||
|
:underline="true"
|
||||||
|
size="small"
|
||||||
|
color="text-dark"
|
||||||
|
/>
|
||||||
|
<div v-if="multipleValues">
|
||||||
|
<div
|
||||||
|
v-for="(_, index) in mutableValues[property.name]"
|
||||||
|
:key="property.name + index"
|
||||||
|
class="parameter-item"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:class="index ? 'border-top-dashed parameter-item-wrapper ' : 'parameter-item-wrapper'"
|
||||||
|
>
|
||||||
|
<div v-if="!isReadOnly" class="delete-option">
|
||||||
|
<n8n-icon-button
|
||||||
|
type="tertiary"
|
||||||
|
text
|
||||||
|
size="mini"
|
||||||
|
icon="trash"
|
||||||
|
:title="$locale.baseText('fixedCollectionParameter.deleteItem')"
|
||||||
|
@click="deleteOption(property.name, index)"
|
||||||
|
></n8n-icon-button>
|
||||||
|
<n8n-icon-button
|
||||||
|
v-if="sortable && index !== 0"
|
||||||
|
type="tertiary"
|
||||||
|
text
|
||||||
|
size="mini"
|
||||||
|
icon="angle-up"
|
||||||
|
:title="$locale.baseText('fixedCollectionParameter.moveUp')"
|
||||||
|
@click="moveOptionUp(property.name, index)"
|
||||||
|
></n8n-icon-button>
|
||||||
|
<n8n-icon-button
|
||||||
|
v-if="sortable && index !== mutableValues[property.name].length - 1"
|
||||||
|
type="tertiary"
|
||||||
|
text
|
||||||
|
size="mini"
|
||||||
|
icon="angle-down"
|
||||||
|
:title="$locale.baseText('fixedCollectionParameter.moveDown')"
|
||||||
|
@click="moveOptionDown(property.name, index)"
|
||||||
|
></n8n-icon-button>
|
||||||
|
</div>
|
||||||
|
<Suspense>
|
||||||
|
<ParameterInputList
|
||||||
|
:parameters="property.values"
|
||||||
|
:node-values="nodeValues"
|
||||||
|
:path="getPropertyPath(property.name, index)"
|
||||||
|
:hide-delete="true"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
|
@value-changed="valueChanged"
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="parameter-item">
|
||||||
|
<div class="parameter-item-wrapper">
|
||||||
|
<div v-if="!isReadOnly" class="delete-option">
|
||||||
|
<n8n-icon-button
|
||||||
|
type="tertiary"
|
||||||
|
text
|
||||||
|
size="mini"
|
||||||
|
icon="trash"
|
||||||
|
:title="$locale.baseText('fixedCollectionParameter.deleteItem')"
|
||||||
|
@click="deleteOption(property.name)"
|
||||||
|
></n8n-icon-button>
|
||||||
|
</div>
|
||||||
|
<ParameterInputList
|
||||||
|
:parameters="property.values"
|
||||||
|
:node-values="nodeValues"
|
||||||
|
:path="getPropertyPath(property.name)"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
|
class="parameter-item"
|
||||||
|
:hide-delete="true"
|
||||||
|
@value-changed="valueChanged"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="parameterOptions.length > 0 && !isReadOnly" class="controls">
|
||||||
|
<n8n-button
|
||||||
|
v-if="parameter.options && parameter.options.length === 1"
|
||||||
|
type="tertiary"
|
||||||
|
block
|
||||||
|
data-test-id="fixed-collection-add"
|
||||||
|
:label="getPlaceholderText"
|
||||||
|
@click="optionSelected(parameter.options[0].name)"
|
||||||
|
/>
|
||||||
|
<div v-else class="add-option">
|
||||||
|
<n8n-select
|
||||||
|
v-model="selectedOption"
|
||||||
|
:placeholder="getPlaceholderText"
|
||||||
|
size="small"
|
||||||
|
filterable
|
||||||
|
@update:model-value="optionSelected"
|
||||||
|
>
|
||||||
|
<n8n-option
|
||||||
|
v-for="item in parameterOptions"
|
||||||
|
:key="item.name"
|
||||||
|
:label="$locale.nodeText().collectionOptionDisplayName(parameter, item, path)"
|
||||||
|
:value="item.name"
|
||||||
|
></n8n-option>
|
||||||
|
</n8n-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.fixed-collection-parameter {
|
.fixed-collection-parameter {
|
||||||
padding-left: var(--spacing-s);
|
padding-left: var(--spacing-s);
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.wrapper" @click="navigateTo">
|
|
||||||
<font-awesome-icon :class="$style.icon" icon="arrow-left" />
|
|
||||||
<div :class="$style.text" v-text="$locale.baseText('template.buttons.goBackButton')" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { VIEWS } from '@/constants';
|
import { VIEWS } from '@/constants';
|
||||||
@@ -16,6 +9,13 @@ const navigateTo = () => {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.wrapper" @click="navigateTo">
|
||||||
|
<font-awesome-icon :class="$style.icon" icon="arrow-left" />
|
||||||
|
<div :class="$style.text" v-text="$locale.baseText('template.buttons.goBackButton')" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,44 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="$style.wrapper"
|
|
||||||
:style="iconStyleData"
|
|
||||||
@click="() => $emit('click')"
|
|
||||||
@mouseover="showTooltip = true"
|
|
||||||
@mouseleave="showTooltip = false"
|
|
||||||
>
|
|
||||||
<div :class="$style.tooltip">
|
|
||||||
<n8n-tooltip placement="top" :visible="showTooltip">
|
|
||||||
<template #content>
|
|
||||||
<div v-text="nodeType.displayName"></div>
|
|
||||||
</template>
|
|
||||||
<span />
|
|
||||||
</n8n-tooltip>
|
|
||||||
</div>
|
|
||||||
<div v-if="nodeIconData !== null" :class="$style.icon" title="">
|
|
||||||
<div :class="$style.iconWrapper" :style="iconStyleData">
|
|
||||||
<div v-if="nodeIconData !== null" :class="$style.icon">
|
|
||||||
<img
|
|
||||||
v-if="nodeIconData.type === 'file'"
|
|
||||||
:src="nodeIconData.fileBuffer || nodeIconData.path"
|
|
||||||
:style="imageStyleData"
|
|
||||||
/>
|
|
||||||
<font-awesome-icon
|
|
||||||
v-else
|
|
||||||
:icon="nodeIconData.icon || nodeIconData.path"
|
|
||||||
:style="fontStyleData"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-else class="node-icon-placeholder">
|
|
||||||
{{ nodeType !== null ? nodeType.displayName.charAt(0) : '?' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-else :class="$style.placeholder">
|
|
||||||
{{ nodeType !== null ? nodeType.displayName.charAt(0) : '?' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type StyleValue, defineComponent, type PropType } from 'vue';
|
import { type StyleValue, defineComponent, type PropType } from 'vue';
|
||||||
|
|
||||||
@@ -151,6 +110,47 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="$style.wrapper"
|
||||||
|
:style="iconStyleData"
|
||||||
|
@click="() => $emit('click')"
|
||||||
|
@mouseover="showTooltip = true"
|
||||||
|
@mouseleave="showTooltip = false"
|
||||||
|
>
|
||||||
|
<div :class="$style.tooltip">
|
||||||
|
<n8n-tooltip placement="top" :visible="showTooltip">
|
||||||
|
<template #content>
|
||||||
|
<div v-text="nodeType.displayName"></div>
|
||||||
|
</template>
|
||||||
|
<span />
|
||||||
|
</n8n-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-if="nodeIconData !== null" :class="$style.icon" title="">
|
||||||
|
<div :class="$style.iconWrapper" :style="iconStyleData">
|
||||||
|
<div v-if="nodeIconData !== null" :class="$style.icon">
|
||||||
|
<img
|
||||||
|
v-if="nodeIconData.type === 'file'"
|
||||||
|
:src="nodeIconData.fileBuffer || nodeIconData.path"
|
||||||
|
:style="imageStyleData"
|
||||||
|
/>
|
||||||
|
<font-awesome-icon
|
||||||
|
v-else
|
||||||
|
:icon="nodeIconData.icon || nodeIconData.path"
|
||||||
|
:style="fontStyleData"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else class="node-icon-placeholder">
|
||||||
|
{{ nodeType !== null ? nodeType.displayName.charAt(0) : '?' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else :class="$style.placeholder">
|
||||||
|
{{ nodeType !== null ? nodeType.displayName.charAt(0) : '?' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.wrapper {
|
.wrapper {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.editor">
|
|
||||||
<div ref="htmlEditor" data-test-id="html-editor-container"></div>
|
|
||||||
<slot name="suffix" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { history } from '@codemirror/commands';
|
import { history } from '@codemirror/commands';
|
||||||
import {
|
import {
|
||||||
@@ -247,6 +240,13 @@ onBeforeUnmount(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.editor">
|
||||||
|
<div ref="htmlEditor" data-test-id="html-editor-container"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.editor {
|
.editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,44 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
width="700px"
|
|
||||||
:title="i18n.baseText('importCurlModal.title')"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
:name="IMPORT_CURL_MODAL_KEY"
|
|
||||||
:center="true"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<div :class="$style.container">
|
|
||||||
<N8nInputLabel :label="i18n.baseText('importCurlModal.input.label')" color="text-dark">
|
|
||||||
<N8nInput
|
|
||||||
ref="inputRef"
|
|
||||||
:model-value="curlCommand"
|
|
||||||
type="textarea"
|
|
||||||
:rows="5"
|
|
||||||
:placeholder="i18n.baseText('importCurlModal.input.placeholder')"
|
|
||||||
@update:model-value="onInput"
|
|
||||||
@focus="$event.target.select()"
|
|
||||||
/>
|
|
||||||
</N8nInputLabel>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #footer>
|
|
||||||
<div :class="$style.modalFooter">
|
|
||||||
<N8nNotice
|
|
||||||
:class="$style.notice"
|
|
||||||
:content="i18n.baseText('ImportCurlModal.notice.content')"
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<N8nButton
|
|
||||||
float="right"
|
|
||||||
:label="i18n.baseText('importCurlModal.button.label')"
|
|
||||||
@click="onImport"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import Modal from '@/components/Modal.vue';
|
import Modal from '@/components/Modal.vue';
|
||||||
import { IMPORT_CURL_MODAL_KEY } from '@/constants';
|
import { IMPORT_CURL_MODAL_KEY } from '@/constants';
|
||||||
@@ -116,6 +75,47 @@ async function onImport() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
width="700px"
|
||||||
|
:title="i18n.baseText('importCurlModal.title')"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
:name="IMPORT_CURL_MODAL_KEY"
|
||||||
|
:center="true"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div :class="$style.container">
|
||||||
|
<N8nInputLabel :label="i18n.baseText('importCurlModal.input.label')" color="text-dark">
|
||||||
|
<N8nInput
|
||||||
|
ref="inputRef"
|
||||||
|
:model-value="curlCommand"
|
||||||
|
type="textarea"
|
||||||
|
:rows="5"
|
||||||
|
:placeholder="i18n.baseText('importCurlModal.input.placeholder')"
|
||||||
|
@update:model-value="onInput"
|
||||||
|
@focus="$event.target.select()"
|
||||||
|
/>
|
||||||
|
</N8nInputLabel>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<div :class="$style.modalFooter">
|
||||||
|
<N8nNotice
|
||||||
|
:class="$style.notice"
|
||||||
|
:content="i18n.baseText('ImportCurlModal.notice.content')"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<N8nButton
|
||||||
|
float="right"
|
||||||
|
:label="i18n.baseText('importCurlModal.button.label')"
|
||||||
|
@click="onImport"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.modalFooter {
|
.modalFooter {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
@@ -1,35 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.container">
|
|
||||||
<span v-if="readonly" :class="$style.headline">
|
|
||||||
{{ modelValue }}
|
|
||||||
</span>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
:class="[$style.headline, $style['headline-editable']]"
|
|
||||||
@keydown.stop
|
|
||||||
@click="enableNameEdit"
|
|
||||||
>
|
|
||||||
<div v-if="!isNameEdit">
|
|
||||||
<span>{{ modelValue }}</span>
|
|
||||||
<i><font-awesome-icon icon="pen" /></i>
|
|
||||||
</div>
|
|
||||||
<div v-else :class="$style.nameInput">
|
|
||||||
<n8n-input
|
|
||||||
ref="nameInput"
|
|
||||||
:model-value="modelValue"
|
|
||||||
size="xlarge"
|
|
||||||
:maxlength="64"
|
|
||||||
@update:model-value="onNameEdit"
|
|
||||||
@change="disableNameEdit"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="!isNameEdit && subtitle" :class="$style.subtitle">
|
|
||||||
{{ subtitle }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, ref } from 'vue';
|
import { nextTick, ref } from 'vue';
|
||||||
import { useToast } from '@/composables/useToast';
|
import { useToast } from '@/composables/useToast';
|
||||||
@@ -79,6 +47,38 @@ const disableNameEdit = () => {
|
|||||||
onClickOutside(nameInput, disableNameEdit);
|
onClickOutside(nameInput, disableNameEdit);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.container">
|
||||||
|
<span v-if="readonly" :class="$style.headline">
|
||||||
|
{{ modelValue }}
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
:class="[$style.headline, $style['headline-editable']]"
|
||||||
|
@keydown.stop
|
||||||
|
@click="enableNameEdit"
|
||||||
|
>
|
||||||
|
<div v-if="!isNameEdit">
|
||||||
|
<span>{{ modelValue }}</span>
|
||||||
|
<i><font-awesome-icon icon="pen" /></i>
|
||||||
|
</div>
|
||||||
|
<div v-else :class="$style.nameInput">
|
||||||
|
<n8n-input
|
||||||
|
ref="nameInput"
|
||||||
|
:model-value="modelValue"
|
||||||
|
size="xlarge"
|
||||||
|
:maxlength="64"
|
||||||
|
@update:model-value="onNameEdit"
|
||||||
|
@change="disableNameEdit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!isNameEdit && subtitle" :class="$style.subtitle">
|
||||||
|
{{ subtitle }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style module lang="scss">
|
<style module lang="scss">
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,25 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span class="inline-edit" @keydown.stop>
|
|
||||||
<span v-if="isEditEnabled && !isDisabled">
|
|
||||||
<ExpandableInputEdit
|
|
||||||
v-model="newValue"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:maxlength="maxLength"
|
|
||||||
:autofocus="true"
|
|
||||||
:event-bus="inputBus"
|
|
||||||
@update:model-value="onInput"
|
|
||||||
@esc="onEscape"
|
|
||||||
@blur="onBlur"
|
|
||||||
@enter="submit"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span v-else class="preview" @click="onClick">
|
|
||||||
<ExpandableInputPreview :model-value="previewValue || modelValue" />
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import ExpandableInputEdit from '@/components/ExpandableInput/ExpandableInputEdit.vue';
|
import ExpandableInputEdit from '@/components/ExpandableInput/ExpandableInputEdit.vue';
|
||||||
@@ -100,6 +78,28 @@ function onEscape() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span class="inline-edit" @keydown.stop>
|
||||||
|
<span v-if="isEditEnabled && !isDisabled">
|
||||||
|
<ExpandableInputEdit
|
||||||
|
v-model="newValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:maxlength="maxLength"
|
||||||
|
:autofocus="true"
|
||||||
|
:event-bus="inputBus"
|
||||||
|
@update:model-value="onInput"
|
||||||
|
@esc="onEscape"
|
||||||
|
@blur="onBlur"
|
||||||
|
@enter="submit"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span v-else class="preview" @click="onClick">
|
||||||
|
<ExpandableInputPreview :model-value="previewValue || modelValue" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.preview {
|
.preview {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -1,138 +1,3 @@
|
|||||||
<template>
|
|
||||||
<RunData
|
|
||||||
:node="currentNode"
|
|
||||||
:nodes="isMappingMode ? rootNodesParents : parentNodes"
|
|
||||||
:workflow="workflow"
|
|
||||||
:run-index="runIndex"
|
|
||||||
:linked-runs="linkedRuns"
|
|
||||||
:can-link-runs="!mappedNode && canLinkRuns"
|
|
||||||
:too-much-data-title="$locale.baseText('ndv.input.tooMuchData.title')"
|
|
||||||
:no-data-in-branch-message="$locale.baseText('ndv.input.noOutputDataInBranch')"
|
|
||||||
:is-executing="isExecutingPrevious"
|
|
||||||
:executing-message="$locale.baseText('ndv.input.executingPrevious')"
|
|
||||||
:push-ref="pushRef"
|
|
||||||
:override-outputs="connectedCurrentNodeOutputs"
|
|
||||||
:mapping-enabled="isMappingEnabled"
|
|
||||||
:distance-from-active="currentNodeDepth"
|
|
||||||
:is-production-execution-preview="isProductionExecutionPreview"
|
|
||||||
:is-pane-active="isPaneActive"
|
|
||||||
pane-type="input"
|
|
||||||
data-test-id="ndv-input-panel"
|
|
||||||
@activate-pane="activatePane"
|
|
||||||
@item-hover="$emit('itemHover', $event)"
|
|
||||||
@link-run="onLinkRun"
|
|
||||||
@unlink-run="onUnlinkRun"
|
|
||||||
@run-change="onRunIndexChange"
|
|
||||||
@table-mounted="$emit('tableMounted', $event)"
|
|
||||||
@search="$emit('search', $event)"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<div :class="$style.titleSection">
|
|
||||||
<span :class="$style.title">{{ $locale.baseText('ndv.input') }}</span>
|
|
||||||
<n8n-radio-buttons
|
|
||||||
v-if="isActiveNodeConfig && !readOnly"
|
|
||||||
:options="inputModes"
|
|
||||||
:model-value="inputMode"
|
|
||||||
@update:model-value="onInputModeChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #input-select>
|
|
||||||
<InputNodeSelect
|
|
||||||
v-if="parentNodes.length && currentNodeName"
|
|
||||||
:model-value="currentNodeName"
|
|
||||||
:workflow="workflow"
|
|
||||||
:nodes="parentNodes"
|
|
||||||
@update:model-value="onInputNodeChange"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="isMappingMode" #before-data>
|
|
||||||
<!--
|
|
||||||
Hide the run linking buttons for both input and ouput panels when in 'Mapping Mode' because the run indices wouldn't match.
|
|
||||||
Although this is not the most elegant solution, it's straightforward and simpler than introducing a new props and logic to handle this.
|
|
||||||
-->
|
|
||||||
<component :is="'style'">button.linkRun { display: none }</component>
|
|
||||||
<div :class="$style.mappedNode">
|
|
||||||
<InputNodeSelect
|
|
||||||
:model-value="mappedNode"
|
|
||||||
:workflow="workflow"
|
|
||||||
:nodes="rootNodesParents"
|
|
||||||
@update:model-value="onMappedNodeSelected"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #node-not-run>
|
|
||||||
<div
|
|
||||||
v-if="(isActiveNodeConfig && rootNode) || parentNodes.length"
|
|
||||||
:class="$style.noOutputData"
|
|
||||||
>
|
|
||||||
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
|
||||||
$locale.baseText('ndv.input.noOutputData.title')
|
|
||||||
}}</n8n-text>
|
|
||||||
<n8n-tooltip v-if="!readOnly" :visible="showDraggableHint && showDraggableHintWithDelay">
|
|
||||||
<template #content>
|
|
||||||
<div
|
|
||||||
v-html="
|
|
||||||
$locale.baseText('dataMapping.dragFromPreviousHint', {
|
|
||||||
interpolate: { name: focusedMappableInput },
|
|
||||||
})
|
|
||||||
"
|
|
||||||
></div>
|
|
||||||
</template>
|
|
||||||
<NodeExecuteButton
|
|
||||||
type="secondary"
|
|
||||||
hide-icon
|
|
||||||
:transparent="true"
|
|
||||||
:node-name="isActiveNodeConfig ? rootNode : currentNodeName ?? ''"
|
|
||||||
:label="$locale.baseText('ndv.input.noOutputData.executePrevious')"
|
|
||||||
telemetry-source="inputs"
|
|
||||||
data-test-id="execute-previous-node"
|
|
||||||
@execute="onNodeExecute"
|
|
||||||
/>
|
|
||||||
</n8n-tooltip>
|
|
||||||
<n8n-text v-if="!readOnly" tag="div" size="small">
|
|
||||||
{{ $locale.baseText('ndv.input.noOutputData.hint') }}
|
|
||||||
</n8n-text>
|
|
||||||
</div>
|
|
||||||
<div v-else :class="$style.notConnected">
|
|
||||||
<div>
|
|
||||||
<WireMeUp />
|
|
||||||
</div>
|
|
||||||
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
|
||||||
$locale.baseText('ndv.input.notConnected.title')
|
|
||||||
}}</n8n-text>
|
|
||||||
<n8n-text tag="div">
|
|
||||||
{{ $locale.baseText('ndv.input.notConnected.message') }}
|
|
||||||
<a
|
|
||||||
href="https://docs.n8n.io/workflows/connections/"
|
|
||||||
target="_blank"
|
|
||||||
@click="onConnectionHelpClick"
|
|
||||||
>
|
|
||||||
{{ $locale.baseText('ndv.input.notConnected.learnMore') }}
|
|
||||||
</a>
|
|
||||||
</n8n-text>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #no-output-data>
|
|
||||||
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
|
||||||
$locale.baseText('ndv.input.noOutputData')
|
|
||||||
}}</n8n-text>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #recovered-artificial-output-data>
|
|
||||||
<div :class="$style.recoveredOutputData">
|
|
||||||
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
|
||||||
$locale.baseText('executionDetails.executionFailed.recoveredNodeTitle')
|
|
||||||
}}</n8n-text>
|
|
||||||
<n8n-text>
|
|
||||||
{{ $locale.baseText('executionDetails.executionFailed.recoveredNodeMessage') }}
|
|
||||||
</n8n-text>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</RunData>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { INodeUi } from '@/Interface';
|
import type { INodeUi } from '@/Interface';
|
||||||
import {
|
import {
|
||||||
@@ -468,6 +333,141 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RunData
|
||||||
|
:node="currentNode"
|
||||||
|
:nodes="isMappingMode ? rootNodesParents : parentNodes"
|
||||||
|
:workflow="workflow"
|
||||||
|
:run-index="runIndex"
|
||||||
|
:linked-runs="linkedRuns"
|
||||||
|
:can-link-runs="!mappedNode && canLinkRuns"
|
||||||
|
:too-much-data-title="$locale.baseText('ndv.input.tooMuchData.title')"
|
||||||
|
:no-data-in-branch-message="$locale.baseText('ndv.input.noOutputDataInBranch')"
|
||||||
|
:is-executing="isExecutingPrevious"
|
||||||
|
:executing-message="$locale.baseText('ndv.input.executingPrevious')"
|
||||||
|
:push-ref="pushRef"
|
||||||
|
:override-outputs="connectedCurrentNodeOutputs"
|
||||||
|
:mapping-enabled="isMappingEnabled"
|
||||||
|
:distance-from-active="currentNodeDepth"
|
||||||
|
:is-production-execution-preview="isProductionExecutionPreview"
|
||||||
|
:is-pane-active="isPaneActive"
|
||||||
|
pane-type="input"
|
||||||
|
data-test-id="ndv-input-panel"
|
||||||
|
@activate-pane="activatePane"
|
||||||
|
@item-hover="$emit('itemHover', $event)"
|
||||||
|
@link-run="onLinkRun"
|
||||||
|
@unlink-run="onUnlinkRun"
|
||||||
|
@run-change="onRunIndexChange"
|
||||||
|
@table-mounted="$emit('tableMounted', $event)"
|
||||||
|
@search="$emit('search', $event)"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div :class="$style.titleSection">
|
||||||
|
<span :class="$style.title">{{ $locale.baseText('ndv.input') }}</span>
|
||||||
|
<n8n-radio-buttons
|
||||||
|
v-if="isActiveNodeConfig && !readOnly"
|
||||||
|
:options="inputModes"
|
||||||
|
:model-value="inputMode"
|
||||||
|
@update:model-value="onInputModeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #input-select>
|
||||||
|
<InputNodeSelect
|
||||||
|
v-if="parentNodes.length && currentNodeName"
|
||||||
|
:model-value="currentNodeName"
|
||||||
|
:workflow="workflow"
|
||||||
|
:nodes="parentNodes"
|
||||||
|
@update:model-value="onInputNodeChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="isMappingMode" #before-data>
|
||||||
|
<!--
|
||||||
|
Hide the run linking buttons for both input and ouput panels when in 'Mapping Mode' because the run indices wouldn't match.
|
||||||
|
Although this is not the most elegant solution, it's straightforward and simpler than introducing a new props and logic to handle this.
|
||||||
|
-->
|
||||||
|
<component :is="'style'">button.linkRun { display: none }</component>
|
||||||
|
<div :class="$style.mappedNode">
|
||||||
|
<InputNodeSelect
|
||||||
|
:model-value="mappedNode"
|
||||||
|
:workflow="workflow"
|
||||||
|
:nodes="rootNodesParents"
|
||||||
|
@update:model-value="onMappedNodeSelected"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #node-not-run>
|
||||||
|
<div
|
||||||
|
v-if="(isActiveNodeConfig && rootNode) || parentNodes.length"
|
||||||
|
:class="$style.noOutputData"
|
||||||
|
>
|
||||||
|
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
||||||
|
$locale.baseText('ndv.input.noOutputData.title')
|
||||||
|
}}</n8n-text>
|
||||||
|
<n8n-tooltip v-if="!readOnly" :visible="showDraggableHint && showDraggableHintWithDelay">
|
||||||
|
<template #content>
|
||||||
|
<div
|
||||||
|
v-html="
|
||||||
|
$locale.baseText('dataMapping.dragFromPreviousHint', {
|
||||||
|
interpolate: { name: focusedMappableInput },
|
||||||
|
})
|
||||||
|
"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
<NodeExecuteButton
|
||||||
|
type="secondary"
|
||||||
|
hide-icon
|
||||||
|
:transparent="true"
|
||||||
|
:node-name="isActiveNodeConfig ? rootNode : currentNodeName ?? ''"
|
||||||
|
:label="$locale.baseText('ndv.input.noOutputData.executePrevious')"
|
||||||
|
telemetry-source="inputs"
|
||||||
|
data-test-id="execute-previous-node"
|
||||||
|
@execute="onNodeExecute"
|
||||||
|
/>
|
||||||
|
</n8n-tooltip>
|
||||||
|
<n8n-text v-if="!readOnly" tag="div" size="small">
|
||||||
|
{{ $locale.baseText('ndv.input.noOutputData.hint') }}
|
||||||
|
</n8n-text>
|
||||||
|
</div>
|
||||||
|
<div v-else :class="$style.notConnected">
|
||||||
|
<div>
|
||||||
|
<WireMeUp />
|
||||||
|
</div>
|
||||||
|
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
||||||
|
$locale.baseText('ndv.input.notConnected.title')
|
||||||
|
}}</n8n-text>
|
||||||
|
<n8n-text tag="div">
|
||||||
|
{{ $locale.baseText('ndv.input.notConnected.message') }}
|
||||||
|
<a
|
||||||
|
href="https://docs.n8n.io/workflows/connections/"
|
||||||
|
target="_blank"
|
||||||
|
@click="onConnectionHelpClick"
|
||||||
|
>
|
||||||
|
{{ $locale.baseText('ndv.input.notConnected.learnMore') }}
|
||||||
|
</a>
|
||||||
|
</n8n-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #no-output-data>
|
||||||
|
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
||||||
|
$locale.baseText('ndv.input.noOutputData')
|
||||||
|
}}</n8n-text>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #recovered-artificial-output-data>
|
||||||
|
<div :class="$style.recoveredOutputData">
|
||||||
|
<n8n-text tag="div" :bold="true" color="text-dark" size="large">{{
|
||||||
|
$locale.baseText('executionDetails.executionFailed.recoveredNodeTitle')
|
||||||
|
}}</n8n-text>
|
||||||
|
<n8n-text>
|
||||||
|
{{ $locale.baseText('executionDetails.executionFailed.recoveredNodeMessage') }}
|
||||||
|
</n8n-text>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</RunData>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.mappedNode {
|
.mappedNode {
|
||||||
padding: 0 var(--spacing-s) var(--spacing-s);
|
padding: 0 var(--spacing-s) var(--spacing-s);
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<span ref="observed">
|
|
||||||
<slot></slot>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
@@ -37,3 +31,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<span ref="observed">
|
||||||
|
<slot></slot>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div ref="root">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
@@ -70,3 +64,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="root">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,65 +1,3 @@
|
|||||||
<template>
|
|
||||||
<Modal
|
|
||||||
:name="INVITE_USER_MODAL_KEY"
|
|
||||||
:title="
|
|
||||||
$locale.baseText(
|
|
||||||
showInviteUrls ? 'settings.users.copyInviteUrls' : 'settings.users.inviteNewUsers',
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:center="true"
|
|
||||||
width="460px"
|
|
||||||
:event-bus="modalBus"
|
|
||||||
@enter="onSubmit"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
<n8n-notice v-if="!isAdvancedPermissionsEnabled">
|
|
||||||
<i18n-t keypath="settings.users.advancedPermissions.warning">
|
|
||||||
<template #link>
|
|
||||||
<n8n-link size="small" @click="goToUpgradeAdvancedPermissions">
|
|
||||||
{{ $locale.baseText('settings.users.advancedPermissions.warning.link') }}
|
|
||||||
</n8n-link>
|
|
||||||
</template>
|
|
||||||
</i18n-t>
|
|
||||||
</n8n-notice>
|
|
||||||
<div v-if="showInviteUrls">
|
|
||||||
<n8n-users-list :users="invitedUsers">
|
|
||||||
<template #actions="{ user }">
|
|
||||||
<n8n-tooltip>
|
|
||||||
<template #content>
|
|
||||||
{{ $locale.baseText('settings.users.inviteLink.copy') }}
|
|
||||||
</template>
|
|
||||||
<n8n-icon-button
|
|
||||||
icon="link"
|
|
||||||
type="tertiary"
|
|
||||||
data-test-id="copy-invite-link-button"
|
|
||||||
:data-invite-link="user.inviteAcceptUrl"
|
|
||||||
@click="onCopyInviteLink(user)"
|
|
||||||
></n8n-icon-button>
|
|
||||||
</n8n-tooltip>
|
|
||||||
</template>
|
|
||||||
</n8n-users-list>
|
|
||||||
</div>
|
|
||||||
<n8n-form-inputs
|
|
||||||
v-else
|
|
||||||
:inputs="config"
|
|
||||||
:event-bus="formBus"
|
|
||||||
:column-view="true"
|
|
||||||
@update="onInput"
|
|
||||||
@submit="onSubmit"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="!showInviteUrls" #footer>
|
|
||||||
<n8n-button
|
|
||||||
:loading="loading"
|
|
||||||
:disabled="!enabledButton"
|
|
||||||
:label="buttonLabel"
|
|
||||||
float="right"
|
|
||||||
@click="onSubmitClick"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { mapStores } from 'pinia';
|
import { mapStores } from 'pinia';
|
||||||
@@ -344,3 +282,65 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
:name="INVITE_USER_MODAL_KEY"
|
||||||
|
:title="
|
||||||
|
$locale.baseText(
|
||||||
|
showInviteUrls ? 'settings.users.copyInviteUrls' : 'settings.users.inviteNewUsers',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:center="true"
|
||||||
|
width="460px"
|
||||||
|
:event-bus="modalBus"
|
||||||
|
@enter="onSubmit"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<n8n-notice v-if="!isAdvancedPermissionsEnabled">
|
||||||
|
<i18n-t keypath="settings.users.advancedPermissions.warning">
|
||||||
|
<template #link>
|
||||||
|
<n8n-link size="small" @click="goToUpgradeAdvancedPermissions">
|
||||||
|
{{ $locale.baseText('settings.users.advancedPermissions.warning.link') }}
|
||||||
|
</n8n-link>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</n8n-notice>
|
||||||
|
<div v-if="showInviteUrls">
|
||||||
|
<n8n-users-list :users="invitedUsers">
|
||||||
|
<template #actions="{ user }">
|
||||||
|
<n8n-tooltip>
|
||||||
|
<template #content>
|
||||||
|
{{ $locale.baseText('settings.users.inviteLink.copy') }}
|
||||||
|
</template>
|
||||||
|
<n8n-icon-button
|
||||||
|
icon="link"
|
||||||
|
type="tertiary"
|
||||||
|
data-test-id="copy-invite-link-button"
|
||||||
|
:data-invite-link="user.inviteAcceptUrl"
|
||||||
|
@click="onCopyInviteLink(user)"
|
||||||
|
></n8n-icon-button>
|
||||||
|
</n8n-tooltip>
|
||||||
|
</template>
|
||||||
|
</n8n-users-list>
|
||||||
|
</div>
|
||||||
|
<n8n-form-inputs
|
||||||
|
v-else
|
||||||
|
:inputs="config"
|
||||||
|
:event-bus="formBus"
|
||||||
|
:column-view="true"
|
||||||
|
@update="onInput"
|
||||||
|
@submit="onSubmit"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="!showInviteUrls" #footer>
|
||||||
|
<n8n-button
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="!enabledButton"
|
||||||
|
:label="buttonLabel"
|
||||||
|
float="right"
|
||||||
|
@click="onSubmitClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.editor" :style="isReadOnly ? 'opacity: 0.7' : ''">
|
|
||||||
<div ref="jsEditorRef" class="ph-no-capture js-editor"></div>
|
|
||||||
<slot name="suffix" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { history, toggleComment } from '@codemirror/commands';
|
import { history, toggleComment } from '@codemirror/commands';
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
@@ -124,6 +117,13 @@ const extensions = computed(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.editor" :style="isReadOnly ? 'opacity: 0.7' : ''">
|
||||||
|
<div ref="jsEditorRef" class="ph-no-capture js-editor"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.editor {
|
.editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
<template>
|
|
||||||
<div :class="$style.editor">
|
|
||||||
<div ref="jsonEditorRef" class="ph-no-capture json-editor"></div>
|
|
||||||
<slot name="suffix" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { history } from '@codemirror/commands';
|
import { history } from '@codemirror/commands';
|
||||||
import { json, jsonParseLinter } from '@codemirror/lang-json';
|
import { json, jsonParseLinter } from '@codemirror/lang-json';
|
||||||
@@ -115,6 +108,13 @@ function destroyEditor() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="$style.editor">
|
||||||
|
<div ref="jsonEditorRef" class="ph-no-capture json-editor"></div>
|
||||||
|
<slot name="suffix" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.editor {
|
.editor {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user