894 lines
26 KiB
Vue
894 lines
26 KiB
Vue
<template>
|
|
<div class="flex flex-1 flex-col">
|
|
<div
|
|
class="sticky z-10 flex flex-1 justify-between border-b border-dividerLight bg-primary"
|
|
:style="
|
|
saveRequest
|
|
? 'top: calc(var(--upper-primary-sticky-fold) - var(--line-height-body))'
|
|
: 'top: var(--upper-primary-sticky-fold)'
|
|
"
|
|
>
|
|
<HoppButtonSecondary
|
|
v-if="hasNoTeamAccess || isShowingSearchResults"
|
|
v-tippy="{ theme: 'tooltip' }"
|
|
disabled
|
|
class="!rounded-none"
|
|
:icon="IconPlus"
|
|
:title="t('team.no_access')"
|
|
:label="t('action.new')"
|
|
/>
|
|
<HoppButtonSecondary
|
|
v-else
|
|
:icon="IconPlus"
|
|
:label="t('action.new')"
|
|
class="!rounded-none"
|
|
@click="emit('display-modal-add')"
|
|
/>
|
|
<span class="flex">
|
|
<HoppButtonSecondary
|
|
v-tippy="{ theme: 'tooltip' }"
|
|
to="https://docs.hoppscotch.io/documentation/features/collections"
|
|
blank
|
|
:title="t('app.wiki')"
|
|
:icon="IconHelpCircle"
|
|
/>
|
|
<HoppButtonSecondary
|
|
v-if="!saveRequest"
|
|
v-tippy="{ theme: 'tooltip' }"
|
|
:disabled="
|
|
(collectionsType.type === 'team-collections' &&
|
|
collectionsType.selectedTeam === undefined) ||
|
|
isShowingSearchResults
|
|
"
|
|
:icon="IconImport"
|
|
:title="t('modal.import_export')"
|
|
@click="emit('display-modal-import-export')"
|
|
/>
|
|
</span>
|
|
</div>
|
|
<div class="flex flex-col overflow-hidden">
|
|
<HoppSmartTree :adapter="teamAdapter">
|
|
<template
|
|
#content="{ node, toggleChildren, isOpen, highlightChildren }"
|
|
>
|
|
<CollectionsCollection
|
|
v-if="node.data.type === 'collections'"
|
|
:id="node.data.data.data.id"
|
|
:parent-i-d="node.data.data.parentIndex"
|
|
:data="node.data.data.data"
|
|
:collections-type="collectionsType.type"
|
|
:is-open="isOpen"
|
|
:export-loading="exportLoading"
|
|
:has-no-team-access="hasNoTeamAccess || isShowingSearchResults"
|
|
:collection-move-loading="collectionMoveLoading"
|
|
:is-last-item="node.data.isLastItem"
|
|
:is-selected="
|
|
isSelected({
|
|
collectionID: node.id,
|
|
})
|
|
"
|
|
folder-type="collection"
|
|
@add-request="
|
|
node.data.type === 'collections' &&
|
|
emit('add-request', {
|
|
path: node.id,
|
|
folder: node.data.data.data,
|
|
})
|
|
"
|
|
@add-folder="
|
|
node.data.type === 'collections' &&
|
|
emit('add-folder', {
|
|
path: node.id,
|
|
folder: node.data.data.data,
|
|
})
|
|
"
|
|
@edit-collection="
|
|
node.data.type === 'collections' &&
|
|
emit('edit-collection', {
|
|
collectionIndex: node.id,
|
|
collection: node.data.data.data,
|
|
})
|
|
"
|
|
@edit-properties="
|
|
node.data.type === 'collections' &&
|
|
emit('edit-properties', {
|
|
collectionIndex: node.id,
|
|
collection: node.data.data.data,
|
|
})
|
|
"
|
|
@export-data="
|
|
node.data.type === 'collections' &&
|
|
emit('export-data', node.data.data.data)
|
|
"
|
|
@remove-collection="emit('remove-collection', node.id)"
|
|
@drop-event="dropEvent($event, node.id)"
|
|
@drag-event="dragEvent($event, node.id)"
|
|
@update-collection-order="
|
|
updateCollectionOrder($event, {
|
|
destinationCollectionIndex: node.data.data.data.id,
|
|
destinationCollectionParentIndex: node.data.data.parentIndex,
|
|
})
|
|
"
|
|
@update-last-collection-order="
|
|
updateCollectionOrder($event, {
|
|
destinationCollectionIndex: null,
|
|
destinationCollectionParentIndex: node.data.data.parentIndex,
|
|
})
|
|
"
|
|
@dragging="
|
|
(isDraging) =>
|
|
highlightChildren(isDraging ? node.data.data.data.id : null)
|
|
"
|
|
@toggle-children="
|
|
() => {
|
|
toggleChildren(),
|
|
saveRequest &&
|
|
emit('select', {
|
|
pickedType: 'teams-collection',
|
|
collectionID: node.id,
|
|
})
|
|
}
|
|
"
|
|
@run-collection="emit('run-collection', $event)"
|
|
@click="
|
|
() => {
|
|
handleCollectionClick({
|
|
collectionID: node.id,
|
|
isOpen,
|
|
})
|
|
}
|
|
"
|
|
/>
|
|
<CollectionsCollection
|
|
v-if="node.data.type === 'folders'"
|
|
:id="node.data.data.data.id"
|
|
:parent-i-d="node.data.data.parentIndex"
|
|
:data="node.data.data.data"
|
|
:collections-type="collectionsType.type"
|
|
:is-open="isOpen"
|
|
:export-loading="exportLoading"
|
|
:has-no-team-access="hasNoTeamAccess || isShowingSearchResults"
|
|
:collection-move-loading="collectionMoveLoading"
|
|
:is-last-item="node.data.isLastItem"
|
|
:is-selected="
|
|
isSelected({
|
|
folderID: node.data.data.data.id,
|
|
})
|
|
"
|
|
folder-type="folder"
|
|
@add-request="
|
|
node.data.type === 'folders' &&
|
|
emit('add-request', {
|
|
path: node.id,
|
|
folder: node.data.data.data,
|
|
})
|
|
"
|
|
@add-folder="
|
|
node.data.type === 'folders' &&
|
|
emit('add-folder', {
|
|
path: node.id,
|
|
folder: node.data.data.data,
|
|
})
|
|
"
|
|
@edit-collection="
|
|
node.data.type === 'folders' &&
|
|
emit('edit-folder', {
|
|
folder: node.data.data.data,
|
|
})
|
|
"
|
|
@edit-properties="
|
|
node.data.type === 'folders' &&
|
|
emit('edit-properties', {
|
|
collectionIndex: node.id,
|
|
collection: node.data.data.data,
|
|
})
|
|
"
|
|
@export-data="
|
|
node.data.type === 'folders' &&
|
|
emit('export-data', node.data.data.data)
|
|
"
|
|
@remove-collection="
|
|
node.data.type === 'folders' &&
|
|
emit('remove-folder', node.data.data.data.id)
|
|
"
|
|
@drop-event="dropEvent($event, node.data.data.data.id)"
|
|
@drag-event="dragEvent($event, node.data.data.data.id)"
|
|
@update-collection-order="
|
|
updateCollectionOrder($event, {
|
|
destinationCollectionIndex: node.data.data.data.id,
|
|
destinationCollectionParentIndex: node.data.data.parentIndex,
|
|
})
|
|
"
|
|
@update-last-collection-order="
|
|
updateCollectionOrder($event, {
|
|
destinationCollectionIndex: null,
|
|
destinationCollectionParentIndex: node.data.data.parentIndex,
|
|
})
|
|
"
|
|
@dragging="
|
|
(isDraging) =>
|
|
highlightChildren(isDraging ? node.data.data.data.id : null)
|
|
"
|
|
@toggle-children="
|
|
() => {
|
|
toggleChildren(),
|
|
saveRequest &&
|
|
emit('select', {
|
|
pickedType: 'teams-folder',
|
|
folderID: node.data.data.data.id,
|
|
})
|
|
}
|
|
"
|
|
@run-collection="emit('run-collection', $event)"
|
|
@click="
|
|
() => {
|
|
handleCollectionClick({
|
|
// for the folders, we get a path, so we need to get the last part of the path which is the folder id
|
|
collectionID: node.id.split('/').pop() as string,
|
|
isOpen,
|
|
})
|
|
}
|
|
"
|
|
/>
|
|
<CollectionsRequest
|
|
v-if="node.data.type === 'requests'"
|
|
:request="node.data.data.data.request"
|
|
:request-i-d="node.data.data.data.id"
|
|
:parent-i-d="node.data.data.parentIndex"
|
|
:collections-type="collectionsType.type"
|
|
:duplicate-loading="duplicateLoading"
|
|
:is-active="isActiveRequest(node.data.data.data.id)"
|
|
:has-no-team-access="hasNoTeamAccess || isShowingSearchResults"
|
|
:request-move-loading="requestMoveLoading"
|
|
:is-last-item="node.data.isLastItem"
|
|
:is-selected="
|
|
isSelected({
|
|
requestID: node.data.data.data.id,
|
|
})
|
|
"
|
|
@edit-request="
|
|
node.data.type === 'requests' &&
|
|
emit('edit-request', {
|
|
requestIndex: node.data.data.data.id,
|
|
request: node.data.data.data.request,
|
|
})
|
|
"
|
|
@duplicate-request="
|
|
node.data.type === 'requests' &&
|
|
emit('duplicate-request', {
|
|
folderPath: node.data.data.parentIndex,
|
|
request: node.data.data.data.request,
|
|
})
|
|
"
|
|
@remove-request="
|
|
node.data.type === 'requests' &&
|
|
emit('remove-request', {
|
|
folderPath: null,
|
|
requestIndex: node.data.data.data.id,
|
|
})
|
|
"
|
|
@select-request="
|
|
node.data.type === 'requests' &&
|
|
selectRequest({
|
|
request: node.data.data.data.request,
|
|
requestIndex: node.data.data.data.id,
|
|
folderPath: getPath(node.id),
|
|
})
|
|
"
|
|
@share-request="
|
|
node.data.type === 'requests' &&
|
|
emit('share-request', {
|
|
request: node.data.data.data.request,
|
|
})
|
|
"
|
|
@drag-request="
|
|
dragRequest($event, {
|
|
folderPath: node.data.data.parentIndex,
|
|
requestIndex: node.data.data.data.id,
|
|
})
|
|
"
|
|
@update-request-order="
|
|
updateRequestOrder($event, {
|
|
folderPath: node.data.data.parentIndex,
|
|
requestIndex: node.data.data.data.id,
|
|
})
|
|
"
|
|
@update-last-request-order="
|
|
updateRequestOrder($event, {
|
|
folderPath: node.data.data.parentIndex,
|
|
requestIndex: null,
|
|
})
|
|
"
|
|
/>
|
|
</template>
|
|
<template #emptyNode="{ node }">
|
|
<HoppSmartPlaceholder
|
|
v-if="filterText.length !== 0 && teamCollectionList.length === 0"
|
|
:text="`${t('state.nothing_found')} ‟${filterText}”`"
|
|
>
|
|
<template #icon>
|
|
<icon-lucide-search class="svg-icons opacity-75" />
|
|
</template>
|
|
</HoppSmartPlaceholder>
|
|
<HoppSmartPlaceholder
|
|
v-else-if="node === null"
|
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
:alt="`${t('empty.collections')}`"
|
|
:text="t('empty.collections')"
|
|
@drop.stop
|
|
>
|
|
<template #body>
|
|
<div class="flex flex-col items-center space-y-4">
|
|
<span class="text-center text-secondaryLight">
|
|
{{ t("collection.import_or_create") }}
|
|
</span>
|
|
<div class="flex flex-col items-stretch gap-4">
|
|
<HoppButtonPrimary
|
|
:icon="IconImport"
|
|
:label="t('import.title')"
|
|
filled
|
|
outline
|
|
:disabled="hasNoTeamAccess"
|
|
:title="hasNoTeamAccess ? t('team.no_access') : ''"
|
|
@click="
|
|
hasNoTeamAccess
|
|
? null
|
|
: emit('display-modal-import-export')
|
|
"
|
|
/>
|
|
<HoppButtonSecondary
|
|
:icon="IconPlus"
|
|
:label="t('add.new')"
|
|
filled
|
|
outline
|
|
:disabled="hasNoTeamAccess"
|
|
:title="hasNoTeamAccess ? t('team.no_access') : ''"
|
|
@click="hasNoTeamAccess ? null : emit('display-modal-add')"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</HoppSmartPlaceholder>
|
|
<HoppSmartPlaceholder
|
|
v-else-if="node.data.type === 'collections'"
|
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
:alt="`${t('empty.collections')}`"
|
|
:text="t('empty.collections')"
|
|
@drop.stop
|
|
>
|
|
<template #body>
|
|
<HoppButtonSecondary
|
|
:label="t('add.new')"
|
|
filled
|
|
outline
|
|
@click="
|
|
node.data.type === 'collections' &&
|
|
emit('add-folder', {
|
|
path: node.id,
|
|
folder: node.data.data.data,
|
|
})
|
|
"
|
|
/>
|
|
</template>
|
|
</HoppSmartPlaceholder>
|
|
<HoppSmartPlaceholder
|
|
v-else-if="node.data.type === 'folders'"
|
|
:src="`/images/states/${colorMode.value}/pack.svg`"
|
|
:alt="`${t('empty.folder')}`"
|
|
:text="t('empty.folder')"
|
|
@drop.stop
|
|
/>
|
|
</template>
|
|
</HoppSmartTree>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import IconPlus from "~icons/lucide/plus"
|
|
import IconHelpCircle from "~icons/lucide/help-circle"
|
|
import IconImport from "~icons/lucide/folder-down"
|
|
import { computed, PropType, Ref, toRef } from "vue"
|
|
import { useI18n } from "@composables/i18n"
|
|
import { useColorMode } from "@composables/theming"
|
|
import { TeamCollection } from "~/helpers/teams/TeamCollection"
|
|
import { TeamRequest } from "~/helpers/teams/TeamRequest"
|
|
import { ChildrenResult, SmartTreeAdapter } from "@hoppscotch/ui/helpers"
|
|
import { cloneDeep } from "lodash-es"
|
|
import { HoppRESTRequest } from "@hoppscotch/data"
|
|
import { pipe } from "fp-ts/function"
|
|
import * as O from "fp-ts/Option"
|
|
import { Picked } from "~/helpers/types/HoppPicked.js"
|
|
import { RESTTabService } from "~/services/tab/rest"
|
|
import { useService } from "dioc/vue"
|
|
import { TeamWorkspace } from "~/services/workspace.service"
|
|
|
|
const t = useI18n()
|
|
const colorMode = useColorMode()
|
|
const tabs = useService(RESTTabService)
|
|
|
|
type CollectionType =
|
|
| {
|
|
type: "team-collections"
|
|
selectedTeam: TeamWorkspace
|
|
}
|
|
| { type: "my-collections"; selectedTeam: undefined }
|
|
|
|
const props = defineProps({
|
|
collectionsType: {
|
|
type: Object as PropType<CollectionType>,
|
|
default: () => ({ type: "my-collections", selectedTeam: undefined }),
|
|
required: true,
|
|
},
|
|
filterText: {
|
|
type: String as PropType<string>,
|
|
default: "",
|
|
required: true,
|
|
},
|
|
teamCollectionList: {
|
|
type: Array as PropType<TeamCollection[]>,
|
|
default: () => [],
|
|
required: true,
|
|
},
|
|
teamLoadingCollections: {
|
|
type: Array as PropType<string[]>,
|
|
default: () => [],
|
|
required: true,
|
|
},
|
|
saveRequest: {
|
|
type: Boolean,
|
|
default: false,
|
|
required: false,
|
|
},
|
|
exportLoading: {
|
|
type: Boolean,
|
|
default: false,
|
|
required: false,
|
|
},
|
|
duplicateLoading: {
|
|
type: Boolean,
|
|
default: false,
|
|
required: false,
|
|
},
|
|
picked: {
|
|
type: Object as PropType<Picked | null>,
|
|
default: null,
|
|
required: false,
|
|
},
|
|
collectionMoveLoading: {
|
|
type: Array as PropType<string[]>,
|
|
default: () => [],
|
|
required: false,
|
|
},
|
|
requestMoveLoading: {
|
|
type: Array as PropType<string[]>,
|
|
default: () => [],
|
|
required: false,
|
|
},
|
|
})
|
|
|
|
const isShowingSearchResults = computed(() => props.filterText.length > 0)
|
|
|
|
const emit = defineEmits<{
|
|
(
|
|
event: "add-request",
|
|
payload: {
|
|
path: string
|
|
folder: TeamCollection
|
|
}
|
|
): void
|
|
(
|
|
event: "add-folder",
|
|
payload: {
|
|
path: string
|
|
folder: TeamCollection
|
|
}
|
|
): void
|
|
(
|
|
event: "edit-collection",
|
|
payload: {
|
|
collectionIndex: string
|
|
collection: TeamCollection
|
|
}
|
|
): void
|
|
(
|
|
event: "edit-folder",
|
|
payload: {
|
|
folder: TeamCollection
|
|
}
|
|
): void
|
|
(
|
|
event: "edit-properties",
|
|
payload: {
|
|
collectionIndex: string
|
|
collection: TeamCollection
|
|
}
|
|
): void
|
|
(
|
|
event: "edit-request",
|
|
payload: {
|
|
requestIndex: string
|
|
request: HoppRESTRequest
|
|
}
|
|
): void
|
|
(
|
|
event: "duplicate-request",
|
|
payload: {
|
|
folderPath: string
|
|
request: HoppRESTRequest
|
|
}
|
|
): void
|
|
(event: "export-data", payload: TeamCollection): void
|
|
(event: "remove-collection", payload: string): void
|
|
(event: "remove-folder", payload: string): void
|
|
(
|
|
event: "remove-request",
|
|
payload: {
|
|
folderPath: string | null
|
|
requestIndex: string
|
|
}
|
|
): void
|
|
(
|
|
event: "select-request",
|
|
payload: {
|
|
request: HoppRESTRequest
|
|
requestIndex: string
|
|
isActive: boolean
|
|
folderPath: string
|
|
}
|
|
): void
|
|
(
|
|
event: "share-request",
|
|
payload: {
|
|
request: HoppRESTRequest
|
|
}
|
|
): void
|
|
(
|
|
event: "drop-request",
|
|
payload: {
|
|
folderPath: string
|
|
requestIndex: string
|
|
destinationCollectionIndex: string
|
|
}
|
|
): void
|
|
(
|
|
event: "drop-collection",
|
|
payload: {
|
|
collectionIndexDragged: string
|
|
destinationCollectionIndex: string
|
|
}
|
|
): void
|
|
(
|
|
event: "update-request-order",
|
|
payload: {
|
|
dragedRequestIndex: string
|
|
destinationRequestIndex: string | null
|
|
destinationCollectionIndex: string
|
|
}
|
|
): void
|
|
(
|
|
event: "update-collection-order",
|
|
payload: {
|
|
dragedCollectionIndex: string
|
|
destinationCollection: {
|
|
destinationCollectionIndex: string | null
|
|
destinationCollectionParentIndex: string | null
|
|
}
|
|
}
|
|
): void
|
|
(
|
|
event: "collection-click",
|
|
payload: {
|
|
// if the collection is open or not in the tree
|
|
isOpen: boolean
|
|
collectionID: string
|
|
}
|
|
): void
|
|
(event: "select", payload: Picked | null): void
|
|
(event: "expand-team-collection", payload: string): void
|
|
(event: "display-modal-add"): void
|
|
(event: "display-modal-import-export"): void
|
|
(event: "run-collection", collectionID: string): void
|
|
}>()
|
|
|
|
const getPath = (path: string) => {
|
|
const pathArray = path.split("/")
|
|
pathArray.pop()
|
|
return pathArray.join("/")
|
|
}
|
|
|
|
const handleCollectionClick = (payload: {
|
|
collectionID: string
|
|
isOpen: boolean
|
|
}) => {
|
|
const { collectionID, isOpen } = payload
|
|
|
|
emit("collection-click", {
|
|
collectionID,
|
|
isOpen,
|
|
})
|
|
}
|
|
|
|
const teamCollectionsList = toRef(props, "teamCollectionList")
|
|
|
|
const hasNoTeamAccess = computed(
|
|
() =>
|
|
props.collectionsType.type === "team-collections" &&
|
|
(props.collectionsType.selectedTeam === undefined ||
|
|
props.collectionsType.selectedTeam.role === "VIEWER")
|
|
)
|
|
|
|
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
|
|
)
|
|
}
|
|
return (
|
|
props.picked &&
|
|
props.picked.pickedType === "teams-folder" &&
|
|
props.picked.folderID === folderID
|
|
)
|
|
}
|
|
|
|
const active = computed(() => tabs.currentActiveTab.value.document.saveContext)
|
|
|
|
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
|
|
requestIndex: string
|
|
folderPath: string | null
|
|
}) => {
|
|
const { request, requestIndex } = data
|
|
if (props.saveRequest) {
|
|
emit("select", {
|
|
pickedType: "teams-request",
|
|
requestID: requestIndex,
|
|
})
|
|
} else {
|
|
emit("select-request", {
|
|
request: request,
|
|
requestIndex: requestIndex,
|
|
isActive: isActiveRequest(requestIndex),
|
|
folderPath: data.folderPath,
|
|
})
|
|
}
|
|
}
|
|
|
|
const dragRequest = (
|
|
dataTransfer: DataTransfer,
|
|
{
|
|
folderPath,
|
|
requestIndex,
|
|
}: { folderPath: string | null; requestIndex: string }
|
|
) => {
|
|
if (!folderPath) return
|
|
dataTransfer.setData("folderPath", folderPath)
|
|
dataTransfer.setData("requestIndex", requestIndex)
|
|
}
|
|
|
|
const dragEvent = (dataTransfer: DataTransfer, collectionIndex: string) => {
|
|
dataTransfer.setData("collectionIndex", collectionIndex)
|
|
}
|
|
|
|
const dropEvent = (
|
|
dataTransfer: DataTransfer,
|
|
destinationCollectionIndex: string
|
|
) => {
|
|
const folderPath = dataTransfer.getData("folderPath")
|
|
const requestIndex = dataTransfer.getData("requestIndex")
|
|
const collectionIndexDragged = dataTransfer.getData("collectionIndex")
|
|
if (folderPath && requestIndex) {
|
|
emit("drop-request", {
|
|
folderPath,
|
|
requestIndex,
|
|
destinationCollectionIndex,
|
|
})
|
|
} else {
|
|
emit("drop-collection", {
|
|
collectionIndexDragged,
|
|
destinationCollectionIndex,
|
|
})
|
|
}
|
|
}
|
|
|
|
const updateRequestOrder = (
|
|
dataTransfer: DataTransfer,
|
|
{
|
|
folderPath,
|
|
requestIndex,
|
|
}: { folderPath: string | null; requestIndex: string | null }
|
|
) => {
|
|
if (!folderPath) return
|
|
const dragedRequestIndex = dataTransfer.getData("requestIndex")
|
|
const destinationRequestIndex = requestIndex
|
|
const destinationCollectionIndex = folderPath
|
|
|
|
emit("update-request-order", {
|
|
dragedRequestIndex,
|
|
destinationRequestIndex,
|
|
destinationCollectionIndex,
|
|
})
|
|
}
|
|
|
|
const updateCollectionOrder = (
|
|
dataTransfer: DataTransfer,
|
|
destinationCollection: {
|
|
destinationCollectionIndex: string | null
|
|
destinationCollectionParentIndex: string | null
|
|
}
|
|
) => {
|
|
const dragedCollectionIndex = dataTransfer.getData("collectionIndex")
|
|
|
|
emit("update-collection-order", {
|
|
dragedCollectionIndex,
|
|
destinationCollection,
|
|
})
|
|
}
|
|
|
|
type TeamCollections = {
|
|
isLastItem: boolean
|
|
type: "collections"
|
|
data: {
|
|
parentIndex: null
|
|
data: TeamCollection
|
|
}
|
|
}
|
|
|
|
type TeamFolder = {
|
|
isLastItem: boolean
|
|
type: "folders"
|
|
data: {
|
|
parentIndex: string
|
|
data: TeamCollection
|
|
}
|
|
}
|
|
|
|
type TeamRequests = {
|
|
isLastItem: boolean
|
|
type: "requests"
|
|
data: {
|
|
parentIndex: string
|
|
data: TeamRequest
|
|
}
|
|
}
|
|
|
|
type TeamCollectionNode = TeamCollections | TeamFolder | TeamRequests
|
|
|
|
class TeamCollectionsAdapter implements SmartTreeAdapter<TeamCollectionNode> {
|
|
constructor(public data: Ref<TeamCollection[]>) {}
|
|
|
|
findCollInTree(
|
|
tree: TeamCollection[],
|
|
targetID: string
|
|
): TeamCollection | null {
|
|
for (const coll of tree) {
|
|
// If the direct child matched, then return that
|
|
if (coll.id === targetID) return coll
|
|
|
|
// Else run it in the children
|
|
if (coll.children) {
|
|
const result = this.findCollInTree(coll.children, targetID)
|
|
if (result) return result
|
|
}
|
|
}
|
|
|
|
// If nothing matched, return null
|
|
return null
|
|
}
|
|
|
|
getChildren(id: string | null): Ref<ChildrenResult<TeamCollectionNode>> {
|
|
return computed(() => {
|
|
if (id === null) {
|
|
if (props.teamLoadingCollections.includes("root")) {
|
|
return {
|
|
status: "loading",
|
|
}
|
|
}
|
|
const data = this.data.value.map((item, index) => ({
|
|
id: item.id,
|
|
data: {
|
|
isLastItem: index === this.data.value.length - 1,
|
|
type: "collections",
|
|
data: {
|
|
parentIndex: null,
|
|
data: item,
|
|
},
|
|
},
|
|
}))
|
|
return {
|
|
status: "loaded",
|
|
data: cloneDeep(data),
|
|
} as ChildrenResult<TeamCollections>
|
|
}
|
|
const parsedID = id.split("/")[id.split("/").length - 1]
|
|
|
|
!props.teamLoadingCollections.includes(parsedID) &&
|
|
emit("expand-team-collection", parsedID)
|
|
|
|
if (props.teamLoadingCollections.includes(parsedID)) {
|
|
return {
|
|
status: "loading",
|
|
}
|
|
}
|
|
const items = this.findCollInTree(this.data.value, parsedID)
|
|
if (items) {
|
|
const data = [
|
|
...(items.children
|
|
? items.children.map((item, index) => ({
|
|
id: `${id}/${item.id}`,
|
|
data: {
|
|
isLastItem:
|
|
items.children && items.children.length > 1
|
|
? index === items.children.length - 1
|
|
: false,
|
|
type: "folders",
|
|
data: {
|
|
parentIndex: parsedID,
|
|
data: item,
|
|
},
|
|
},
|
|
}))
|
|
: []),
|
|
...(items.requests
|
|
? items.requests.map((item, index) => ({
|
|
id: `${id}/${item.id}`,
|
|
data: {
|
|
isLastItem:
|
|
items.requests && items.requests.length > 1
|
|
? index === items.requests.length - 1
|
|
: false,
|
|
type: "requests",
|
|
data: {
|
|
parentIndex: parsedID,
|
|
data: item,
|
|
},
|
|
},
|
|
}))
|
|
: []),
|
|
]
|
|
return {
|
|
status: "loaded",
|
|
data: cloneDeep(data),
|
|
} as ChildrenResult<TeamFolder | TeamRequests>
|
|
}
|
|
return {
|
|
status: "loaded",
|
|
data: [],
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
const teamAdapter: SmartTreeAdapter<TeamCollectionNode> =
|
|
new TeamCollectionsAdapter(teamCollectionsList)
|
|
</script>
|