@@ -13,7 +13,16 @@ import { BAlert, BButton, BButtonGroup, BCollapse, BFormCheckbox, BTooltip } fro
1313import { storeToRefs } from " pinia" ;
1414import { computed , onMounted , type Ref , ref , watch } from " vue" ;
1515
16- import { isDatasetElement , isDCE } from " @/api" ;
16+ import {
17+ type DCESummary ,
18+ type HDAObject ,
19+ type HistoryItemSummary ,
20+ isDatasetElement ,
21+ isDCE ,
22+ isHDCA ,
23+ isHistoryItem ,
24+ } from " @/api" ;
25+ import type { HistoryContentType } from " @/api/datasets" ;
1726import { getGalaxyInstance } from " @/app" ;
1827import { useDatatypesMapper } from " @/composables/datatypesMapper" ;
1928import { useUid } from " @/composables/utils/uid" ;
@@ -40,6 +49,8 @@ type SelectOption = {
4049 value: DataOption | null ;
4150};
4251
52+ type HistoryOrCollectionItem = HistoryItemSummary | DCESummary ;
53+
4354const props = withDefaults (
4455 defineProps <{
4556 loading? : boolean ;
@@ -83,7 +94,7 @@ const currentField = ref(0);
8394const currentHighlighting: Ref <string | null > = ref (null );
8495
8596// Drag/Drop related values
86- const dragData: Ref <EventData | null > = ref (null );
97+ const dragData: Ref <EventData [] > = ref ([] );
8798const dragTarget: Ref <EventTarget | null > = ref (null );
8899
89100// Collection creator modal settings
@@ -331,35 +342,50 @@ function getSourceType(val: DataOption) {
331342}
332343
333344/** Add values from drag/drop or data dialog sources */
334- function handleIncoming(incoming : Record <string , unknown >, partial = true ) {
345+ function handleIncoming(incoming : Record <string , unknown > | Record < string , unknown >[] , partial = true ) {
335346 if (incoming ) {
336347 const values = Array .isArray (incoming ) ? incoming : [incoming ];
337- const extensions = values .map ((v ) => v .extension || v .elements_datatypes ).filter ((v ) => (v ? true : false ));
348+
349+ // ensure all incoming values are isHistoryOrCollectionItem
350+ if (! values .every (isHistoryOrCollectionItem )) {
351+ return false ;
352+ }
353+
354+ const extensions = Array .from (
355+ new Set (
356+ values
357+ .map (getExtensionsForItem )
358+ .flat ()
359+ .filter ((v ) => v !== null && v !== undefined )
360+ )
361+ ) as string [];
362+
338363 if (! canAcceptDatatype (extensions )) {
339364 return false ;
340365 }
341- if (values .some ((v ) => ! canAcceptSrc (v .history_content_type , v .collection_type ))) {
366+ if (
367+ values .some ((v ) => {
368+ const { historyContentType } = getSrcAndContentType (v );
369+ const collectionType = " collection_type" in v && v .collection_type ? v .collection_type : undefined ;
370+ return ! canAcceptSrc (historyContentType , collectionType );
371+ })
372+ ) {
342373 return false ;
343374 }
344375 if (values .length > 0 ) {
345376 const incomingValues: Array <DataOption > = [];
346- values .forEach ((v ) => {
377+ values .forEach ((currVal ) => {
347378 // Map incoming objects to data option values
348- let newSrc;
349- if (isDCE (v )) {
350- if (isDatasetElement (v )) {
351- newSrc = SOURCE .DATASET ;
352- v = v .object ;
353- } else {
354- newSrc = SOURCE .COLLECTION_ELEMENT ;
355- }
379+ const { newSrc, datasetCollectionDataset } = getSrcAndContentType (currVal );
380+ let v: HistoryOrCollectionItem | HDAObject ;
381+ if (datasetCollectionDataset ) {
382+ v = datasetCollectionDataset ;
356383 } else {
357- newSrc =
358- v .src || (v .history_content_type === " dataset_collection" ? SOURCE .COLLECTION : SOURCE .DATASET );
384+ v = currVal ;
359385 }
360- const newHid = v .hid ;
386+ const newHid = isHistoryItem ( v ) ? v .hid : undefined ;
361387 const newId = v .id ;
362- const newName = v .name ? v .name : newId ;
388+ const newName = isHistoryItem ( v ) && v .name ? v .name : newId ;
363389 const newValue: DataOption = {
364390 id: newId ,
365391 src: newSrc ,
@@ -370,10 +396,11 @@ function handleIncoming(incoming: Record<string, unknown>, partial = true) {
370396 keep: true ,
371397 tags: [],
372398 };
373- if (v .collection_type && props .collectionTypes ?.length > 0 ) {
374- if (! props .collectionTypes .includes (v .collection_type )) {
399+ if (isHistoryItem (v ) && isHDCA (v ) && props .collectionTypes ?.length > 0 ) {
400+ const itemCollectionType = v .collection_type ;
401+ if (! props .collectionTypes .includes (itemCollectionType )) {
375402 const mapOverType = props .collectionTypes .find ((collectionType ) =>
376- v . collection_type .endsWith (collectionType )
403+ itemCollectionType .endsWith (collectionType )
377404 );
378405 if (! mapOverType ) {
379406 return false ;
@@ -437,6 +464,9 @@ function onBrowse() {
437464}
438465
439466function canAcceptDatatype(itemDatatypes : string | Array <string >) {
467+ // TODO: Shouldn't we enforce a datatype (at least "data") because of the case:
468+ // What if the drop item is a `DCESummary`, then it has no extension (?) and we
469+ // pass it as a valid item regardless of its elements' datatypes.
440470 if (! (props .extensions ?.length > 0 ) || props .extensions .includes (" data" )) {
441471 return true ;
442472 }
@@ -455,6 +485,39 @@ function canAcceptDatatype(itemDatatypes: string | Array<string>) {
455485 return true ;
456486}
457487
488+ /**
489+ * Given an element, determine the source and content type.
490+ * Also returns the collection element dataset object if it exists.
491+ */
492+ function getSrcAndContentType(element : HistoryOrCollectionItem ): {
493+ historyContentType: HistoryContentType ;
494+ newSrc: string ;
495+ datasetCollectionDataset: HDAObject | undefined ;
496+ } {
497+ let historyContentType: HistoryContentType ;
498+ let newSrc: string ;
499+ let datasetCollectionDataset: HDAObject | undefined ;
500+ if (isDCE (element )) {
501+ if (isDatasetElement (element )) {
502+ historyContentType = " dataset" ;
503+ newSrc = SOURCE .DATASET ;
504+ datasetCollectionDataset = element .object ;
505+ } else {
506+ historyContentType = " dataset_collection" ;
507+ newSrc = SOURCE .COLLECTION_ELEMENT ;
508+ }
509+ } else {
510+ historyContentType = element .history_content_type ;
511+ newSrc =
512+ " src" in element && typeof element .src === " string"
513+ ? element .src
514+ : historyContentType === " dataset_collection"
515+ ? SOURCE .COLLECTION
516+ : SOURCE .DATASET ;
517+ }
518+ return { historyContentType , newSrc , datasetCollectionDataset };
519+ }
520+
458521function canAcceptSrc(historyContentType : " dataset" | " dataset_collection" , collectionType ? : string ) {
459522 if (historyContentType === " dataset" ) {
460523 // HDA can only be fed into data parameters, not collection parameters
@@ -514,27 +577,39 @@ function createdCollection(collection: any) {
514577 handleIncoming (collection );
515578}
516579
580+ /**
581+ * Get the extension(s) for a given item
582+ */
583+ function getExtensionsForItem(item : HistoryOrCollectionItem ): string | string [] | null {
584+ return " extension" in item ? item .extension : " elements_datatypes" in item ? item .elements_datatypes : null ;
585+ }
586+
587+ function isHistoryOrCollectionItem(item : EventData ): item is HistoryOrCollectionItem {
588+ return isHistoryItem (item ) || isDCE (item );
589+ }
590+
517591// Drag/Drop event handlers
518592function onDragEnter(evt : MouseEvent ) {
519- const eventData = eventStore .getDragData ();
520- if (eventData ) {
521- const extensions = (eventData .extension as string ) || (eventData .elements_datatypes as Array <string >);
522- let highlightingState = " success" ;
523- if (! canAcceptDatatype (extensions )) {
524- highlightingState = " warning" ;
525- $emit (" alert" , ` ${extensions } is not an acceptable format for this parameter. ` );
526- } else if (
527- ! canAcceptSrc (
528- eventData .history_content_type as " dataset" | " dataset_collection" ,
529- eventData .collection_type as string
530- )
531- ) {
532- highlightingState = " warning" ;
533- $emit (" alert" , ` ${eventData .history_content_type } is not an acceptable input type for this parameter. ` );
593+ const eventData = eventStore .getDragItems ();
594+ dragData .value = [];
595+
596+ for (const item of eventData ) {
597+ if (isHistoryOrCollectionItem (item )) {
598+ const extensions = getExtensionsForItem (item );
599+ const { historyContentType } = getSrcAndContentType (item );
600+ const collectionType = " collection_type" in item && item .collection_type ? item .collection_type : undefined ;
601+ let highlightingState = " success" ;
602+ if (extensions && ! canAcceptDatatype (extensions )) {
603+ highlightingState = " warning" ;
604+ $emit (" alert" , ` ${extensions } is not an acceptable format for this parameter. ` );
605+ } else if (! canAcceptSrc (historyContentType , collectionType )) {
606+ highlightingState = " warning" ;
607+ $emit (" alert" , ` ${historyContentType } is not an acceptable input type for this parameter. ` );
608+ }
609+ currentHighlighting .value = highlightingState ;
610+ dragTarget .value = evt .target ;
611+ dragData .value .push (item );
534612 }
535- currentHighlighting .value = highlightingState ;
536- dragTarget .value = evt .target ;
537- dragData .value = eventData ;
538613 }
539614}
540615
@@ -546,20 +621,14 @@ function onDragLeave(evt: MouseEvent) {
546621}
547622
548623function onDrop() {
549- if (dragData .value ) {
550- let accept = false ;
551- if (eventStore .multipleDragData ) {
552- accept = handleIncoming (Object .values (dragData .value ) as any , false );
553- } else {
554- accept = handleIncoming (dragData .value );
555- }
556- if (accept ) {
624+ if (dragData .value .length ) {
625+ if (handleIncoming (dragData .value , dragData .value .length === 1 )) {
557626 currentHighlighting .value = " success" ;
558627 } else {
559628 currentHighlighting .value = " warning" ;
560629 }
561630 $emit (" alert" , undefined );
562- dragData .value = null ;
631+ dragData .value = [] ;
563632 clearHighlighting ();
564633 }
565634}
0 commit comments