fix(fe2): Use NuxtLink. Move nested button to relatively positioned div (#2702)

* Use NuxtLink. Move nested button to relatively positioned div

* No nuxtlink on empty model

* Move other button

* Changes to viewer boundaries

* Fixed resizer position

* Responsive updates

* Update Card.vue

---------

Co-authored-by: Mike Tasset <mike.tasset@gmail.com>
This commit is contained in:
andrewwallacespeckle
2024-08-20 13:46:36 +01:00
committed by GitHub
parent e4cc0cbc83
commit 8a9b179fe7
8 changed files with 226 additions and 203 deletions
@@ -3,7 +3,7 @@
<template>
<div>
<Portal to="mobile-navigation">
<div class="md:hidden">
<div class="lg:hidden">
<FormButton
:color="isOpenMobile ? 'outline' : 'subtle'"
size="sm"
@@ -17,13 +17,13 @@
</Portal>
<div
v-keyboard-clickable
class="md:hidden absolute inset-0 backdrop-blur-sm z-40 transition-all"
class="lg:hidden absolute inset-0 backdrop-blur-sm z-40 transition-all"
:class="isOpenMobile ? 'opacity-100' : 'opacity-0 pointer-events-none'"
@click="isOpenMobile = false"
/>
<div
class="absolute z-40 md:static h-full flex w-60 md:w-64 shrink-0 transition-all"
:class="isOpenMobile ? '' : '-translate-x-60 md:translate-x-0'"
class="absolute z-40 lg:static h-full flex w-60 lg:w-64 shrink-0 transition-all"
:class="isOpenMobile ? '' : '-translate-x-60 lg:translate-x-0'"
>
<LayoutSidebar class="border-r border-outline-3 px-2 py-3 bg-foundation-page">
<LayoutSidebarMenu>
@@ -4,7 +4,7 @@
<div
class="flex gap-4 items-center justify-between h-full w-screen py-4 px-3 sm:px-4"
>
<HeaderLogoBlock :active="false" to="/" class="hidden md:flex lg:min-w-40" />
<HeaderLogoBlock :active="false" to="/" class="hidden lg:flex lg:min-w-40" />
<div class="flex items-center truncate">
<ClientOnly>
<PortalTarget name="mobile-navigation"></PortalTarget>
@@ -8,7 +8,7 @@
:to="modelRoute(projectId, model.id)"
class="absolute z-10 inset-0"
/>
<div class="relative z-30 flex justify-between items-center h-10">
<div class="relative z-40 flex justify-between items-center h-10">
<NuxtLink
:to="!defaultLinkDisabled ? modelRoute(projectId, model.id) : undefined"
class="w-full"
@@ -1,9 +1,9 @@
<template>
<div>
<div
class="flex flex-col space-y-2 lg:space-y-0 lg:flex-row lg:justify-between lg:items-center mb-4"
class="flex flex-col space-y-2 xl:space-y-0 xl:flex-row xl:justify-between xl:items-center mb-4"
>
<div class="flex justify-between items-center flex-wrap sm:flex-nowrap">
<div class="flex justify-between items-center flex-wrap xl:flex-nowrap">
<h1 class="block text-heading-xl">Models</h1>
<div class="flex items-center space-x-2 w-full mt-2 sm:w-auto sm:mt-0">
<FormButton
@@ -25,7 +25,7 @@
</div>
</div>
<div
class="flex flex-col space-y-2 md:space-y-0 md:flex-row md:items-center md:space-x-2"
class="flex flex-col space-y-2 xl:space-y-0 xl:flex-row xl:items-center xl:space-x-2"
>
<FormTextInput
v-model="localSearch"
@@ -33,7 +33,7 @@
:show-label="false"
placeholder="Search models..."
color="foundation"
wrapper-classes="grow lg:grow-0 lg:ml-2 lg:w-40 xl:w-60"
wrapper-classes="grow lg:grow-0 xl:ml-2 xl:w-40 min-w-40 shrink-0"
:show-clear="localSearch !== ''"
@change="($event) => updateSearchImmediately($event.value)"
@update:model-value="updateDebouncedSearch"
@@ -2,164 +2,189 @@
<!-- eslint-disable vuejs-accessibility/mouse-events-have-key-events -->
<!-- eslint-disable vuejs-accessibility/click-events-have-key-events -->
<template>
<NuxtLink
<div
v-keyboard-clickable
class="space-y-4 relative"
:to="model && !isEmptyModel ? modelRoute(props.project.id, model.id) : undefined"
:class="model && !isEmptyModel ? 'cursor-pointer' : undefined"
@click="onCardClick"
@mouseleave="showActionsMenu = false"
>
<div
v-if="itemType !== StructureItemType.ModelWithOnlySubmodels"
class="group relative bg-foundation w-full p-2 flex flex-row rounded-md transition-all border border-outline-3 hover:border-outline-5 items-stretch"
<div class="absolute z-10 right-2 top-5">
<FormButton
v-if="hasSubmodels"
size="sm"
color="outline"
:to="viewAllUrl"
:disabled="!viewAllUrl"
class="mt-px"
@click.stop="trackFederateModels"
>
View all
</FormButton>
<FormButton
v-if="hasVersions && !isPendingFileUpload(item) && model?.id"
rounded
size="sm"
class="hidden sm:flex z-20 gap-0.5 mt-[9px]"
color="subtle"
@click.stop="onVersionsClick"
>
<IconVersions class="h-4 w-4" />
{{ model?.versionCount.totalCount }}
</FormButton>
</div>
<NuxtLink
:to="model && !isEmptyModel ? modelRoute(props.project.id, model.id) : undefined"
>
<div class="flex items-center flex-grow order-2 sm:order-1 pl-2 sm:pl-4">
<!-- Name -->
<div
class="flex justify-between sm:justify-start gap-2 items-center w-full sm:w-auto"
>
<span class="text-heading text-foreground">
{{ name }}
</span>
<span
v-if="model"
class="opacity-100 sm:opacity-0 group-hover:opacity-100 transition"
<div
v-if="itemType !== StructureItemType.ModelWithOnlySubmodels"
class="group relative bg-foundation w-full p-2 flex flex-row rounded-md transition-all border border-outline-3 hover:border-outline-5 items-stretch"
>
<div class="flex items-center flex-grow order-2 sm:order-1 pl-2 sm:pl-4">
<!-- Name -->
<div
class="flex justify-between sm:justify-start gap-2 items-center w-full sm:w-auto"
>
<ProjectPageModelsActions
v-model:open="showActionsMenu"
:model="model"
:project="project"
:can-edit="canContribute"
@click.stop.prevent
@model-updated="$emit('model-updated')"
@upload-version="triggerVersionUpload"
/>
</span>
</div>
<!-- Empty model action -->
<NuxtLink
v-if="itemType === StructureItemType.EmptyModel"
:class="[
'cursor-pointer ml-2 text-xs text-foreground-2 flex items-center space-x-1',
'opacity-0 group-hover:opacity-100 transition duration-200',
'hover:text-primary p-1'
]"
@click.stop="$emit('create-submodel', model?.name || '')"
>
<PlusIcon class="w-3 h-3" />
submodel
</NuxtLink>
<!-- Spacer -->
<div class="flex-grow"></div>
<ProjectCardImportFileArea
v-if="!isPendingFileUpload(item)"
ref="importArea"
:project-id="project.id"
:model-name="item.fullName"
class="hidden"
/>
<div
v-if="
!isPendingFileUpload(item) &&
(pendingVersion || itemType === StructureItemType.EmptyModel)
"
class="flex items-center h-full"
>
<ProjectPendingFileImportStatus
v-if="pendingVersion"
:upload="pendingVersion"
type="subversion"
class="px-4 w-full"
/>
<ProjectCardImportFileArea
v-else
:project-id="project.id"
:model-name="item.fullName"
class="h-full w-full"
/>
</div>
<div v-else-if="hasVersions" class="hidden sm:flex items-center gap-x-2">
<div class="text-body-3xs text-foreground-2 text-right">
Updated
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
<span class="text-heading text-foreground">
{{ name }}
</span>
<span
v-if="model"
class="opacity-100 sm:opacity-0 group-hover:opacity-100 transition"
>
<ProjectPageModelsActions
v-model:open="showActionsMenu"
:model="model"
:project="project"
:can-edit="canContribute"
@click.stop.prevent
@model-updated="$emit('model-updated')"
@upload-version="triggerVersionUpload"
/>
</span>
</div>
<div class="space-x-2 flex flex-row pils">
<div class="text-body-xs text-foreground flex items-center space-x-1 pl-2">
<IconDiscussions class="w-4 h-4" />
<span>{{ model?.commentThreadCount.totalCount }}</span>
</div>
<div v-if="model?.automationsStatus">
<AutomateRunsTriggerStatus
:project-id="project.id"
:status="model.automationsStatus"
:model-id="model.id"
/>
</div>
<div class="flex gap-2 items-center">
<FormButton
v-if="!isPendingFileUpload(item) && model?.id"
rounded
size="sm"
class="gap-0.5"
color="subtle"
@click.stop="onVersionsClick"
>
<IconVersions class="h-4 w-4" />
{{ model?.versionCount.totalCount }}
</FormButton>
</div>
<!-- Empty model action -->
<div
v-if="itemType === StructureItemType.EmptyModel"
:class="[
'cursor-pointer ml-2 text-xs text-foreground-2 flex items-center space-x-1',
'opacity-0 group-hover:opacity-100 transition duration-200',
'hover:text-primary p-1'
]"
@click.stop="$emit('create-submodel', model?.name || '')"
>
<PlusIcon class="w-3 h-3" />
submodel
</div>
</div>
<ProjectPendingFileImportStatus
v-else-if="pendingModel && itemType === StructureItemType.PendingModel"
:upload="pendingModel"
class="text-foreground-2 text-sm flex flex-col items-center space-y-1 mr-4"
/>
</div>
<!-- Preview or icon section -->
<div
v-if="!isPendingFileUpload(item) && item.model?.previewUrl && !pendingVersion"
class="w-20 h-16"
>
<NuxtLink
:to="modelLink || ''"
class="h-full w-full block bg-foundation-page rounded-lg border border-outline-3"
>
<PreviewImage
v-if="item.model?.previewUrl"
:preview-url="item.model.previewUrl"
<!-- Spacer -->
<div class="flex-grow"></div>
<ProjectCardImportFileArea
v-if="!isPendingFileUpload(item)"
ref="importArea"
:project-id="project.id"
:model-name="item.fullName"
class="hidden"
/>
</NuxtLink>
</div>
</div>
<!-- Doubling up for mixed items -->
<div
v-if="hasSubmodels"
class="border-l-2 border-primary-muted hover:border-primary transition rounded-md"
>
<button
class="group bg-foundation w-full py-1 pr-2 sm:pr-4 flex items-center rounded-md cursor-pointer hover:border-outline-5 transition-all border border-outline-3 border-l-0"
href="/test"
@click.stop="expanded = !expanded"
>
<!-- Icon -->
<div>
<div class="mx-2 flex items-center hover:text-primary text-foreground-2 h-14">
<ChevronDownIcon
:class="`w-4 h-4 transition ${expanded ? 'rotate-180' : ''}`"
<div
v-if="
!isPendingFileUpload(item) &&
(pendingVersion || itemType === StructureItemType.EmptyModel)
"
class="flex items-center h-full"
>
<ProjectPendingFileImportStatus
v-if="pendingVersion"
:upload="pendingVersion"
type="subversion"
class="px-4 w-full"
/>
<ProjectCardImportFileArea
v-else
:project-id="project.id"
:model-name="item.fullName"
class="h-full w-full"
/>
</div>
<div
v-else-if="hasVersions"
class="hidden sm:flex items-center gap-x-2 pr-12"
>
<div class="text-body-3xs text-foreground-2 text-right">
Updated
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
</span>
</div>
<div class="space-x-2 flex flex-row pils">
<div
class="text-body-xs text-foreground flex items-center space-x-1 pl-2"
>
<IconDiscussions class="w-4 h-4" />
<span>{{ model?.commentThreadCount.totalCount }}</span>
</div>
<div v-if="model?.automationsStatus">
<AutomateRunsTriggerStatus
:project-id="project.id"
:status="model.automationsStatus"
:model-id="model.id"
/>
</div>
</div>
</div>
<ProjectPendingFileImportStatus
v-else-if="pendingModel && itemType === StructureItemType.PendingModel"
:upload="pendingModel"
class="text-foreground-2 text-sm flex flex-col items-center space-y-1 mr-4"
/>
</div>
<!-- Name -->
<FolderIcon class="w-4 h-4 text-foreground" />
<div class="ml-2 text-heading text-foreground flex-grow text-left">
{{ name }}
<!-- Preview or icon section -->
<div
v-if="!isPendingFileUpload(item) && item.model?.previewUrl && !pendingVersion"
class="w-20 h-16"
>
<NuxtLink
:to="modelLink || ''"
class="h-full w-full block bg-foundation-page rounded-lg border border-outline-3"
>
<PreviewImage
v-if="item.model?.previewUrl"
:preview-url="item.model.previewUrl"
/>
</NuxtLink>
</div>
<!-- Preview -->
<div class="flex flex-col items-end sm:flex-row sm:items-center gap-1 sm:gap-4">
<!-- Commented out so that we need to load less data, can be added back -->
<!-- <div
</div>
<!-- Doubling up for mixed items -->
<div
v-if="hasSubmodels"
class="border-l-2 border-primary-muted hover:border-primary transition rounded-md"
>
<button
class="group bg-foundation w-full py-1 pr-2 sm:pr-4 flex items-center rounded-md cursor-pointer hover:border-outline-5 transition-all border border-outline-3 border-l-0"
href="/test"
@click.stop="expanded = !expanded"
>
<!-- Icon -->
<div>
<div
class="mx-2 flex items-center hover:text-primary text-foreground-2 h-14"
>
<ChevronDownIcon
:class="`w-4 h-4 transition ${expanded ? 'rotate-180' : ''}`"
/>
</div>
</div>
<!-- Name -->
<FolderIcon class="w-4 h-4 text-foreground" />
<div class="ml-2 text-heading text-foreground flex-grow text-left">
{{ name }}
</div>
<!-- Preview -->
<div
class="flex flex-col items-end sm:flex-row sm:items-center gap-1 sm:gap-4"
>
<!-- Commented out so that we need to load less data, can be added back -->
<!-- <div
v-for="(child, index) in item.children"
:key="index"
:class="`w-16 h-16 ml-2`"
@@ -170,52 +195,44 @@
{{ child?.name }}
</div>
</div> -->
<div class="text-body-3xs text-foreground-2">
Updated
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
</span>
</div>
<FormButton
size="sm"
color="outline"
:to="viewAllUrl"
:disabled="!viewAllUrl"
@click.stop="trackFederateModels"
>
View all
</FormButton>
</div>
</button>
<!-- Children list -->
<div
v-if="hasChildren && expanded && !isPendingFileUpload(item)"
class="pl-8 mt-2 space-y-2"
>
<div v-if="childrenLoading" class="mr-8">
<CommonLoadingBar loading />
</div>
<template v-else>
<div v-for="child in children" :key="child.fullName" class="flex">
<div class="h-20 absolute -ml-8 flex items-center mt-0 mr-1 pl-1">
<ChevronDownIcon class="w-4 h-4 rotate-45 text-foreground-2" />
<div class="text-body-3xs text-foreground-2 pr-20 sm:pr-16">
Updated
<span v-tippy="updatedAt.full">
{{ updatedAt.relative }}
</span>
</div>
<ProjectPageModelsStructureItem
:item="child"
:project="project"
:can-contribute="canContribute"
class="flex-grow"
@model-updated="onModelUpdated"
@create-submodel="emit('create-submodel', $event)"
/>
</div>
</template>
<div v-if="canContribute" class="mr-8"></div>
</button>
<!-- Children list -->
<div
v-if="hasChildren && expanded && !isPendingFileUpload(item)"
class="pl-8 mt-2 space-y-2"
>
<div v-if="childrenLoading" class="mr-8">
<CommonLoadingBar loading />
</div>
<template v-else>
<div v-for="child in children" :key="child.fullName" class="flex">
<div class="h-20 absolute -ml-8 flex items-center mt-0 mr-1 pl-1">
<ChevronDownIcon class="w-4 h-4 rotate-45 text-foreground-2" />
</div>
<ProjectPageModelsStructureItem
:item="child"
:project="project"
:can-contribute="canContribute"
class="flex-grow"
@model-updated="onModelUpdated"
@create-submodel="emit('create-submodel', $event)"
/>
</div>
</template>
<div v-if="canContribute" class="mr-8"></div>
</div>
</div>
</div>
</NuxtLink>
</NuxtLink>
</div>
</template>
<script lang="ts" setup>
import { modelVersionsRoute, modelRoute } from '~~/lib/common/helpers/route'
@@ -416,6 +433,12 @@ const triggerVersionUpload = () => {
importArea.value?.triggerPicker()
}
const onCardClick = () => {
if (model.value && !isEmptyModel.value) {
router.push(modelRoute(props.project.id, model.value.id))
}
}
const onVersionsClick = () => {
if (model.value) {
router.push(modelVersionsRoute(props.project.id, model.value.id))
@@ -5,7 +5,7 @@
class="absolute z-20 flex max-h-screen simple-scrollbar flex-col space-y-1 md:space-y-2 bg-green-300/0 px-2"
:class="
showNavbar && !isEmbedEnabled
? 'pt-[2.1rem]'
? 'pt-[3.8rem]'
: isTransparent
? 'pt-2'
: 'pt-2 pb-16'
@@ -152,7 +152,7 @@
<div
v-if="activeControl !== 'none'"
ref="resizeHandle"
class="absolute z-10 max-h-[calc(100dvh-4.5rem)] w-7 mt-[4.3rem] hidden sm:flex group overflow-hidden items-center rounded-r cursor-ew-resize z-30"
class="absolute z-10 max-h-[calc(100dvh-4rem)] w-7 mt-[3.9rem] hidden sm:flex group overflow-hidden items-center rounded-r cursor-ew-resize z-30"
:style="`left:${width - 2}px; height:${height ? height - 10 : 0}px`"
@mousedown="startResizing"
>
@@ -173,7 +173,7 @@
activeControl !== 'none'
? 'translate-x-0 opacity-100'
: '-translate-x-[100%] opacity-0'
} ${isEmbedEnabled ? 'mt-1.5' : 'mt-[2rem]'}`"
} ${isEmbedEnabled ? 'mt-1.5' : 'mt-[3.7rem]'}`"
:style="`width: ${isMobile ? '100%' : `${width + 4}px`};`"
>
<div v-if="activeControl.length !== 0 && activeControl === 'measurements'">
@@ -47,7 +47,7 @@
</div>
<!-- Global loading bar -->
<ViewerLoadingBar class="relative z-20" />
<ViewerLoadingBar class="absolute -top-2 left-0 w-full z-40" />
<!-- Sidebar controls -->
<Transition
+1 -1
View File
@@ -21,7 +21,7 @@
enter-from-class="opacity-0"
enter-active-class="transition duration-1000"
>
<HeaderNavBar v-show="showNavbar" class="relative z-20 mb-6" />
<HeaderNavBar v-show="showNavbar" class="relative z-20" />
</Transition>
</ClientOnly>
<main class="absolute top-0 left-0 z-10 h-[100dvh] w-screen">