refactor(editor): Standardize components sections order (no-changelog) (#10540)
This commit is contained in:
@@ -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>
|
||||
<div :class="['n8n-action-box', $style.container]" data-test-id="action-box">
|
||||
<div v-if="emoji" :class="$style.emoji">
|
||||
@@ -41,32 +67,6 @@
|
||||
</div>
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
// This component is visually similar to the ActionToggle component
|
||||
// but it offers more options when it comes to dropdown items styling
|
||||
@@ -129,6 +75,60 @@ const close = () => elementDropdown.value?.handleClose();
|
||||
defineExpose({ open, close });
|
||||
</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>
|
||||
:global(.el-dropdown__list) {
|
||||
.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>
|
||||
<span :class="$style.container" data-test-id="action-toggle" @click.stop.prevent>
|
||||
<ElDropdown
|
||||
@@ -41,41 +76,6 @@
|
||||
</span>
|
||||
</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>
|
||||
.container > * {
|
||||
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>
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
@@ -76,6 +51,31 @@ const alertBoxClassNames = computed(() => {
|
||||
});
|
||||
</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>
|
||||
@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>
|
||||
import { computed } from 'vue';
|
||||
import Avatar from 'vue-boring-avatars';
|
||||
@@ -57,6 +41,22 @@ const sizes: { [size: string]: number } = {
|
||||
const getSize = (size: string): number => sizes[size];
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
import type { TextSize } from 'n8n-design-system/types/text';
|
||||
import N8nText from '../N8nText';
|
||||
@@ -34,6 +26,14 @@ withDefaults(defineProps<BadgeProps>(), {
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="['n8n-badge', $style[theme]]">
|
||||
<N8nText :size="size" :bold="bold" :compact="true">
|
||||
<slot></slot>
|
||||
</N8nText>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
<script lang="ts" setup>
|
||||
type BlockUiProps = {
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
withDefaults(defineProps<BlockUiProps>(), {
|
||||
show: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div
|
||||
@@ -9,16 +19,6 @@
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
type BlockUiProps = {
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
withDefaults(defineProps<BlockUiProps>(), {
|
||||
show: false,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.uiBlocker {
|
||||
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">
|
||||
import { useCssModule, computed, useAttrs, watchEffect } from 'vue';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
@@ -75,6 +51,30 @@ const classes = computed(() => {
|
||||
});
|
||||
</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">
|
||||
@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>
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import N8nText from '../N8nText';
|
||||
@@ -70,6 +53,23 @@ const getIconSize = computed<IconSize>(() => {
|
||||
});
|
||||
</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>
|
||||
.callout {
|
||||
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>
|
||||
<div :class="classes" v-bind="$attrs">
|
||||
<div v-if="$slots.prepend" :class="$style.icon">
|
||||
@@ -20,26 +40,6 @@
|
||||
</div>
|
||||
</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>
|
||||
.card {
|
||||
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>
|
||||
import { ref } from 'vue';
|
||||
import { ElCheckbox } from 'element-plus';
|
||||
@@ -58,6 +36,28 @@ const onLabelClick = () => {
|
||||
};
|
||||
</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>
|
||||
.n8nCheckbox {
|
||||
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">
|
||||
import { computed } from 'vue';
|
||||
const props = withDefaults(
|
||||
@@ -50,6 +27,29 @@ const style = computed(() => ({
|
||||
}));
|
||||
</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>
|
||||
.progressRingCircle {
|
||||
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>
|
||||
import { computed, ref, useCssModule } from 'vue';
|
||||
import N8nSelect from '../N8nSelect';
|
||||
@@ -138,6 +76,68 @@ function getThStyle(column: DatatableColumn) {
|
||||
}
|
||||
</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>
|
||||
.datatableWrapper {
|
||||
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>
|
||||
import N8nFormInputs from '../N8nFormInputs';
|
||||
import N8nHeading from '../N8nHeading';
|
||||
@@ -80,6 +40,46 @@ const onButtonClick = () => formBus.emit('submit');
|
||||
const onSecondaryButtonClick = (event: Event) => emit('secondaryClick', event);
|
||||
</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>
|
||||
.heading {
|
||||
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>
|
||||
import { computed, reactive, onMounted, ref, watch, useSlots } from 'vue';
|
||||
|
||||
@@ -271,6 +178,99 @@ watch(
|
||||
defineExpose({ inputRef });
|
||||
</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>
|
||||
.infoText {
|
||||
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>
|
||||
import { computed, useCssModule } from 'vue';
|
||||
|
||||
@@ -50,6 +44,12 @@ const classes = computed(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="tag" :class="['n8n-heading', ...classes]" v-bind="$attrs">
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.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>
|
||||
import type { FontAwesomeIconProps } from '@fortawesome/vue-fontawesome';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
@@ -24,6 +18,12 @@ withDefaults(defineProps<IconProps>(), {
|
||||
});
|
||||
</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>
|
||||
.xlarge {
|
||||
width: var(--font-size-xl) !important;
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<template>
|
||||
<N8nButton square v-bind="{ ...$attrs, ...$props }" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { IconButtonProps } from 'n8n-design-system/types/button';
|
||||
import N8nButton from '../N8nButton';
|
||||
@@ -17,3 +13,7 @@ withDefaults(defineProps<IconButtonProps>(), {
|
||||
active: false,
|
||||
});
|
||||
</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>
|
||||
import { onMounted, ref } from 'vue';
|
||||
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);
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
import { computed } from 'vue';
|
||||
import type { Placement } from 'element-plus';
|
||||
@@ -92,6 +58,40 @@ const iconData = computed((): { icon: string; color: string } => {
|
||||
});
|
||||
</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>
|
||||
.infoTip {
|
||||
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>
|
||||
import { computed, ref } from 'vue';
|
||||
import { ElInput } from 'element-plus';
|
||||
@@ -96,6 +64,38 @@ const select = () => inputElement.value?.select();
|
||||
defineExpose({ focus, blur, select });
|
||||
</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>
|
||||
.xlarge {
|
||||
--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>
|
||||
<div :class="$style.container" v-bind="$attrs" data-test-id="input-label">
|
||||
<label
|
||||
@@ -45,39 +78,6 @@
|
||||
</div>
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
import N8nText from '../N8nText';
|
||||
@@ -35,6 +25,16 @@ withDefaults(defineProps<LinkProps>(), {
|
||||
});
|
||||
</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>
|
||||
@import '../../utils';
|
||||
@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>
|
||||
<ElSkeleton
|
||||
:loading="loading"
|
||||
@@ -35,40 +69,6 @@
|
||||
</ElSkeleton>
|
||||
</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>
|
||||
.h1Last {
|
||||
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>
|
||||
import { computed, ref } from 'vue';
|
||||
import type { Options as MarkdownOptions } from 'markdown-it';
|
||||
@@ -213,6 +193,26 @@ const onCheckboxChange = (index: number) => {
|
||||
};
|
||||
</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>
|
||||
.markdown {
|
||||
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>
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
@@ -125,6 +70,61 @@ const onSelect = (item: IMenuItem): void => {
|
||||
};
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
<div :class="['n8n-menu-item', $style.item]">
|
||||
<ElSubMenu
|
||||
@@ -88,70 +152,6 @@
|
||||
</div>
|
||||
</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">
|
||||
// Element menu-item overrides
|
||||
: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>
|
||||
import { computed } from 'vue';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
@@ -107,6 +70,43 @@ const badgeStyleData = computed((): Record<string, string> => {
|
||||
});
|
||||
</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>
|
||||
.nodeIconWrapper {
|
||||
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>
|
||||
import { computed, ref, useCssModule } from 'vue';
|
||||
import sanitize from 'sanitize-html';
|
||||
@@ -81,6 +64,23 @@ const onClick = (event: MouseEvent) => {
|
||||
};
|
||||
</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>
|
||||
.notice {
|
||||
font-size: var(--font-size-2xs);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'N8nPulse' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="['pulse', $style.pulseContainer]">
|
||||
<div :class="$style.pulse">
|
||||
@@ -8,10 +12,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'N8nPulse' });
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
$--light-pulse-color: hsla(
|
||||
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>
|
||||
<label
|
||||
role="radio"
|
||||
@@ -23,22 +39,6 @@
|
||||
</label>
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
import RadioButton from './RadioButton.vue';
|
||||
|
||||
@@ -53,6 +36,23 @@ const onClick = (
|
||||
};
|
||||
</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>
|
||||
.radioGroup {
|
||||
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>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
@@ -180,6 +167,19 @@ const resizerMove = (event: MouseEvent) => {
|
||||
};
|
||||
</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>
|
||||
.resize {
|
||||
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>
|
||||
import { computed, ref, useAttrs } from 'vue';
|
||||
import N8nResizeWrapper, { type ResizeData } from '../N8nResizeWrapper/ResizeWrapper.vue';
|
||||
@@ -59,3 +42,20 @@ const onResizeEnd = () => {
|
||||
emit('resizeend');
|
||||
};
|
||||
</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>
|
||||
import { computed } from 'vue';
|
||||
import { type RouteLocationRaw } from 'vue-router';
|
||||
@@ -39,3 +25,17 @@ const useRouterLink = computed(() => {
|
||||
|
||||
const openNewWindow = computed(() => !useRouterLink.value);
|
||||
</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>
|
||||
import type { TextSize } from 'n8n-design-system/types/text';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
@@ -28,6 +16,18 @@ withDefaults(defineProps<SpinnerProps>(), {
|
||||
});
|
||||
</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">
|
||||
.lds-ring {
|
||||
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>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import N8nInput from '../N8nInput';
|
||||
@@ -122,6 +74,54 @@ const onInputScroll = (event: WheelEvent) => {
|
||||
};
|
||||
</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>
|
||||
.sticky {
|
||||
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>
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import N8nIcon from '../N8nIcon';
|
||||
@@ -128,6 +72,62 @@ const scrollLeft = () => scroll(-50);
|
||||
const scrollRight = () => scroll(50);
|
||||
</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>
|
||||
.container {
|
||||
position: relative;
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
<template>
|
||||
<span :class="['n8n-tag', $style.tag]" v-bind="$attrs">
|
||||
{{ text }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
interface TagProps {
|
||||
text: string;
|
||||
@@ -12,6 +6,12 @@ defineOptions({ name: 'N8nTag' });
|
||||
defineProps<TagProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="['n8n-tag', $style.tag]" v-bind="$attrs">
|
||||
{{ text }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.tag {
|
||||
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>
|
||||
import { computed, ref } from 'vue';
|
||||
import N8nTag from '../N8nTag';
|
||||
@@ -67,6 +47,26 @@ const onExpand = () => {
|
||||
};
|
||||
</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>
|
||||
.tags {
|
||||
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>
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import type { TextSize, TextColor, TextAlign } from 'n8n-design-system/types/text';
|
||||
@@ -46,6 +40,12 @@ const classes = computed(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="tag" :class="['n8n-text', ...classes]" v-bind="$attrs">
|
||||
<slot></slot>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.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">
|
||||
import type { PropType } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
@@ -65,6 +43,28 @@ export default defineComponent({
|
||||
});
|
||||
</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>
|
||||
.buttons {
|
||||
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>
|
||||
import { computed, useCssModule } from 'vue';
|
||||
|
||||
@@ -85,6 +57,34 @@ const getPath = (key: string): Array<string | number> => {
|
||||
};
|
||||
</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>
|
||||
$--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>
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import N8nText from '../N8nText';
|
||||
@@ -59,6 +32,33 @@ const classes = computed(
|
||||
);
|
||||
</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>
|
||||
.container {
|
||||
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>
|
||||
import { computed, ref } from 'vue';
|
||||
import N8nUserInfo from '../N8nUserInfo';
|
||||
@@ -112,6 +80,38 @@ const getLabel = (user: IUser) =>
|
||||
!user.fullName ? user.email : `${user.fullName} (${user.email})`;
|
||||
</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>
|
||||
.itemContainer {
|
||||
--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>
|
||||
import { computed } from 'vue';
|
||||
import N8nActionToggle from '../N8nActionToggle';
|
||||
@@ -115,6 +79,42 @@ const onUserAction = (user: IUser, action: string) =>
|
||||
});
|
||||
</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>
|
||||
.itemContainer {
|
||||
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>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
|
||||
@@ -64,6 +47,23 @@ onUnmounted(() => {
|
||||
});
|
||||
</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>
|
||||
.table {
|
||||
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>
|
||||
import { computed } from 'vue';
|
||||
|
||||
@@ -42,6 +31,17 @@ const props = withDefaults(defineProps<SpacingPreviewProps>(), {
|
||||
const sizes = computed(() => [...SIZES, ...(props.property === 'margin' ? ['auto'] : [])]);
|
||||
</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">
|
||||
$box-size: 64px;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user