Skip to content

Commit

Permalink
fully implement list collection creator in FormData
Browse files Browse the repository at this point in the history
This allows a collection type `list` to be created via the collection creater from the workflow/tool form directly.
It tracks the current history changes via the new `useHistoryItemsForType` composable.
It utilises the `FormSelectMany` component to easily move items between selected and unselected for list columns.
The items in the list creator can be filtered for extension, parent datatype or all items in the history, based on whether the form field required a certain extension(s) as input for the list.
  • Loading branch information
ahmedhamidawan committed Sep 26, 2024
1 parent 526476e commit 7dd7e49
Show file tree
Hide file tree
Showing 18 changed files with 665 additions and 230 deletions.
3 changes: 3 additions & 0 deletions client/src/api/histories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { type components } from "@/api";

export type HistoryContentsResult = components["schemas"]["HistoryContentsResult"];
338 changes: 261 additions & 77 deletions client/src/components/Collections/ListCollectionCreator.vue

Large diffs are not rendered by default.

16 changes: 13 additions & 3 deletions client/src/components/Collections/ListCollectionCreatorModal.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import _l from "utils/localization";
import Vue from "vue";

import { orList } from "@/utils/strings";

import { collectionCreatorModalSetup } from "./common/modal";

function listCollectionCreatorModal(elements, options) {
options = options || {};
options.title = _l("Create a collection from a list of datasets");
options.title = _l(
`Create a collection from a list of ${options.fromSelection ? "selected" : ""} ${
options.extensions?.length ? orList(options.extensions) : ""
} datasets`
);
const { promise, showEl } = collectionCreatorModalSetup(options);
return import(/* webpackChunkName: "ListCollectionCreator" */ "./ListCollectionCreator.vue").then((module) => {
const listCollectionCreatorInstance = Vue.extend(module.default);
Expand All @@ -18,6 +24,8 @@ function listCollectionCreatorModal(elements, options) {
oncancel: options.oncancel,
oncreate: options.oncreate,
defaultHideSourceItems: options.defaultHideSourceItems,
fromSelection: options.fromSelection,
extensions: options.extensions,
},
}).$mount(vm);
return promise;
Expand All @@ -27,10 +35,12 @@ function listCollectionCreatorModal(elements, options) {
/** Use a modal to create a list collection, then add it to the given history contents.
* @returns {Promise} resolved when the collection is added to the history.
*/
function createListCollection(contents, defaultHideSourceItems = true) {
function createListCollection(contents) {
const elements = contents.toJSON();
const promise = listCollectionCreatorModal(elements, {
defaultHideSourceItems: defaultHideSourceItems,
defaultHideSourceItems: contents.defaultHideSourceItems,
fromSelection: contents.fromSelection,
extensions: contents.extensions,
creationFn: function (elements, name, hideSourceItems) {
elements = elements.map((element) => ({
id: element.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { faCheck, faMinus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { ref, watch } from "vue";
import localize from "@/utils/localization";
import ClickToEdit from "@/components/Collections/common/ClickToEdit.vue";
interface Props {
element: any;
selected?: boolean;
hasActions?: boolean;
}
const props = defineProps<Props>();
Expand All @@ -17,7 +21,7 @@ const emit = defineEmits<{
(event: "element-is-discarded", element: any): void;
}>();
const elementName = ref("");
const elementName = ref(props.element.name);
watch(elementName, () => {
emit("onRename", elementName.value);
Expand All @@ -26,28 +30,37 @@ watch(elementName, () => {
function clickDiscard() {
emit("element-is-discarded", props.element);
}
onMounted(() => {
elementName.value = props.element.name;
});
</script>

<template>
<div class="collection-element" @click="emit('element-is-selected', element)">
<ClickToEdit v-model="elementName" :title="localize('Click to rename')" />
<div
class="collection-element d-flex justify-content-between"
:class="{ 'with-actions': hasActions }"
role="button"
tabindex="0"
@keyup.enter="emit('element-is-selected', element)"
@click="emit('element-is-selected', element)">
<span class="d-flex flex-gapx-1">
{{ element.hid }}:
<strong>
<ClickToEdit v-model="elementName" style="cursor: text" :title="localize('Click to rename')" />
</strong>
<i> ({{ element.extension }}) </i>
</span>

<button class="discard-btn btn-sm" :title="localize('Remove this dataset from the list')" @click="clickDiscard">
{{ localize("Discard") }}
</button>
<div v-if="hasActions" class="float-right">
<i v-if="!selected" class="mr-2"><FontAwesomeIcon :icon="faCheck" class="text-success" /> Added to list</i>
<i v-else class="text-secondary">Selected</i>
<button class="btn-sm" :title="localize('Remove this dataset from the list')" @click="clickDiscard">
<FontAwesomeIcon :icon="faMinus" fixed-width />
{{ localize("Remove") }}
</button>
</div>
</div>
</template>

<style scoped lang="scss">
.collection-element {
height: auto;
.discard-btn {
float: right;
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ onMounted(() => {
:oncancel="oncancel"
:hide-source-items="hideSourceItems"
:suggested-name="initialSuggestedName"
:extensions-toggle="removeExtensions"
@onUpdateHideSourceItems="onUpdateHideSourceItems"
@clicked-create="clickedCreate"
@remove-extensions-toggle="removeExtensionsToggle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
:oncancel="oncancel"
:hide-source-items="hideSourceItems"
:render-extensions-toggle="true"
:extensions-toggle="removeExtensions"
@onUpdateHideSourceItems="onUpdateHideSourceItems"
@clicked-create="clickedCreate"
@remove-extensions-toggle="removeExtensionsToggle">
Expand Down
52 changes: 32 additions & 20 deletions client/src/components/Collections/common/ClickToEdit.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<script setup lang="ts">
import { faSave } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BButton } from "bootstrap-vue";
import { ref, watch } from "vue";
interface Props {
Expand All @@ -16,37 +19,46 @@ const editable = ref(false);
const localValue = ref(props.value);
watch(
() => localValue.value,
(newLocalValue) => {
emit("input", newLocalValue);
}
);
watch(
() => props.value,
(newValue) => {
localValue.value = newValue;
() => editable.value,
(value) => {
if (!value) {
emit("input", localValue.value);
localValue.value = props.value;
}
}
);
</script>

<template>
<input
v-if="editable"
v-model="localValue"
class="click-to-edit-input"
contenteditable
@blur="editable = false"
@keyup.enter="editable = false" />

<label v-else @click="editable = true">
<div v-if="editable">
<input
id="click-to-edit-input"
v-model="localValue"
class="click-to-edit-input"
tabindex="0"
contenteditable
@blur="editable = false"
@keyup.prevent.stop.enter="editable = false"
@keyup.prevent.stop.escape="editable = false"
@click.prevent.stop />
<BButton class="p-0" style="border: none" variant="link" size="sm" @click.prevent.stop="editable = false">
<FontAwesomeIcon :icon="faSave" />
</BButton>
</div>

<label
v-else
role="button"
for="click-to-edit-input"
tabindex="0"
@keyup.enter="editable = true"
@click.stop="editable = true">
{{ localValue }}
</label>
</template>

<style scoped lang="scss">
.click-to-edit-input {
width: 600px;
line-height: 1 !important;
}
</style>
Loading

0 comments on commit 7dd7e49

Please sign in to comment.