Collection.vue (hoppscotch-2.2.1) | : | Collection.vue (hoppscotch-3.0.0) | ||
---|---|---|---|---|
<template> | <template> | |||
<div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]"> | <div class="flex flex-col" :class="[{ 'bg-primaryLight': dragging }]"> | |||
<div | <div | |||
class="flex items-stretch group" | class="flex items-stretch group" | |||
@dragover.prevent | @dragover.prevent | |||
@drop.prevent="dropEvent" | @drop.prevent="dropEvent" | |||
@dragover="dragging = true" | @dragover="dragging = true" | |||
@drop="dragging = false" | @drop="dragging = false" | |||
@dragleave="dragging = false" | @dragleave="dragging = false" | |||
@dragend="dragging = false" | @dragend="dragging = false" | |||
@contextmenu.prevent="options.tippy().show()" | @contextmenu.prevent="options.tippy.show()" | |||
> | > | |||
<span | <span | |||
class="cursor-pointer flex px-4 items-center justify-center" | class="flex items-center justify-center px-4 cursor-pointer" | |||
@click="toggleShowChildren()" | @click="toggleShowChildren()" | |||
> | > | |||
<SmartIcon | <component | |||
:is="getCollectionIcon" | ||||
class="svg-icons" | class="svg-icons" | |||
:class="{ 'text-accent': isSelected }" | :class="{ 'text-accent': isSelected }" | |||
:name="getCollectionIcon" | ||||
/> | /> | |||
</span> | </span> | |||
<span | <span | |||
class="cursor-pointer flex flex-1 min-w-0 py-2 pr-2 transition group-hov er:text-secondaryDark" | class="flex flex-1 min-w-0 py-2 pr-2 cursor-pointer transition group-hov er:text-secondaryDark" | |||
@click="toggleShowChildren()" | @click="toggleShowChildren()" | |||
> | > | |||
<span class="truncate" :class="{ 'text-accent': isSelected }"> | <span class="truncate" :class="{ 'text-accent': isSelected }"> | |||
{{ collection.name }} | {{ collection.name }} | |||
</span> | </span> | |||
</span> | </span> | |||
<div class="flex"> | <div class="flex"> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-if="doc && !selected" | ||||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
:title="$t('import.title')" | :icon="IconFilePlus" | |||
svg="circle" | :title="t('request.new')" | |||
color="green" | class="hidden group-hover:inline-flex" | |||
@click.native="$emit('select-collection')" | @click=" | |||
/> | $emit('add-request', { | |||
<ButtonSecondary | path: `${collectionIndex}`, | |||
v-if="doc && selected" | }) | |||
v-tippy="{ theme: 'tooltip' }" | " | |||
:title="$t('action.remove')" | ||||
svg="check-circle" | ||||
color="green" | ||||
@click.native="$emit('unselect-collection')" | ||||
/> | /> | |||
<ButtonSecondary | <ButtonSecondary | |||
v-if="!doc" | ||||
v-tippy="{ theme: 'tooltip' }" | v-tippy="{ theme: 'tooltip' }" | |||
svg="folder-plus" | :icon="IconFolderPlus" | |||
:title="$t('folder.new')" | :title="t('folder.new')" | |||
class="hidden group-hover:inline-flex" | class="hidden group-hover:inline-flex" | |||
@click.native=" | @click=" | |||
$emit('add-folder', { | $emit('add-folder', { | |||
folder: collection, | folder: collection, | |||
path: `${collectionIndex}`, | path: `${collectionIndex}`, | |||
}) | }) | |||
" | " | |||
/> | /> | |||
<span> | <span> | |||
<tippy | <tippy | |||
ref="options" | ref="options" | |||
interactive | interactive | |||
trigger="click" | trigger="click" | |||
theme="popover" | theme="popover" | |||
arrow | arrow | |||
:on-shown="() => tippyActions.focus()" | :on-shown="() => tippyActions.focus()" | |||
> | > | |||
<template #trigger> | <ButtonSecondary | |||
<ButtonSecondary | v-tippy="{ theme: 'tooltip' }" | |||
v-tippy="{ theme: 'tooltip' }" | :title="t('action.more')" | |||
:title="$t('action.more')" | :icon="IconMoreVertical" | |||
svg="more-vertical" | /> | |||
/> | <template #content="{ hide }"> | |||
<div | ||||
ref="tippyActions" | ||||
class="flex flex-col focus:outline-none" | ||||
tabindex="0" | ||||
role="menu" | ||||
@keyup.r="requestAction.$el.click()" | ||||
@keyup.n="folderAction.$el.click()" | ||||
@keyup.e="edit.$el.click()" | ||||
@keyup.delete="deleteAction.$el.click()" | ||||
@keyup.x="exportAction.$el.click()" | ||||
@keyup.escape="options.tippy().hide()" | ||||
> | ||||
<SmartItem | ||||
ref="requestAction" | ||||
:icon="IconFilePlus" | ||||
:label="t('request.new')" | ||||
:shortcut="['R']" | ||||
@click=" | ||||
() => { | ||||
$emit('add-request', { | ||||
path: `${collectionIndex}`, | ||||
}) | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="folderAction" | ||||
:icon="IconFolderPlus" | ||||
:label="t('folder.new')" | ||||
:shortcut="['N']" | ||||
@click=" | ||||
() => { | ||||
$emit('add-folder', { | ||||
folder: collection, | ||||
path: `${collectionIndex}`, | ||||
}) | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="edit" | ||||
:icon="IconEdit" | ||||
:label="t('action.edit')" | ||||
:shortcut="['E']" | ||||
@click=" | ||||
() => { | ||||
$emit('edit-collection') | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="exportAction" | ||||
:icon="IconDownload" | ||||
:label="t('export.title')" | ||||
:shortcut="['X']" | ||||
@click=" | ||||
() => { | ||||
exportCollection() | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="deleteAction" | ||||
:icon="IconTrash2" | ||||
:label="t('action.delete')" | ||||
:shortcut="['⌫']" | ||||
@click=" | ||||
() => { | ||||
removeCollection() | ||||
hide() | ||||
} | ||||
" | ||||
/> | ||||
</div> | ||||
</template> | </template> | |||
<div | ||||
ref="tippyActions" | ||||
class="flex flex-col focus:outline-none" | ||||
tabindex="0" | ||||
@keyup.n="folderAction.$el.click()" | ||||
@keyup.e="edit.$el.click()" | ||||
@keyup.delete="deleteAction.$el.click()" | ||||
@keyup.escape="options.tippy().hide()" | ||||
> | ||||
<SmartItem | ||||
ref="folderAction" | ||||
svg="folder-plus" | ||||
:label="$t('folder.new')" | ||||
:shortcut="['N']" | ||||
@click.native=" | ||||
() => { | ||||
$emit('add-folder', { | ||||
folder: collection, | ||||
path: `${collectionIndex}`, | ||||
}) | ||||
options.tippy().hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="edit" | ||||
svg="edit" | ||||
:label="$t('action.edit')" | ||||
:shortcut="['E']" | ||||
@click.native=" | ||||
() => { | ||||
$emit('edit-collection') | ||||
options.tippy().hide() | ||||
} | ||||
" | ||||
/> | ||||
<SmartItem | ||||
ref="deleteAction" | ||||
svg="trash-2" | ||||
:label="$t('action.delete')" | ||||
:shortcut="['⌫']" | ||||
@click.native=" | ||||
() => { | ||||
confirmRemove = true | ||||
options.tippy().hide() | ||||
} | ||||
" | ||||
/> | ||||
</div> | ||||
</tippy> | </tippy> | |||
</span> | </span> | |||
</div> | </div> | |||
</div> | </div> | |||
<div v-if="showChildren || isFiltered" class="flex"> | <div v-if="showChildren || isFiltered" class="flex"> | |||
<div | <div | |||
class="bg-dividerLight cursor-nsResize flex ml-5.5 transform transition w-1 hover:bg-dividerDark hover:scale-x-125" | class="bg-dividerLight cursor-nsResize flex ml-5.5 transform transition w-1 hover:bg-dividerDark hover:scale-x-125" | |||
@click="toggleShowChildren()" | @click="toggleShowChildren()" | |||
></div> | ></div> | |||
<div class="flex flex-col flex-1 truncate"> | <div class="flex flex-col flex-1 truncate"> | |||
<CollectionsMyFolder | <CollectionsMyFolder | |||
v-for="(folder, index) in collection.folders" | v-for="(folder, index) in collection.folders" | |||
:key="`folder-${index}`" | :key="`folder-${index}`" | |||
:folder="folder" | :folder="folder" | |||
:folder-index="index" | :folder-index="index" | |||
:folder-path="`${collectionIndex}/${index}`" | :folder-path="`${collectionIndex}/${index}`" | |||
:collection-index="collectionIndex" | :collection-index="collectionIndex" | |||
:doc="doc" | ||||
:save-request="saveRequest" | :save-request="saveRequest" | |||
:collections-type="collectionsType" | :collections-type="collectionsType" | |||
:is-filtered="isFiltered" | :is-filtered="isFiltered" | |||
:picked="picked" | :picked="picked" | |||
@add-request="$emit('add-request', $event)" | ||||
@add-folder="$emit('add-folder', $event)" | @add-folder="$emit('add-folder', $event)" | |||
@edit-folder="$emit('edit-folder', $event)" | @edit-folder="$emit('edit-folder', $event)" | |||
@edit-request="$emit('edit-request', $event)" | @edit-request="$emit('edit-request', $event)" | |||
@duplicate-request="$emit('duplicate-request', $event)" | @duplicate-request="$emit('duplicate-request', $event)" | |||
@select="$emit('select', $event)" | @select="$emit('select', $event)" | |||
@remove-request="$emit('remove-request', $event)" | @remove-request="$emit('remove-request', $event)" | |||
@remove-folder="$emit('remove-folder', $event)" | ||||
/> | /> | |||
<CollectionsMyRequest | <CollectionsMyRequest | |||
v-for="(request, index) in collection.requests" | v-for="(request, index) in collection.requests" | |||
:key="`request-${index}`" | :key="`request-${index}`" | |||
:request="request" | :request="request" | |||
:collection-index="collectionIndex" | :collection-index="collectionIndex" | |||
:folder-index="-1" | :folder-index="-1" | |||
:folder-name="collection.name" | :folder-name="collection.name" | |||
:folder-path="`${collectionIndex}`" | :folder-path="`${collectionIndex}`" | |||
:request-index="index" | :request-index="index" | |||
:doc="doc" | ||||
:save-request="saveRequest" | :save-request="saveRequest" | |||
:collections-type="collectionsType" | :collections-type="collectionsType" | |||
:picked="picked" | :picked="picked" | |||
@edit-request="$emit('edit-request', $event)" | @edit-request="$emit('edit-request', $event)" | |||
@duplicate-request="$emit('duplicate-request', $event)" | @duplicate-request="$emit('duplicate-request', $event)" | |||
@select="$emit('select', $event)" | @select="$emit('select', $event)" | |||
@remove-request="$emit('remove-request', $event)" | @remove-request="$emit('remove-request', $event)" | |||
/> | /> | |||
<div | <div | |||
v-if=" | v-if=" | |||
(collection.folders == undefined || | (collection.folders == undefined || | |||
collection.folders.length === 0) && | collection.folders.length === 0) && | |||
(collection.requests == undefined || | (collection.requests == undefined || | |||
collection.requests.length === 0) | collection.requests.length === 0) | |||
" | " | |||
class="flex flex-col text-secondaryLight p-4 items-center justify-cent er" | class="flex flex-col items-center justify-center p-4 text-secondaryLig ht" | |||
> | > | |||
<img | <img | |||
:src="`/images/states/${$colorMode.value}/pack.svg`" | :src="`/images/states/${colorMode.value}/pack.svg`" | |||
loading="lazy" | loading="lazy" | |||
class="flex-col object-contain object-center h-16 mb-4 w-16 inline-f | class="inline-flex flex-col object-contain object-center w-16 h-16 m | |||
lex" | b-4" | |||
:alt="`${$t('empty.collection')}`" | :alt="`${t('empty.collection')}`" | |||
/> | /> | |||
<span class="text-center"> | <span class="text-center"> | |||
{{ $t("empty.collection") }} | {{ t("empty.collection") }} | |||
</span> | </span> | |||
</div> | </div> | |||
</div> | </div> | |||
</div> | </div> | |||
<SmartConfirmModal | ||||
:show="confirmRemove" | ||||
:title="$t('confirm.remove_collection')" | ||||
@hide-modal="confirmRemove = false" | ||||
@resolve="removeCollection" | ||||
/> | ||||
</div> | </div> | |||
</template> | </template> | |||
<script lang="ts"> | <script lang="ts"> | |||
import { defineComponent, ref } from "@nuxtjs/composition-api" | import IconCircle from "~icons/lucide/circle" | |||
import IconCheckCircle from "~icons/lucide/check-circle" | ||||
import IconFolderPlus from "~icons/lucide/folder-plus" | ||||
import IconFilePlus from "~icons/lucide/file-plus" | ||||
import IconMoreVertical from "~icons/lucide/more-vertical" | ||||
import IconDownload from "~icons/lucide/download" | ||||
import IconTrash2 from "~icons/lucide/trash-2" | ||||
import IconEdit from "~icons/lucide/edit" | ||||
import IconFolder from "~icons/lucide/folder" | ||||
import IconFolderOpen from "~icons/lucide/folder-open" | ||||
import { useColorMode } from "@composables/theming" | ||||
import { useI18n } from "@composables/i18n" | ||||
import { useToast } from "@composables/toast" | ||||
import { defineComponent, ref, markRaw } from "vue" | ||||
import { moveRESTRequest } from "~/newstore/collections" | import { moveRESTRequest } from "~/newstore/collections" | |||
export default defineComponent({ | export default defineComponent({ | |||
props: { | props: { | |||
collectionIndex: { type: Number, default: null }, | collectionIndex: { type: Number, default: null }, | |||
collection: { type: Object, default: () => {} }, | collection: { type: Object, default: () => ({}) }, | |||
doc: Boolean, | ||||
isFiltered: Boolean, | isFiltered: Boolean, | |||
selected: Boolean, | ||||
saveRequest: Boolean, | saveRequest: Boolean, | |||
collectionsType: { type: Object, default: () => {} }, | collectionsType: { type: Object, default: () => ({}) }, | |||
picked: { type: Object, default: () => {} }, | picked: { type: Object, default: () => ({}) }, | |||
}, | }, | |||
emits: [ | ||||
"select", | ||||
"expand-collection", | ||||
"add-collection", | ||||
"remove-collection", | ||||
"add-folder", | ||||
"add-request", | ||||
"edit-folder", | ||||
"edit-request", | ||||
"duplicate-request", | ||||
"remove-folder", | ||||
"remove-request", | ||||
"select-collection", | ||||
"unselect-collection", | ||||
"edit-collection", | ||||
], | ||||
setup() { | setup() { | |||
return { | return { | |||
colorMode: useColorMode(), | ||||
toast: useToast(), | ||||
t: useI18n(), | ||||
tippyActions: ref<any | null>(null), | tippyActions: ref<any | null>(null), | |||
options: ref<any | null>(null), | options: ref<any | null>(null), | |||
requestAction: ref<any | null>(null), | ||||
folderAction: ref<any | null>(null), | folderAction: ref<any | null>(null), | |||
edit: ref<any | null>(null), | edit: ref<any | null>(null), | |||
deleteAction: ref<any | null>(null), | deleteAction: ref<any | null>(null), | |||
exportAction: ref<any | null>(null), | ||||
} | } | |||
}, | }, | |||
data() { | data() { | |||
return { | return { | |||
IconCircle: markRaw(IconCircle), | ||||
IconCheckCircle: markRaw(IconCheckCircle), | ||||
IconFilePlus: markRaw(IconFilePlus), | ||||
IconFolderPlus: markRaw(IconFolderPlus), | ||||
IconMoreVertical: markRaw(IconMoreVertical), | ||||
IconEdit: markRaw(IconEdit), | ||||
IconDownload: markRaw(IconDownload), | ||||
IconTrash2: markRaw(IconTrash2), | ||||
showChildren: false, | showChildren: false, | |||
dragging: false, | dragging: false, | |||
selectedFolder: {}, | selectedFolder: {}, | |||
confirmRemove: false, | ||||
prevCursor: "", | prevCursor: "", | |||
cursor: "", | cursor: "", | |||
pageNo: 0, | pageNo: 0, | |||
} | } | |||
}, | }, | |||
computed: { | computed: { | |||
isSelected(): boolean { | isSelected(): boolean { | |||
return ( | return ( | |||
this.picked && | this.picked && | |||
this.picked.pickedType === "my-collection" && | this.picked.pickedType === "my-collection" && | |||
this.picked.collectionIndex === this.collectionIndex | this.picked.collectionIndex === this.collectionIndex | |||
) | ) | |||
}, | }, | |||
getCollectionIcon() { | getCollectionIcon() { | |||
if (this.isSelected) return "check-circle" | if (this.isSelected) return IconCheckCircle | |||
else if (!this.showChildren && !this.isFiltered) return "folder" | else if (!this.showChildren && !this.isFiltered) return IconFolder | |||
else if (this.showChildren || this.isFiltered) return "folder-open" | else if (this.showChildren || this.isFiltered) return IconFolderOpen | |||
else return "folder" | else return IconFolder | |||
}, | }, | |||
}, | }, | |||
methods: { | methods: { | |||
exportCollection() { | ||||
const collectionJSON = JSON.stringify(this.collection) | ||||
const file = new Blob([collectionJSON], { type: "application/json" }) | ||||
const a = document.createElement("a") | ||||
const url = URL.createObjectURL(file) | ||||
a.href = url | ||||
a.download = `${this.collection.name}.json` | ||||
document.body.appendChild(a) | ||||
a.click() | ||||
this.toast.success(this.t("state.download_started").toString()) | ||||
setTimeout(() => { | ||||
document.body.removeChild(a) | ||||
URL.revokeObjectURL(url) | ||||
}, 1000) | ||||
}, | ||||
toggleShowChildren() { | toggleShowChildren() { | |||
if (this.$props.saveRequest) | if (this.$props.saveRequest) | |||
this.$emit("select", { | this.$emit("select", { | |||
picked: { | picked: { | |||
pickedType: "my-collection", | pickedType: "my-collection", | |||
collectionIndex: this.collectionIndex, | collectionIndex: this.collectionIndex, | |||
}, | }, | |||
}) | }) | |||
this.$emit("expand-collection", this.collection.id) | this.$emit("expand-collection", this.collection.id) | |||
this.showChildren = !this.showChildren | this.showChildren = !this.showChildren | |||
}, | }, | |||
removeCollection() { | removeCollection() { | |||
this.$emit("remove-collection", { | this.$emit("remove-collection", { | |||
collectionsType: this.collectionsType, | ||||
collectionIndex: this.collectionIndex, | collectionIndex: this.collectionIndex, | |||
collectionID: this.collection.id, | collectionID: this.collection.id, | |||
}) | }) | |||
}, | }, | |||
dropEvent({ dataTransfer }) { | dropEvent({ dataTransfer }: any) { | |||
this.dragging = !this.dragging | this.dragging = !this.dragging | |||
const folderPath = dataTransfer.getData("folderPath") | const folderPath = dataTransfer.getData("folderPath") | |||
const requestIndex = dataTransfer.getData("requestIndex") | const requestIndex = dataTransfer.getData("requestIndex") | |||
moveRESTRequest(folderPath, requestIndex, `${this.collectionIndex}`) | moveRESTRequest(folderPath, requestIndex, `${this.collectionIndex}`) | |||
}, | }, | |||
}, | }, | |||
}) | }) | |||
</script> | </script> | |||
End of changes. 35 change blocks. | ||||
104 lines changed or deleted | 178 lines changed or added |