diff --git a/client/src/components/DragGhost.vue b/client/src/components/DragGhost.vue index ae4dbfc6eaa..bc15f7c46af 100644 --- a/client/src/components/DragGhost.vue +++ b/client/src/components/DragGhost.vue @@ -25,7 +25,7 @@ const name = computed(() => { diff --git a/client/src/components/Grid/configs/histories.ts b/client/src/components/Grid/configs/histories.ts index 8216cda7156..08201f24be0 100644 --- a/client/src/components/Grid/configs/histories.ts +++ b/client/src/components/Grid/configs/histories.ts @@ -32,6 +32,7 @@ type SortKeyLiteral = "create_time" | "name" | "update_time" | undefined; */ async function getData(offset: number, limit: number, search: string, sort_by: string, sort_desc: boolean) { const { data, headers } = await historiesFetcher({ + view: "summary", limit, offset, search, diff --git a/client/src/components/Grid/configs/historiesShared.ts b/client/src/components/Grid/configs/historiesShared.ts index 65a65e8a54a..7052c4d3e35 100644 --- a/client/src/components/Grid/configs/historiesShared.ts +++ b/client/src/components/Grid/configs/historiesShared.ts @@ -22,6 +22,8 @@ type SortKeyLiteral = "create_time" | "name" | "update_time" | undefined; */ async function getData(offset: number, limit: number, search: string, sort_by: string, sort_desc: boolean) { const { data, headers } = await historiesFetcher({ + view: "summary", + keys: "username,create_time", limit, offset, search, diff --git a/client/src/components/History/Content/ContentItem.vue b/client/src/components/History/Content/ContentItem.vue index dc7022d3a82..11ecbf1df64 100644 --- a/client/src/components/History/Content/ContentItem.vue +++ b/client/src/components/History/Content/ContentItem.vue @@ -317,9 +317,12 @@ function unexpandedClick(event: Event) { :data-state="dataState" tabindex="0" role="button" + draggable + @dragstart="onDragStart" + @dragend="onDragEnd" @keydown="onKeyDown"> -
+
diff --git a/client/src/components/History/CurrentHistory/HistoryPanel.vue b/client/src/components/History/CurrentHistory/HistoryPanel.vue index 8b2062323ca..ab8bc7d6358 100644 --- a/client/src/components/History/CurrentHistory/HistoryPanel.vue +++ b/client/src/components/History/CurrentHistory/HistoryPanel.vue @@ -212,6 +212,36 @@ watch( } ); +function dragSameHistory() { + return getDragData().sameHistory; +} + +function getDragData() { + const eventStore = useEventStore(); + const multiple = eventStore.multipleDragData; + let data: HistoryItem[] | undefined; + let historyId: string | undefined; + try { + if (multiple) { + const dragData = eventStore.getDragData() as Record; + // set historyId to the first history_id in the multiple drag data + const firstItem = Object.values(dragData)[0]; + if (firstItem) { + historyId = firstItem.history_id; + } + data = Object.values(dragData); + } else { + data = [eventStore.getDragData() as HistoryItem]; + if (data[0]) { + historyId = data[0].history_id; + } + } + } catch (error) { + // this was not a valid object for this dropzone, ignore + } + return { data, sameHistory: historyId === props.history.id, multiple }; +} + function getHighlight(item: HistoryItem) { if (unref(isLoading)) { return undefined; @@ -319,45 +349,33 @@ function onOperationError(error: any) { } function onDragEnter(e: DragEvent) { + if (dragSameHistory()) { + return; + } dragTarget.value = e.target; showDropZone.value = true; } +function onDragOver(e: DragEvent) { + if (dragSameHistory()) { + return; + } + e.preventDefault(); +} + function onDragLeave(e: DragEvent) { + if (dragSameHistory()) { + return; + } if (dragTarget.value === e.target) { showDropZone.value = false; } } -async function onDrop(evt: any) { - const eventStore = useEventStore(); +async function onDrop() { showDropZone.value = false; - let data: HistoryItem[] | undefined; - let historyId: string | undefined; - const multiple = eventStore.multipleDragData; - try { - if (multiple) { - const dragData = eventStore.getDragData() as Record; - // set historyId to the first history_id in the multiple drag data - const firstItem = Object.values(dragData)[0]; - if (firstItem) { - historyId = firstItem.history_id; - } - data = Object.values(dragData); - } else { - data = [eventStore.getDragData() as HistoryItem]; - if (data[0]) { - historyId = data[0].history_id; - } - } - } catch (error) { - // this was not a valid object for this dropzone, ignore - } - - if (!data) { - return; - } else if (historyId === props.history.id) { - Toast.error("Cannot copy to the same history"); + const { data, sameHistory, multiple } = getDragData(); + if (!data || sameHistory) { return; } @@ -486,7 +504,7 @@ function setItemDragstart( class="history-layout d-flex flex-column w-100 h-100" @drop.prevent="onDrop" @dragenter.prevent="onDragEnter" - @dragover.prevent + @dragover="onDragOver" @dragleave.prevent="onDragLeave"> diff --git a/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts b/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts index ca97b4615ba..4ef562b2efd 100644 --- a/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts +++ b/client/src/components/TagsMultiselect/HeadlessMultiselect.test.ts @@ -20,7 +20,7 @@ describe("HeadlessMultiselect", () => { }); }; - const sampleOptions = ["#named", "#named_2", "#named_3", "abc", "def", "ghi"]; + const sampleOptions = ["name:named", "name:named_2", "name:named_3", "abc", "def", "ghi"]; const selectors = { openButton: ".toggle-button", @@ -197,27 +197,27 @@ describe("HeadlessMultiselect", () => { const input = await open(wrapper); await keyPress(input, "Enter"); - expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["#named"]); + expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["name:named"]); await keyPress(input, "ArrowDown"); await keyPress(input, "Enter"); - expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["#named_2"]); + expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["name:named_2"]); }); it("deselects options via keyboard", async () => { const wrapper = mountWithProps({ options: sampleOptions, - selected: ["#named", "#named_2", "#named_3"], + selected: ["name:named", "name:named_2", "name:named_3"], }); const input = await open(wrapper); await keyPress(input, "Enter"); - expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["#named_2", "#named_3"]); + expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["name:named_2", "name:named_3"]); await keyPress(input, "ArrowDown"); await keyPress(input, "Enter"); - expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["#named", "#named_3"]); + expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["name:named", "name:named_3"]); }); it("allows for adding new options", async () => { @@ -243,26 +243,26 @@ describe("HeadlessMultiselect", () => { const options = wrapper.findAll(selectors.option); await options.at(0).trigger("click"); - expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["#named"]); + expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["name:named"]); await options.at(1).trigger("click"); - expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["#named_2"]); + expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["name:named_2"]); }); it("deselects options with mouse", async () => { const wrapper = mountWithProps({ options: sampleOptions, - selected: ["#named", "#named_2", "#named_3"], + selected: ["name:named", "name:named_2", "name:named_3"], }); await open(wrapper); const options = wrapper.findAll(selectors.option); await options.at(0).trigger("click"); - expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["#named_2", "#named_3"]); + expect(wrapper.emitted()["input"]?.[0]?.[0]).toEqual(["name:named_2", "name:named_3"]); await options.at(1).trigger("click"); - expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["#named", "#named_3"]); + expect(wrapper.emitted()["input"]?.[1]?.[0]).toEqual(["name:named", "name:named_3"]); }); }); }); diff --git a/client/src/components/TagsMultiselect/HeadlessMultiselect.vue b/client/src/components/TagsMultiselect/HeadlessMultiselect.vue index 3a07de33a79..95660d1ced8 100644 --- a/client/src/components/TagsMultiselect/HeadlessMultiselect.vue +++ b/client/src/components/TagsMultiselect/HeadlessMultiselect.vue @@ -15,6 +15,7 @@ import { computed, nextTick, ref, watch } from "vue"; import Vue2Teleport from "vue2-teleport"; import { useUid } from "@/composables/utils/uid"; +import { normalizeTag } from "@/stores/userTagsStore"; library.add(faCheck, faChevronUp, faPlus, faTags, faTimes); @@ -147,6 +148,8 @@ function getOptionWithId(id: number) { * was not part of the provided options */ function onOptionSelected(option: string) { + option = normalizeTag(option); + if (!optionsAsSet.value.has(option)) { emit("addOption", option); return; @@ -318,7 +321,7 @@ whenever(isOpen, async () => { :data-parent-id="props.id" class="headless-multiselect__option" role="option" - :aria-selected="props.selected.includes(option)" + :aria-selected="props.selected.includes(normalizeTag(option))" :class="{ invalid: i === 0 && !searchValueValid, highlighted: highlightedOption === i, @@ -332,7 +335,7 @@ whenever(isOpen, async () => { {{ option }} - +