Support drag and drop for moving views into groups (#5353)
* Initiale drag and drop moving to group * Update some styling * Reenable ungrouped groups * Support moving to ungrouped from dropdown * Remove unneeded comments * undo unnecessary groupId changes * group cleanup * clean up drag logic --------- Co-authored-by: Kristaps Fabians Geikins <fabis94@live.com>
This commit is contained in:
committed by
GitHub
parent
c641ce63e3
commit
4fc1aa6d40
@@ -1,7 +1,14 @@
|
||||
<!-- eslint-disable vuejs-accessibility/click-events-have-key-events -->
|
||||
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
||||
<template>
|
||||
<div v-keyboard-clickable :class="wrapperClasses" :view-id="view.id" @click="apply">
|
||||
<div
|
||||
v-keyboard-clickable
|
||||
:class="[wrapperClasses, draggableClasses]"
|
||||
:view-id="view.id"
|
||||
draggable="true"
|
||||
v-on="on"
|
||||
@click="apply"
|
||||
>
|
||||
<div class="flex items-center shrink-0">
|
||||
<div class="relative">
|
||||
<img
|
||||
@@ -112,6 +119,7 @@ import {
|
||||
useCollectNewSavedViewViewerData,
|
||||
useUpdateSavedView
|
||||
} from '~/lib/viewer/composables/savedViews/management'
|
||||
import { useDraggableView } from '~/lib/viewer/composables/savedViews/ui'
|
||||
import { useSavedViewValidationHelpers } from '~/lib/viewer/composables/savedViews/validation'
|
||||
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
|
||||
|
||||
@@ -151,6 +159,7 @@ graphql(`
|
||||
...UseUpdateSavedView_SavedView
|
||||
...ViewerSavedViewsPanelViewEditDialog_SavedView
|
||||
...UseSavedViewValidationHelpers_SavedView
|
||||
...UseDraggableView_SavedView
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -178,6 +187,9 @@ const {
|
||||
} = useSavedViewValidationHelpers({
|
||||
view: computed(() => props.view)
|
||||
})
|
||||
const { classes: draggableClasses, on } = useDraggableView({
|
||||
view: computed(() => props.view)
|
||||
})
|
||||
|
||||
const showMenu = ref(false)
|
||||
const menuId = useId()
|
||||
@@ -252,7 +264,7 @@ const menuItems = computed((): LayoutMenuItem<MenuItems>[][] => [
|
||||
|
||||
const wrapperClasses = computed(() => {
|
||||
const classParts = [
|
||||
'flex items-center gap-2 p-1.5 w-full group rounded-md cursor-pointer'
|
||||
'flex items-center gap-2 p-1.5 w-full group rounded-md cursor-pointer relative transition-all'
|
||||
]
|
||||
|
||||
if (isActive.value) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<LayoutDialog
|
||||
v-model:open="open"
|
||||
title="Edit view details"
|
||||
title="Edit view"
|
||||
max-width="sm"
|
||||
:buttons="buttons"
|
||||
:on-submit="onSubmit"
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<FormTextInput
|
||||
name="name"
|
||||
label="View name"
|
||||
label="Name"
|
||||
show-label
|
||||
color="foundation"
|
||||
auto-focus
|
||||
|
||||
@@ -1,62 +1,66 @@
|
||||
<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
|
||||
<template>
|
||||
<LayoutDisclosure
|
||||
v-if="!isUngroupedGroup"
|
||||
v-model:open="open"
|
||||
v-model:edit-title="renameMode"
|
||||
color="subtle"
|
||||
:title="group.title"
|
||||
lazy-load
|
||||
@update:title="onRename"
|
||||
>
|
||||
<div :class="dropZoneClasses" v-on="on">
|
||||
<LayoutDisclosure
|
||||
v-if="!isUngroupedGroup"
|
||||
v-model:open="open"
|
||||
v-model:edit-title="renameMode"
|
||||
color="subtle"
|
||||
:title="group.title"
|
||||
lazy-load
|
||||
@update:title="onRename"
|
||||
>
|
||||
<ViewerSavedViewsPanelViewsGroupInner
|
||||
:group="group"
|
||||
:search="search"
|
||||
:views-type="viewsType"
|
||||
/>
|
||||
<template #title-actions>
|
||||
<div
|
||||
class="flex gap-0.5 items-center opacity-0 group-hover/disclosure:opacity-100"
|
||||
@click.stop
|
||||
>
|
||||
<LayoutMenu
|
||||
v-if="!isUngroupedGroup"
|
||||
v-model:open="showMenu"
|
||||
:items="menuItems"
|
||||
:menu-id="menuId"
|
||||
mount-menu-on-body
|
||||
show-ticks="right"
|
||||
@chosen="({ item: actionItem }) => onActionChosen(actionItem)"
|
||||
>
|
||||
<FormButton
|
||||
name="viewActions"
|
||||
size="sm"
|
||||
color="subtle"
|
||||
:icon-left="Ellipsis"
|
||||
hide-text
|
||||
@click="showMenu = !showMenu"
|
||||
/>
|
||||
</LayoutMenu>
|
||||
<div v-tippy="canCreateView?.errorMessage">
|
||||
<FormButton
|
||||
v-tippy="getTooltipProps('Create view')"
|
||||
size="sm"
|
||||
color="subtle"
|
||||
:icon-left="Plus"
|
||||
hide-text
|
||||
name="addGroupView"
|
||||
:disabled="!canCreateView.authorized || isLoading"
|
||||
@click="onAddGroupView"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutDisclosure>
|
||||
<ViewerSavedViewsPanelViewsGroupInner
|
||||
v-else
|
||||
class="mb-[1px]"
|
||||
:group="group"
|
||||
:search="search"
|
||||
:views-type="viewsType"
|
||||
/>
|
||||
<template #title-actions>
|
||||
<div
|
||||
class="flex gap-0.5 items-center opacity-0 group-hover/disclosure:opacity-100"
|
||||
@click.stop
|
||||
>
|
||||
<LayoutMenu
|
||||
v-model:open="showMenu"
|
||||
:items="menuItems"
|
||||
:menu-id="menuId"
|
||||
mount-menu-on-body
|
||||
show-ticks="right"
|
||||
@chosen="({ item: actionItem }) => onActionChosen(actionItem)"
|
||||
>
|
||||
<FormButton
|
||||
name="viewActions"
|
||||
size="sm"
|
||||
color="subtle"
|
||||
:icon-left="Ellipsis"
|
||||
hide-text
|
||||
@click="showMenu = !showMenu"
|
||||
/>
|
||||
</LayoutMenu>
|
||||
<div v-tippy="canCreateView?.errorMessage">
|
||||
<FormButton
|
||||
v-tippy="getTooltipProps('Create view in group')"
|
||||
size="sm"
|
||||
color="subtle"
|
||||
:icon-left="Plus"
|
||||
hide-text
|
||||
name="addGroupView"
|
||||
:disabled="!canCreateView.authorized || isLoading"
|
||||
@click="onAddGroupView"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</LayoutDisclosure>
|
||||
<ViewerSavedViewsPanelViewsGroupInner
|
||||
v-else
|
||||
class="mb-[1px]"
|
||||
:group="group"
|
||||
:search="search"
|
||||
:views-type="viewsType"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { StringEnum, throwUncoveredError, type StringEnumValues } from '@speckle/shared'
|
||||
@@ -70,11 +74,13 @@ import type {
|
||||
ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragment,
|
||||
ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroupFragment
|
||||
} from '~/lib/common/generated/gql/graphql'
|
||||
import { ToastNotificationType } from '~/lib/common/composables/toast'
|
||||
import {
|
||||
useCreateSavedView,
|
||||
useUpdateSavedViewGroup
|
||||
} from '~/lib/viewer/composables/savedViews/management'
|
||||
import type { ViewsType } from '~/lib/viewer/helpers/savedViews'
|
||||
import { useDraggableViewTargetGroup } from '~/lib/viewer/composables/savedViews/ui'
|
||||
|
||||
const { getTooltipProps } = useSmartTooltipDelay()
|
||||
|
||||
@@ -105,6 +111,7 @@ graphql(`
|
||||
...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup
|
||||
...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup
|
||||
...UseUpdateSavedViewGroup_SavedViewGroup
|
||||
...UseDraggableViewTargetGroup_SavedViewGroup
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -140,8 +147,17 @@ const { triggerNotification } = useGlobalToast()
|
||||
const isLoading = useMutationLoading()
|
||||
const createView = useCreateSavedView()
|
||||
const updateGroup = useUpdateSavedViewGroup()
|
||||
const renameMode = defineModel<boolean>('renameMode')
|
||||
const { on, classes: dropZoneClasses } = useDraggableViewTargetGroup({
|
||||
group: computed(() => props.group),
|
||||
onMoved: () => {
|
||||
// Auto-open the group if it was closed
|
||||
if (!open.value) {
|
||||
open.value = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const renameMode = defineModel<boolean>('renameMode')
|
||||
const open = defineModel<boolean>('open')
|
||||
const showMenu = ref(false)
|
||||
const menuId = useId()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="mb-1.5">
|
||||
<div v-if="isVeryFirstLoading" class="flex justify-center">
|
||||
<CommonLoadingIcon class="m-4" />
|
||||
</div>
|
||||
|
||||
@@ -175,12 +175,12 @@ type Documents = {
|
||||
"\n fragment ViewerSavedViewsPanel_Project on Project {\n id\n permissions {\n canCreateSavedView {\n ...FullPermissionCheckResult\n }\n }\n workspace {\n id\n seatType\n planSupportsSavedViews: hasAccessToFeature(featureName: savedViews)\n }\n }\n": typeof types.ViewerSavedViewsPanel_ProjectFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelGroups_Project on Project {\n id\n savedViewGroups(input: $savedViewGroupsInput) {\n totalCount\n cursor\n items {\n id\n ...ViewerSavedViewsPanelViewsGroup_SavedViewGroup\n }\n }\n ...ViewerSavedViewsPanelViewsGroup_Project\n }\n": typeof types.ViewerSavedViewsPanelGroups_ProjectFragmentDoc,
|
||||
"\n query ViewerSavedViewsPanelGroups_SavedViewGroups(\n $projectId: String!\n $savedViewGroupsInput: SavedViewGroupsInput!\n ) {\n project(id: $projectId) {\n id\n ...ViewerSavedViewsPanelGroups_Project\n }\n }\n": typeof types.ViewerSavedViewsPanelGroups_SavedViewGroupsDocument,
|
||||
"\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n }\n": typeof types.ViewerSavedViewsPanelView_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n ...UseDraggableView_SavedView\n }\n": typeof types.ViewerSavedViewsPanelView_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewDeleteDialog_SavedView on SavedView {\n id\n name\n ...UseDeleteSavedView_SavedView\n }\n": typeof types.ViewerSavedViewsPanelViewDeleteDialog_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewEditDialog_SavedView on SavedView {\n id\n name\n description\n visibility\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n }\n": typeof types.ViewerSavedViewsPanelViewEditDialog_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewMoveDialog_SavedView on SavedView {\n id\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n": typeof types.ViewerSavedViewsPanelViewMoveDialog_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_Project on Project {\n id\n permissions {\n canCreateSavedView {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.ViewerSavedViewsPanelViewsGroup_ProjectFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n }\n": typeof types.ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n ...UseDraggableViewTargetGroup_SavedViewGroup\n }\n": typeof types.ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup_Paginated on SavedViewGroup {\n id\n views(input: $savedViewsInput) {\n cursor\n totalCount\n items {\n id\n ...ViewerSavedViewsPanelView_SavedView\n }\n }\n }\n": typeof types.ViewerSavedViewsPanelViewsGroup_SavedViewGroup_PaginatedFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup on SavedViewGroup {\n id\n title\n ...UseDeleteSavedViewGroup_SavedViewGroup\n }\n": typeof types.ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroupFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n": typeof types.ViewerSavedViewsPanelViewsGroupInner_SavedViewGroupFragmentDoc,
|
||||
@@ -433,6 +433,8 @@ type Documents = {
|
||||
"\n fragment UseDeleteSavedViewGroup_SavedViewGroup on SavedViewGroup {\n id\n groupId\n projectId\n isUngroupedViewsGroup\n }\n": typeof types.UseDeleteSavedViewGroup_SavedViewGroupFragmentDoc,
|
||||
"\n mutation UpdateSavedViewGroup($input: UpdateSavedViewGroupInput!) {\n projectMutations {\n savedViewMutations {\n updateGroup(input: $input) {\n id\n ...UseUpdateSavedViewGroup_SavedViewGroup\n }\n }\n }\n }\n": typeof types.UpdateSavedViewGroupDocument,
|
||||
"\n fragment UseUpdateSavedViewGroup_SavedViewGroup on SavedViewGroup {\n id\n projectId\n groupId\n title\n isUngroupedViewsGroup\n }\n": typeof types.UseUpdateSavedViewGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment UseDraggableView_SavedView on SavedView {\n id\n projectId\n name\n group {\n id\n }\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseUpdateSavedView_SavedView\n }\n": typeof types.UseDraggableView_SavedViewFragmentDoc,
|
||||
"\n fragment UseDraggableViewTargetGroup_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n": typeof types.UseDraggableViewTargetGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment UseSavedViewValidationHelpers_SavedView on SavedView {\n id\n isHomeView\n visibility\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n }\n": typeof types.UseSavedViewValidationHelpers_SavedViewFragmentDoc,
|
||||
"\n fragment UseViewerSavedViewSetup_SavedView on SavedView {\n id\n viewerState\n }\n": typeof types.UseViewerSavedViewSetup_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerCommentThread on Comment {\n ...ViewerCommentsListItem\n ...ViewerCommentBubblesData\n ...ViewerCommentsReplyItem\n ...ViewerCommentThreadData\n }\n": typeof types.ViewerCommentThreadFragmentDoc,
|
||||
@@ -693,12 +695,12 @@ const documents: Documents = {
|
||||
"\n fragment ViewerSavedViewsPanel_Project on Project {\n id\n permissions {\n canCreateSavedView {\n ...FullPermissionCheckResult\n }\n }\n workspace {\n id\n seatType\n planSupportsSavedViews: hasAccessToFeature(featureName: savedViews)\n }\n }\n": types.ViewerSavedViewsPanel_ProjectFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelGroups_Project on Project {\n id\n savedViewGroups(input: $savedViewGroupsInput) {\n totalCount\n cursor\n items {\n id\n ...ViewerSavedViewsPanelViewsGroup_SavedViewGroup\n }\n }\n ...ViewerSavedViewsPanelViewsGroup_Project\n }\n": types.ViewerSavedViewsPanelGroups_ProjectFragmentDoc,
|
||||
"\n query ViewerSavedViewsPanelGroups_SavedViewGroups(\n $projectId: String!\n $savedViewGroupsInput: SavedViewGroupsInput!\n ) {\n project(id: $projectId) {\n id\n ...ViewerSavedViewsPanelGroups_Project\n }\n }\n": types.ViewerSavedViewsPanelGroups_SavedViewGroupsDocument,
|
||||
"\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n }\n": types.ViewerSavedViewsPanelView_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n ...UseDraggableView_SavedView\n }\n": types.ViewerSavedViewsPanelView_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewDeleteDialog_SavedView on SavedView {\n id\n name\n ...UseDeleteSavedView_SavedView\n }\n": types.ViewerSavedViewsPanelViewDeleteDialog_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewEditDialog_SavedView on SavedView {\n id\n name\n description\n visibility\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n }\n": types.ViewerSavedViewsPanelViewEditDialog_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewMoveDialog_SavedView on SavedView {\n id\n group {\n ...FormSelectSavedViewGroup_SavedViewGroup\n }\n ...UseUpdateSavedView_SavedView\n }\n": types.ViewerSavedViewsPanelViewMoveDialog_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_Project on Project {\n id\n permissions {\n canCreateSavedView {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.ViewerSavedViewsPanelViewsGroup_ProjectFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n }\n": types.ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n ...UseDraggableViewTargetGroup_SavedViewGroup\n }\n": types.ViewerSavedViewsPanelViewsGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup_Paginated on SavedViewGroup {\n id\n views(input: $savedViewsInput) {\n cursor\n totalCount\n items {\n id\n ...ViewerSavedViewsPanelView_SavedView\n }\n }\n }\n": types.ViewerSavedViewsPanelViewsGroup_SavedViewGroup_PaginatedFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup on SavedViewGroup {\n id\n title\n ...UseDeleteSavedViewGroup_SavedViewGroup\n }\n": types.ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroupFragmentDoc,
|
||||
"\n fragment ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n": types.ViewerSavedViewsPanelViewsGroupInner_SavedViewGroupFragmentDoc,
|
||||
@@ -951,6 +953,8 @@ const documents: Documents = {
|
||||
"\n fragment UseDeleteSavedViewGroup_SavedViewGroup on SavedViewGroup {\n id\n groupId\n projectId\n isUngroupedViewsGroup\n }\n": types.UseDeleteSavedViewGroup_SavedViewGroupFragmentDoc,
|
||||
"\n mutation UpdateSavedViewGroup($input: UpdateSavedViewGroupInput!) {\n projectMutations {\n savedViewMutations {\n updateGroup(input: $input) {\n id\n ...UseUpdateSavedViewGroup_SavedViewGroup\n }\n }\n }\n }\n": types.UpdateSavedViewGroupDocument,
|
||||
"\n fragment UseUpdateSavedViewGroup_SavedViewGroup on SavedViewGroup {\n id\n projectId\n groupId\n title\n isUngroupedViewsGroup\n }\n": types.UseUpdateSavedViewGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment UseDraggableView_SavedView on SavedView {\n id\n projectId\n name\n group {\n id\n }\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseUpdateSavedView_SavedView\n }\n": types.UseDraggableView_SavedViewFragmentDoc,
|
||||
"\n fragment UseDraggableViewTargetGroup_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n": types.UseDraggableViewTargetGroup_SavedViewGroupFragmentDoc,
|
||||
"\n fragment UseSavedViewValidationHelpers_SavedView on SavedView {\n id\n isHomeView\n visibility\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n }\n": types.UseSavedViewValidationHelpers_SavedViewFragmentDoc,
|
||||
"\n fragment UseViewerSavedViewSetup_SavedView on SavedView {\n id\n viewerState\n }\n": types.UseViewerSavedViewSetup_SavedViewFragmentDoc,
|
||||
"\n fragment ViewerCommentThread on Comment {\n ...ViewerCommentsListItem\n ...ViewerCommentBubblesData\n ...ViewerCommentsReplyItem\n ...ViewerCommentThreadData\n }\n": types.ViewerCommentThreadFragmentDoc,
|
||||
@@ -1711,7 +1715,7 @@ export function graphql(source: "\n query ViewerSavedViewsPanelGroups_SavedView
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n }\n"): (typeof documents)["\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n }\n"];
|
||||
export function graphql(source: "\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n ...UseDraggableView_SavedView\n }\n"): (typeof documents)["\n fragment ViewerSavedViewsPanelView_SavedView on SavedView {\n id\n name\n description\n screenshot\n visibility\n isHomeView\n resourceIds\n author {\n id\n name\n }\n updatedAt\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseDeleteSavedView_SavedView\n ...UseUpdateSavedView_SavedView\n ...ViewerSavedViewsPanelViewEditDialog_SavedView\n ...UseSavedViewValidationHelpers_SavedView\n ...UseDraggableView_SavedView\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -1731,7 +1735,7 @@ export function graphql(source: "\n fragment ViewerSavedViewsPanelViewsGroup_Pr
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n }\n"): (typeof documents)["\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n }\n"];
|
||||
export function graphql(source: "\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n ...UseDraggableViewTargetGroup_SavedViewGroup\n }\n"): (typeof documents)["\n fragment ViewerSavedViewsPanelViewsGroup_SavedViewGroup on SavedViewGroup {\n id\n isUngroupedViewsGroup\n title\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...ViewerSavedViewsPanelViewsGroupInner_SavedViewGroup\n ...ViewerSavedViewsPanelViewsGroupDeleteDialog_SavedViewGroup\n ...UseUpdateSavedViewGroup_SavedViewGroup\n ...UseDraggableViewTargetGroup_SavedViewGroup\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
@@ -2740,6 +2744,14 @@ export function graphql(source: "\n mutation UpdateSavedViewGroup($input: Updat
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment UseUpdateSavedViewGroup_SavedViewGroup on SavedViewGroup {\n id\n projectId\n groupId\n title\n isUngroupedViewsGroup\n }\n"): (typeof documents)["\n fragment UseUpdateSavedViewGroup_SavedViewGroup on SavedViewGroup {\n id\n projectId\n groupId\n title\n isUngroupedViewsGroup\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment UseDraggableView_SavedView on SavedView {\n id\n projectId\n name\n group {\n id\n }\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseUpdateSavedView_SavedView\n }\n"): (typeof documents)["\n fragment UseDraggableView_SavedView on SavedView {\n id\n projectId\n name\n group {\n id\n }\n permissions {\n canUpdate {\n ...FullPermissionCheckResult\n }\n }\n ...UseUpdateSavedView_SavedView\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
export function graphql(source: "\n fragment UseDraggableViewTargetGroup_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n"): (typeof documents)["\n fragment UseDraggableViewTargetGroup_SavedViewGroup on SavedViewGroup {\n id\n title\n }\n"];
|
||||
/**
|
||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -236,10 +236,18 @@ export const useUpdateSavedView = () => {
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
const { isLoggedIn } = useActiveUser()
|
||||
|
||||
return async (params: {
|
||||
view: UseUpdateSavedView_SavedViewFragment
|
||||
input: UpdateSavedViewInput
|
||||
}) => {
|
||||
return async (
|
||||
params: {
|
||||
view: UseUpdateSavedView_SavedViewFragment
|
||||
input: UpdateSavedViewInput
|
||||
},
|
||||
options?: Partial<{
|
||||
/**
|
||||
* Whether to skip toast notifications
|
||||
*/
|
||||
skipToast: boolean
|
||||
}>
|
||||
) => {
|
||||
if (!isLoggedIn.value) return
|
||||
const { input } = params
|
||||
|
||||
@@ -313,18 +321,20 @@ export const useUpdateSavedView = () => {
|
||||
).catch(convertThrowIntoFetchResult)
|
||||
|
||||
const res = result?.data?.projectMutations.savedViewMutations.updateView
|
||||
if (res?.id) {
|
||||
triggerNotification({
|
||||
title: 'View updated',
|
||||
type: ToastNotificationType.Success
|
||||
})
|
||||
} else {
|
||||
const err = getFirstGqlErrorMessage(result?.errors)
|
||||
triggerNotification({
|
||||
title: "Couldn't update view",
|
||||
description: err,
|
||||
type: ToastNotificationType.Danger
|
||||
})
|
||||
if (!options?.skipToast) {
|
||||
if (res?.id) {
|
||||
triggerNotification({
|
||||
title: 'View updated',
|
||||
type: ToastNotificationType.Success
|
||||
})
|
||||
} else {
|
||||
const err = getFirstGqlErrorMessage(result?.errors)
|
||||
triggerNotification({
|
||||
title: "Couldn't update view",
|
||||
description: err,
|
||||
type: ToastNotificationType.Danger
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
import { useMutationLoading } from '@vue/apollo-composable'
|
||||
import { graphql } from '~/lib/common/generated/gql'
|
||||
import type {
|
||||
UseDraggableView_SavedViewFragment,
|
||||
UseDraggableViewTargetGroup_SavedViewGroupFragment
|
||||
} from '~/lib/common/generated/gql/graphql'
|
||||
import { ensureError, safeParse } from '@speckle/shared'
|
||||
import { has, isObjectLike } from 'lodash-es'
|
||||
import { useUpdateSavedView } from '~/lib/viewer/composables/savedViews/management'
|
||||
|
||||
const isDraggableView = (view: unknown): view is UseDraggableView_SavedViewFragment =>
|
||||
isObjectLike(view) && has(view, 'id') && has(view, 'permissions.canUpdate')
|
||||
|
||||
graphql(`
|
||||
fragment UseDraggableView_SavedView on SavedView {
|
||||
id
|
||||
projectId
|
||||
name
|
||||
group {
|
||||
id
|
||||
}
|
||||
permissions {
|
||||
canUpdate {
|
||||
...FullPermissionCheckResult
|
||||
}
|
||||
}
|
||||
...UseUpdateSavedView_SavedView
|
||||
}
|
||||
`)
|
||||
|
||||
export const useDraggableView = (params: {
|
||||
view: Ref<UseDraggableView_SavedViewFragment>
|
||||
}) => {
|
||||
const isDragging = ref(false)
|
||||
const isLoading = useMutationLoading()
|
||||
|
||||
const classes = computed(() => {
|
||||
const classParts: string[] = ['draggable-view']
|
||||
|
||||
if (isDragging.value) {
|
||||
classParts.push('opacity-50 scale-95')
|
||||
}
|
||||
|
||||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
const vOn = {
|
||||
dragstart: (event: DragEvent) => {
|
||||
if (!event.dataTransfer) return
|
||||
if (!params.view.value.permissions.canUpdate.authorized || isLoading.value) {
|
||||
event.preventDefault()
|
||||
return
|
||||
}
|
||||
|
||||
isDragging.value = true
|
||||
event.dataTransfer.setData('application/json', JSON.stringify(params.view.value))
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
|
||||
const imageTarget =
|
||||
(event.target as HTMLElement).closest('.draggable-view') ||
|
||||
(event.target as HTMLElement)
|
||||
event.dataTransfer.setDragImage(imageTarget, 0, 0)
|
||||
},
|
||||
dragend: () => {
|
||||
isDragging.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
classes,
|
||||
on: vOn
|
||||
}
|
||||
}
|
||||
|
||||
graphql(`
|
||||
fragment UseDraggableViewTargetGroup_SavedViewGroup on SavedViewGroup {
|
||||
id
|
||||
title
|
||||
}
|
||||
`)
|
||||
|
||||
export const useDraggableViewTargetGroup = (params: {
|
||||
group: Ref<UseDraggableViewTargetGroup_SavedViewGroupFragment>
|
||||
onMoved?: () => void
|
||||
}) => {
|
||||
const isDragOver = ref(false)
|
||||
const dragCounter = ref(0)
|
||||
const { triggerNotification } = useGlobalToast()
|
||||
const updateView = useUpdateSavedView()
|
||||
|
||||
const vOn = {
|
||||
dragover: (event: DragEvent) => {
|
||||
if (!event.dataTransfer) return
|
||||
|
||||
event.preventDefault()
|
||||
event.dataTransfer.dropEffect = 'move'
|
||||
},
|
||||
drop: async (event: DragEvent) => {
|
||||
if (!event.dataTransfer) return
|
||||
|
||||
event.preventDefault()
|
||||
isDragOver.value = false
|
||||
dragCounter.value = 0
|
||||
|
||||
try {
|
||||
const data = event.dataTransfer.getData('application/json')
|
||||
const view = safeParse(data, isDraggableView)
|
||||
if (!view || view.group.id === params.group.value.id) {
|
||||
return
|
||||
}
|
||||
|
||||
const success = await updateView({
|
||||
view,
|
||||
input: {
|
||||
id: view.id,
|
||||
projectId: view.projectId,
|
||||
groupId: params.group.value.id
|
||||
}
|
||||
})
|
||||
|
||||
if (success) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Success,
|
||||
title: `Moved "${view.name}" to "${params.group.value.title}"`
|
||||
})
|
||||
params.onMoved?.()
|
||||
} else {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Failed to move view'
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
triggerNotification({
|
||||
type: ToastNotificationType.Danger,
|
||||
title: 'Failed to move view',
|
||||
description: ensureError(e).message
|
||||
})
|
||||
}
|
||||
},
|
||||
dragenter: (event: DragEvent) => {
|
||||
event.preventDefault()
|
||||
dragCounter.value++
|
||||
isDragOver.value = true
|
||||
},
|
||||
dragleave: () => {
|
||||
dragCounter.value--
|
||||
if (dragCounter.value === 0) {
|
||||
isDragOver.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const classes = computed(() => {
|
||||
const classParts: string[] = ['draggable-view-target']
|
||||
|
||||
if (isDragOver.value) {
|
||||
classParts.push('rounded-md ring-2 ring-primary ring-opacity-50 bg-primary/5')
|
||||
}
|
||||
|
||||
return classParts.join(' ')
|
||||
})
|
||||
|
||||
return {
|
||||
on: vOn,
|
||||
classes
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ const config = {
|
||||
`./plugins/**/*.{js,ts}`,
|
||||
'./stories/**/*.{js,ts,vue,mdx}',
|
||||
'./app.vue',
|
||||
'./lib/**/composables/*.{js,ts}',
|
||||
'./lib/**/composables/**/*.{js,ts}',
|
||||
...themeEntries(),
|
||||
...uiLibEntries()
|
||||
],
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const safeParse = <Result = any>(
|
||||
json: string,
|
||||
guard?: (data: unknown) => data is Result
|
||||
): Result | null => {
|
||||
try {
|
||||
const parsed = JSON.parse(json) as Result
|
||||
return guard ? (guard(parsed) ? parsed : null) : parsed
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -11,3 +11,4 @@ export * from './helpers/os.js'
|
||||
export * from './helpers/optimization.js'
|
||||
export * from './helpers/debugging.js'
|
||||
export * from './helpers/url.js'
|
||||
export * from './helpers/encoding.js'
|
||||
|
||||
Reference in New Issue
Block a user