Unverified Commit 53aa74a0 authored by Marius van den Beek's avatar Marius van den Beek Committed by GitHub
Browse files

Merge pull request #19913 from davelopez/24.2_fix_duplicate_data_on_drop

[24.2] Fix duplicate entries when using drag and drop in multiple mode
parents 64648e36 d36e8674
Loading
Loading
Loading
Loading
+78 −37
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import { useHistoryStore } from "@/stores/historyStore";
import { orList } from "@/utils/strings";

import type { DataOption } from "./types";
import { containsDataOption } from "./types";
import { BATCH, SOURCE, VARIANTS } from "./variants";

import FormSelection from "../FormSelection.vue";
@@ -376,12 +377,64 @@ function handleIncoming(incoming: Record<string, unknown> | Record<string, unkno
            const incomingValues: Array<DataOption> = [];
            values.forEach((currVal) => {
                // Map incoming objects to data option values
                const { newSrc, datasetCollectionDataset } = getSrcAndContentType(currVal);
                const newValue = toDataOption(currVal);
                if (!newValue) {
                    return false;
                }
                // Verify that new value has corresponding option
                const keepKey = `${newValue.id}_${newValue.src}`;
                const existingOptions = props.options && props.options[newValue.src];
                const foundOption = existingOptions && existingOptions.find((option) => option.id === newValue.id);
                if (!foundOption && !(keepKey in keepOptions)) {
                    keepOptions[keepKey] = {
                        label: `${newValue.hid || "Selected"}: ${newValue.name}`,
                        value: newValue,
                    };
                }
                // Add new value to list
                incomingValues.push(newValue);
            });
            let hasDuplicates = false;
            if (incomingValues.length > 0 && incomingValues[0]) {
                // Set new value
                const config = currentVariant.value;
                const firstValue = incomingValues[0];
                if (config && config.src == firstValue.src && partial) {
                    if (config.multiple) {
                        const newValues = currentValue.value ? currentValue.value.slice() : [];
                        incomingValues.forEach((v) => {
                            if (containsDataOption(newValues, v)) {
                                hasDuplicates = true;
                            } else {
                                newValues.push(v);
                            }
                        });
                        currentValue.value = newValues;
                    } else {
                        if (containsDataOption(currentValue.value ?? [], firstValue)) {
                            hasDuplicates = true;
                        }
                        currentValue.value = [firstValue];
                    }
                } else {
                    currentValue.value = incomingValues;
                }
            }
            if (hasDuplicates) {
                return false;
            }
        }
    }
    return true;
}

function toDataOption(item: HistoryOrCollectionItem): DataOption | null {
    const { newSrc, datasetCollectionDataset } = getSrcAndContentType(item);
    let v: HistoryOrCollectionItem | HDAObject;
    if (datasetCollectionDataset) {
        v = datasetCollectionDataset;
    } else {
                    v = currVal;
        v = item;
    }
    const newHid = isHistoryItem(v) ? v.hid : undefined;
    const newId = v.id;
@@ -403,43 +456,13 @@ function handleIncoming(incoming: Record<string, unknown> | Record<string, unkno
                itemCollectionType.endsWith(collectionType)
            );
            if (!mapOverType) {
                            return false;
                return null;
            }
            newValue["batch"] = true;
            newValue["map_over_type"] = mapOverType;
        }
    }
                // Verify that new value has corresponding option
                const keepKey = `${newId}_${newSrc}`;
                const existingOptions = props.options && props.options[newSrc];
                const foundOption = existingOptions && existingOptions.find((option) => option.id === newId);
                if (!foundOption && !(keepKey in keepOptions)) {
                    keepOptions[keepKey] = { label: `${newHid || "Selected"}: ${newName}`, value: newValue };
                }
                // Add new value to list
                incomingValues.push(newValue);
            });
            if (incomingValues.length > 0 && incomingValues[0]) {
                // Set new value
                const config = currentVariant.value;
                const firstValue = incomingValues[0];
                if (config && config.src == firstValue.src && partial) {
                    if (config.multiple) {
                        const newValues = currentValue.value ? currentValue.value.slice() : [];
                        incomingValues.forEach((v) => {
                            newValues.push(v);
                        });
                        currentValue.value = newValues;
                    } else {
                        currentValue.value = [firstValue];
                    }
                } else {
                    currentValue.value = incomingValues;
                }
            }
        }
    }
    return true;
    return newValue;
}

/**
@@ -588,6 +611,16 @@ function isHistoryOrCollectionItem(item: EventData): item is HistoryOrCollection
    return isHistoryItem(item) || isDCE(item);
}

function getNameForItem(item: HistoryOrCollectionItem): string {
    if (isHistoryItem(item)) {
        return item.name ?? `Item ${item.hid}`;
    } else if (isDCE(item)) {
        return item.element_identifier;
    } else {
        throw new Error("Unknown item type");
    }
}

// Drag/Drop event handlers
function onDragEnter(evt: MouseEvent) {
    const eventData = eventStore.getDragItems();
@@ -606,6 +639,14 @@ function onDragEnter(evt: MouseEvent) {
                highlightingState = "warning";
                $emit("alert", `${historyContentType} is not an acceptable input type for this parameter.`);
            }
            // Check if the item is already in the current value
            const option = toDataOption(item);
            const isAlreadyInValue = containsDataOption(currentValue.value ?? [], option);
            if (isAlreadyInValue) {
                highlightingState = "warning";
                $emit("alert", `${getNameForItem(item)} is already selected.`);
            }

            currentHighlighting.value = highlightingState;
            dragTarget.value = evt.target;
            dragData.value.push(item);
+8 −0
Original line number Diff line number Diff line
@@ -13,3 +13,11 @@ export type DataOption = {
export function isDataOption(item: object): item is DataOption {
    return !!item && "src" in item;
}

export function itemUniqueKey(item: DataOption): string {
    return `${item.src}-${item.id}`;
}

export function containsDataOption(items: DataOption[], item: DataOption | null): boolean {
    return item !== null && items.some((i) => itemUniqueKey(i) === itemUniqueKey(item));
}
+1 −5
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ import { useFilterObjectArray } from "@/composables/filter";
import { useMultiselect } from "@/composables/useMultiselect";
import { uid } from "@/utils/utils";

import { type DataOption, isDataOption } from "./FormData/types";
import { type DataOption, isDataOption, itemUniqueKey } from "./FormData/types";

import StatelessTags from "@/components/TagsMultiselect/StatelessTags.vue";

@@ -154,10 +154,6 @@ const currentValue = computed({
    },
});

function itemUniqueKey(item: DataOption): string {
    return `${item.src}-${item.id}`;
}

/**
 * Ensures that an initial value is selected for non-optional inputs
 */