Skip to content

Commit

Permalink
feat(ui): multiple improvements of no code editor (#6923)
Browse files Browse the repository at this point in the history
* chore(ui): make sure to check if property exists

* chore(ui): prevent auto-focus of editor fields

* chore(ui): re-order the list of visible fields in the no code editor

* chore(ui): make concurrency field work in the main form

* chore(ui): initial work on passing component panel to store
  • Loading branch information
MilosPaunovic authored Jan 24, 2025
1 parent 09ef115 commit aa22f50
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 84 deletions.
11 changes: 1 addition & 10 deletions ui/src/components/code/NoCode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,14 @@
"
:flow
:metadata
:schemas
@update-metadata="(k, v) => emits('updateMetadata', {[k]: v})"
@update-task="(yaml) => emits('updateTask', yaml)"
/>
</div>
</template>

<script setup lang="ts">
import {onBeforeMount, computed, ref} from "vue";
import {Schemas} from "./utils/types";
import {onBeforeMount, computed} from "vue";
import YamlUtils from "../../utils/yamlUtils";
Expand All @@ -35,20 +32,14 @@
const metadata = computed(() => YamlUtils.getMetadata(props.flow));
import {useStore} from "vuex";
const store = useStore();
import {useRouter, useRoute} from "vue-router";
const router = useRouter();
const route = useRoute();
const schemas = ref<Schemas>({});
onBeforeMount(async () => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {section, identifier, type, ...rest} = route.query;
router.replace({query: {...rest}});
schemas.value = await store.dispatch("plugin/loadSchemaType");
});
</script>

Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/code/components/collapse/Collapse.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<el-collapse v-model="expanded" class="mt-3 collapse">
<el-collapse v-model="expanded" class="collapse">
<el-collapse-item
v-for="(item, index) in props.items"
:key="index"
Expand Down
56 changes: 44 additions & 12 deletions ui/src/components/code/segments/Editor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,33 @@
<div class="p-4">
<template v-if="!route.query.section && !route.query.identifier">
<component
v-for="([k, v], index) in Object.entries(fields)"
v-for="([k, v], index) in Object.entries(getFields())"
:key="index"
:is="v.component"
v-model="v.value"
v-bind="trimmed(v)"
@update:model-value="emits('updateMetadata', k, v.value)"
/>

<hr class="m-0 mt-3">
<hr class="my-4">

<Collapse
:items="sections"
creation
:flow
@remove="(yaml) => emits('updateTask', yaml)"
/>

<hr class="my-4">

<component
v-for="([k, v], index) in Object.entries(getFields(false))"
:key="index"
:is="v.component"
v-model="v.value"
v-bind="trimmed(v)"
@update:model-value="emits('updateMetadata', k, v.value)"
/>
</template>

<Task
Expand All @@ -41,11 +52,10 @@
import Editor from "../../inputs/Editor.vue";
import MetadataInputs from "../../flows/MetadataInputs.vue";
import TaskBasic from "../../flows/tasks/TaskBasic.vue";
import Task from "./Task.vue";
// const CONCURRENCY = "io.kestra.core.models.flows.Concurrency";
import {useRoute} from "vue-router";
const route = useRoute();
Expand All @@ -67,7 +77,6 @@
creation: {type: Boolean, default: false},
flow: {type: String, required: true},
metadata: {type: Object, required: true},
schemas: {type: Object, required: true},
});
const trimmed = (field: Field) => {
Expand Down Expand Up @@ -104,6 +113,7 @@
navbar: false,
input: true,
lang: "yaml",
shouldFocus: false,
style: {height: "100px"},
},
labels: {
Expand All @@ -125,6 +135,7 @@
navbar: false,
input: true,
lang: "yaml",
shouldFocus: false,
style: {height: "100px"},
},
variables: {
Expand All @@ -133,20 +144,34 @@
label: t("no_code.fields.general.variables"),
property: t("no_code.labels.variable"),
},
// concurrency: {
// component: shallowRef(InputSwitch), // TODO: To improve slot content
// value: props.metadata.concurrency,
// label: t("no_code.fields.general.concurrency"),
// schema: props.schemas?.definitions?.[CONCURRENCY] ?? {},
// root: "concurrency",
// },
concurrency: {
component: shallowRef(TaskBasic),
value: props.metadata.concurrency,
label: t("no_code.fields.general.concurrency"),
// TODO: Pass schema for concurrency dynamically
schema: {
type: "object",
properties: {
behavior: {
type: "string",
enum: ["QUEUE", "CANCEL", "FAIL"],
default: "QUEUE",
markdownDescription: "Default value is : `QUEUE`",
},
limit: {type: "integer", exclusiveMinimum: 0},
},
required: ["limit"],
},
root: "concurrency",
},
pluginDefaults: {
component: shallowRef(Editor),
value: props.metadata.pluginDefaults,
label: t("no_code.fields.general.plugin_defaults"),
navbar: false,
input: true,
lang: "yaml",
shouldFocus: false,
style: {height: "100px"},
},
disabled: {
Expand All @@ -156,6 +181,13 @@
},
});
const getFields = (main = true) => {
const {id, namespace, description, inputs, ...rest} = fields.value;
if (main) return {id, namespace, description, inputs};
else return rest;
};
import YamlUtils from "../../../utils/yamlUtils";
const getSectionTitle = (label: string, elements = []) => {
const title = t(`no_code.sections.${label}`);
Expand Down
1 change: 1 addition & 0 deletions ui/src/components/code/segments/Task.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
:section
@update:model-value="validateTask"
/>

<component
v-else
:is="lastBreadcumb.component.type"
Expand Down
13 changes: 8 additions & 5 deletions ui/src/components/code/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type EditorField = Field & {
navbar: boolean;
input: boolean;
lang: string;
shouldFocus: boolean;
style: {
height: string;
};
Expand All @@ -49,11 +50,16 @@ export type Fields = {
inputs: InputField;
outputs: EditorField;
variables: PairField;
concurrency?: ConcurrencyField; // TODO: Make it not optional
concurrency: ConcurrencyField;
pluginDefaults: EditorField;
disabled: Field;
};

export type CollapseItem = {
title: string;
elements?: Record<string, any>[];
};

export type Breadcrumb = {
label: string;
to: {
Expand All @@ -63,7 +69,4 @@ export type Breadcrumb = {
component?: ReturnType<typeof defineComponent>;
};

export type CollapseItem = {
title: string;
elements?: Record<string, any>[];
};
export type Component = ReturnType<typeof defineComponent>;
2 changes: 1 addition & 1 deletion ui/src/components/flows/tasks/OneOfContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
const schema = this.schemaOptions.find((item) =>
typeof this.modelValue === "string"
? item.id === "string"
: item.id === this.modelValue.type,
: item.id === this.modelValue?.type,
);
this.onSelect(schema?.value);
Expand Down
120 changes: 71 additions & 49 deletions ui/src/components/flows/tasks/TaskBasic.vue
Original file line number Diff line number Diff line change
@@ -1,38 +1,54 @@
<template>
<el-form-item
:key="index"
:required="isRequired(key)"
v-for="(schema, key, index) in properties"
>
<template #label>
<span class="d-flex flex-grow-1">
<span class="me-auto">
<code>{{ getKey(key) }}</code>&nbsp;
<el-tooltip v-if="hasTooltip(schema)" :persistent="false" transition="" :hide-after="0" effect="light">
<template #content>
<markdown class="markdown-tooltip" :source="helpText(schema)" />
</template>
<help />
</el-tooltip>
</span>
<span>
<el-tag disable-transitions type="info" size="small">
{{ getType(schema, key) }}
</el-tag>
</span>
</span>
</template>
<component
:is="`task-${getType(schema)}`"
:model-value="getPropertiesValue(key)"
@update:model-value="onObjectInput(key, $event)"
:root="getKey(key)"
:schema="schema"
<el-form label-position="top">
<el-form-item
:key="index"
:required="isRequired(key)"
:definitions="definitions"
:min="getExclusiveMinimum(key)"
/>
</el-form-item>
v-for="(schema, key, index) in properties"
>
<template #label>
<span v-if="required" class="me-1 text-danger">*</span>
<span v-if="getKey(key)" class="label">
{{
getKey(key)
.split(".")
.map(
(word) =>
word.charAt(0).toUpperCase() +
word.slice(1),
)
.join(" ")
}}
</span>
<el-tag disable-transitions size="small" class="ms-2 type-tag">
{{ getType(schema, key) }}
</el-tag>
<el-tooltip
v-if="hasTooltip(schema)"
:persistent="false"
:hide-after="0"
effect="light"
>
<template #content>
<markdown
class="markdown-tooltip"
:source="helpText(schema)"
/>
</template>
<help class="ms-2" />
</el-tooltip>
</template>
<component
:is="`task-${getType(schema)}`"
:model-value="getPropertiesValue(key)"
@update:model-value="onObjectInput(key, $event)"
:root="getKey(key)"
:schema="schema"
:required="isRequired(key)"
:definitions="definitions"
:min="getExclusiveMinimum(key)"
/>
</el-form-item>
</el-form>
</template>

<script>
Expand All @@ -57,11 +73,11 @@
computed: {
properties() {
if (this.schema) {
const properties = this.schema.properties
return this.sortProperties(properties)
const properties = this.schema.properties;
return this.sortProperties(properties);
}
return undefined;
}
},
},
methods: {
getPropertiesValue(properties) {
Expand All @@ -74,17 +90,20 @@
return properties;
}
return Object
.entries(properties)
return Object.entries(properties)
.sort((a, b) => {
if (a[0] === "id") {
return -1;
} else if (b[0] === "id") {
return 1;
}
const aRequired = (this.schema.required || []).includes(a[0]);
const bRequired = (this.schema.required || []).includes(b[0]);
const aRequired = (this.schema.required || []).includes(
a[0],
);
const bRequired = (this.schema.required || []).includes(
b[0],
);
if (aRequired && !bRequired) {
return -1;
Expand All @@ -105,7 +124,7 @@
})
.reduce((result, entry) => {
result[entry[0]] = entry[1];
return result
return result;
}, {});
},
onObjectInput(properties, value) {
Expand All @@ -125,18 +144,21 @@
},
getExclusiveMinimum(key) {
const property = this.schema.properties[key];
const propertyHasExclusiveMinimum = property && property.exclusiveMinimum
return propertyHasExclusiveMinimum ? property.exclusiveMinimum : null;
}
const propertyHasExclusiveMinimum =
property && property.exclusiveMinimum;
return propertyHasExclusiveMinimum
? property.exclusiveMinimum
: null;
},
},
};
</script>

<style lang="scss" scoped>
.el-form-item.is-required:not(.is-no-asterisk).asterisk-left {
> :deep(.el-form-item__label) {
display: flex;
}
}
@import "../../code/styles/code.scss";
.type-tag {
background-color: var(--ks-tag-background);
color: var(--ks-tag-content);
}
</style>
Loading

0 comments on commit aa22f50

Please sign in to comment.