From a9ca0691ac7f5b8c893b9b2cec054611fb43ebb7 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Tue, 13 Aug 2024 19:00:40 -0500 Subject: [PATCH 1/9] [24.1] Allow drag and drop for collection elements We extract the `HDAObject` from the `DCESummary` to be able to drag and drop it. For now, this works on dragging and dropping items to histories, since that process only needs the dataset's id. However, this doesn't work on tool forms; as we would need to get the full `HDASummary` to do that. --- client/src/api/datasets.ts | 2 +- client/src/api/index.ts | 17 ++++++ .../CurrentCollection/CollectionPanel.vue | 2 + .../History/CurrentHistory/HistoryPanel.vue | 59 ++++++++++--------- client/src/utils/setDrag.ts | 18 ++++++ 5 files changed, 70 insertions(+), 28 deletions(-) diff --git a/client/src/api/datasets.ts b/client/src/api/datasets.ts index c6096ea18f65..2a568f77130c 100644 --- a/client/src/api/datasets.ts +++ b/client/src/api/datasets.ts @@ -67,7 +67,7 @@ export async function purgeDataset(datasetId: string) { } const datasetCopy = fetcher.path("/api/histories/{history_id}/contents/{type}s").method("post").create(); -type HistoryContentsArgs = FetchArgType; +export type HistoryContentsArgs = FetchArgType; export async function copyDataset( datasetId: HistoryContentsArgs["content"], historyId: HistoryContentsArgs["history_id"], diff --git a/client/src/api/index.ts b/client/src/api/index.ts index d1dbc8854bbb..be992a9a504f 100644 --- a/client/src/api/index.ts +++ b/client/src/api/index.ts @@ -128,6 +128,14 @@ export interface DCECollection extends DCESummary { object: DCObject; } +/** + * DatasetCollectionElement specific type for datasets. + */ +export interface DCEDataset extends DCESummary { + element_type: "hda"; + object: HDAObject; +} + /** * Contains summary information about a HDCA (HistoryDatasetCollectionAssociation). * @@ -148,6 +156,8 @@ export type HDCADetailed = components["schemas"]["HDCADetailed"]; */ export type DCObject = components["schemas"]["DCObject"]; +export type HDAObject = components["schemas"]["HDAObject"]; + export type DatasetCollectionAttributes = components["schemas"]["DatasetCollectionAttributesResult"]; export type ConcreteObjectStoreModel = components["schemas"]["ConcreteObjectStoreModel"]; @@ -187,6 +197,13 @@ export function isCollectionElement(element: DCESummary): element is DCECollecti return element.element_type === "dataset_collection"; } +/** + * Returns true if the given element of a collection is a Dataset. + */ +export function isDatasetElement(element: DCESummary): element is DCEDataset { + return element.element_type === "hda"; +} + /** * Returns true if the given dataset entry is an instance of DatasetDetails. */ diff --git a/client/src/components/History/CurrentCollection/CollectionPanel.vue b/client/src/components/History/CurrentCollection/CollectionPanel.vue index 47ca7a497827..058c05430110 100644 --- a/client/src/components/History/CurrentCollection/CollectionPanel.vue +++ b/client/src/components/History/CurrentCollection/CollectionPanel.vue @@ -8,6 +8,7 @@ import { canMutateHistory, isCollectionElement, isHDCA } from "@/api"; import ExpandedItems from "@/components/History/Content/ExpandedItems"; import { updateContentFields } from "@/components/History/model/queries"; import { useCollectionElementsStore } from "@/stores/collectionElementsStore"; +import { setItemDragstart } from "@/utils/setDrag"; import { errorMessageAsString } from "@/utils/simple-error"; import CollectionDetails from "./CollectionDetails.vue"; @@ -164,6 +165,7 @@ watch( :expand-dataset="isExpanded(item)" :is-dataset="item.element_type == 'hda'" :filterable="filterable" + @drag-start="setItemDragstart(item, $event)" @update:expand-dataset="setExpanded(item, $event)" @view-collection="onViewDatasetCollectionElement(item)" /> diff --git a/client/src/components/History/CurrentHistory/HistoryPanel.vue b/client/src/components/History/CurrentHistory/HistoryPanel.vue index 869548649a95..57a039676408 100644 --- a/client/src/components/History/CurrentHistory/HistoryPanel.vue +++ b/client/src/components/History/CurrentHistory/HistoryPanel.vue @@ -3,8 +3,15 @@ import { BAlert } from "bootstrap-vue"; import { storeToRefs } from "pinia"; import { computed, onMounted, type Ref, ref, set as VueSet, unref, watch } from "vue"; -import { type HistoryItemSummary, type HistorySummaryExtended, isHistoryItem, userOwnsHistory } from "@/api"; -import { copyDataset } from "@/api/datasets"; +import { + type HDAObject, + type HistoryItemSummary, + type HistorySummaryExtended, + isDatasetElement, + isHistoryItem, + userOwnsHistory, +} from "@/api"; +import { copyDataset, HistoryContentsArgs } from "@/api/datasets"; import ExpandedItems from "@/components/History/Content/ExpandedItems"; import SelectedItems from "@/components/History/Content/SelectedItems"; import { HistoryFilters } from "@/components/History/HistoryFilters"; @@ -17,7 +24,7 @@ import { useHistoryItemsStore } from "@/stores/historyItemsStore"; import { useHistoryStore } from "@/stores/historyStore"; import { useUserStore } from "@/stores/userStore"; import { type Alias, getOperatorForAlias } from "@/utils/filtering"; -import { setDrag } from "@/utils/setDrag"; +import { setItemDragstart } from "@/utils/setDrag"; import HistoryCounter from "./HistoryCounter.vue"; import HistoryDetails from "./HistoryDetails.vue"; @@ -54,6 +61,8 @@ interface Props { isMultiViewItem?: boolean; } +type DraggableHistoryItem = HistoryItemSummary | HDAObject; + type ContentItemRef = Record | null>>; const props = withDefaults(defineProps(), { @@ -227,7 +236,13 @@ function getDragData() { const eventStore = useEventStore(); const dragItems = eventStore.getDragItems(); // Filter out any non-history items - const historyItems = dragItems?.filter((item: any) => isHistoryItem(item)) as HistoryItemSummary[]; + const historyItems = dragItems?.map((item: any) => { + if (isHistoryItem(item)) { + return item; + } else if (isDatasetElement(item)) { + return item.object; + } + }) as DraggableHistoryItem[]; const historyId = historyItems?.[0]?.history_id; return { data: historyItems, sameHistory: historyId === props.history.id, multiple: historyItems?.length > 1 }; } @@ -381,10 +396,18 @@ async function onDrop() { try { // iterate over the data array and copy each item to the current history for (const item of data) { - const dataSource = item.history_content_type === "dataset" ? "hda" : "hdca"; - await copyDataset(item.id, props.history.id, item.history_content_type, dataSource); + let dataSource: HistoryContentsArgs["source"]; + const type = item.history_content_type as "dataset" | "dataset_collection" | undefined; + if (type) { + // it's a `HistoryItemSummary` + dataSource = type === "dataset" ? "hda" : "hdca"; + } else { + // it's a `HDAObject` from a collection + dataSource = "hda"; + } + await copyDataset(item.id, props.history.id, type, dataSource); - if (item.history_content_type === "dataset") { + if (dataSource === "hda") { datasetCount++; if (!multiple) { Toast.info("Dataset copied to history"); @@ -451,24 +474,6 @@ function arrowNavigate(item: HistoryItemSummary, eventKey: string) { } return nextItem; } - -function setItemDragstart( - item: HistoryItemSummary, - itemIsSelected: boolean, - selectedItems: Map, - selectionSize: number, - event: DragEvent -) { - if (itemIsSelected && selectionSize > 1) { - const selectedItemsObj: any = {}; - for (const [key, value] of selectedItems) { - selectedItemsObj[key] = value; - } - setDrag(event, selectedItemsObj, true); - } else { - setDrag(event, item as any); - } -}