Merge pull request #15180 from ElectronicBlueberry/typeathon

Day 1 Typeathon
This commit is contained in:
Dannon
2022-12-12 13:38:23 -05:00
committed by GitHub
8 changed files with 212 additions and 192 deletions
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { ref } from "vue";
import { useDetailedDatatypes } from "composables/datatypes";
import { useDetailedDatatypes, type DetailedDatatypes } from "composables/datatypes";
import { useFilterObjectArray } from "composables/utils/filter";
import DelayedInput from "components/Common/DelayedInput.vue";
const filter = ref("");
const filterFields = ["extension"];
const filterFields: Array<keyof DetailedDatatypes> = ["extension"];
const { datatypes } = useDetailedDatatypes();
const filteredDatatypes = useFilterObjectArray(datatypes, filter, filterFields);
+71 -105
View File
@@ -1,95 +1,67 @@
<script setup>
import FormBoolean from "./Elements/FormBoolean";
import FormHidden from "./Elements/FormHidden";
import FormInput from "./Elements/FormInput";
import FormParameter from "./Elements/FormParameter";
import FormSelection from "./Elements/FormSelection";
import FormColor from "./Elements/FormColor";
import FormDirectory from "./Elements/FormDirectory";
import FormNumber from "./Elements/FormNumber";
import FormRulesEdit from "./Elements/FormRulesEdit";
<script setup lang="ts">
import FormBoolean from "./Elements/FormBoolean.vue";
import FormHidden from "./Elements/FormHidden.vue";
import FormInput from "./Elements/FormInput.vue";
import FormParameter from "./Elements/FormParameter.vue";
import FormSelection from "./Elements/FormSelection.vue";
import FormColor from "./Elements/FormColor.vue";
import FormDirectory from "./Elements/FormDirectory.vue";
import FormNumber from "./Elements/FormNumber.vue";
import FormRulesEdit from "./Elements/FormRulesEdit.vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { ref, computed, useAttrs } from "vue";
import { library } from "@fortawesome/fontawesome-svg-core";
import { faExclamation, faTimes, faArrowsAltH } from "@fortawesome/free-solid-svg-icons";
import { faCaretSquareDown, faCaretSquareUp } from "@fortawesome/free-regular-svg-icons";
const props = defineProps({
id: {
type: String,
default: "identifier",
},
type: {
type: String,
default: null,
},
value: {
default: null,
},
title: {
type: String,
default: null,
},
refreshOnChange: {
type: Boolean,
default: false,
},
help: {
type: String,
default: null,
},
error: {
type: String,
default: null,
},
backbonejs: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
attributes: {
type: Object,
default: null,
},
collapsedEnableText: {
type: String,
default: "Enable",
},
collapsedDisableText: {
type: String,
default: "Disable",
},
collapsedEnableIcon: {
type: String,
default: "far fa-caret-square-down",
},
collapsedDisableIcon: {
type: String,
default: "far fa-caret-square-up",
},
connectedEnableText: {
type: String,
default: "Remove connection from module.",
},
connectedDisableText: {
type: String,
default: "Add connection to module.",
},
connectedEnableIcon: {
type: String,
default: "fa fa-times",
},
connectedDisableIcon: {
type: String,
default: "fa fa-arrows-alt-h",
},
workflowBuildingMode: {
type: Boolean,
default: false,
},
import type { ComputedRef } from "vue";
import type { FormParameterTypes, FormParameterAttributes, FormParameterValue } from "./parameterTypes";
export interface FormElementProps {
id?: string;
type?: FormParameterTypes;
value?: FormParameterValue;
title?: string;
refreshOnChange?: boolean;
help?: string;
error?: string;
backbonejs?: boolean;
disabled?: boolean;
attributes?: FormParameterAttributes;
collapsedEnableText?: string;
collapsedDisableText?: string;
collapsedEnableIcon?: string;
collapsedDisableIcon?: string;
connectedEnableText?: string;
connectedDisableText?: string;
connectedEnableIcon?: string;
connectedDisableIcon?: string;
workflowBuildingMode?: boolean;
}
const props = withDefaults(defineProps<FormElementProps>(), {
id: "identifier",
refreshOnChange: false,
backbonejs: false,
disabled: false,
collapsedEnableText: "Enable",
collapsedDisableText: "Disable",
collapsedEnableIcon: "far fa-caret-square-down",
collapsedDisableIcon: "far fa-caret-square-up",
connectedEnableText: "Remove connection from module.",
connectedDisableText: "Add connection to module.",
connectedEnableIcon: "fa fa-times",
connectedDisableIcon: "fa fa-arrows-alt-h",
workflowBuildingMode: false,
});
const emit = defineEmits(["input", "change"]);
const emit = defineEmits<{
(e: "input", value: FormParameterValue, id: string): void;
(e: "change", shouldRefresh: boolean): void;
}>();
//@ts-ignore bad library types
library.add(faExclamation, faTimes, faArrowsAltH, faCaretSquareDown, faCaretSquareUp);
/** TODO: remove attrs computed.
useAttrs is *not* reactive, and does not play nice with type safety.
@@ -97,15 +69,15 @@ const emit = defineEmits(["input", "change"]);
but should be removed as soon as that component is removed.
*/
const attrs = computed(() => props.attributes || useAttrs());
const collapsibleValue = computed(() => attrs.value["collapsible_value"]);
const defaultValue = computed(() => attrs.value["default_value"]);
const connectedValue = { __class__: "ConnectedValue" };
const collapsibleValue: ComputedRef<FormParameterValue> = computed(() => attrs.value["collapsible_value"]);
const defaultValue: ComputedRef<FormParameterValue> = computed(() => attrs.value["default_value"]);
const connectedValue: FormParameterValue = { __class__: "ConnectedValue" };
const connected = ref(false);
const collapsed = ref(false);
const collapsible = computed(() => !props.disabled && collapsibleValue.value !== undefined);
const connectable = computed(() => collapsible.value && attrs.value["connectable"]);
const connectable = computed(() => collapsible.value && Boolean(attrs.value["connectable"]));
// Determines to wether expand or collapse the input
{
@@ -118,7 +90,7 @@ const connectable = computed(() => collapsible.value && attrs.value["connectable
}
/** Submits a changed value. */
function setValue(value) {
function setValue(value: FormParameterValue) {
emit("input", value, props.id);
emit("change", props.refreshOnChange);
}
@@ -154,7 +126,7 @@ const showField = computed(() => !collapsed.value && !props.disabled);
const previewText = computed(() => attrs.value["text_value"]);
const helpText = computed(() => {
const helpArgument = attrs.value["argument"];
if (helpArgument && !props.help.includes(`(${helpArgument})`)) {
if (helpArgument && !props.help?.includes(`(${helpArgument})`)) {
return `${props.help} (${helpArgument})`;
} else {
return props.help;
@@ -171,7 +143,9 @@ const currentValue = computed({
});
const isHiddenType = computed(
() => ["hidden", "hidden_data", "baseurl"].includes(props.type) || (props.attributes && props.attributes.titleonly)
() =>
["hidden", "hidden_data", "baseurl"].includes(props.type ?? "") ||
(props.attributes && props.attributes.titleonly)
);
const collapseText = computed(() => (collapsed.value ? props.collapsedEnableText : props.collapsedDisableText));
@@ -182,7 +156,7 @@ const isEmpty = computed(() => {
return true;
}
if (["text", "integer", "float", "password"].includes(props.type) && currentValue.value === "") {
if (["text", "integer", "float", "password"].includes(props.type ?? "") && currentValue.value === "") {
return true;
}
@@ -194,14 +168,6 @@ const isRequiredType = computed(() => props.type !== "boolean");
const isOptional = computed(() => !isRequired.value && attrs.value["optional"] !== undefined);
</script>
<script>
import { library } from "@fortawesome/fontawesome-svg-core";
import { faExclamation, faTimes, faArrowsAltH } from "@fortawesome/free-solid-svg-icons";
import { faCaretSquareDown, faCaretSquareUp } from "@fortawesome/free-regular-svg-icons";
library.add(faExclamation, faTimes, faArrowsAltH, faCaretSquareDown, faCaretSquareUp);
</script>
<template>
<div
v-show="!isHidden"
@@ -258,7 +224,7 @@ library.add(faExclamation, faTimes, faArrowsAltH, faCaretSquareDown, faCaretSqua
v-model="currentValue"
:max="attrs.max"
:min="attrs.min"
:type="type"
:type="props.type ?? 'float'"
:workflow-building-mode="workflowBuildingMode" />
<FormSelection
v-else-if="props.type == 'select' && attrs.display == 'radio'"
@@ -277,7 +243,7 @@ library.add(faExclamation, faTimes, faArrowsAltH, faCaretSquareDown, faCaretSqua
:id="props.id"
v-model="currentValue"
:data-label="props.title"
:type="props.type"
:type="props.type ?? 'text'"
:attributes="attrs" />
<FormInput v-else :id="props.id" v-model="currentValue" :area="attrs['area']" />
</div>
+30
View File
@@ -0,0 +1,30 @@
// TODO: stricter types
export type FormParameterValue = any;
export type FormParameterAttributes = {
[attribute: string]: any;
};
export type FormParameterTypes =
| "boolean"
| "hidden"
| "hidden_data"
| "baseurl"
| "integer"
| "float"
| "radio"
| "color"
| "directory_uri"
| "text"
| "password"
| "select"
| "data_column"
| "genomebuild"
| "data"
| "data_collection"
| "drill_down"
| "group_tag"
| "library_data"
| "ftpfile"
| "upload"
| "rules"
| "data_dialog";
-51
View File
@@ -1,51 +0,0 @@
import { ref } from "vue";
import axios from "axios";
import { getAppRoot } from "onload/loadConfig";
/**
* Fetches a detailed array of datatypes available on this galaxy instance.
* Does not cache the result or use a store.
*/
export function useDetailedDatatypes() {
const datatypesLoading = ref(true);
const datatypes = ref([]);
async function getDatatypes() {
try {
const datatypesPromise = axios.get(`${getAppRoot()}api/datatypes?extension_only=false`);
const datatypeEDAMFormatsPromise = axios.get(`${getAppRoot()}api/datatypes/edam_formats/detailed`);
const datatypeEDAMDataPromise = axios.get(`${getAppRoot()}api/datatypes/edam_data/detailed`);
const [baseTypes, datatypeEDAMFormats, datatypeEDAMData] = await Promise.all([
datatypesPromise,
datatypeEDAMFormatsPromise,
datatypeEDAMDataPromise,
]);
datatypes.value = baseTypes.data.map((type, i) => {
const typeEDAMFormat = datatypeEDAMFormats.data[type.extension] ?? null;
const typeEDAMData = datatypeEDAMData.data[type.extension] ?? null;
return {
extension: type.extension,
description: type.description,
descriptionUrl: type.description_url,
edamFormat: typeEDAMFormat.prefix_IRI,
edamFormatLabel: typeEDAMFormat.label,
edamFormatDefinition: typeEDAMFormat.definition,
edamData: typeEDAMData.prefix_IRI,
edamDataLabel: typeEDAMData.label,
edamDataDefinition: typeEDAMData.definition,
};
});
} catch (e) {
console.error("unable to fetch available datatypes\n", e);
} finally {
datatypesLoading.value = false;
}
}
getDatatypes();
return { datatypes, datatypesLoading };
}
+70
View File
@@ -0,0 +1,70 @@
import { ref } from "vue";
import { fetcher } from "schema";
import type { Ref } from "vue";
const datatypesFetcher = fetcher.path("/api/datatypes").method("get").create();
const edamFormatsFetcher = fetcher.path("/api/datatypes/edam_formats/detailed").method("get").create();
const edamDataFetcher = fetcher.path("/api/datatypes/edam_data/detailed").method("get").create();
export interface DetailedDatatypes {
extension: string;
description?: string;
descriptionUrl?: string;
edamFormat?: string;
edamFormatLabel?: string;
edamFormatDefinition?: string;
edamData?: string;
edamDataLabel?: string;
edamDataDefinition?: string;
}
/**
* Fetches a detailed array of datatypes available on this galaxy instance.
* Does not cache the result or use a store.
*/
export function useDetailedDatatypes() {
const datatypesLoading = ref(true);
const datatypes: Ref<DetailedDatatypes[]> = ref([]);
async function getDatatypes() {
try {
const datatypesPromise = datatypesFetcher({ extension_only: true });
const datatypeEDAMFormatsPromise = edamFormatsFetcher({});
const datatypeEDAMDataPromise = edamDataFetcher({});
const [baseTypes, datatypeEDAMFormats, datatypeEDAMData] = await Promise.all([
datatypesPromise,
datatypeEDAMFormatsPromise,
datatypeEDAMDataPromise,
]);
type BaseTypes = Exclude<typeof baseTypes.data, string[]>;
datatypes.value = (baseTypes.data as BaseTypes).map((type) => {
const typeEDAMFormat = datatypeEDAMFormats.data[type.extension] ?? null;
const typeEDAMData = datatypeEDAMData.data[type.extension] ?? null;
return {
extension: type.extension,
description: type.description,
descriptionUrl: type.description_url,
edamFormat: typeEDAMFormat?.prefix_IRI,
edamFormatLabel: typeEDAMFormat?.label,
edamFormatDefinition: typeEDAMFormat?.definition,
edamData: typeEDAMData?.prefix_IRI,
edamDataLabel: typeEDAMData?.label,
edamDataDefinition: typeEDAMData?.definition,
};
});
} catch (e) {
console.error("unable to fetch available datatypes\n", e);
} finally {
datatypesLoading.value = false;
}
}
getDatatypes();
return { datatypes, datatypesLoading };
}
-34
View File
@@ -1,34 +0,0 @@
import { ref } from "vue";
let toastRef = ref(null);
export const setToastComponentRef = (newRef) => {
// eslint-disable-next-line vue/no-ref-as-operand
toastRef = newRef;
};
/**
* Direct export to simplify usage in Options Api component.
* Use 'useToast' for the Composition Api instead.
*/
export const Toast = {
success(message, title = "Success") {
toastRef.value.showToast(message, title, "success");
},
info(message, title = "Info") {
toastRef.value.showToast(message, title, "info");
},
warning(message, title = "Warning") {
toastRef.value.showToast(message, title, "warning");
},
error(message, title = "Error") {
toastRef.value.showToast(message, title, "error");
},
};
export function useToast() {
return { ...Toast };
}
+39
View File
@@ -0,0 +1,39 @@
import { ref, type Ref } from "vue";
import type Vue from "vue";
import type ToastComponentFile from "components/Toast";
export type ToastComponent = Vue & typeof ToastComponentFile.methods;
let toastRef: Ref<ToastComponent | null> = ref(null);
export const setToastComponentRef = (newRef: typeof toastRef) => {
// eslint-disable-next-line vue/no-ref-as-operand
toastRef = newRef;
};
/**
* Direct export to simplify usage in Options Api component.
* Use 'useToast' for the Composition Api instead.
*/
export const Toast = {
success(message: string, title = "Success") {
toastRef.value?.showToast(message, title, "success");
},
info(message: string, title = "Info") {
toastRef.value?.showToast(message, title, "info");
},
warning(message: string, title = "Warning") {
toastRef.value?.showToast(message, title, "warning");
},
error(message: string, title = "Error") {
toastRef.value?.showToast(message, title, "error");
},
};
export function useToast() {
return { ...Toast };
}
View File