mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 05:17:28 -08:00
refactor(editor): Port more components over to composition API (no-changelog) (#8794)
This commit is contained in:
parent
edce632ee6
commit
e2131b9ab6
|
@ -35,48 +35,27 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import N8nButton from '../N8nButton';
|
import N8nButton from '../N8nButton';
|
||||||
import N8nHeading from '../N8nHeading';
|
import N8nHeading from '../N8nHeading';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import N8nCallout from '../N8nCallout';
|
import N8nCallout, { type CalloutTheme } from '../N8nCallout';
|
||||||
import { defineComponent } from 'vue';
|
import type { ButtonType } from '@/types/button';
|
||||||
|
|
||||||
export default defineComponent({
|
interface ActionBoxProps {
|
||||||
name: 'N8nActionBox',
|
emoji: string;
|
||||||
components: {
|
heading: string;
|
||||||
N8nButton,
|
buttonText: string;
|
||||||
N8nHeading,
|
buttonType: ButtonType;
|
||||||
N8nText,
|
description: string;
|
||||||
N8nCallout,
|
calloutText: string;
|
||||||
},
|
calloutTheme: CalloutTheme;
|
||||||
props: {
|
calloutIcon: string;
|
||||||
emoji: {
|
}
|
||||||
type: String,
|
|
||||||
},
|
defineOptions({ name: 'N8nActionBox' });
|
||||||
heading: {
|
withDefaults(defineProps<ActionBoxProps>(), {
|
||||||
type: String,
|
calloutTheme: 'info',
|
||||||
},
|
|
||||||
buttonText: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
buttonType: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
calloutText: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
calloutTheme: {
|
|
||||||
type: String,
|
|
||||||
default: 'info',
|
|
||||||
},
|
|
||||||
calloutIcon: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ exports[`N8NActionBox > should render correctly 1`] = `
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<n8n-text-stub color="text-base" bold="false" size="medium" compact="false" tag="span"></n8n-text-stub>
|
<n8n-text-stub color="text-base" bold="false" size="medium" compact="false" tag="span"></n8n-text-stub>
|
||||||
</div>
|
</div>
|
||||||
<n8n-button-stub label="Do something" type="primary" size="large" loading="false" disabled="false" outline="false" text="false" block="false" active="false" square="false" element="button"></n8n-button-stub>
|
<n8n-button-stub block="false" element="button" label="Do something" square="false" active="false" disabled="false" loading="false" outline="false" size="large" text="false" type="primary"></n8n-button-stub>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -50,15 +50,21 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import type { PropType } from 'vue';
|
// This component is visually similar to the ActionToggle component
|
||||||
import { defineComponent } from 'vue';
|
// but it offers more options when it comes to dropdown items styling
|
||||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus';
|
// (supports icons, separators, custom styling and all options provided
|
||||||
|
// by Element UI dropdown component).
|
||||||
|
// It can be used in different parts of editor UI while ActionToggle
|
||||||
|
// is designed to be used in card components.
|
||||||
|
import { ref, useCssModule, useAttrs } from 'vue';
|
||||||
|
import { ElDropdown, ElDropdownMenu, ElDropdownItem, type Placement } from 'element-plus';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
import { N8nKeyboardShortcut } from '../N8nKeyboardShortcut';
|
import { N8nKeyboardShortcut } from '../N8nKeyboardShortcut';
|
||||||
import type { KeyboardShortcut } from '../../types';
|
import type { KeyboardShortcut } from '../../types';
|
||||||
|
import type { IconSize } from '@/types/icon';
|
||||||
|
|
||||||
export interface IActionDropdownItem {
|
interface IActionDropdownItem {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
@ -68,92 +74,56 @@ export interface IActionDropdownItem {
|
||||||
customClass?: string;
|
customClass?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This component is visually similar to the ActionToggle component
|
const TRIGGER = ['click', 'hover'] as const;
|
||||||
// but it offers more options when it comes to dropdown items styling
|
|
||||||
// (supports icons, separators, custom styling and all options provided
|
interface ActionDropdownProps {
|
||||||
// by Element UI dropdown component).
|
items: IActionDropdownItem[];
|
||||||
// It can be used in different parts of editor UI while ActionToggle
|
placement?: Placement;
|
||||||
// is designed to be used in card components.
|
activatorIcon?: string;
|
||||||
export default defineComponent({
|
activatorSize?: IconSize;
|
||||||
name: 'N8nActionDropdown',
|
iconSize?: IconSize;
|
||||||
components: {
|
trigger?: (typeof TRIGGER)[number];
|
||||||
ElDropdown,
|
hideArrow?: boolean;
|
||||||
ElDropdownMenu,
|
}
|
||||||
ElDropdownItem,
|
|
||||||
N8nIcon,
|
withDefaults(defineProps<ActionDropdownProps>(), {
|
||||||
N8nKeyboardShortcut,
|
placement: 'bottom',
|
||||||
},
|
activatorIcon: 'ellipsis-h',
|
||||||
props: {
|
activatorSize: 'medium',
|
||||||
items: {
|
iconSize: 'medium',
|
||||||
type: Array as PropType<IActionDropdownItem[]>,
|
trigger: 'click',
|
||||||
required: true,
|
hideArrow: false,
|
||||||
},
|
});
|
||||||
placement: {
|
|
||||||
type: String,
|
const $attrs = useAttrs();
|
||||||
default: 'bottom',
|
const testIdPrefix = $attrs['data-test-id'];
|
||||||
validator: (value: string): boolean =>
|
|
||||||
['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'].includes(value),
|
const $style = useCssModule();
|
||||||
},
|
const getItemClasses = (item: IActionDropdownItem): Record<string, boolean> => {
|
||||||
activatorIcon: {
|
|
||||||
type: String,
|
|
||||||
default: 'ellipsis-h',
|
|
||||||
},
|
|
||||||
activatorSize: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
},
|
|
||||||
iconSize: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
validator: (value: string): boolean => ['small', 'medium', 'large'].includes(value),
|
|
||||||
},
|
|
||||||
trigger: {
|
|
||||||
type: String,
|
|
||||||
default: 'click',
|
|
||||||
validator: (value: string): boolean => ['click', 'hover'].includes(value),
|
|
||||||
},
|
|
||||||
hideArrow: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
const testIdPrefix = this.$attrs['data-test-id'];
|
|
||||||
return { testIdPrefix };
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getItemClasses(item: IActionDropdownItem): Record<string, boolean> {
|
|
||||||
return {
|
return {
|
||||||
[this.$style.itemContainer]: true,
|
[$style.itemContainer]: true,
|
||||||
[this.$style.disabled]: item.disabled,
|
[$style.disabled]: !!item.disabled,
|
||||||
[this.$style.hasCustomStyling]: item.customClass !== undefined,
|
[$style.hasCustomStyling]: item.customClass !== undefined,
|
||||||
...(item.customClass !== undefined ? { [item.customClass]: true } : {}),
|
...(item.customClass !== undefined ? { [item.customClass]: true } : {}),
|
||||||
};
|
};
|
||||||
},
|
};
|
||||||
onSelect(action: string): void {
|
|
||||||
this.$emit('select', action);
|
|
||||||
},
|
|
||||||
onVisibleChange(open: boolean): void {
|
|
||||||
this.$emit('visibleChange', open);
|
|
||||||
},
|
|
||||||
onButtonBlur(event: FocusEvent): void {
|
|
||||||
const elementDropdown = this.$refs.elementDropdown as InstanceType<typeof ElDropdown>;
|
|
||||||
|
|
||||||
|
const $emit = defineEmits(['select', 'visibleChange']);
|
||||||
|
const elementDropdown = ref<InstanceType<typeof ElDropdown>>();
|
||||||
|
|
||||||
|
const onSelect = (action: string) => $emit('select', action);
|
||||||
|
const onVisibleChange = (open: boolean) => $emit('visibleChange', open);
|
||||||
|
|
||||||
|
const onButtonBlur = (event: FocusEvent) => {
|
||||||
// Hide dropdown when clicking outside of current document
|
// Hide dropdown when clicking outside of current document
|
||||||
if (elementDropdown?.handleClose && event.relatedTarget === null) {
|
if (elementDropdown.value?.handleClose && event.relatedTarget === null) {
|
||||||
elementDropdown.handleClose();
|
elementDropdown.value.handleClose();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
open() {
|
|
||||||
const elementDropdown = this.$refs.elementDropdown as InstanceType<typeof ElDropdown>;
|
const open = () => elementDropdown.value?.handleOpen();
|
||||||
elementDropdown.handleOpen();
|
const close = () => elementDropdown.value?.handleClose();
|
||||||
},
|
defineExpose({ open, close });
|
||||||
close() {
|
|
||||||
const elementDropdown = this.$refs.elementDropdown as InstanceType<typeof ElDropdown>;
|
|
||||||
elementDropdown.handleClose();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -41,60 +41,36 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import type { PropType } from 'vue';
|
import { ElDropdown, ElDropdownMenu, ElDropdownItem, type Placement } from 'element-plus';
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { ElDropdown, ElDropdownMenu, ElDropdownItem } from 'element-plus';
|
|
||||||
import N8nIcon from '../N8nIcon';
|
|
||||||
import type { UserAction } from '@/types';
|
import type { UserAction } from '@/types';
|
||||||
|
import N8nIcon from '../N8nIcon';
|
||||||
|
import type { IconOrientation, IconSize } from '@/types/icon';
|
||||||
|
|
||||||
export default defineComponent({
|
const SIZE = ['mini', 'small', 'medium'] as const;
|
||||||
name: 'N8nActionToggle',
|
const THEME = ['default', 'dark'] as const;
|
||||||
components: {
|
|
||||||
ElDropdown,
|
interface ActionToggleProps {
|
||||||
ElDropdownMenu,
|
actions?: UserAction[];
|
||||||
ElDropdownItem,
|
placement?: Placement;
|
||||||
N8nIcon,
|
size?: (typeof SIZE)[number];
|
||||||
},
|
iconSize?: IconSize;
|
||||||
props: {
|
theme?: (typeof THEME)[number];
|
||||||
actions: {
|
iconOrientation?: IconOrientation;
|
||||||
type: Array as PropType<UserAction[]>,
|
}
|
||||||
default: () => [],
|
|
||||||
},
|
defineOptions({ name: 'N8nActionToggle' });
|
||||||
placement: {
|
withDefaults(defineProps<ActionToggleProps>(), {
|
||||||
type: String,
|
actions: () => [],
|
||||||
default: 'bottom',
|
placement: 'bottom',
|
||||||
validator: (value: string): boolean =>
|
size: 'medium',
|
||||||
['top', 'top-end', 'top-start', 'bottom', 'bottom-end', 'bottom-start'].includes(value),
|
theme: 'default',
|
||||||
},
|
iconOrientation: 'vertical',
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
validator: (value: string): boolean => ['mini', 'small', 'medium'].includes(value),
|
|
||||||
},
|
|
||||||
iconSize: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
default: 'default',
|
|
||||||
validator: (value: string): boolean => ['default', 'dark'].includes(value),
|
|
||||||
},
|
|
||||||
iconOrientation: {
|
|
||||||
type: String,
|
|
||||||
default: 'vertical',
|
|
||||||
validator: (value: string): boolean => ['horizontal', 'vertical'].includes(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onCommand(value: string) {
|
|
||||||
this.$emit('action', value);
|
|
||||||
},
|
|
||||||
onVisibleChange(value: boolean) {
|
|
||||||
this.$emit('visible-change', value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $emit = defineEmits(['action', 'visible-change']);
|
||||||
|
const onCommand = (value: string) => $emit('action', value);
|
||||||
|
const onVisibleChange = (value: boolean) => $emit('visible-change', value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -57,20 +57,20 @@ const icon = computed(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const style = useCssModule();
|
const $style = useCssModule();
|
||||||
const alertBoxClassNames = computed(() => {
|
const alertBoxClassNames = computed(() => {
|
||||||
const classNames = ['n8n-alert', style.alert];
|
const classNames = ['n8n-alert', $style.alert];
|
||||||
if (props.type) {
|
if (props.type) {
|
||||||
classNames.push(style[props.type]);
|
classNames.push($style[props.type]);
|
||||||
}
|
}
|
||||||
if (props.effect) {
|
if (props.effect) {
|
||||||
classNames.push(style[props.effect]);
|
classNames.push($style[props.effect]);
|
||||||
}
|
}
|
||||||
if (props.center) {
|
if (props.center) {
|
||||||
classNames.push(style.center);
|
classNames.push($style.center);
|
||||||
}
|
}
|
||||||
if (props.background) {
|
if (props.background) {
|
||||||
classNames.push(style.background);
|
classNames.push($style.background);
|
||||||
}
|
}
|
||||||
return classNames;
|
return classNames;
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,63 +12,48 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
import Avatar from 'vue-boring-avatars';
|
import Avatar from 'vue-boring-avatars';
|
||||||
|
|
||||||
const sizes: { [size: string]: number } = {
|
interface AvatarProps {
|
||||||
small: 28,
|
firstName: string;
|
||||||
large: 48,
|
lastName: string;
|
||||||
medium: 40,
|
size: string;
|
||||||
};
|
colors: string[];
|
||||||
|
}
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
defineOptions({ name: 'N8nAvatar' });
|
||||||
|
const props = withDefaults(defineProps<AvatarProps>(), {
|
||||||
export default defineComponent({
|
firstName: '',
|
||||||
name: 'N8nAvatar',
|
lastName: '',
|
||||||
components: {
|
size: 'medium',
|
||||||
Avatar,
|
colors: () => [
|
||||||
},
|
|
||||||
props: {
|
|
||||||
firstName: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
lastName: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
},
|
|
||||||
colors: {
|
|
||||||
default: () => [
|
|
||||||
'--color-primary',
|
'--color-primary',
|
||||||
'--color-secondary',
|
'--color-secondary',
|
||||||
'--color-avatar-accent-1',
|
'--color-avatar-accent-1',
|
||||||
'--color-avatar-accent-2',
|
'--color-avatar-accent-2',
|
||||||
'--color-primary-tint-1',
|
'--color-primary-tint-1',
|
||||||
],
|
],
|
||||||
},
|
});
|
||||||
},
|
|
||||||
computed: {
|
const initials = computed(
|
||||||
initials() {
|
() =>
|
||||||
return (
|
(props.firstName ? props.firstName.charAt(0) : '') +
|
||||||
(this.firstName ? this.firstName.charAt(0) : '') +
|
(props.lastName ? props.lastName.charAt(0) : ''),
|
||||||
(this.lastName ? this.lastName.charAt(0) : '')
|
|
||||||
);
|
);
|
||||||
},
|
|
||||||
},
|
const getColors = (colors: string[]): string[] => {
|
||||||
methods: {
|
|
||||||
getColors(colors: string[]): string[] {
|
|
||||||
const style = getComputedStyle(document.body);
|
const style = getComputedStyle(document.body);
|
||||||
return colors.map((color: string) => style.getPropertyValue(color));
|
return colors.map((color: string) => style.getPropertyValue(color));
|
||||||
},
|
};
|
||||||
getSize(size: string): number {
|
|
||||||
return sizes[size];
|
const sizes: { [size: string]: number } = {
|
||||||
},
|
small: 28,
|
||||||
},
|
large: 48,
|
||||||
});
|
medium: 40,
|
||||||
|
};
|
||||||
|
const getSize = (size: string): number => sizes[size];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -6,33 +6,31 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import type { TextSize } from '@/types/text';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
const THEME = [
|
||||||
|
'default',
|
||||||
|
'success',
|
||||||
|
'warning',
|
||||||
|
'danger',
|
||||||
|
'primary',
|
||||||
|
'secondary',
|
||||||
|
'tertiary',
|
||||||
|
] as const;
|
||||||
|
|
||||||
export default defineComponent({
|
interface BadgeProps {
|
||||||
components: {
|
theme?: (typeof THEME)[number];
|
||||||
N8nText,
|
size?: TextSize;
|
||||||
},
|
bold: boolean;
|
||||||
props: {
|
}
|
||||||
theme: {
|
|
||||||
type: String,
|
defineOptions({ name: 'N8nBadge' });
|
||||||
default: 'default',
|
withDefaults(defineProps<BadgeProps>(), {
|
||||||
validator: (value: string) =>
|
theme: 'default',
|
||||||
['default', 'success', 'warning', 'danger', 'primary', 'secondary', 'tertiary'].includes(
|
size: 'small',
|
||||||
value,
|
bold: false,
|
||||||
),
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'small',
|
|
||||||
},
|
|
||||||
bold: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -20,69 +20,27 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useCssModule, computed, useAttrs, watchEffect } from 'vue';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
import N8nSpinner from '../N8nSpinner';
|
import N8nSpinner from '../N8nSpinner';
|
||||||
import { useCssModule, computed, useAttrs, watchEffect } from 'vue';
|
import type { ButtonProps } from '@/types/button';
|
||||||
|
|
||||||
const $style = useCssModule();
|
const $style = useCssModule();
|
||||||
const $attrs = useAttrs();
|
const $attrs = useAttrs();
|
||||||
|
|
||||||
const props = defineProps({
|
defineOptions({ name: 'N8nButton' });
|
||||||
label: {
|
const props = withDefaults(defineProps<ButtonProps>(), {
|
||||||
type: String,
|
label: '',
|
||||||
default: '',
|
type: 'primary',
|
||||||
},
|
size: 'medium',
|
||||||
type: {
|
loading: false,
|
||||||
type: String,
|
disabled: false,
|
||||||
default: 'primary',
|
outline: false,
|
||||||
},
|
text: false,
|
||||||
size: {
|
block: false,
|
||||||
type: String,
|
active: false,
|
||||||
default: 'medium',
|
square: false,
|
||||||
},
|
element: 'button',
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
outline: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: [String, Array],
|
|
||||||
},
|
|
||||||
block: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
float: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
square: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
element: {
|
|
||||||
type: String,
|
|
||||||
default: 'button',
|
|
||||||
validator: (value: string) => ['button', 'a'].includes(value),
|
|
||||||
},
|
|
||||||
href: {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
|
|
|
@ -15,72 +15,57 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
|
|
||||||
const CALLOUT_DEFAULT_ICONS: { [key: string]: string } = {
|
const THEMES = ['info', 'success', 'secondary', 'warning', 'danger', 'custom'] as const;
|
||||||
|
export type CalloutTheme = (typeof THEMES)[number];
|
||||||
|
|
||||||
|
const CALLOUT_DEFAULT_ICONS = {
|
||||||
info: 'info-circle',
|
info: 'info-circle',
|
||||||
success: 'check-circle',
|
success: 'check-circle',
|
||||||
warning: 'exclamation-triangle',
|
warning: 'exclamation-triangle',
|
||||||
danger: 'exclamation-triangle',
|
danger: 'exclamation-triangle',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default defineComponent({
|
interface CalloutProps {
|
||||||
name: 'N8nCallout',
|
theme: CalloutTheme;
|
||||||
components: {
|
icon?: string;
|
||||||
N8nText,
|
iconSize?: string;
|
||||||
N8nIcon,
|
iconless?: boolean;
|
||||||
},
|
slim?: boolean;
|
||||||
props: {
|
roundCorners?: boolean;
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
['info', 'success', 'secondary', 'warning', 'danger', 'custom'].includes(value),
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
iconSize: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
},
|
|
||||||
iconless: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
slim: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
roundCorners: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
classes(): string[] {
|
|
||||||
return [
|
|
||||||
'n8n-callout',
|
|
||||||
this.$style.callout,
|
|
||||||
this.$style[this.theme],
|
|
||||||
this.slim ? this.$style.slim : '',
|
|
||||||
this.roundCorners ? this.$style.round : '',
|
|
||||||
];
|
|
||||||
},
|
|
||||||
getIcon(): string {
|
|
||||||
return this.icon ?? CALLOUT_DEFAULT_ICONS?.[this.theme] ?? CALLOUT_DEFAULT_ICONS.info;
|
|
||||||
},
|
|
||||||
getIconSize(): string {
|
|
||||||
if (this.iconSize) {
|
|
||||||
return this.iconSize;
|
|
||||||
}
|
}
|
||||||
if (this.theme === 'secondary') {
|
|
||||||
|
defineOptions({ name: 'N8nCallout' });
|
||||||
|
const props = withDefaults(defineProps<CalloutProps>(), {
|
||||||
|
iconSize: 'medium',
|
||||||
|
roundCorners: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
const classes = computed(() => [
|
||||||
|
'n8n-callout',
|
||||||
|
$style.callout,
|
||||||
|
$style[props.theme],
|
||||||
|
props.slim ? $style.slim : '',
|
||||||
|
props.roundCorners ? $style.round : '',
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getIcon = computed(
|
||||||
|
() => props.icon ?? CALLOUT_DEFAULT_ICONS?.[props.theme] ?? CALLOUT_DEFAULT_ICONS.info,
|
||||||
|
);
|
||||||
|
|
||||||
|
const getIconSize = computed(() => {
|
||||||
|
if (props.iconSize) {
|
||||||
|
return props.iconSize;
|
||||||
|
}
|
||||||
|
if (props.theme === 'secondary') {
|
||||||
return 'medium';
|
return 'medium';
|
||||||
}
|
}
|
||||||
return 'large';
|
return 'large';
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import N8nCallout from './Callout.vue';
|
import N8nCallout from './Callout.vue';
|
||||||
|
export type { CalloutTheme } from './Callout.vue';
|
||||||
export default N8nCallout;
|
export default N8nCallout;
|
||||||
|
|
|
@ -20,28 +20,24 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
interface CardProps {
|
||||||
name: 'N8nCard',
|
hoverable?: boolean;
|
||||||
inheritAttrs: true,
|
}
|
||||||
props: {
|
|
||||||
hoverable: {
|
defineOptions({ name: 'N8nCard' });
|
||||||
type: Boolean,
|
const props = withDefaults(defineProps<CardProps>(), {
|
||||||
default: false,
|
hoverable: false,
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
classes(): Record<string, boolean> {
|
|
||||||
return {
|
|
||||||
card: true,
|
|
||||||
[this.$style.card]: true,
|
|
||||||
[this.$style.hoverable]: this.hoverable,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
const classes = computed(() => ({
|
||||||
|
card: true,
|
||||||
|
[$style.card]: true,
|
||||||
|
[$style.hoverable]: props.hoverable,
|
||||||
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -20,56 +20,38 @@
|
||||||
</ElCheckbox>
|
</ElCheckbox>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { ElCheckbox } from 'element-plus';
|
import { ElCheckbox } from 'element-plus';
|
||||||
import N8nInputLabel from '../N8nInputLabel';
|
import N8nInputLabel from '../N8nInputLabel';
|
||||||
|
|
||||||
export default defineComponent({
|
const LABEL_SIZE = ['small', 'medium'] as const;
|
||||||
name: 'N8nCheckbox',
|
|
||||||
components: {
|
interface CheckboxProps {
|
||||||
ElCheckbox,
|
label?: string;
|
||||||
N8nInputLabel,
|
disabled?: boolean;
|
||||||
},
|
tooltipText?: string;
|
||||||
props: {
|
indeterminate?: boolean;
|
||||||
label: {
|
modelValue?: boolean;
|
||||||
type: String,
|
labelSize?: (typeof LABEL_SIZE)[number];
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
tooltipText: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
indeterminate: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
labelSize: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
validator: (value: string): boolean => ['small', 'medium'].includes(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onUpdateModelValue(value: boolean) {
|
|
||||||
this.$emit('update:modelValue', value);
|
|
||||||
},
|
|
||||||
onLabelClick() {
|
|
||||||
const checkboxComponent = this.$refs.checkbox as ElCheckbox;
|
|
||||||
if (!checkboxComponent) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(checkboxComponent.$el as HTMLElement).click();
|
defineOptions({ name: 'N8nCheckbox' });
|
||||||
},
|
withDefaults(defineProps<CheckboxProps>(), {
|
||||||
},
|
disabled: false,
|
||||||
|
indeterminate: false,
|
||||||
|
modelValue: false,
|
||||||
|
labelSize: 'medium',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $emit = defineEmits(['update:modelValue']);
|
||||||
|
const onUpdateModelValue = (value: boolean) => $emit('update:modelValue', value);
|
||||||
|
|
||||||
|
const checkbox = ref<InstanceType<typeof ElCheckbox>>();
|
||||||
|
const onLabelClick = () => {
|
||||||
|
if (!checkbox?.value) return;
|
||||||
|
(checkbox.value.$el as HTMLElement).click();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { uid } from '../../utils';
|
||||||
import { ElColorPicker } from 'element-plus';
|
import { ElColorPicker } from 'element-plus';
|
||||||
import N8nInput from '../N8nInput';
|
import N8nInput from '../N8nInput';
|
||||||
|
|
||||||
export type Props = {
|
export type ColorPickerProps = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
size?: 'small' | 'medium' | 'mini';
|
size?: 'small' | 'medium' | 'mini';
|
||||||
showAlpha?: boolean;
|
showAlpha?: boolean;
|
||||||
|
@ -16,21 +16,21 @@ export type Props = {
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
defineOptions({ name: 'N8nColorPicker' });
|
||||||
|
const props = withDefaults(defineProps<ColorPickerProps>(), {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
size: 'medium',
|
size: 'medium',
|
||||||
showAlpha: false,
|
showAlpha: false,
|
||||||
colorFormat: 'hex',
|
colorFormat: 'hex',
|
||||||
popperClass: '',
|
popperClass: '',
|
||||||
showInput: true,
|
showInput: true,
|
||||||
modelValue: null,
|
|
||||||
name: uid('color-picker'),
|
name: uid('color-picker'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const color = ref(props.modelValue);
|
const color = ref(props.modelValue);
|
||||||
|
|
||||||
const colorPickerProps = computed(() => {
|
const colorPickerProps = computed(() => {
|
||||||
const { value, showInput, ...rest } = props;
|
const { showInput, ...rest } = props;
|
||||||
return rest;
|
return rest;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ const onActiveChange = (value: string) => {
|
||||||
emit('active-change', value);
|
emit('active-change', value);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span :class="['n8n-color-picker', $style.component]">
|
<span :class="['n8n-color-picker', $style.component]">
|
||||||
<ElColorPicker
|
<ElColorPicker
|
||||||
|
@ -82,6 +83,7 @@ const onActiveChange = (value: string) => {
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.component {
|
.component {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
|
@ -1,118 +1,3 @@
|
||||||
<script lang="ts">
|
|
||||||
import type { PropType } from 'vue';
|
|
||||||
import { computed, defineComponent, ref, useCssModule } from 'vue';
|
|
||||||
import type { DatatableColumn, DatatableRow, DatatableRowDataType } from '../../types';
|
|
||||||
import { getValueByPath } from '../../utils';
|
|
||||||
import { useI18n } from '../../composables/useI18n';
|
|
||||||
import N8nSelect from '../N8nSelect';
|
|
||||||
import N8nOption from '../N8nOption';
|
|
||||||
import N8nPagination from '../N8nPagination';
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: 'N8nDatatable',
|
|
||||||
components: {
|
|
||||||
N8nSelect,
|
|
||||||
N8nOption,
|
|
||||||
N8nPagination,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
columns: {
|
|
||||||
type: Array as PropType<DatatableColumn[]>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
rows: {
|
|
||||||
type: Array as PropType<DatatableRow[]>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
currentPage: {
|
|
||||||
type: Number,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
pagination: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
rowsPerPage: {
|
|
||||||
type: [Number, String] as PropType<number | '*'>,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
emits: ['update:currentPage', 'update:rowsPerPage'],
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const { t } = useI18n();
|
|
||||||
const rowsPerPageOptions = ref([10, 25, 50, 100]);
|
|
||||||
|
|
||||||
const style = useCssModule();
|
|
||||||
|
|
||||||
const totalPages = computed(() => {
|
|
||||||
if (props.rowsPerPage === '*') {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.ceil(props.rows.length / props.rowsPerPage);
|
|
||||||
});
|
|
||||||
|
|
||||||
const totalRows = computed(() => {
|
|
||||||
return props.rows.length;
|
|
||||||
});
|
|
||||||
|
|
||||||
const visibleRows = computed(() => {
|
|
||||||
if (props.rowsPerPage === '*') {
|
|
||||||
return props.rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = (props.currentPage - 1) * props.rowsPerPage;
|
|
||||||
const end = start + props.rowsPerPage;
|
|
||||||
|
|
||||||
return props.rows.slice(start, end);
|
|
||||||
});
|
|
||||||
|
|
||||||
const classes = computed(() => {
|
|
||||||
return {
|
|
||||||
datatable: true,
|
|
||||||
[style.datatableWrapper]: true,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
function onUpdateCurrentPage(value: number) {
|
|
||||||
emit('update:currentPage', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRowsPerPageChange(value: number | '*') {
|
|
||||||
emit('update:rowsPerPage', value);
|
|
||||||
|
|
||||||
const maxPage = value === '*' ? 1 : Math.ceil(totalRows.value / value);
|
|
||||||
if (maxPage < props.currentPage) {
|
|
||||||
onUpdateCurrentPage(maxPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTdValue(row: DatatableRow, column: DatatableColumn) {
|
|
||||||
return getValueByPath<DatatableRowDataType>(row, column.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getThStyle(column: DatatableColumn) {
|
|
||||||
return {
|
|
||||||
...(column.width ? { width: column.width } : {}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
t,
|
|
||||||
classes,
|
|
||||||
totalPages,
|
|
||||||
totalRows,
|
|
||||||
visibleRows,
|
|
||||||
rowsPerPageOptions,
|
|
||||||
getTdValue,
|
|
||||||
getThStyle,
|
|
||||||
onUpdateCurrentPage,
|
|
||||||
onRowsPerPageChange,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="classes" v-bind="$attrs">
|
<div :class="classes" v-bind="$attrs">
|
||||||
<table :class="$style.datatable">
|
<table :class="$style.datatable">
|
||||||
|
@ -175,6 +60,89 @@ export default defineComponent({
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref, useCssModule } from 'vue';
|
||||||
|
import N8nSelect from '../N8nSelect';
|
||||||
|
import N8nOption from '../N8nOption';
|
||||||
|
import N8nPagination from '../N8nPagination';
|
||||||
|
import type { DatatableColumn, DatatableRow, DatatableRowDataType } from '../../types';
|
||||||
|
import { useI18n } from '../../composables/useI18n';
|
||||||
|
import { getValueByPath } from '../../utils';
|
||||||
|
|
||||||
|
interface DatatableProps {
|
||||||
|
columns: DatatableColumn[];
|
||||||
|
rows: DatatableRow[];
|
||||||
|
currentPage?: number;
|
||||||
|
pagination?: boolean;
|
||||||
|
rowsPerPage?: number | '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({ name: 'N8nDatatable' });
|
||||||
|
const props = withDefaults(defineProps<DatatableProps>(), {
|
||||||
|
currentPage: 1,
|
||||||
|
pagination: true,
|
||||||
|
rowsPerPage: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
const $emit = defineEmits(['update:currentPage', 'update:rowsPerPage']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const rowsPerPageOptions = ref([10, 25, 50, 100]);
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
|
||||||
|
const totalPages = computed(() => {
|
||||||
|
if (props.rowsPerPage === '*') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.ceil(props.rows.length / props.rowsPerPage);
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalRows = computed(() => {
|
||||||
|
return props.rows.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibleRows = computed(() => {
|
||||||
|
if (props.rowsPerPage === '*') {
|
||||||
|
return props.rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = (props.currentPage - 1) * props.rowsPerPage;
|
||||||
|
const end = start + props.rowsPerPage;
|
||||||
|
|
||||||
|
return props.rows.slice(start, end);
|
||||||
|
});
|
||||||
|
|
||||||
|
const classes = computed(() => ({
|
||||||
|
datatable: true,
|
||||||
|
[$style.datatableWrapper]: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
function onUpdateCurrentPage(value: number) {
|
||||||
|
$emit('update:currentPage', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowsPerPageChange(value: number | '*') {
|
||||||
|
$emit('update:rowsPerPage', value);
|
||||||
|
|
||||||
|
const maxPage = value === '*' ? 1 : Math.ceil(totalRows.value / value);
|
||||||
|
if (maxPage < props.currentPage) {
|
||||||
|
onUpdateCurrentPage(maxPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTdValue(row: DatatableRow, column: DatatableColumn) {
|
||||||
|
return getValueByPath<DatatableRowDataType>(row, column.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getThStyle(column: DatatableColumn) {
|
||||||
|
return {
|
||||||
|
...(column.width ? { width: column.width } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.datatableWrapper {
|
.datatableWrapper {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -16,81 +16,81 @@ exports[`components > N8nDatatable > should render correctly 1`] = `
|
||||||
<td class=""><span>1</span></td>
|
<td class=""><span>1</span></td>
|
||||||
<td class=""><span>Richard Hendricks</span></td>
|
<td class=""><span>Richard Hendricks</span></td>
|
||||||
<td class=""><span>29</span></td>
|
<td class=""><span>29</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 1</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>2</span></td>
|
<td class=""><span>2</span></td>
|
||||||
<td class=""><span>Bertram Gilfoyle</span></td>
|
<td class=""><span>Bertram Gilfoyle</span></td>
|
||||||
<td class=""><span>44</span></td>
|
<td class=""><span>44</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 2</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>3</span></td>
|
<td class=""><span>3</span></td>
|
||||||
<td class=""><span>Dinesh Chugtai</span></td>
|
<td class=""><span>Dinesh Chugtai</span></td>
|
||||||
<td class=""><span>31</span></td>
|
<td class=""><span>31</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 3</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>4</span></td>
|
<td class=""><span>4</span></td>
|
||||||
<td class=""><span>Jared Dunn </span></td>
|
<td class=""><span>Jared Dunn </span></td>
|
||||||
<td class=""><span>38</span></td>
|
<td class=""><span>38</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 4</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>5</span></td>
|
<td class=""><span>5</span></td>
|
||||||
<td class=""><span>Richard Hendricks</span></td>
|
<td class=""><span>Richard Hendricks</span></td>
|
||||||
<td class=""><span>29</span></td>
|
<td class=""><span>29</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 5</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>6</span></td>
|
<td class=""><span>6</span></td>
|
||||||
<td class=""><span>Bertram Gilfoyle</span></td>
|
<td class=""><span>Bertram Gilfoyle</span></td>
|
||||||
<td class=""><span>44</span></td>
|
<td class=""><span>44</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 6</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>7</span></td>
|
<td class=""><span>7</span></td>
|
||||||
<td class=""><span>Dinesh Chugtai</span></td>
|
<td class=""><span>Dinesh Chugtai</span></td>
|
||||||
<td class=""><span>31</span></td>
|
<td class=""><span>31</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 7</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>8</span></td>
|
<td class=""><span>8</span></td>
|
||||||
<td class=""><span>Jared Dunn </span></td>
|
<td class=""><span>Jared Dunn </span></td>
|
||||||
<td class=""><span>38</span></td>
|
<td class=""><span>38</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 8</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>9</span></td>
|
<td class=""><span>9</span></td>
|
||||||
<td class=""><span>Richard Hendricks</span></td>
|
<td class=""><span>Richard Hendricks</span></td>
|
||||||
<td class=""><span>29</span></td>
|
<td class=""><span>29</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 9</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class=""><span>10</span></td>
|
<td class=""><span>10</span></td>
|
||||||
<td class=""><span>Bertram Gilfoyle</span></td>
|
<td class=""><span>Bertram Gilfoyle</span></td>
|
||||||
<td class=""><span>44</span></td>
|
<td class=""><span>44</span></td>
|
||||||
<td class=""><button class="button button primary medium" aria-live="polite" column="[object Object]">
|
<td class="">
|
||||||
<!--v-if--><span>Button 10</span>
|
<n8n-button-stub block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||||
</button></td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -38,70 +38,40 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import N8nFormInputs from '../N8nFormInputs';
|
import N8nFormInputs from '../N8nFormInputs';
|
||||||
import N8nHeading from '../N8nHeading';
|
import N8nHeading from '../N8nHeading';
|
||||||
import N8nLink from '../N8nLink';
|
import N8nLink from '../N8nLink';
|
||||||
import N8nButton from '../N8nButton';
|
import N8nButton from '../N8nButton';
|
||||||
|
import type { IFormInput } from '@/types';
|
||||||
import { createEventBus } from '../../utils';
|
import { createEventBus } from '../../utils';
|
||||||
|
|
||||||
export default defineComponent({
|
interface FormBoxProps {
|
||||||
name: 'N8nFormBox',
|
title?: string;
|
||||||
components: {
|
inputs?: IFormInput[];
|
||||||
N8nHeading,
|
buttonText?: string;
|
||||||
N8nFormInputs,
|
buttonLoading?: boolean;
|
||||||
N8nLink,
|
secondaryButtonText?: string;
|
||||||
N8nButton,
|
redirectText?: string;
|
||||||
},
|
redirectLink?: string;
|
||||||
props: {
|
}
|
||||||
title: {
|
|
||||||
type: String,
|
defineOptions({ name: 'N8nFormBox' });
|
||||||
default: '',
|
withDefaults(defineProps<FormBoxProps>(), {
|
||||||
},
|
title: '',
|
||||||
inputs: {
|
inputs: () => [],
|
||||||
type: Array,
|
buttonLoading: false,
|
||||||
default: () => [],
|
redirectText: '',
|
||||||
},
|
redirectLink: '',
|
||||||
buttonText: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
buttonLoading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
secondaryButtonText: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
redirectText: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
redirectLink: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
formBus: createEventBus(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onUpdateModelValue(e: { name: string; value: string }) {
|
|
||||||
this.$emit('update', e);
|
|
||||||
},
|
|
||||||
onSubmit(e: { [key: string]: string }) {
|
|
||||||
this.$emit('submit', e);
|
|
||||||
},
|
|
||||||
onButtonClick() {
|
|
||||||
this.formBus.emit('submit');
|
|
||||||
},
|
|
||||||
onSecondaryButtonClick(event: Event) {
|
|
||||||
this.$emit('secondaryClick', event);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const formBus = createEventBus();
|
||||||
|
const $emit = defineEmits(['submit', 'update', 'secondaryClick']);
|
||||||
|
|
||||||
|
const onUpdateModelValue = (e: { name: string; value: string }) => $emit('update', e);
|
||||||
|
const onSubmit = (e: { [key: string]: string }) => $emit('submit', e);
|
||||||
|
const onButtonClick = () => formBus.emit('submit');
|
||||||
|
const onSecondaryButtonClick = (event: Event) => $emit('secondaryClick', event);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -143,7 +143,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
tagSize: 'small',
|
tagSize: 'small',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const $emit = defineEmits<{
|
||||||
(event: 'validate', shouldValidate: boolean): void;
|
(event: 'validate', shouldValidate: boolean): void;
|
||||||
(event: 'update:modelValue', value: unknown): void;
|
(event: 'update:modelValue', value: unknown): void;
|
||||||
(event: 'focus'): void;
|
(event: 'focus'): void;
|
||||||
|
@ -203,22 +203,22 @@ function getInputValidationError(): ReturnType<IValidator['validate']> {
|
||||||
function onBlur() {
|
function onBlur() {
|
||||||
state.hasBlurred = true;
|
state.hasBlurred = true;
|
||||||
state.isTyping = false;
|
state.isTyping = false;
|
||||||
emit('blur');
|
$emit('blur');
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUpdateModelValue(value: FormState) {
|
function onUpdateModelValue(value: FormState) {
|
||||||
state.isTyping = true;
|
state.isTyping = true;
|
||||||
emit('update:modelValue', value);
|
$emit('update:modelValue', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFocus() {
|
function onFocus() {
|
||||||
emit('focus');
|
$emit('focus');
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnter(event: Event) {
|
function onEnter(event: Event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
emit('enter');
|
$emit('enter');
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationError = computed<string | null>(() => {
|
const validationError = computed<string | null>(() => {
|
||||||
|
@ -244,14 +244,14 @@ const showErrors = computed(
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
emit('validate', !validationError.value);
|
$emit('validate', !validationError.value);
|
||||||
|
|
||||||
if (props.focusInitially && inputRef.value) inputRef.value.focus();
|
if (props.focusInitially && inputRef.value) inputRef.value.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => validationError.value,
|
() => validationError.value,
|
||||||
(error) => emit('validate', !error),
|
(error) => $emit('validate', !error),
|
||||||
);
|
);
|
||||||
|
|
||||||
defineExpose({ inputRef });
|
defineExpose({ inputRef });
|
||||||
|
|
|
@ -4,55 +4,49 @@
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
const SIZES = ['2xlarge', 'xlarge', 'large', 'medium', 'small'] as const;
|
||||||
name: 'N8nHeading',
|
const COLORS = [
|
||||||
props: {
|
'primary',
|
||||||
tag: {
|
'text-dark',
|
||||||
type: String,
|
'text-base',
|
||||||
default: 'span',
|
'text-light',
|
||||||
},
|
'text-xlight',
|
||||||
bold: {
|
'danger',
|
||||||
type: Boolean,
|
] as const;
|
||||||
default: false,
|
const ALIGN = ['right', 'left', 'center'] as const;
|
||||||
},
|
|
||||||
size: {
|
interface HeadingProps {
|
||||||
type: String,
|
tag?: string;
|
||||||
default: 'medium',
|
bold?: boolean;
|
||||||
validator: (value: string): boolean =>
|
size?: (typeof SIZES)[number];
|
||||||
['2xlarge', 'xlarge', 'large', 'medium', 'small'].includes(value),
|
color?: (typeof COLORS)[number];
|
||||||
},
|
align?: (typeof ALIGN)[number];
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
['primary', 'text-dark', 'text-base', 'text-light', 'text-xlight', 'danger'].includes(
|
|
||||||
value,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
align: {
|
|
||||||
type: String,
|
|
||||||
validator: (value: string): boolean => ['right', 'left', 'center'].includes(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
classes() {
|
|
||||||
const applied = [];
|
|
||||||
if (this.align) {
|
|
||||||
applied.push(`align-${this.align}`);
|
|
||||||
}
|
|
||||||
if (this.color) {
|
|
||||||
applied.push(this.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
applied.push(`size-${this.size}`);
|
defineOptions({ name: 'N8nHeading' });
|
||||||
|
const props = withDefaults(defineProps<HeadingProps>(), {
|
||||||
|
tag: 'span',
|
||||||
|
bold: false,
|
||||||
|
size: 'medium',
|
||||||
|
});
|
||||||
|
|
||||||
applied.push(this.bold ? 'bold' : 'regular');
|
const $style = useCssModule();
|
||||||
|
const classes = computed(() => {
|
||||||
|
const applied: string[] = [];
|
||||||
|
if (props.align) {
|
||||||
|
applied.push(`align-${props.align}`);
|
||||||
|
}
|
||||||
|
if (props.color) {
|
||||||
|
applied.push(props.color);
|
||||||
|
}
|
||||||
|
|
||||||
return applied.map((c) => this.$style[c]);
|
applied.push(`size-${props.size}`);
|
||||||
},
|
applied.push(props.bold ? 'bold' : 'regular');
|
||||||
},
|
|
||||||
|
return applied.map((c) => $style[c]);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -4,34 +4,22 @@
|
||||||
</N8nText>
|
</N8nText>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
|
import type { IconSize, IconColor } from '@/types/icon';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
interface IconProps {
|
||||||
|
icon: string;
|
||||||
|
size?: IconSize;
|
||||||
|
spin?: boolean;
|
||||||
|
color?: IconColor;
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
defineOptions({ name: 'N8nIcon' });
|
||||||
name: 'N8nIcon',
|
withDefaults(defineProps<IconProps>(), {
|
||||||
components: {
|
size: 'medium',
|
||||||
FontAwesomeIcon,
|
spin: false,
|
||||||
N8nText,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
icon: {
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
},
|
|
||||||
spin: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -2,53 +2,18 @@
|
||||||
<N8nButton square v-bind="{ ...$attrs, ...$props }" />
|
<N8nButton square v-bind="{ ...$attrs, ...$props }" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import type { IconButtonProps } from '@/types/button';
|
||||||
import N8nButton from '../N8nButton';
|
import N8nButton from '../N8nButton';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
defineOptions({ name: 'N8nIconButton' });
|
||||||
|
withDefaults(defineProps<IconButtonProps>(), {
|
||||||
export default defineComponent({
|
type: 'primary',
|
||||||
name: 'N8nIconButton',
|
size: 'medium',
|
||||||
components: {
|
loading: false,
|
||||||
N8nButton,
|
outline: false,
|
||||||
},
|
text: false,
|
||||||
props: {
|
disabled: false,
|
||||||
type: {
|
active: false,
|
||||||
type: String,
|
|
||||||
default: 'primary',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
outline: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
active: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: [String, Array],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
float: {
|
|
||||||
type: String,
|
|
||||||
validator: (value: string): boolean => ['left', 'right'].includes(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -38,75 +38,53 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import { onMounted } from 'vue';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
import type { PropType } from 'vue';
|
import type { IconColor } from '@/types/icon';
|
||||||
import { defineComponent } from 'vue';
|
import { createEventBus, type EventBus } from '../../utils';
|
||||||
import type { EventBus } from '../../utils';
|
|
||||||
import { createEventBus } from '../../utils';
|
|
||||||
|
|
||||||
export interface IAccordionItem {
|
interface IAccordionItem {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
iconColor?: string;
|
iconColor?: IconColor;
|
||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
interface InfoAccordionProps {
|
||||||
name: 'N8nInfoAccordion',
|
title?: string;
|
||||||
components: {
|
description?: string;
|
||||||
N8nText,
|
items?: IAccordionItem[];
|
||||||
N8nIcon,
|
initiallyExpanded?: boolean;
|
||||||
},
|
headerIcon?: { icon: string; color: IconColor };
|
||||||
props: {
|
eventBus?: EventBus;
|
||||||
title: {
|
}
|
||||||
type: String,
|
|
||||||
},
|
defineOptions({ name: 'N8nInfoAccordion' });
|
||||||
description: {
|
const props = withDefaults(defineProps<InfoAccordionProps>(), {
|
||||||
type: String,
|
items: () => [],
|
||||||
},
|
initiallyExpanded: false,
|
||||||
items: {
|
eventBus: () => createEventBus(),
|
||||||
type: Array as PropType<IAccordionItem[]>,
|
});
|
||||||
default: () => [],
|
const $emit = defineEmits(['click:body', 'tooltipClick']);
|
||||||
},
|
|
||||||
initiallyExpanded: {
|
let expanded = false;
|
||||||
type: Boolean,
|
onMounted(() => {
|
||||||
default: false,
|
props.eventBus.on('expand', () => {
|
||||||
},
|
expanded = true;
|
||||||
headerIcon: {
|
});
|
||||||
type: Object as PropType<{ icon: string; color: string }>,
|
expanded = props.initiallyExpanded;
|
||||||
required: false,
|
});
|
||||||
},
|
|
||||||
eventBus: {
|
const toggle = () => {
|
||||||
type: Object as PropType<EventBus>,
|
expanded = !expanded;
|
||||||
default: () => createEventBus(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
expanded: false,
|
|
||||||
};
|
};
|
||||||
},
|
|
||||||
mounted() {
|
const onClick = (e: MouseEvent) => $emit('click:body', e);
|
||||||
this.eventBus.on('expand', () => {
|
|
||||||
this.expanded = true;
|
const onTooltipClick = (item: string, event: MouseEvent) => $emit('tooltipClick', item, event);
|
||||||
});
|
|
||||||
this.expanded = this.initiallyExpanded;
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggle() {
|
|
||||||
this.expanded = !this.expanded;
|
|
||||||
},
|
|
||||||
onClick(e: MouseEvent) {
|
|
||||||
this.$emit('click:body', e);
|
|
||||||
},
|
|
||||||
onTooltipClick(item: string, event: MouseEvent) {
|
|
||||||
this.$emit('tooltipClick', item, event);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -32,42 +32,32 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import type { Placement } from 'element-plus';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
import N8nTooltip from '../N8nTooltip';
|
import N8nTooltip from '../N8nTooltip';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
const THEME = ['info', 'info-light', 'warning', 'danger', 'success'] as const;
|
||||||
|
const TYPE = ['note', 'tooltip'] as const;
|
||||||
|
|
||||||
export default defineComponent({
|
interface InfoTipProps {
|
||||||
name: 'N8nInfoTip',
|
theme?: (typeof THEME)[number];
|
||||||
components: {
|
type?: (typeof TYPE)[number];
|
||||||
N8nIcon,
|
bold?: boolean;
|
||||||
N8nTooltip,
|
tooltipPlacement?: Placement;
|
||||||
},
|
}
|
||||||
props: {
|
|
||||||
theme: {
|
defineOptions({ name: 'N8nInfoTip' });
|
||||||
type: String,
|
const props = withDefaults(defineProps<InfoTipProps>(), {
|
||||||
default: 'info',
|
theme: 'info',
|
||||||
validator: (value: string): boolean =>
|
type: 'note',
|
||||||
['info', 'info-light', 'warning', 'danger', 'success'].includes(value),
|
bold: true,
|
||||||
},
|
tooltipPlacement: 'top',
|
||||||
type: {
|
});
|
||||||
type: String,
|
|
||||||
default: 'note',
|
const iconData = computed((): { icon: string; color: string } => {
|
||||||
validator: (value: string): boolean => ['note', 'tooltip'].includes(value),
|
switch (props.theme) {
|
||||||
},
|
|
||||||
bold: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
tooltipPlacement: {
|
|
||||||
type: String,
|
|
||||||
default: 'top',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
iconData(): { icon: string; color: string } {
|
|
||||||
switch (this.theme) {
|
|
||||||
case 'info':
|
case 'info':
|
||||||
return {
|
return {
|
||||||
icon: 'info-circle',
|
icon: 'info-circle',
|
||||||
|
@ -99,8 +89,6 @@ export default defineComponent({
|
||||||
color: '--color-text-light)',
|
color: '--color-text-light)',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -22,133 +22,68 @@
|
||||||
</ElInput>
|
</ElInput>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
import { ElInput } from 'element-plus';
|
import { ElInput } from 'element-plus';
|
||||||
import type { PropType } from 'vue';
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { uid } from '../../utils';
|
import { uid } from '../../utils';
|
||||||
|
|
||||||
type InputRef = InstanceType<typeof ElInput>;
|
const INPUT = ['text', 'textarea', 'number', 'password', 'email'] as const;
|
||||||
|
const SIZE = ['mini', 'small', 'medium', 'large', 'xlarge'] as const;
|
||||||
|
|
||||||
export default defineComponent({
|
interface InputProps {
|
||||||
name: 'N8nInput',
|
modelValue?: string | number;
|
||||||
components: {
|
type?: (typeof INPUT)[number];
|
||||||
ElInput,
|
size?: (typeof SIZE)[number];
|
||||||
},
|
placeholder?: string;
|
||||||
props: {
|
disabled?: boolean;
|
||||||
modelValue: {
|
readonly?: boolean;
|
||||||
type: [String, Number] as PropType<string | number>,
|
clearable?: boolean;
|
||||||
default: '',
|
rows?: number;
|
||||||
},
|
maxlength?: number;
|
||||||
type: {
|
title?: string;
|
||||||
type: String,
|
name?: string;
|
||||||
validator: (value: string): boolean =>
|
autocomplete?: 'off' | 'autocomplete';
|
||||||
['text', 'textarea', 'number', 'password', 'email'].includes(value),
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'large',
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
['mini', 'small', 'medium', 'large', 'xlarge'].includes(value),
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
readonly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
clearable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
rows: {
|
|
||||||
type: Number,
|
|
||||||
default: 2,
|
|
||||||
},
|
|
||||||
maxlength: {
|
|
||||||
type: Number,
|
|
||||||
default: Infinity,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: () => uid('input'),
|
|
||||||
},
|
|
||||||
autocomplete: {
|
|
||||||
type: String,
|
|
||||||
default: 'off',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
computedSize(): string | undefined {
|
|
||||||
if (this.size === 'xlarge') {
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.size;
|
defineOptions({ name: 'N8nInput' });
|
||||||
},
|
const props = withDefaults(defineProps<InputProps>(), {
|
||||||
classes(): string[] {
|
modelValue: '',
|
||||||
const classes = [];
|
size: 'large',
|
||||||
if (this.size === 'xlarge') {
|
placeholder: '',
|
||||||
classes.push('xlarge');
|
disabled: false,
|
||||||
}
|
readonly: false,
|
||||||
if (this.type === 'password') {
|
clearable: false,
|
||||||
classes.push('ph-no-capture');
|
rows: 2,
|
||||||
}
|
maxlength: Infinity,
|
||||||
return classes;
|
title: '',
|
||||||
},
|
name: () => uid('input'),
|
||||||
},
|
autocomplete: 'off',
|
||||||
methods: {
|
|
||||||
focus() {
|
|
||||||
const innerInput = this.$refs.innerInput as InputRef | undefined;
|
|
||||||
|
|
||||||
if (!innerInput) return;
|
|
||||||
|
|
||||||
const inputElement = innerInput.$el.querySelector(
|
|
||||||
this.type === 'textarea' ? 'textarea' : 'input',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!inputElement) return;
|
|
||||||
|
|
||||||
inputElement.focus();
|
|
||||||
},
|
|
||||||
blur() {
|
|
||||||
const innerInput = this.$refs.innerInput as InputRef | undefined;
|
|
||||||
|
|
||||||
if (!innerInput) return;
|
|
||||||
|
|
||||||
const inputElement = innerInput.$el.querySelector(
|
|
||||||
this.type === 'textarea' ? 'textarea' : 'input',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!inputElement) return;
|
|
||||||
|
|
||||||
inputElement.blur();
|
|
||||||
},
|
|
||||||
select() {
|
|
||||||
const innerInput = this.$refs.innerInput as InputRef | undefined;
|
|
||||||
|
|
||||||
if (!innerInput) return;
|
|
||||||
|
|
||||||
const inputElement = innerInput.$el.querySelector(
|
|
||||||
this.type === 'textarea' ? 'textarea' : 'input',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!inputElement) return;
|
|
||||||
|
|
||||||
inputElement.select();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const computedSize = computed(() => (props.size === 'xlarge' ? undefined : props.size));
|
||||||
|
|
||||||
|
const classes = computed(() => {
|
||||||
|
const applied: string[] = [];
|
||||||
|
if (props.size === 'xlarge') {
|
||||||
|
applied.push('xlarge');
|
||||||
|
}
|
||||||
|
if (props.type === 'password') {
|
||||||
|
applied.push('ph-no-capture');
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
});
|
||||||
|
|
||||||
|
const innerInput = ref<InstanceType<typeof ElInput>>();
|
||||||
|
const inputElement = computed(() => {
|
||||||
|
if (!innerInput?.value) return;
|
||||||
|
const inputType = props.type === 'textarea' ? 'textarea' : 'input';
|
||||||
|
return (innerInput.value.$el as HTMLElement).querySelector(inputType);
|
||||||
|
});
|
||||||
|
|
||||||
|
const focus = () => inputElement.value?.focus();
|
||||||
|
const blur = () => inputElement.value?.blur();
|
||||||
|
const select = () => inputElement.value?.select();
|
||||||
|
defineExpose({ focus, blur, select });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -45,65 +45,37 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import N8nTooltip from '../N8nTooltip';
|
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
|
import N8nTooltip from '../N8nTooltip';
|
||||||
|
import type { TextColor } from '@/types/text';
|
||||||
|
|
||||||
import { addTargetBlank } from '../utils/helpers';
|
const SIZE = ['small', 'medium'] as const;
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
defineOptions({ name: 'N8nInputLabel' });
|
||||||
name: 'N8nInputLabel',
|
withDefaults(defineProps<InputLabelProps>(), {
|
||||||
components: {
|
compact: false,
|
||||||
N8nText,
|
bold: true,
|
||||||
N8nIcon,
|
size: 'medium',
|
||||||
N8nTooltip,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
compact: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
tooltipText: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
inputName: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
bold: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'medium',
|
|
||||||
validator: (value: string): boolean => ['small', 'medium'].includes(value),
|
|
||||||
},
|
|
||||||
underline: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
showTooltip: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
showOptions: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addTargetBlank,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const addTargetBlank = (html: string) =>
|
||||||
|
html && html.includes('href=') ? html.replace(/href=/g, 'target="_blank" href=') : html;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -8,43 +8,27 @@
|
||||||
</N8nRoute>
|
</N8nRoute>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import N8nRoute from '../N8nRoute';
|
import N8nRoute, { type RouteTo } from '../N8nRoute';
|
||||||
|
import type { TextSize } from '@/types/text';
|
||||||
|
|
||||||
export default defineComponent({
|
const THEME = ['primary', 'danger', 'text', 'secondary'] as const;
|
||||||
name: 'N8nLink',
|
|
||||||
components: {
|
interface LinkProps {
|
||||||
N8nText,
|
size?: TextSize;
|
||||||
N8nRoute,
|
to?: RouteTo;
|
||||||
},
|
newWindow?: boolean;
|
||||||
props: {
|
bold?: boolean;
|
||||||
size: {
|
underline?: boolean;
|
||||||
type: String,
|
theme?: (typeof THEME)[number];
|
||||||
},
|
}
|
||||||
to: {
|
|
||||||
type: String || Object,
|
defineOptions({ name: 'N8nLink' });
|
||||||
},
|
withDefaults(defineProps<LinkProps>(), {
|
||||||
newWindow: {
|
bold: false,
|
||||||
type: Boolean || undefined,
|
underline: false,
|
||||||
default: undefined,
|
theme: 'primary',
|
||||||
},
|
|
||||||
bold: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
underline: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
default: 'primary',
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
['primary', 'danger', 'text', 'secondary'].includes(value),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -35,38 +35,10 @@
|
||||||
</ElSkeleton>
|
</ElSkeleton>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { ElSkeleton, ElSkeletonItem } from 'element-plus';
|
import { ElSkeleton, ElSkeletonItem } from 'element-plus';
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
const VARIANT = [
|
||||||
name: 'N8nLoading',
|
|
||||||
components: {
|
|
||||||
ElSkeleton,
|
|
||||||
ElSkeletonItem,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
animated: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
rows: {
|
|
||||||
type: Number,
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
shrinkLast: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
variant: {
|
|
||||||
type: String,
|
|
||||||
default: 'p',
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
[
|
|
||||||
'custom',
|
'custom',
|
||||||
'p',
|
'p',
|
||||||
'text',
|
'text',
|
||||||
|
@ -78,9 +50,22 @@ export default defineComponent({
|
||||||
'image',
|
'image',
|
||||||
'circle',
|
'circle',
|
||||||
'rect',
|
'rect',
|
||||||
].includes(value),
|
] 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>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -16,111 +16,82 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import N8nLoading from '../N8nLoading';
|
import { computed } from 'vue';
|
||||||
import type { PluginSimple } from 'markdown-it';
|
import type { Options as MarkdownOptions } from 'markdown-it';
|
||||||
import Markdown from 'markdown-it';
|
import Markdown from 'markdown-it';
|
||||||
|
|
||||||
import markdownLink from 'markdown-it-link-attributes';
|
import markdownLink from 'markdown-it-link-attributes';
|
||||||
import markdownEmoji from 'markdown-it-emoji';
|
import markdownEmoji from 'markdown-it-emoji';
|
||||||
import markdownTasklists from 'markdown-it-task-lists';
|
import markdownTaskLists from 'markdown-it-task-lists';
|
||||||
|
|
||||||
import type { PropType } from 'vue';
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
import xss, { friendlyAttrValue } from 'xss';
|
import xss, { friendlyAttrValue } from 'xss';
|
||||||
|
|
||||||
|
import N8nLoading from '../N8nLoading';
|
||||||
import { escapeMarkdown } from '../../utils/markdown';
|
import { escapeMarkdown } from '../../utils/markdown';
|
||||||
|
|
||||||
const DEFAULT_OPTIONS_MARKDOWN = {
|
interface IImage {
|
||||||
html: true,
|
|
||||||
linkify: true,
|
|
||||||
typographer: true,
|
|
||||||
breaks: true,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
const DEFAULT_OPTIONS_LINK_ATTRIBUTES = {
|
|
||||||
attrs: {
|
|
||||||
target: '_blank',
|
|
||||||
rel: 'noopener',
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
const DEFAULT_OPTIONS_TASKLISTS = {
|
|
||||||
label: true,
|
|
||||||
labelAfter: true,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export interface IImage {
|
|
||||||
id: string;
|
id: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Options {
|
interface Options {
|
||||||
markdown: typeof DEFAULT_OPTIONS_MARKDOWN;
|
markdown: MarkdownOptions;
|
||||||
linkAttributes: typeof DEFAULT_OPTIONS_LINK_ATTRIBUTES;
|
linkAttributes: markdownLink.Config;
|
||||||
tasklists: typeof DEFAULT_OPTIONS_TASKLISTS;
|
tasklists: markdownTaskLists.Config;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
interface MarkdownProps {
|
||||||
name: 'N8nMarkdown',
|
content?: string;
|
||||||
components: {
|
withMultiBreaks?: boolean;
|
||||||
N8nLoading,
|
images?: IImage[];
|
||||||
|
loading?: boolean;
|
||||||
|
loadingBlocks?: number;
|
||||||
|
loadingRows?: number;
|
||||||
|
theme?: string;
|
||||||
|
options?: Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<MarkdownProps>(), {
|
||||||
|
content: '',
|
||||||
|
withMultiBreaks: false,
|
||||||
|
images: () => [],
|
||||||
|
loading: false,
|
||||||
|
loadingBlocks: 2,
|
||||||
|
loadingRows: 3,
|
||||||
|
theme: 'markdown',
|
||||||
|
options: () => ({
|
||||||
|
markdown: {
|
||||||
|
html: true,
|
||||||
|
linkify: true,
|
||||||
|
typographer: true,
|
||||||
|
breaks: true,
|
||||||
},
|
},
|
||||||
props: {
|
linkAttributes: {
|
||||||
content: {
|
attrs: {
|
||||||
type: String,
|
target: '_blank',
|
||||||
default: '',
|
rel: 'noopener',
|
||||||
},
|
},
|
||||||
withMultiBreaks: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
},
|
||||||
images: {
|
tasklists: {
|
||||||
type: Array as PropType<IImage[]>,
|
label: true,
|
||||||
default: () => [],
|
labelAfter: true,
|
||||||
},
|
},
|
||||||
loading: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
loadingBlocks: {
|
|
||||||
type: Number,
|
|
||||||
default: 2,
|
|
||||||
},
|
|
||||||
loadingRows: {
|
|
||||||
type: Number,
|
|
||||||
default: 3,
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
type: String,
|
|
||||||
default: 'markdown',
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
type: Object as PropType<Options>,
|
|
||||||
default: (): Options => ({
|
|
||||||
markdown: DEFAULT_OPTIONS_MARKDOWN,
|
|
||||||
linkAttributes: DEFAULT_OPTIONS_LINK_ATTRIBUTES,
|
|
||||||
tasklists: DEFAULT_OPTIONS_TASKLISTS,
|
|
||||||
}),
|
}),
|
||||||
},
|
});
|
||||||
},
|
|
||||||
data(): { md: Markdown } {
|
const { options } = props;
|
||||||
return {
|
const md = new Markdown(options.markdown)
|
||||||
md: new Markdown(this.options.markdown)
|
.use(markdownLink, options.linkAttributes)
|
||||||
.use(markdownLink, this.options.linkAttributes)
|
|
||||||
.use(markdownEmoji)
|
.use(markdownEmoji)
|
||||||
.use(markdownTasklists as PluginSimple, this.options.tasklists),
|
.use(markdownTaskLists, options.tasklists);
|
||||||
};
|
|
||||||
},
|
const htmlContent = computed(() => {
|
||||||
computed: {
|
if (!props.content) {
|
||||||
htmlContent(): string {
|
|
||||||
if (!this.content) {
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageUrls: { [key: string]: string } = {};
|
const imageUrls: { [key: string]: string } = {};
|
||||||
if (this.images) {
|
if (props.images) {
|
||||||
this.images.forEach((image: IImage) => {
|
props.images.forEach((image: IImage) => {
|
||||||
if (!image) {
|
if (!image) {
|
||||||
// Happens if an image got deleted but the workflow
|
// Happens if an image got deleted but the workflow
|
||||||
// still has a reference to it
|
// still has a reference to it
|
||||||
|
@ -131,11 +102,11 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileIdRegex = new RegExp('fileId:([0-9]+)');
|
const fileIdRegex = new RegExp('fileId:([0-9]+)');
|
||||||
let contentToRender = this.content;
|
let contentToRender = props.content;
|
||||||
if (this.withMultiBreaks) {
|
if (props.withMultiBreaks) {
|
||||||
contentToRender = contentToRender.replaceAll('\n\n', '\n \n');
|
contentToRender = contentToRender.replaceAll('\n\n', '\n \n');
|
||||||
}
|
}
|
||||||
const html = this.md.render(escapeMarkdown(contentToRender));
|
const html = md.render(escapeMarkdown(contentToRender));
|
||||||
const safeHtml = xss(html, {
|
const safeHtml = xss(html, {
|
||||||
onTagAttr: (tag, name, value) => {
|
onTagAttr: (tag, name, value) => {
|
||||||
if (tag === 'img' && name === 'src') {
|
if (tag === 'img' && name === 'src') {
|
||||||
|
@ -162,11 +133,11 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
|
|
||||||
return safeHtml;
|
return safeHtml;
|
||||||
},
|
});
|
||||||
},
|
|
||||||
methods: {
|
const $emit = defineEmits(['markdown-click']);
|
||||||
onClick(event: MouseEvent) {
|
const onClick = (event: MouseEvent) => {
|
||||||
let clickedLink = null;
|
let clickedLink: HTMLAnchorElement | null = null;
|
||||||
|
|
||||||
if (event.target instanceof HTMLAnchorElement) {
|
if (event.target instanceof HTMLAnchorElement) {
|
||||||
clickedLink = event.target;
|
clickedLink = event.target;
|
||||||
|
@ -178,10 +149,8 @@ export default defineComponent({
|
||||||
clickedLink = parentLink;
|
clickedLink = parentLink;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$emit('markdown-click', clickedLink, event);
|
$emit('markdown-click', clickedLink, event);
|
||||||
},
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -120,7 +120,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
currentRoute(): RouteObject {
|
currentRoute(): RouteObject {
|
||||||
return (
|
return (
|
||||||
(this as typeof this & { $route: RouteObject }).$route || {
|
this.$route || {
|
||||||
name: '',
|
name: '',
|
||||||
path: '',
|
path: '',
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
currentRoute(): RouteObject {
|
currentRoute(): RouteObject {
|
||||||
return (
|
return (
|
||||||
(this as typeof this & { $route: RouteObject }).$route || {
|
this.$route || {
|
||||||
name: '',
|
name: '',
|
||||||
path: '',
|
path: '',
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
import { defineComponent, type PropType } from 'vue';
|
import { defineComponent, type PropType } from 'vue';
|
||||||
|
import type { Placement } from 'element-plus';
|
||||||
import N8nTooltip from '../N8nTooltip';
|
import N8nTooltip from '../N8nTooltip';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -77,7 +78,7 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
tooltipPosition: {
|
tooltipPosition: {
|
||||||
type: String,
|
type: String as PropType<Placement>,
|
||||||
default: 'top',
|
default: 'top',
|
||||||
},
|
},
|
||||||
badge: { type: Object as PropType<{ src: string; type: string }> },
|
badge: { type: Object as PropType<{ src: string; type: string }> },
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { defineComponent } from 'vue';
|
||||||
import { ElPagination } from 'element-plus';
|
import { ElPagination } from 'element-plus';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
name: 'N8nPagination',
|
||||||
components: {
|
components: {
|
||||||
ElPagination,
|
ElPagination,
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,43 +7,41 @@
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
// TODO: replace `object` with a more detailed type
|
||||||
name: 'N8nRoute',
|
export type RouteTo = string | object;
|
||||||
props: {
|
|
||||||
to: {
|
interface RouteProps {
|
||||||
type: String || Object,
|
to?: RouteTo;
|
||||||
},
|
newWindow?: boolean;
|
||||||
newWindow: {
|
}
|
||||||
type: Boolean || undefined,
|
|
||||||
default: undefined,
|
defineOptions({ name: 'N8nRoute' });
|
||||||
},
|
const props = withDefaults(defineProps<RouteProps>(), {});
|
||||||
},
|
|
||||||
computed: {
|
const useRouterLink = computed(() => {
|
||||||
useRouterLink() {
|
if (props.newWindow) {
|
||||||
if (this.newWindow) {
|
|
||||||
// router-link does not support click events and opening in new window
|
// router-link does not support click events and opening in new window
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.to === 'string') {
|
if (typeof props.to === 'string') {
|
||||||
return this.to.startsWith('/');
|
return props.to.startsWith('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.to !== undefined;
|
return props.to !== undefined;
|
||||||
},
|
});
|
||||||
openNewWindow() {
|
|
||||||
if (this.newWindow !== undefined) {
|
const openNewWindow = computed(() => {
|
||||||
return this.newWindow;
|
if (props.newWindow !== undefined) {
|
||||||
|
return props.newWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.to === 'string') {
|
if (typeof props.to === 'string') {
|
||||||
return !this.to.startsWith('/');
|
return !props.to.startsWith('/');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
import N8nRoute from './Route.vue';
|
import N8nRoute from './Route.vue';
|
||||||
|
export type { RouteTo } from './Route.vue';
|
||||||
export default N8nRoute;
|
export default N8nRoute;
|
||||||
|
|
|
@ -10,31 +10,20 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import type { TextSize } from '@/types/text';
|
||||||
import N8nIcon from '../N8nIcon';
|
import N8nIcon from '../N8nIcon';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
const TYPE = ['dots', 'ring'] as const;
|
||||||
|
|
||||||
export default defineComponent({
|
interface SpinnerProps {
|
||||||
name: 'N8nSpinner',
|
size?: Exclude<TextSize, 'xsmall' | 'mini' | 'xlarge'>;
|
||||||
components: {
|
type?: (typeof TYPE)[number];
|
||||||
N8nIcon,
|
}
|
||||||
},
|
|
||||||
props: {
|
defineOptions({ name: 'N8nSpinner' });
|
||||||
size: {
|
withDefaults(defineProps<SpinnerProps>(), {
|
||||||
type: String,
|
type: 'dots',
|
||||||
validator(value: string): boolean {
|
|
||||||
return ['small', 'medium', 'large'].includes(value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
validator(value: string): boolean {
|
|
||||||
return ['dots', 'ring'].includes(value);
|
|
||||||
},
|
|
||||||
default: 'dots',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
<div v-if="editMode && shouldShowFooter" :class="$style.footer">
|
||||||
<N8nText size="xsmall" aligh="right">
|
<N8nText size="xsmall" align="right">
|
||||||
<span v-html="t('sticky.markdownHint')"></span>
|
<span v-html="t('sticky.markdownHint')"></span>
|
||||||
</N8nText>
|
</N8nText>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,17 +4,12 @@
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
interface TagProps {
|
||||||
|
text: string;
|
||||||
export default defineComponent({
|
}
|
||||||
name: 'N8nTag',
|
defineOptions({ name: 'N8nTag' });
|
||||||
props: {
|
defineProps<TagProps>();
|
||||||
text: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -13,68 +13,55 @@
|
||||||
size="small"
|
size="small"
|
||||||
@click.stop.prevent="onExpand"
|
@click.stop.prevent="onExpand"
|
||||||
>
|
>
|
||||||
{{ t('tags.showMore', `${hiddenTagsLength}`) }}
|
{{ t('tags.showMore', [`${hiddenTagsLength}`]) }}
|
||||||
</N8nLink>
|
</N8nLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
import N8nTag from '../N8nTag';
|
import N8nTag from '../N8nTag';
|
||||||
import N8nLink from '../N8nLink';
|
import N8nLink from '../N8nLink';
|
||||||
import Locale from '../../mixins/locale';
|
import { useI18n } from '../../composables/useI18n';
|
||||||
import type { PropType } from 'vue';
|
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export interface ITag {
|
export interface ITag {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
interface TagsProp {
|
||||||
name: 'N8nTags',
|
tags?: ITag[];
|
||||||
components: {
|
truncate?: boolean;
|
||||||
N8nTag,
|
truncateAt?: number;
|
||||||
N8nLink,
|
|
||||||
},
|
|
||||||
mixins: [Locale],
|
|
||||||
props: {
|
|
||||||
tags: {
|
|
||||||
type: Array as PropType<ITag[]>,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
truncate: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
truncateAt: {
|
|
||||||
type: Number,
|
|
||||||
default: 3,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showAll: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
visibleTags(): ITag[] {
|
|
||||||
if (this.truncate && !this.showAll && this.tags.length > this.truncateAt) {
|
|
||||||
return this.tags.slice(0, this.truncateAt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tags;
|
defineOptions({ name: 'N8nTags' });
|
||||||
},
|
const props = withDefaults(defineProps<TagsProp>(), {
|
||||||
hiddenTagsLength(): number {
|
tags: () => [],
|
||||||
return this.tags.length - this.truncateAt;
|
truncate: false,
|
||||||
},
|
truncateAt: 3,
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onExpand() {
|
|
||||||
this.showAll = true;
|
|
||||||
this.$emit('expand', true);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $emit = defineEmits(['expand', 'click:tag']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
let showAll = false;
|
||||||
|
|
||||||
|
const visibleTags = computed((): ITag[] => {
|
||||||
|
const { tags, truncate, truncateAt } = props;
|
||||||
|
if (truncate && !showAll && tags.length > truncateAt) {
|
||||||
|
return tags.slice(0, truncateAt);
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hiddenTagsLength = computed((): number => props.tags.length - props.truncateAt);
|
||||||
|
|
||||||
|
const onExpand = () => {
|
||||||
|
showAll = true;
|
||||||
|
$emit('expand', true);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -4,69 +4,45 @@
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent } from 'vue';
|
import { computed, useCssModule } from 'vue';
|
||||||
export default defineComponent({
|
import type { TextSize, TextColor, TextAlign } from '@/types/text';
|
||||||
name: 'N8nText',
|
|
||||||
props: {
|
interface TextProps {
|
||||||
bold: {
|
bold?: boolean;
|
||||||
type: Boolean,
|
size?: TextSize;
|
||||||
default: false,
|
color?: TextColor;
|
||||||
},
|
align?: TextAlign;
|
||||||
size: {
|
compact?: boolean;
|
||||||
type: String,
|
tag?: string;
|
||||||
default: 'medium',
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
['xsmall', 'small', 'mini', 'medium', 'large', 'xlarge'].includes(value),
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
validator: (value: string): boolean =>
|
|
||||||
[
|
|
||||||
'primary',
|
|
||||||
'text-dark',
|
|
||||||
'text-base',
|
|
||||||
'text-light',
|
|
||||||
'text-xlight',
|
|
||||||
'danger',
|
|
||||||
'success',
|
|
||||||
'warning',
|
|
||||||
].includes(value),
|
|
||||||
},
|
|
||||||
align: {
|
|
||||||
type: String,
|
|
||||||
validator: (value: string): boolean => ['right', 'left', 'center'].includes(value),
|
|
||||||
},
|
|
||||||
compact: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
tag: {
|
|
||||||
type: String,
|
|
||||||
default: 'span',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
classes() {
|
|
||||||
const applied = [];
|
|
||||||
if (this.align) {
|
|
||||||
applied.push(`align-${this.align}`);
|
|
||||||
}
|
|
||||||
if (this.color) {
|
|
||||||
applied.push(this.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.compact) {
|
defineOptions({ name: 'N8nText' });
|
||||||
|
const props = withDefaults(defineProps<TextProps>(), {
|
||||||
|
bold: false,
|
||||||
|
size: 'medium',
|
||||||
|
compact: false,
|
||||||
|
tag: 'span',
|
||||||
|
});
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
const classes = computed(() => {
|
||||||
|
const applied: string[] = [];
|
||||||
|
if (props.align) {
|
||||||
|
applied.push(`align-${props.align}`);
|
||||||
|
}
|
||||||
|
if (props.color) {
|
||||||
|
applied.push(props.color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.compact) {
|
||||||
applied.push('compact');
|
applied.push('compact');
|
||||||
}
|
}
|
||||||
|
|
||||||
applied.push(`size-${this.size}`);
|
applied.push(`size-${props.size}`);
|
||||||
|
applied.push(props.bold ? 'bold' : 'regular');
|
||||||
|
|
||||||
applied.push(this.bold ? 'bold' : 'regular');
|
return applied.map((c) => $style[c]);
|
||||||
|
|
||||||
return applied.map((c) => this.$style[c]);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -25,61 +25,38 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
|
import { computed, useCssModule } from 'vue';
|
||||||
import N8nText from '../N8nText';
|
import N8nText from '../N8nText';
|
||||||
import N8nAvatar from '../N8nAvatar';
|
import N8nAvatar from '../N8nAvatar';
|
||||||
import N8nBadge from '../N8nBadge';
|
import N8nBadge from '../N8nBadge';
|
||||||
import Locale from '../../mixins/locale';
|
import { useI18n } from '../../composables/useI18n';
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
interface UsersInfoProps {
|
||||||
name: 'N8nUsersInfo',
|
firstName?: string;
|
||||||
components: {
|
lastName?: string;
|
||||||
N8nAvatar,
|
email?: string;
|
||||||
N8nText,
|
isOwner?: boolean;
|
||||||
N8nBadge,
|
isPendingUser?: boolean;
|
||||||
},
|
isCurrentUser?: boolean;
|
||||||
mixins: [Locale],
|
disabled?: boolean;
|
||||||
props: {
|
settings?: object;
|
||||||
firstName: {
|
isSamlLoginEnabled?: boolean;
|
||||||
type: String,
|
}
|
||||||
},
|
|
||||||
lastName: {
|
const props = withDefaults(defineProps<UsersInfoProps>(), {
|
||||||
type: String,
|
disabled: false,
|
||||||
},
|
|
||||||
email: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
isOwner: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
isPendingUser: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
isCurrentUser: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
type: Object,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
isSamlLoginEnabled: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
classes(): Record<string, boolean> {
|
|
||||||
return {
|
|
||||||
[this.$style.container]: true,
|
|
||||||
[this.$style.disabled]: this.disabled,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const $style = useCssModule();
|
||||||
|
const classes = computed(
|
||||||
|
(): Record<string, boolean> => ({
|
||||||
|
[$style.container]: true,
|
||||||
|
[$style.disabled]: props.disabled,
|
||||||
|
}),
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -34,50 +34,33 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import type { IUser, UserAction } from '../../types';
|
import { computed } from 'vue';
|
||||||
import N8nActionToggle from '../N8nActionToggle';
|
import N8nActionToggle from '../N8nActionToggle';
|
||||||
import N8nBadge from '../N8nBadge';
|
import N8nBadge from '../N8nBadge';
|
||||||
import N8nUserInfo from '../N8nUserInfo';
|
import N8nUserInfo from '../N8nUserInfo';
|
||||||
import Locale from '../../mixins/locale';
|
import type { IUser, UserAction } from '../../types';
|
||||||
import type { PropType } from 'vue';
|
import { useI18n } from '../../composables/useI18n';
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
|
|
||||||
export default defineComponent({
|
interface UsersListProps {
|
||||||
name: 'N8nUsersList',
|
users: IUser[];
|
||||||
components: {
|
readonly?: boolean;
|
||||||
N8nActionToggle,
|
currentUserId?: string;
|
||||||
N8nBadge,
|
actions?: UserAction[];
|
||||||
N8nUserInfo,
|
isSamlLoginEnabled?: boolean;
|
||||||
},
|
}
|
||||||
mixins: [Locale],
|
|
||||||
props: {
|
const props = withDefaults(defineProps<UsersListProps>(), {
|
||||||
readonly: {
|
readonly: false,
|
||||||
type: Boolean,
|
users: () => [],
|
||||||
default: false,
|
actions: () => [],
|
||||||
},
|
isSamlLoginEnabled: false,
|
||||||
users: {
|
});
|
||||||
type: Array,
|
|
||||||
required: true,
|
const { t } = useI18n();
|
||||||
default(): IUser[] {
|
|
||||||
return [];
|
const sortedUsers = computed(() =>
|
||||||
},
|
[...props.users].sort((a: IUser, b: IUser) => {
|
||||||
},
|
|
||||||
currentUserId: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
type: Array as PropType<UserAction[]>,
|
|
||||||
default: () => [],
|
|
||||||
},
|
|
||||||
isSamlLoginEnabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
sortedUsers(): IUser[] {
|
|
||||||
return [...(this.users as IUser[])].sort((a: IUser, b: IUser) => {
|
|
||||||
if (!a.email || !b.email) {
|
if (!a.email || !b.email) {
|
||||||
throw new Error('Expected all users to have email');
|
throw new Error('Expected all users to have email');
|
||||||
}
|
}
|
||||||
|
@ -111,24 +94,18 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.email > b.email ? 1 : -1;
|
return a.email > b.email ? 1 : -1;
|
||||||
});
|
}),
|
||||||
},
|
);
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getActions(user: IUser): UserAction[] {
|
|
||||||
if (user.isOwner) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultGuard = () => true;
|
const defaultGuard = () => true;
|
||||||
|
const getActions = (user: IUser): UserAction[] => {
|
||||||
|
if (user.isOwner) return [];
|
||||||
|
|
||||||
return this.actions.filter((action) => (action.guard || defaultGuard)(user));
|
return props.actions.filter((action) => (action.guard || defaultGuard)(user));
|
||||||
},
|
};
|
||||||
onUserAction(user: IUser, action: string): void {
|
|
||||||
this.$emit(action, user.id);
|
const $emit = defineEmits(['*']);
|
||||||
},
|
const onUserAction = (user: IUser, action: string) => $emit(action, user.id);
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export function addTargetBlank(html: string) {
|
|
||||||
return html && html.includes('href=') ? html.replace(/href=/g, 'target="_blank" href=') : html;
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ import { useDeviceSupport } from '@/composables/useDeviceSupport';
|
||||||
describe('useDeviceSupport()', () => {
|
describe('useDeviceSupport()', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
global.window = Object.create(window);
|
global.window = Object.create(window);
|
||||||
global.navigator = { userAgent: 'test-agent', maxTouchPoints: 0 };
|
global.navigator = { userAgent: 'test-agent', maxTouchPoints: 0 } as Navigator;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('isTouchDevice', () => {
|
describe('isTouchDevice', () => {
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { t } from '../locale';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
t(...args: string[]) {
|
t(path: string, ...args: string[]) {
|
||||||
return t.apply(this, args);
|
return t.call(this, path, ...args);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
declare module 'markdown-it-task-lists' {
|
declare module 'markdown-it-task-lists' {
|
||||||
import type { PluginSimple } from 'markdown-it';
|
import type { PluginWithOptions } from 'markdown-it';
|
||||||
export default plugin as PluginSimple<{}>;
|
|
||||||
|
declare namespace markdownItTaskLists {
|
||||||
|
interface Config {
|
||||||
|
enabled?: boolean;
|
||||||
|
label?: boolean;
|
||||||
|
labelAfter?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const markdownItTaskLists: PluginWithOptions<markdownItTaskLists.Config>;
|
||||||
|
|
||||||
|
export = markdownItTaskLists;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,36 @@
|
||||||
export type IN8nButton = {
|
import type { TextFloat } from './text';
|
||||||
attrs: {
|
|
||||||
label: string;
|
const BUTTON_ELEMENT = ['button', 'a'] as const;
|
||||||
type?: 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'danger';
|
export type ButtonElement = (typeof BUTTON_ELEMENT)[number];
|
||||||
size?: 'mini' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
||||||
loading?: boolean;
|
const BUTTON_TYPE = ['primary', 'secondary', 'tertiary', 'success', 'warning', 'danger'] as const;
|
||||||
disabled?: boolean;
|
export type ButtonType = (typeof BUTTON_TYPE)[number];
|
||||||
outline?: boolean;
|
|
||||||
text?: boolean;
|
const BUTTON_SIZE = ['small', 'medium', 'large'] as const;
|
||||||
icon?: string;
|
export type ButtonSize = (typeof BUTTON_SIZE)[number];
|
||||||
block?: boolean;
|
|
||||||
|
export interface IconButtonProps {
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
float?: 'left' | 'right';
|
disabled?: boolean;
|
||||||
|
float?: TextFloat;
|
||||||
|
icon?: string;
|
||||||
|
loading?: boolean;
|
||||||
|
outline?: boolean;
|
||||||
|
size?: ButtonSize;
|
||||||
|
text?: boolean;
|
||||||
|
type?: ButtonType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ButtonProps extends IconButtonProps {
|
||||||
|
block?: boolean;
|
||||||
|
element?: ButtonElement;
|
||||||
|
href?: string;
|
||||||
|
label?: string;
|
||||||
square?: boolean;
|
square?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type IN8nButton = {
|
||||||
|
attrs: ButtonProps & {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
'data-test-id'?: string;
|
'data-test-id'?: string;
|
||||||
};
|
};
|
||||||
|
|
8
packages/design-system/src/types/icon.ts
Normal file
8
packages/design-system/src/types/icon.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
const ICON_SIZE = ['xsmall', 'small', 'medium', 'large'] as const;
|
||||||
|
export type IconSize = (typeof ICON_SIZE)[number];
|
||||||
|
|
||||||
|
const ICON_COLOR = ['primary', 'danger', 'success', 'warning', 'text-base'] as const;
|
||||||
|
export type IconColor = (typeof ICON_COLOR)[number];
|
||||||
|
|
||||||
|
const ICON_ORIENTATION = ['horizontal', 'vertical'] as const;
|
||||||
|
export type IconOrientation = (typeof ICON_ORIENTATION)[number];
|
20
packages/design-system/src/types/text.ts
Normal file
20
packages/design-system/src/types/text.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
const TEXT_SIZE = ['xsmall', 'small', 'mini', 'medium', 'large', 'xlarge'] as const;
|
||||||
|
export type TextSize = (typeof TEXT_SIZE)[number];
|
||||||
|
|
||||||
|
const TEXT_COLOR = [
|
||||||
|
'primary',
|
||||||
|
'text-dark',
|
||||||
|
'text-base',
|
||||||
|
'text-light',
|
||||||
|
'text-xlight',
|
||||||
|
'danger',
|
||||||
|
'success',
|
||||||
|
'warning',
|
||||||
|
] as const;
|
||||||
|
export type TextColor = (typeof TEXT_COLOR)[number];
|
||||||
|
|
||||||
|
const TEXT_ALIGN = ['right', 'left', 'center'] as const;
|
||||||
|
export type TextAlign = (typeof TEXT_ALIGN)[number];
|
||||||
|
|
||||||
|
const TEXT_FLOAT = ['left', 'right'] as const;
|
||||||
|
export type TextFloat = (typeof TEXT_FLOAT)[number];
|
|
@ -10,13 +10,15 @@
|
||||||
"incremental": false,
|
"incremental": false,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"types": ["webpack-env", "vitest/globals"],
|
"types": ["vitest/globals"],
|
||||||
"typeRoots": ["@testing-library", "@types"],
|
"typeRoots": ["@testing-library", "@types", "../../node_modules"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
},
|
},
|
||||||
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
|
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
|
||||||
// TODO: remove all options below this line
|
// TODO: remove all options below this line
|
||||||
|
"strict": false,
|
||||||
|
"noImplicitAny": false,
|
||||||
"noUnusedLocals": false,
|
"noUnusedLocals": false,
|
||||||
"noImplicitReturns": false
|
"noImplicitReturns": false
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue