-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Build dataset collection input definition on the client.
- Loading branch information
Showing
11 changed files
with
261 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
client/src/components/Workflow/Editor/Forms/FormCollectionType.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<script setup lang="ts"> | ||
import { ref, watch } from "vue"; | ||
import { isValidCollectionTypeStr } from "@/components/Workflow/Editor/modules/collectionTypeDescription"; | ||
import FormElement from "@/components/Form/FormElement.vue"; | ||
interface Props { | ||
value?: string; | ||
} | ||
const props = defineProps<Props>(); | ||
const currentValue = ref<string | undefined>(undefined); | ||
const warning = ref<string | null>(null); | ||
const error = ref<string | null>(null); | ||
function onInput(newCollectionType: string | undefined) { | ||
emit("onChange", newCollectionType); | ||
} | ||
const collectionTypeOptions = [ | ||
{ value: "list", label: "List of Datasets" }, | ||
{ value: "paired", label: "Dataset Pair" }, | ||
{ value: "list:paired", label: "List of Dataset Pairs" }, | ||
]; | ||
function updateValue(newValue: string | undefined) { | ||
currentValue.value = newValue; | ||
warning.value = null; | ||
error.value = null; | ||
if (!newValue) { | ||
warning.value = "Typically, a value for this collection type should be specified."; | ||
} else if (!isValidCollectionTypeStr(newValue)) { | ||
error.value = "Invalid collection type"; | ||
} | ||
} | ||
watch(() => props.value, updateValue, { immediate: true }); | ||
const emit = defineEmits(["onChange"]); | ||
</script> | ||
|
||
<template> | ||
<FormElement | ||
id="collection_type" | ||
:value="currentValue" | ||
:attributes="{ datalist: collectionTypeOptions }" | ||
:warning="warning" | ||
:error="error" | ||
title="Collection type" | ||
:optional="true" | ||
type="text" | ||
@input="onInput" /> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
client/src/components/Workflow/Editor/Forms/FormInputCollection.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<script setup lang="ts"> | ||
import { computed, toRef } from "vue"; | ||
import type { DatatypesMapperModel } from "@/components/Datatypes/model"; | ||
import type { Step } from "@/stores/workflowStepStore"; | ||
import { useToolState } from "../composables/useToolState"; | ||
import FormElement from "@/components/Form/FormElement.vue"; | ||
import FormCollectionType from "@/components/Workflow/Editor/Forms/FormCollectionType.vue"; | ||
import FormDatatype from "@/components/Workflow/Editor/Forms/FormDatatype.vue"; | ||
interface ToolState { | ||
collection_type: string | null; | ||
optional: boolean; | ||
format: string | null; | ||
tag: string | null; | ||
} | ||
const props = defineProps<{ | ||
step: Step; | ||
datatypes: DatatypesMapperModel["datatypes"]; | ||
}>(); | ||
const stepRef = toRef(props, "step"); | ||
const { toolState } = useToolState(stepRef); | ||
function cleanToolState(): ToolState { | ||
if (toolState.value) { | ||
return { ...toolState.value } as unknown as ToolState; | ||
} else { | ||
return { | ||
collection_type: null, | ||
optional: false, | ||
tag: null, | ||
format: null, | ||
}; | ||
} | ||
} | ||
const emit = defineEmits(["onChange"]); | ||
function onDatatype(newDatatype: string[]) { | ||
const state = cleanToolState(); | ||
state.format = newDatatype.join(","); | ||
emit("onChange", state); | ||
} | ||
function onTags(newTags: string | null) { | ||
const state = cleanToolState(); | ||
state.tag = newTags; | ||
emit("onChange", state); | ||
} | ||
function onOptional(newOptional: boolean) { | ||
const state = cleanToolState(); | ||
state.optional = newOptional; | ||
emit("onChange", state); | ||
} | ||
function onCollectionType(newCollectionType: string | null) { | ||
const state = cleanToolState(); | ||
state.collection_type = newCollectionType; | ||
emit("onChange", state); | ||
} | ||
const formatsAsList = computed(() => { | ||
const formatStr = toolState.value?.format as (string | string[] | null); | ||
if (formatStr && (typeof formatStr === "string")) { | ||
return formatStr.split(/\s*,\s*/); | ||
} else if (formatStr) { | ||
return formatStr; | ||
} else { | ||
return []; | ||
} | ||
}); | ||
// Terrible Hack: The parent component (./FormDefault.vue) ignores the first update, so | ||
// I am sending a dummy update here. Ideally, the parent FormDefault would not expect this. | ||
emit("onChange", cleanToolState()); | ||
</script> | ||
|
||
<template> | ||
<div> | ||
<FormCollectionType :value="toolState?.collection_type" :optional="true" @onChange="onCollectionType" /> | ||
<FormElement id="optional" :value="toolState?.optional" title="Optional" type="boolean" @input="onOptional" /> | ||
<FormDatatype | ||
id="format" | ||
:value="formatsAsList" | ||
:datatypes="datatypes" | ||
title="Format(s)" | ||
:multiple="true" | ||
help="Leave empty to auto-generate filtered list at runtime based on connections." | ||
@onChange="onDatatype" /> | ||
<FormElement | ||
id="tag" | ||
:value="toolState?.tag" | ||
title="Tag filter" | ||
:optional="true" | ||
type="text" | ||
help="Tags to automatically filter inputs" | ||
@input="onTags" /> | ||
</div> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
client/src/components/Workflow/Editor/composables/useToolState.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { toRefs } from "@vueuse/core"; | ||
import { computed, type Ref } from "vue"; | ||
|
||
import { type Step } from "@/stores/workflowStepStore"; | ||
|
||
export function useToolState(step: Ref<Step>) { | ||
const { tool_state: rawToolStateRef } = toRefs(step); | ||
|
||
const toolState = computed(() => { | ||
const rawToolState: Record<string, unknown> = rawToolStateRef.value; | ||
const parsedToolState: Record<string, unknown> = {}; | ||
|
||
// This is less than ideal in a couple ways. The fact the JSON response | ||
// has encoded JSON is gross and it would be great for module types that | ||
// do not use the tool form to just return a simple JSON blob without | ||
// the extra encoded. As a step two if each of these module types could | ||
// also define a schema so we could use typed entities shared between the | ||
// client and server that would be ideal. | ||
for (const key in rawToolState) { | ||
if (Object.prototype.hasOwnProperty.call(rawToolState, key)) { | ||
const value = rawToolState[key]; | ||
if (typeof value === "string") { | ||
try { | ||
const parsedValue = JSON.parse(value); | ||
parsedToolState[key] = parsedValue; | ||
} catch (error) { | ||
parsedToolState[key] = rawToolState[key]; | ||
} | ||
} else { | ||
parsedToolState[key] = rawToolState[key]; | ||
} | ||
} | ||
} | ||
return parsedToolState; | ||
}); | ||
|
||
return { | ||
toolState, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.