From 7229e555a2e1acc7ab9b679edb4c43d6450c3b31 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Mon, 13 Jan 2025 13:54:59 -0600 Subject: [PATCH 1/8] show message for mixed extensions, show individual extension conditionally --- .../Collections/ListCollectionCreator.vue | 26 +++++++++++++++++++ .../ListDatasetCollectionElementView.vue | 3 ++- .../Collections/PairCollectionCreator.vue | 21 ++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/client/src/components/Collections/ListCollectionCreator.vue b/client/src/components/Collections/ListCollectionCreator.vue index d33f98927d83..7c995ba95fdc 100644 --- a/client/src/components/Collections/ListCollectionCreator.vue +++ b/client/src/components/Collections/ListCollectionCreator.vue @@ -79,6 +79,12 @@ const datatypesMapper = computed(() => datatypesMapperStore.datatypesMapper); /** Are we filtering by datatype? */ const filterExtensions = computed(() => !!datatypesMapper.value && !!props.extensions?.length); +/** Does `inListElements` have elements with different extensions? */ +const listHasMixedExtensions = computed(() => { + const extensions = new Set(inListElements.value.map((e) => e.extension)); + return extensions.size > 1; +}); + // ----------------------------------------------------------------------- process raw list /** set up main data */ function _elementsSetUp() { @@ -173,6 +179,21 @@ function _isElementInvalid(element: HistoryItemSummary): string | null { return null; } +/** Show the element's extension next to its name: + * 1. If there are no required extensions, so users can avoid creating mixed extension lists. + * 2. If the extension is not in the list of required extensions but is a subtype of one of them, + * so users can see that those elements were still included as they are implicitly convertible. + */ +function showElementExtension(element: HDASummary) { + return ( + !props.extensions?.length || + (filterExtensions.value && + element.extension && + !props.extensions?.includes(element.extension) && + datatypesMapper.value?.isSubTypeOfAny(element.extension, props.extensions!)) + ); +} + // /** mangle duplicate names using a mac-like '(counter)' addition to any duplicates */ function _mangleDuplicateNames() { var counter = 1; @@ -523,6 +544,10 @@ function renameElement(element: any, name: string) { diff --git a/client/src/components/Collections/ListDatasetCollectionElementView.vue b/client/src/components/Collections/ListDatasetCollectionElementView.vue index 537b3e40f6cb..f242499581a1 100644 --- a/client/src/components/Collections/ListDatasetCollectionElementView.vue +++ b/client/src/components/Collections/ListDatasetCollectionElementView.vue @@ -13,6 +13,7 @@ interface Props { selected?: boolean; hasActions?: boolean; notEditable?: boolean; + hideExtension?: boolean; } const props = defineProps(); @@ -48,7 +49,7 @@ function clickDiscard() { {{ elementName }} - ({{ element.extension }}) + ({{ element.extension }})
diff --git a/client/src/components/Collections/PairCollectionCreator.vue b/client/src/components/Collections/PairCollectionCreator.vue index c00c2c0f9b1e..56af345f2fd4 100644 --- a/client/src/components/Collections/PairCollectionCreator.vue +++ b/client/src/components/Collections/PairCollectionCreator.vue @@ -66,6 +66,13 @@ const pairElements = computed(() => { return inListElements.value; } }); +const pairHasMixedExtensions = computed(() => { + return ( + pairElements.value.forward?.extension && + pairElements.value.reverse?.extension && + pairElements.value.forward.extension !== pairElements.value.reverse.extension + ); +}); // variables for datatype mapping and then filtering const datatypesMapperStore = useDatatypesMapperStore(); @@ -355,7 +362,7 @@ function _naiveStartingAndEndingLCS(s1: string, s2: string) {
-
+
{{ localize("Exactly two elements are needed for the pair.") }} @@ -366,6 +373,18 @@ function _naiveStartingAndEndingLCS(s1: string, s2: string) {
+
+ + {{ localize("The selected datasets have mixed extensions.") }} + {{ localize("You can still create the pair but its elements will have different extensions.") }} + +
+
+ + {{ localize("The Dataset Pair is ready to be created.") }} + {{ localize("Provide a name and click the button below to create the pair.") }} + +
Date: Mon, 13 Jan 2025 13:56:01 -0600 Subject: [PATCH 2/8] provide default value for `CollectionCreator` prop --- client/src/components/Collections/common/CollectionCreator.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/Collections/common/CollectionCreator.vue b/client/src/components/Collections/common/CollectionCreator.vue index 6947a1d592fb..aae681963a75 100644 --- a/client/src/components/Collections/common/CollectionCreator.vue +++ b/client/src/components/Collections/common/CollectionCreator.vue @@ -54,6 +54,7 @@ const props = withDefaults(defineProps(), { extensions: undefined, extensionsToggle: false, showUpload: true, + collectionType: undefined, }); const emit = defineEmits<{ From b8f2e34635b74f8c2304327b3b421a12a4e92080 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Mon, 13 Jan 2025 17:58:37 -0600 Subject: [PATCH 3/8] fit `PairCollectionCreator` on one page - Adjusts default alert to always appear next to swap button - Adds minor `scroll-list` styling to the listing --- .../Collections/PairCollectionCreator.vue | 132 +++++++++++------- 1 file changed, 83 insertions(+), 49 deletions(-) diff --git a/client/src/components/Collections/PairCollectionCreator.vue b/client/src/components/Collections/PairCollectionCreator.vue index 56af345f2fd4..29f974997c23 100644 --- a/client/src/components/Collections/PairCollectionCreator.vue +++ b/client/src/components/Collections/PairCollectionCreator.vue @@ -5,6 +5,8 @@ import { BAlert, BButton } from "bootstrap-vue"; import { computed, ref, watch } from "vue"; import type { HDASummary, HistoryItemSummary } from "@/api"; +import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver"; +import { useAnimationFrameScroll } from "@/composables/sensors/animationFrameScroll"; import { Toast } from "@/composables/toast"; import STATES from "@/mvc/dataset/states"; import { useDatatypesMapperStore } from "@/stores/datatypesMapperStore"; @@ -81,6 +83,16 @@ const datatypesMapper = computed(() => datatypesMapperStore.datatypesMapper); /** Are we filtering by datatype? */ const filterExtensions = computed(() => !!datatypesMapper.value && !!props.extensions?.length); +// check if we have scrolled to the top or bottom of the scrollable div +const scrollableDiv = ref(null); +const { arrived } = useAnimationFrameScroll(scrollableDiv); +const isScrollable = ref(false); +useAnimationFrameResizeObserver(scrollableDiv, ({ clientSize, scrollSize }) => { + isScrollable.value = scrollSize.height >= clientSize.height + 1; +}); +const scrolledTop = computed(() => !isScrollable.value || arrived.top); +const scrolledBottom = computed(() => !isScrollable.value || arrived.bottom); + watch( () => props.initialElements, () => { @@ -362,29 +374,6 @@ function _naiveStartingAndEndingLCS(s1: string, s2: string) {
-
- - {{ localize("Exactly two elements are needed for the pair.") }} - - - {{ localize("Cancel") }} - - {{ localize("and reselect new elements.") }} - - -
-
- - {{ localize("The selected datasets have mixed extensions.") }} - {{ localize("You can still create the pair but its elements will have different extensions.") }} - -
-
- - {{ localize("The Dataset Pair is ready to be created.") }} - {{ localize("Provide a name and click the button below to create the pair.") }} - -
-
- - - {{ localize("Swap") }} - +
+
+ + + {{ localize("Swap") }} + +
+
+ + {{ localize("Exactly two elements are needed for the pair.") }} + + + {{ localize("Cancel") }} + + {{ localize("and reselect new elements.") }} + + + + {{ localize("The selected datasets have mixed extensions.") }} + {{ + localize( + "You can still create the pair but its elements will have different extensions." + ) + }} + + + {{ localize("The Dataset Pair is ready to be created.") }} + {{ localize("Provide a name and click the button below to create the pair.") }} + +
-
+
{{ localize(dataset) }}:
- {{ localize("Manually select a forward and reverse dataset to create a pair collection:") }} -
- + + {{ + localize("Manually select a forward and reverse dataset to create a dataset pair:") + }} + +
+
+ +
@@ -550,11 +576,19 @@ function _naiveStartingAndEndingLCS(s1: string, s2: string) { } .collection-elements-controls { - margin-bottom: 8px; + display: flex; + justify-content: space-between; + align-items: center; + + .alert { + padding: 0.25rem 0.5rem; + margin: 0; + text-align: center; + } } .collection-elements { - max-height: 400px; + max-height: 30vh; border: 0px solid lightgrey; overflow-y: auto; overflow-x: hidden; From 2f034a51f87fc379f0b31708e9ce3a0a2c373184 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Mon, 13 Jan 2025 18:19:14 -0600 Subject: [PATCH 4/8] add filtering to `PairCollectionCreator` --- .../Collections/PairCollectionCreator.vue | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/client/src/components/Collections/PairCollectionCreator.vue b/client/src/components/Collections/PairCollectionCreator.vue index 29f974997c23..c19c60eaec3a 100644 --- a/client/src/components/Collections/PairCollectionCreator.vue +++ b/client/src/components/Collections/PairCollectionCreator.vue @@ -14,6 +14,7 @@ import localize from "@/utils/localization"; import type { DatasetPair } from "../History/adapters/buildCollectionModal"; +import DelayedInput from "../Common/DelayedInput.vue"; import DatasetCollectionElementView from "./ListDatasetCollectionElementView.vue"; import CollectionCreator from "@/components/Collections/common/CollectionCreator.vue"; @@ -44,6 +45,13 @@ const removeExtensions = ref(true); const initialSuggestedName = ref(""); const invalidElements = ref([]); const workingElements = ref([]); +const filterText = ref(""); + +const filteredElements = computed(() => { + return workingElements.value.filter((element) => { + return `${element.hid}: ${element.name}`.toLowerCase().includes(filterText.value.toLowerCase()); + }); +}); /** If not `fromSelection`, the manually added elements that will become the pair */ const inListElements = ref({ forward: undefined, reverse: undefined }); @@ -539,17 +547,19 @@ function _naiveStartingAndEndingLCS(s1: string, s2: string) {
+ {{ localize("Manually select a forward and reverse dataset to create a dataset pair:") }}
(element.name = name)" />
+ + {{ localize(`No datasets found${filterText ? " matching '" + filterText + "'" : ""}`) }} +
From d9fe6cdc7328bf96ba87dde69ebf5d2970e4f0ed Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Wed, 22 Jan 2025 12:14:37 -0600 Subject: [PATCH 5/8] improve wording of mixed format alert and add helptext Co-authored-by: John Chilton --- .../Collections/ListCollectionCreator.vue | 11 ++++++++--- .../Collections/PairCollectionCreator.vue | 13 +++++++------ client/src/components/Help/terms.yml | 9 +++++++++ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/client/src/components/Collections/ListCollectionCreator.vue b/client/src/components/Collections/ListCollectionCreator.vue index 7c995ba95fdc..83c215934ebe 100644 --- a/client/src/components/Collections/ListCollectionCreator.vue +++ b/client/src/components/Collections/ListCollectionCreator.vue @@ -16,6 +16,7 @@ import { useDatatypesMapperStore } from "@/stores/datatypesMapperStore"; import localize from "@/utils/localization"; import FormSelectMany from "../Form/Elements/FormSelectMany/FormSelectMany.vue"; +import HelpText from "../Help/HelpText.vue"; import CollectionCreator from "@/components/Collections/common/CollectionCreator.vue"; import DatasetCollectionElementView from "@/components/Collections/ListDatasetCollectionElementView.vue"; @@ -544,9 +545,13 @@ function renameElement(element: any, name: string) {