Loading client/src/components/Form/Elements/FormData/FormData.vue +78 −37 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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; Loading @@ -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; } /** Loading Loading @@ -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(); Loading @@ -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); Loading client/src/components/Form/Elements/FormData/types.ts +8 −0 Original line number Diff line number Diff line Loading @@ -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)); } client/src/components/Form/Elements/FormSelect.vue +1 −5 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 */ Loading Loading
client/src/components/Form/Elements/FormData/FormData.vue +78 −37 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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; Loading @@ -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; } /** Loading Loading @@ -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(); Loading @@ -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); Loading
client/src/components/Form/Elements/FormData/types.ts +8 −0 Original line number Diff line number Diff line Loading @@ -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)); }
client/src/components/Form/Elements/FormSelect.vue +1 −5 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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 */ Loading