mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-31 02:28:09 +08:00
153 lines
4.6 KiB
Vue
153 lines
4.6 KiB
Vue
<script setup lang="ts">
|
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
|
import { defineAsyncComponent, nextTick, type PropType } from "vue";
|
|
import { computed } from "vue";
|
|
|
|
import { useKeyedObjects } from "@/composables/keyedObjects";
|
|
import localize from "@/utils/localization";
|
|
|
|
import FormCard from "./FormCard.vue";
|
|
import FormListElementOperations from "./FormListElementOperations.vue";
|
|
|
|
const FormNode = defineAsyncComponent(() => import("./FormInputs.vue"));
|
|
|
|
interface Input {
|
|
name: string;
|
|
title: string;
|
|
min: number;
|
|
default: number;
|
|
max: number | string;
|
|
help?: string;
|
|
cache: Array<Record<string, unknown>>;
|
|
}
|
|
|
|
const maxRepeats = computed(() => {
|
|
return typeof props.input.max === "number" ? props.input.cache?.length >= props.input.max : false;
|
|
});
|
|
|
|
const minRepeats = computed(() => {
|
|
return typeof props.input.min === "number" ? props.input.cache?.length > props.input.min : true;
|
|
});
|
|
|
|
const buttonTooltip = computed(() => {
|
|
return maxRepeats.value
|
|
? localize(`Maximum number of ${props.input.title || "Repeat"} fields reached`)
|
|
: localize(`Click to add ${props.input.title || "Repeat"} fields`);
|
|
});
|
|
|
|
const deleteTooltip = computed(() => {
|
|
return !minRepeats.value
|
|
? localize(`Minimum number of ${props.input.title || "Repeat"} fields reached`)
|
|
: localize(`Click to delete ${props.input.title || "Repeat"} fields`);
|
|
});
|
|
|
|
const props = defineProps({
|
|
input: {
|
|
type: Object as PropType<Input>,
|
|
required: true,
|
|
},
|
|
sustainRepeats: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
passthroughProps: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
prefix: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
(e: "insert"): void;
|
|
(e: "delete", index: number): void;
|
|
(e: "swap", a: number, b: number): void;
|
|
}>();
|
|
|
|
function onInsert() {
|
|
emit("insert");
|
|
}
|
|
|
|
function onDelete(index: number) {
|
|
emit("delete", index);
|
|
}
|
|
|
|
function getPrefix(index: number) {
|
|
const name = `${props.input.name}_${index}`;
|
|
|
|
if (props.prefix) {
|
|
return `${props.prefix}|${name}`;
|
|
} else {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
function getTitle(index: number) {
|
|
return `${index + 1}: ${props.input.title}`;
|
|
}
|
|
|
|
/** swap blocks if possible */
|
|
async function swap(index: number, swapWith: number, direction: "up" | "down") {
|
|
if (swapWith >= 0 && swapWith < props.input.cache?.length) {
|
|
emit("swap", index, swapWith);
|
|
|
|
await nextTick();
|
|
|
|
const buttonId = getButtonId(swapWith, direction);
|
|
document.getElementById(buttonId)?.focus();
|
|
}
|
|
}
|
|
|
|
/** get a uid for the up/down button */
|
|
function getButtonId(index: number, direction: "up" | "down") {
|
|
const prefix = getPrefix(index);
|
|
return `${prefix}_${direction}`;
|
|
}
|
|
|
|
const { keyObject } = useKeyedObjects();
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<div v-if="!props.sustainRepeats || props.input.cache?.length > 0">
|
|
<div class="font-weight-bold mb-2">{{ props.input.title }}</div>
|
|
<div v-if="props.input.help" class="mb-2" data-description="repeat help">{{ props.input.help }}</div>
|
|
</div>
|
|
|
|
<FormCard
|
|
v-for="(cache, cacheId) in props.input.cache"
|
|
:key="keyObject(cache)"
|
|
data-description="repeat block"
|
|
class="card"
|
|
:title="getTitle(cacheId)">
|
|
<template v-slot:operations>
|
|
<FormListElementOperations
|
|
v-if="!props.sustainRepeats"
|
|
:index="cacheId"
|
|
:num-elements="props.input.cache?.length || 0"
|
|
:up-button-id="getButtonId(cacheId, 'up')"
|
|
:down-button-id="getButtonId(cacheId, 'down')"
|
|
:delete-tooltip="deleteTooltip"
|
|
:can-delete="minRepeats"
|
|
@swap-up="() => swap(cacheId, cacheId - 1, 'up')"
|
|
@swap-down="() => swap(cacheId, cacheId + 1, 'down')"
|
|
@delete="() => onDelete(cacheId)" />
|
|
</template>
|
|
|
|
<template v-slot:body>
|
|
<FormNode v-bind="props.passthroughProps" :inputs="cache" :prefix="getPrefix(cacheId)" />
|
|
</template>
|
|
</FormCard>
|
|
|
|
<span v-b-tooltip.hover :title="buttonTooltip">
|
|
<b-button v-if="!props.sustainRepeats" :disabled="maxRepeats" @click="onInsert">
|
|
<FontAwesomeIcon :icon="faPlus" class="mr-1" />
|
|
<span data-description="repeat insert">Insert {{ props.input.title || "Repeat" }}</span>
|
|
</b-button>
|
|
</span>
|
|
</div>
|
|
</template>
|