diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 714945c49..593041153 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -16,7 +16,6 @@ declare module '@vue/runtime-core' { AppHeader: typeof import('./components/app/Header.vue')['default'] AppInterceptor: typeof import('./components/app/Interceptor.vue')['default'] AppLogo: typeof import('./components/app/Logo.vue')['default'] - AppNavigation: typeof import('./components/app/Navigation.vue')['default'] AppOptions: typeof import('./components/app/Options.vue')['default'] AppPaneLayout: typeof import('./components/app/PaneLayout.vue')['default'] AppPowerSearch: typeof import('./components/app/PowerSearch.vue')['default'] @@ -99,6 +98,20 @@ declare module '@vue/runtime-core' { HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default'] HttpTests: typeof import('./components/http/Tests.vue')['default'] HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default'] + IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] + IconLucideBrush: typeof import('~icons/lucide/brush')['default'] + IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] + IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] + IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] + IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default'] + IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] + IconLucideInfo: typeof import('~icons/lucide/info')['default'] + IconLucideLayers: typeof import('~icons/lucide/layers')['default'] + IconLucideMinus: typeof import('~icons/lucide/minus')['default'] + IconLucideRss: typeof import('~icons/lucide/rss')['default'] + IconLucideSearch: typeof import('~icons/lucide/search')['default'] + IconLucideUser: typeof import('~icons/lucide/user')['default'] + IconLucideUsers: typeof import('~icons/lucide/users')['default'] LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default'] LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default'] LensesRenderersHTMLLensRenderer: typeof import('./components/lenses/renderers/HTMLLensRenderer.vue')['default'] diff --git a/packages/hoppscotch-common/src/components/collections/Collection.vue b/packages/hoppscotch-common/src/components/collections/Collection.vue index 7babb29f1..0c069a545 100644 --- a/packages/hoppscotch-common/src/components/collections/Collection.vue +++ b/packages/hoppscotch-common/src/components/collections/Collection.vue @@ -16,7 +16,8 @@
dragging.value, (val) => { - if (val && notSameDestination.value) { + if (val && notSameDestination.value && notSameParentDestination.value) { emit("dragging", true) } else { emit("dragging", false) @@ -338,6 +339,10 @@ watch( } ) +const notSameParentDestination = computed(() => { + return currentReorderingStatus.value.parentID !== props.id +}) + const isRequestDragging = computed(() => { return currentReorderingStatus.value.type === "request" }) @@ -392,7 +397,8 @@ const handleDragOver = (e: DragEvent) => { e.offsetY > 18 && notSameDestination.value && !isRequestDragging.value && - isSameParent.value + isSameParent.value && + props.isLastItem ) { orderingLastItem.value = true dragging.value = false @@ -409,7 +415,7 @@ const handelDrop = (e: DragEvent) => { } else if (orderingLastItem.value) { updateLastItemOrder(e) } else { - dropEvent(e) + notSameParentDestination.value ? dropEvent(e) : e.stopPropagation() } } diff --git a/packages/hoppscotch-common/src/components/collections/MyCollections.vue b/packages/hoppscotch-common/src/components/collections/MyCollections.vue index ca13025a4..5b9f2be0d 100644 --- a/packages/hoppscotch-common/src/components/collections/MyCollections.vue +++ b/packages/hoppscotch-common/src/components/collections/MyCollections.vue @@ -504,45 +504,41 @@ const emit = defineEmits<{ const refFilterCollection = toRef(props, "filteredCollections") -const pathToIndex = computed(() => { - return (path: string) => { - const pathArr = path.split("/") - return pathArr[pathArr.length - 1] - } -}) +const pathToIndex = (path: string) => { + const pathArr = path.split("/") + return pathArr[pathArr.length - 1] +} -const isSelected = computed(() => { - return ({ - collectionIndex, - folderPath, - requestIndex, - }: { - collectionIndex?: number | undefined - folderPath?: string | undefined - requestIndex?: number | undefined - }) => { - if (collectionIndex !== undefined) { - return ( - props.picked && - props.picked.pickedType === "my-collection" && - props.picked.collectionIndex === collectionIndex - ) - } else if (requestIndex !== undefined && folderPath !== undefined) { - return ( - props.picked && - props.picked.pickedType === "my-request" && - props.picked.folderPath === folderPath && - props.picked.requestIndex === requestIndex - ) - } else { - return ( - props.picked && - props.picked.pickedType === "my-folder" && - props.picked.folderPath === folderPath - ) - } +const isSelected = ({ + collectionIndex, + folderPath, + requestIndex, +}: { + collectionIndex?: number | undefined + folderPath?: string | undefined + requestIndex?: number | undefined +}) => { + if (collectionIndex !== undefined) { + return ( + props.picked && + props.picked.pickedType === "my-collection" && + props.picked.collectionIndex === collectionIndex + ) + } else if (requestIndex !== undefined && folderPath !== undefined) { + return ( + props.picked && + props.picked.pickedType === "my-request" && + props.picked.folderPath === folderPath && + props.picked.requestIndex === requestIndex + ) + } else { + return ( + props.picked && + props.picked.pickedType === "my-folder" && + props.picked.folderPath === folderPath + ) } -}) +} const active = computed(() => currentActiveTab.value.document.saveContext) @@ -706,7 +702,10 @@ class MyCollectionsAdapter implements SmartTreeAdapter { ...item.folders.map((folder, index) => ({ id: `${id}/${index}`, data: { - isLastItem: index === item.folders.length - 1, + isLastItem: + item.folders && item.folders.length > 1 + ? index === item.folders.length - 1 + : false, type: "folders", data: { parentIndex: id, @@ -717,7 +716,10 @@ class MyCollectionsAdapter implements SmartTreeAdapter { ...item.requests.map((requests, index) => ({ id: `${id}/${index}`, data: { - isLastItem: index === item.requests.length - 1, + isLastItem: + item.requests && item.requests.length > 1 + ? index === item.requests.length - 1 + : false, type: "requests", data: { parentIndex: id, diff --git a/packages/hoppscotch-common/src/components/collections/TeamCollections.vue b/packages/hoppscotch-common/src/components/collections/TeamCollections.vue index 2c7a8c379..9a05330e3 100644 --- a/packages/hoppscotch-common/src/components/collections/TeamCollections.vue +++ b/packages/hoppscotch-common/src/components/collections/TeamCollections.vue @@ -517,54 +517,50 @@ const hasNoTeamAccess = computed( props.collectionsType.selectedTeam.myRole === "VIEWER") ) -const isSelected = computed(() => { - return ({ - collectionID, - folderID, - requestID, - }: { - collectionID?: string | undefined - folderID?: string | undefined - requestID?: string | undefined - }) => { - if (collectionID !== undefined) { - return ( - props.picked && - props.picked.pickedType === "teams-collection" && - props.picked.collectionID === collectionID - ) - } else if (requestID !== undefined) { - return ( - props.picked && - props.picked.pickedType === "teams-request" && - props.picked.requestID === requestID - ) - } else { - return ( - props.picked && - props.picked.pickedType === "teams-folder" && - props.picked.folderID === folderID - ) - } +const isSelected = ({ + collectionID, + folderID, + requestID, +}: { + collectionID?: string | undefined + folderID?: string | undefined + requestID?: string | undefined +}) => { + if (collectionID !== undefined) { + return ( + props.picked && + props.picked.pickedType === "teams-collection" && + props.picked.collectionID === collectionID + ) + } else if (requestID !== undefined) { + return ( + props.picked && + props.picked.pickedType === "teams-request" && + props.picked.requestID === requestID + ) + } else { + return ( + props.picked && + props.picked.pickedType === "teams-folder" && + props.picked.folderID === folderID + ) } -}) +} const active = computed(() => currentActiveTab.value.document.saveContext) -const isActiveRequest = computed(() => { - return (requestID: string) => { - return pipe( - active.value, - O.fromNullable, - O.filter( - (active) => - active.originLocation === "team-collection" && - active.requestID === requestID - ), - O.isSome - ) - } -}) +const isActiveRequest = (requestID: string) => { + return pipe( + active.value, + O.fromNullable, + O.filter( + (active) => + active.originLocation === "team-collection" && + active.requestID === requestID + ), + O.isSome + ) +} const selectRequest = (data: { request: HoppRESTRequest @@ -580,7 +576,7 @@ const selectRequest = (data: { emit("select-request", { request: request, requestIndex: requestIndex, - isActive: isActiveRequest.value(requestIndex), + isActive: isActiveRequest(requestIndex), }) } } @@ -749,7 +745,10 @@ class TeamCollectionsAdapter implements SmartTreeAdapter { ? items.children.map((item, index) => ({ id: `${id}/${item.id}`, data: { - isLastItem: index === items.children.length - 1, + isLastItem: + items.children && items.children.length > 1 + ? index === items.children.length - 1 + : false, type: "folders", data: { parentIndex: parsedID, @@ -762,7 +761,10 @@ class TeamCollectionsAdapter implements SmartTreeAdapter { ? items.requests.map((item, index) => ({ id: `${id}/${item.id}`, data: { - isLastItem: index === items.requests.length - 1, + isLastItem: + items.requests && items.requests.length > 1 + ? index === items.requests.length - 1 + : false, type: "requests", data: { parentIndex: parsedID, diff --git a/packages/hoppscotch-common/src/components/collections/index.vue b/packages/hoppscotch-common/src/components/collections/index.vue index 61d53a996..979630df7 100644 --- a/packages/hoppscotch-common/src/components/collections/index.vue +++ b/packages/hoppscotch-common/src/components/collections/index.vue @@ -1395,6 +1395,27 @@ const checkIfCollectionIsAParentOfTheChildren = ( return false } +const isMoveToSameLocation = ( + draggedItemPath: string, + destinationPath: string +) => { + const draggedItemPathArr = pathToIndex(draggedItemPath) + const destinationPathArr = pathToIndex(destinationPath) + + if (draggedItemPathArr.length > 0) { + const draggedItemParentPathArr = draggedItemPathArr.slice( + 0, + draggedItemPathArr.length - 1 + ) + + if (isEqual(draggedItemParentPathArr, destinationPathArr)) { + return true + } else { + return false + } + } +} + /** * This function is called when the user moves the collection * to a different collection or folder @@ -1419,6 +1440,13 @@ const dropCollection = (payload: { return } + //check if the collection is being moved to its own parent + if ( + isMoveToSameLocation(collectionIndexDragged, destinationCollectionIndex) + ) { + return + } + const parentFolder = collectionIndexDragged .split("/") .slice(0, -1) @@ -1556,10 +1584,14 @@ const isSameSameParent = ( draggedItemIndex.length === 1 ) { return draggedItemIndex[0] === destinationCollectionIndex - } else if (destinationItemPath === null && draggedItemIndex.length !== 1) { + } else if ( + destinationItemPath === null && + draggedItemIndex.length !== 1 && + destinationCollectionIndex !== null + ) { const dragedItemParent = draggedItemIndex.slice(0, -1) - return dragedItemParent[0] === destinationCollectionIndex + return dragedItemParent.join("/") === destinationCollectionIndex } else { if (destinationItemPath === null) return false const destinationItemIndex = pathToIndex(destinationItemPath) diff --git a/packages/hoppscotch-common/src/pages/index.vue b/packages/hoppscotch-common/src/pages/index.vue index b095878bc..954737ccd 100644 --- a/packages/hoppscotch-common/src/pages/index.vue +++ b/packages/hoppscotch-common/src/pages/index.vue @@ -16,19 +16,33 @@ :key="tab.id" :label="tab.document.request.name" :is-removable="tabs.length > 1" + :close-visibility="'hover'" > + (), + { + label: null, + info: null, + isRemovable: true, + closeVisibility: "always", + selected: false, + } +) const tabMeta = computed(() => ({ info: props.info, label: props.label, isRemovable: props.isRemovable, icon: slots.icon, - tabhead: slots.tabhead + suffix: slots.suffix, + tabhead: slots.tabhead, + closeVisibility: props.closeVisibility, })) const { diff --git a/packages/hoppscotch-ui/src/components/smart/Windows.vue b/packages/hoppscotch-ui/src/components/smart/Windows.vue index d15c4bbef..c22b5cedc 100644 --- a/packages/hoppscotch-ui/src/components/smart/Windows.vue +++ b/packages/hoppscotch-ui/src/components/smart/Windows.vue @@ -1,43 +1,95 @@