Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): introduced the revamped no code editor #6787

Merged
merged 22 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0d0f99c
chore(ui): enforce the use of the <script setup> block in components …
MilosPaunovic Dec 25, 2024
246287c
refactor(ui): remove obsolete code related to old saved filters
MilosPaunovic Dec 25, 2024
413fc21
feat(ui): initial work on no code editor
MilosPaunovic Dec 27, 2024
10ca8a0
feat(ui): initial work on no code editor
MilosPaunovic Dec 30, 2024
c7370b2
chore(ui): tab rename tweaks
MilosPaunovic Dec 30, 2024
96fbfee
feat(ui): work on no code editor
MilosPaunovic Dec 30, 2024
1d04a2d
feat(ui): work on no code editor
MilosPaunovic Jan 8, 2025
78d3192
chore(ui): align with new color variables
MilosPaunovic Jan 8, 2025
088c518
chore(ui): start of creation work
MilosPaunovic Jan 8, 2025
a3428be
chore(ui): amend imports of js files
MilosPaunovic Jan 8, 2025
c2c3a3e
chore(ui): improve the creation process
MilosPaunovic Jan 8, 2025
aaf6634
chore(ui): improve the creation process
MilosPaunovic Jan 8, 2025
2a99457
chore(ui): work on creation
MilosPaunovic Jan 9, 2025
c0b62af
chore(ui): work on creation
MilosPaunovic Jan 9, 2025
2ed20b2
chore(ui): listing tasks
MilosPaunovic Jan 10, 2025
92431dd
chore(ui): listing tasks
MilosPaunovic Jan 10, 2025
2c582ec
chore(ui): poen collapse items it there are elements inside of it
MilosPaunovic Jan 13, 2025
d6465e2
feat(ui): work on editing the flow sing no code editor
MilosPaunovic Jan 15, 2025
2138436
feat(ui): work on editing the flow sing no code editor
MilosPaunovic Jan 15, 2025
5e8a82e
feat(ui): work on editing the flow sing no code editor
MilosPaunovic Jan 15, 2025
24a2414
Merge branch 'develop' into no-code
MilosPaunovic Jan 15, 2025
76718f3
chore(translations): auto generate values for languages other than en…
actions-user Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions ui/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginVue from "eslint-plugin-vue";

const components = (folder) => `src/components/${folder}/**/*.vue`;

/** @type {import('eslint').Linter.Config[]} */
export default [
{
Expand All @@ -12,8 +14,13 @@ export default [
{languageOptions: {globals: globals.browser}},
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
files: ["**/*.spec.js", "**/*.spec.ts", "vite.config.js", "vitest.config.js"],
{
files: [
"**/*.spec.js",
"**/*.spec.ts",
"vite.config.js",
"vitest.config.js",
],
languageOptions: {globals: globals.node},
},
...pluginVue.configs["flat/strongly-recommended"],
Expand Down Expand Up @@ -66,7 +73,12 @@ export default [
},
],
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-explicit-any": "off"
}
}
"@typescript-eslint/no-explicit-any": "off",
},
},
{
// Enforce the use of the <script setup> block in components within these paths
files: [components("filter"), components("code")],
rules: {"vue/component-api-style": ["error", ["script-setup"]]},
},
];
2 changes: 1 addition & 1 deletion ui/src/components/Tabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@
.filter(tab => (this.embedActiveTab ?? this.$route.params.tab) === tab.name)[0] || this.tabs[0];
},
isEditorActiveTab() {
return this.activeTab.name === "editor";
return this.activeTab.name === "edit";
},
isNamespaceEditor(){
return this.activeTab?.props?.isNamespace === true;
Expand Down
54 changes: 54 additions & 0 deletions ui/src/components/code/NoCode.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<template>
<div class="h-100 overflow-y-auto no-code">
<Breadcrumbs :flow="YamlUtils.parse(props.flow)" />

<hr class="m-0">

<Editor
:creation="route.query.identifier === 'new'"
: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 YamlUtils from "../../utils/yamlUtils";

import Breadcrumbs from "./components/Breadcrumbs.vue";
import Editor from "./segments/Editor.vue";

const emits = defineEmits(["updateTask", "updateMetadata"]);
const props = defineProps({
flow: {type: String, required: true},
});

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>

<style scoped lang="scss">
@import "./styles/code.scss";
</style>
5 changes: 5 additions & 0 deletions ui/src/components/code/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Guide on How to Handle the `NoCode` Editor

> Code within `Vue` components must be written using the `<script setup>` block, as enforced by the `ESLint` rule.

This is a brief guide on working with the `NoCode` editor component.
23 changes: 23 additions & 0 deletions ui/src/components/code/components/Add.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<div @click="emits('add', props.what)" class="pb-3 adding">
{{ t("no_code.adding", {what: props.what}) }}
</div>
</template>

<script setup lang="ts">
import {useI18n} from "vue-i18n";
const {t} = useI18n({useScope: "global"});

const emits = defineEmits(["add"]);
const props = defineProps({what: {type: String, required: true}});
</script>

<style scoped lang="scss">
@import "../styles/code.scss";

.adding {
cursor: pointer;
color: $code-gray-700;
font-size: $code-font-sm;
}
</style>
61 changes: 61 additions & 0 deletions ui/src/components/code/components/Breadcrumbs.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<el-breadcrumb class="p-4">
<el-breadcrumb-item
v-for="(breadcrumb, index) in breadcrumbs"
:key="index"
class="item"
>
<router-link :to="breadcrumb.to">
{{ breadcrumb.label }}
</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
</template>

<script setup lang="ts">
import {computed} from "vue";

import {Breadcrumb} from "../utils/types";

import {useRoute} from "vue-router";
const route = useRoute();

import {useI18n} from "vue-i18n";
const {t} = useI18n({useScope: "global"});

const props = defineProps({flow: {type: Object, required: true}});

const params = {
namespace: route.params.namespace,
id: props.flow.id ?? "new",
tab: "edit",
};

const breadcrumbs = computed<Breadcrumb[]>(() => {
return [
{
label: props.flow.id ?? t("create_flow"),
to: {name: route.name, params},
},
...(route.query.section
? [
{
label:
route.query.identifier === "new"
? t(`no_code.creation.${route.query.section}`)
: route.query.identifier,
to: {name: route.name, params},
},
]
: []),
];
});
</script>

<style scoped lang="scss">
@import "../styles/code.scss";

.item:last-child > .el-breadcrumb__inner > a {
color: $code-primary !important;
}
</style>
16 changes: 16 additions & 0 deletions ui/src/components/code/components/Save.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<el-button type="primary" :icon="ContentSave">
{{ t(`no_code.save.${props.what}`) }}
</el-button>
</template>

<script setup lang="ts">
import {ContentSave} from "../utils/icons";

const props = defineProps({
what: {type: String, required: true, default: "tasks"},
});

import {useI18n} from "vue-i18n";
const {t} = useI18n({useScope: "global"});
</script>
79 changes: 79 additions & 0 deletions ui/src/components/code/components/collapse/Collapse.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<el-collapse v-model="expanded" class="mt-3 collapse">
<el-collapse-item
v-for="(item, index) in props.items"
:key="index"
:name="item.title"
:title="`${item.title}${item.elements ? ` (${item.elements.length})` : ''}`"
:class="{creation: props.creation}"
>
<template v-if="creation" #icon>
<Creation :section="item.title" />
</template>

<template v-if="creation">
<Element
v-for="(element, elementIndex) in item.elements"
:key="elementIndex"
:section="item.title"
:element
/>
</template>

<slot name="content" />
</el-collapse-item>
</el-collapse>
</template>

<script setup lang="ts">
import {PropType, ref} from "vue";

import {CollapseItem} from "../../utils/types";

import Creation from "./buttons/Creation.vue";
import Element from "./Element.vue";

const props = defineProps({
items: {
type: Array as PropType<CollapseItem[]>,
required: true,
},
creation: {type: Boolean, default: false},
});
const expanded = ref<CollapseItem["title"][]>([]);

if (props.creation) {
props.items.forEach((item) => {
if (item.elements?.length) expanded.value.push(item.title);
});
}
</script>

<style scoped lang="scss">
@import "../../styles/code.scss";

.collapse {
& * {
font-size: $code-font-sm;
}

:deep(*) {
--el-collapse-header-bg-color: initial;
--el-collapse-header-text-color: #{$code-gray-700};
--el-collapse-content-bg-color: initial;

.el-collapse-item__header,
.el-collapse-item__content {
padding: 0.5rem 0;
}

.el-collapse-item__header {
justify-content: space-between;

&.is-active {
color: $code-primary;
}
}
}
}
</style>
62 changes: 62 additions & 0 deletions ui/src/components/code/components/collapse/Element.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<template>
<div @click="handleClick" class="d-flex my-2 p-2 rounded element">
<div class="me-2 icon">
<TaskIcon :cls="props.element.type" :icons only-icon />
</div>

<div class="flex-grow-1 label">
{{ props.element.id }}
</div>
</div>
</template>

<script setup lang="ts">
import {computed} from "vue";

// TODO: Update import once https://github.com/kestra-io/kestra/pull/6643 is merged
import TaskIcon from "@kestra-io/ui-libs/src/components/misc/TaskIcon.vue";

const props = defineProps({
section: {type: String, required: true},
element: {type: Object, required: true},
});

import {useStore} from "vuex";
const store = useStore();

const icons = computed(() => store.state.plugin.icons);

import {useRouter, useRoute} from "vue-router";
const router = useRouter();
const route = useRoute();

const handleClick = () => {
router.replace({
query: {
...route.query,
section: props.section.toLowerCase(),
identifier: props.element.id,
type: props.element.type,
},
});
};
</script>

<style scoped lang="scss">
@import "../../styles/code.scss";

.element {
cursor: pointer;
background-color: $code-card-color;
border: 1px solid $code-border-color;

& > .icon {
width: 1.25rem;
}

& > .label {
color: initial;
font-size: $code-font-sm;
}
}
</style>
28 changes: 28 additions & 0 deletions ui/src/components/code/components/collapse/buttons/Creation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<el-button @click.prevent.stop="handleClick()" type="primary" :icon="Plus">
{{ t("add") }}
</el-button>
</template>

<script setup lang="ts">
import {Plus} from "../../../utils/icons";

import {useRouter, useRoute} from "vue-router";
const router = useRouter();
const route = useRoute();

import {useI18n} from "vue-i18n";
const {t} = useI18n({useScope: "global"});

const props = defineProps({section: {type: String, required: true}});

const handleClick = () => {
router.replace({
query: {
...route.query,
section: props.section.toLowerCase(),
identifier: "new",
},
});
};
</script>
Loading