refactor(editor): Move editor-ui and design-system to frontend dir (no-changelog) (#13564)

This commit is contained in:
Alex Grozav
2025-02-28 14:28:30 +02:00
committed by GitHub
parent 684353436d
commit f5743176e5
1635 changed files with 805 additions and 1079 deletions

View File

@@ -0,0 +1,207 @@
<script setup lang="ts">
import { useI18n } from '@/composables/useI18n';
import type { INodeProperties, NodeParameterValueType } from 'n8n-workflow';
import { isResourceLocatorValue } from '@/utils/typeGuards';
import { isValueExpression } from '@/utils/nodeTypesUtils';
import { computed } from 'vue';
import { useNDVStore } from '@/stores/ndv.store';
import { AI_TRANSFORM_NODE_TYPE } from '@/constants';
interface Props {
parameter: INodeProperties;
isReadOnly: boolean;
value: NodeParameterValueType;
showOptions?: boolean;
showExpressionSelector?: boolean;
customActions?: Array<{ label: string; value: string; disabled?: boolean }>;
iconOrientation?: 'horizontal' | 'vertical';
loading?: boolean;
loadingMessage?: string;
}
const props = withDefaults(defineProps<Props>(), {
showOptions: true,
showExpressionSelector: true,
customActions: () => [],
iconOrientation: 'vertical',
loading: false,
loadingMessage: () => useI18n().baseText('genericHelpers.loading'),
});
const emit = defineEmits<{
'update:modelValue': [value: string];
'menu-expanded': [visible: boolean];
}>();
const i18n = useI18n();
const isDefault = computed(() => props.parameter.default === props.value);
const isValueAnExpression = computed(() => isValueExpression(props.parameter, props.value));
const isHtmlEditor = computed(() => getArgument('editor') === 'htmlEditor');
const shouldShowExpressionSelector = computed(
() => !props.parameter.noDataExpression && props.showExpressionSelector && !props.isReadOnly,
);
const shouldShowOptions = computed(() => {
if (props.isReadOnly) {
return false;
}
if (props.parameter.type === 'collection' || props.parameter.type === 'credentialsSelect') {
return false;
}
if (['codeNodeEditor', 'sqlEditor'].includes(props.parameter.typeOptions?.editor ?? '')) {
return false;
}
if (props.showOptions) {
return true;
}
return false;
});
const selectedView = computed(() => (isValueAnExpression.value ? 'expression' : 'fixed'));
const activeNode = computed(() => useNDVStore().activeNode);
const hasRemoteMethod = computed(
() =>
!!props.parameter.typeOptions?.loadOptionsMethod || !!props.parameter.typeOptions?.loadOptions,
);
const resetValueLabel = computed(() => {
if (activeNode.value && [AI_TRANSFORM_NODE_TYPE].includes(activeNode.value.type)) {
return i18n.baseText('parameterInput.clearContents');
}
return i18n.baseText('parameterInput.resetValue');
});
const actions = computed(() => {
if (Array.isArray(props.customActions) && props.customActions.length > 0) {
return props.customActions;
}
if (isHtmlEditor.value && !isValueAnExpression.value) {
return [
{
label: i18n.baseText('parameterInput.formatHtml'),
value: 'formatHtml',
},
];
}
const parameterActions = [
{
label: resetValueLabel.value,
value: 'resetValue',
disabled: isDefault.value,
},
];
if (
hasRemoteMethod.value ||
(props.parameter.type === 'resourceLocator' &&
isResourceLocatorValue(props.value) &&
props.value.mode === 'list')
) {
return [
{
label: i18n.baseText('parameterInput.refreshList'),
value: 'refreshOptions',
},
...parameterActions,
];
}
return parameterActions;
});
const onMenuToggle = (visible: boolean) => emit('menu-expanded', visible);
const onViewSelected = (selected: string) => {
if (selected === 'expression') {
emit('update:modelValue', isValueAnExpression.value ? 'openExpression' : 'addExpression');
}
if (selected === 'fixed' && isValueAnExpression.value) {
emit('update:modelValue', 'removeExpression');
}
};
const getArgument = (argumentName: string) => {
if (props.parameter.typeOptions === undefined) {
return undefined;
}
if (props.parameter.typeOptions[argumentName] === undefined) {
return undefined;
}
return props.parameter.typeOptions[argumentName];
};
</script>
<template>
<div :class="$style.container" data-test-id="parameter-options-container">
<div v-if="loading" :class="$style.loader" data-test-id="parameter-options-loader">
<n8n-text v-if="loading" size="small">
<n8n-icon icon="sync-alt" size="xsmall" :spin="true" />
{{ loadingMessage }}
</n8n-text>
</div>
<div v-else :class="$style.controlsContainer">
<div
:class="{
[$style.noExpressionSelector]: !shouldShowExpressionSelector,
}"
>
<n8n-action-toggle
v-if="shouldShowOptions"
placement="bottom-end"
size="small"
color="foreground-xdark"
icon-size="small"
:actions="actions"
:icon-orientation="iconOrientation"
@action="(action: string) => $emit('update:modelValue', action)"
@visible-change="onMenuToggle"
/>
</div>
<n8n-radio-buttons
v-if="shouldShowExpressionSelector"
size="small"
:model-value="selectedView"
:disabled="isReadOnly"
:options="[
{ label: i18n.baseText('parameterInput.fixed'), value: 'fixed' },
{ label: i18n.baseText('parameterInput.expression'), value: 'expression' },
]"
@update:model-value="onViewSelected"
/>
</div>
</div>
</template>
<style lang="scss" module>
.container {
display: flex;
min-height: 22px;
}
.loader {
padding-bottom: var(--spacing-4xs);
& > span {
line-height: 1em;
}
}
.controlsContainer {
display: flex;
align-items: center;
flex-direction: row;
}
.noExpressionSelector {
margin-bottom: var(--spacing-4xs);
span {
padding-right: 0 !important;
}
}
</style>