Files
speckle-server/packages/frontend-2/components/preview/Image.vue
T
andrewwallacespeckle fcb924d3a5 DO NOT MERGE - refactor: new design system implementation (#2537)
* refactor WIP

* Button design changes

* FE2 FormButton Updates

* ts composition api

* CommonTextLink Changes

* CommonTextLink prop updates

* Add disabled styles

* WIP

* Design system updates

* Colour Updates

* New Text Styles. Initial FE2 changes

* More fe2 styling classes

* Minor update

* Minor update

* Fix build

* More updates for discussion

* More styling updates

* Minor updates to inputs

* Revert change to size options

* More text updates

* More font class swapping

* Revert dui3 changes

* Confirmed Lineheights

* Add story files for new text styles

* Minor copy changes

* Minor typo

* Revert variant>color

* New Colours WIP

* andrew/web-1371-misalignment-in-account-dropdown

* andrew/web-1374-settings-text-styles-are-not-right

* andrew/web-1375-nav-texts-should-be-14px

* andrew/web-1376-decrease-size-of-versions-header

* andrew/web-1377-version-card-title

* Updates

* semibold>medium

* Colour updates

* Sizing updates

* Colour updates

* Colour updates

* Measure mode

* Updates

* Fix build

* Fix build

* WIP Updates

* Changes from PR

* Updated login, registration and reset password styling

* Make share dropdown bg white

* Updated viewer titles

* Fix: Resize panel highlight color in the viewer should be blue

* Fix: Blue + Add link in Models. And other blue links in Viewer

* Add labelPosition Prop. Fix Button stories

* Updated CommonLink to remove default underline

* Add Highlight Color

* Card updates from Michal

* Updated discussion icon on version card

* Small tweaks to version card

* Small tweaks to version card

* Fix: Ghost button doesn't have padding

* Fix: Write Delete...

* Fix: Version hover border color

* Updates to Project Card. Updates to PageTabs

* Fix: Adjust title in announcement modal

* Updates from Comments

* Select Background Colour

* Fix: Select dropdown color

* Improve list view. Improve discussions

* Fix: Minor tweaks to onboarding checklist

* Fix: Clean up nav

* Hide third item when not >md

* Change project heading size

* Add border to version card

* Adjust spacing in dropdowns

* Slight change

* Update button style in Version card

* Tweaked nav menu

* Tweaked nav menu

* Various styling tweaks

* Fix settings modal subheader

* Various styling tweaks and fixes

* Tweak settings dialog styling

* Tweak simple scrollbar

* Minor tweaks to model page

* Minor tweaks to model page

* Minor tweak to login

* Tweak discussion card

* Tweak settings page

* Tweak vertical tabs

* Tweak Dialog alignment

* Fix some paddings

* Change IconVersions to ClockIcon

* Tweak spacing between icons

* Updates to Card Icons

* Bold "connectors" in empty project message

* Remove padding in Profile field

* Update inline model create

* Remove icons from share menu

* Updated Delete dialog

* Wrong text positioning in alert

* Updated copy in dropdown

* Change bg to bg-foundation in select dropdown component

* Fix merge conflicy

* Selection Info title colour

* Wrong text class

* Update card colours based on call

* Update card colours

* Update empty state

* Input label font weight

* Updates to Embed

* Various styling fixes

* Fix; Viewer panel header styling

* Fix; Adjust BG in dev mode list items

* Fix; Fix button placement in video modal

* Fix: Share menu is not using LayoutMenu

* Fix: Buttons clash under filters

* Fix: Adjust spacing in selection info

* Fix: Adjust gray BG behind model preview images

* Fix: No hover cursor on model card

* Fix: Align text styling in dev mode and selection info panel

* Fix for menu width

* Fix mobile problems

* Fix Add spacing on new login screens

* Revert prose change. Add prose-sm

* Text - Use contain for bg image

* Fix onboarding screens

* Responsive fixes

* Fix hydration errors

* Added padding to Add Model Dialog

* Fix versions buttons

* Fix build problem

* Changes PRE PR

* Final Pre PR Changes

* Remove DUI3 change

* Fix small issue with dialog after merge conflict

* Remove label classes from Visibility Select

* Revert changes made in Controls.vue

* Remove old-webhooks

* Add highlight colours to Storybook

* Add v-keyboard-clickable

---------

Co-authored-by: Mike Tasset <mike.tasset@gmail.com>
2024-07-30 15:34:41 +01:00

241 lines
6.1 KiB
Vue

<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
<template>
<!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->
<div
ref="parent"
class="relative w-full h-full"
@mouseenter="hovered = true"
@mouseleave="hovered = false"
@mousemove="(e: MouseEvent) => calculatePanoramaStyle(e)"
@touchmove="(e: TouchEvent) =>
calculatePanoramaStyle({
target: e.target,
clientX: e.touches[0].clientX,
clientY: e.touches[0].clientY
})"
>
<Transition
enter-from-class="opacity-0"
enter-active-class="transition duration-300"
leave-to-class="opacity-0"
leave-active-class="transition duration-300"
>
<div v-if="shouldShowMainPreview" class="relative w-full h-full">
<CommonTransitioningContents
ref="finalPreviewTransitioner"
class="w-full h-full"
>
<div
v-if="!hasDoneFirstLoad || !finalPreviewUrl?.length"
:class="[mainPreviewClasses, 'flex items-center justify-center']"
>
<div class="lds-ripple">
<div></div>
<div></div>
</div>
</div>
<div
v-else
:class="mainPreviewClasses"
:style="{
backgroundImage: `url('${finalPreviewUrl}')`
}"
/>
</CommonTransitioningContents>
</div>
</Transition>
<Transition
enter-from-class="opacity-0"
enter-active-class="transition duration-300"
leave-to-class="opacity-0"
leave-active-class="transition duration-300"
>
<!-- eslint-disable-next-line vuejs-accessibility/mouse-events-have-key-events -->
<div
v-show="shouldShowPanoramicPreview"
ref="panorama"
:style="{
backgroundImage: panoramaPreviewUrl
? `url('${panoramaPreviewUrl}')`
: undefined,
backgroundSize: 'cover',
backgroundPosition: `${positionMagic}px 0`,
position: 'absolute',
top: '0',
width: '100%',
height: '100%'
}"
/>
</Transition>
<Transition
enter-from-class="opacity-0"
leave-to-class="opacity-0"
enter-active-class="transition duration-300"
leave-active-class="transition duration-300"
>
<CommonLoadingBar
:loading="isLoadingPanorama && hovered"
class="absolute bottom-0 w-full"
/>
</Transition>
</div>
</template>
<script setup lang="ts">
import { type Nullable } from '@speckle/shared'
import { useElementVisibility, useResizeObserver } from '@vueuse/core'
import { usePreviewImageBlob } from '~~/lib/projects/composables/previewImage'
type PanoramaStyleMouseOrTouchEvent = Pick<MouseEvent, 'target' | 'clientX' | 'clientY'>
const props = withDefaults(
defineProps<{
previewUrl: string
panoramaOnHover?: boolean
}>(),
{
panoramaOnHover: true
}
)
const parent = ref(null as Nullable<HTMLDivElement>)
const finalPreviewTransitioner = ref(
null as Nullable<{ triggerTransition: () => Promise<void> }>
)
const isInViewport = useElementVisibility(parent)
const basePreviewUrl = computed(() => props.previewUrl)
const {
previewUrl: finalPreviewUrl,
panoramaPreviewUrl,
shouldLoadPanorama,
isLoadingPanorama,
hasDoneFirstLoad
} = usePreviewImageBlob(basePreviewUrl, { enabled: isInViewport })
const hovered = ref(false)
const panorama = ref(null as Nullable<HTMLDivElement>)
const mainPreviewClasses = computed(
() => 'w-full h-full bg-cover bg-no-repeat bg-center'
)
const parentWidth = ref(0)
const parentHeight = ref(0)
const setParentDimensions = () => {
const { width = 0, height = 0 } = parent.value?.getBoundingClientRect() || {}
parentWidth.value = width
parentHeight.value = height
}
if (import.meta.client) useResizeObserver(document.body, () => setParentDimensions())
const positionMagic = ref(0)
const latestMouseEvent = ref<PanoramaStyleMouseOrTouchEvent>()
const calculatePanoramaStyle = (e: PanoramaStyleMouseOrTouchEvent) => {
latestMouseEvent.value = e
const rect = panorama.value?.getBoundingClientRect()
if (parentHeight.value === 0) setParentDimensions()
if (!rect) return
const x = e.clientX - rect.left
const step = rect.width / 24
let index = Math.abs(24 - Math.round(x / step))
if (index >= 24) index = 24 - 1
const scaleFactor = parentHeight.value / 400
const actualWidth = scaleFactor * 700
const widthDiff = (parentWidth.value - actualWidth) * 0.5
positionMagic.value = -(actualWidth * (2 * index + 1) - widthDiff)
}
const shouldShowMainPreview = computed(
() =>
(!hovered.value && finalPreviewUrl.value) ||
isLoadingPanorama.value ||
!props.panoramaOnHover
)
const shouldShowPanoramicPreview = computed(
() => hovered.value && panoramaPreviewUrl.value && props.panoramaOnHover
)
onMounted(() => setParentDimensions())
watch(hovered, (newVal) => {
if (newVal && !panoramaPreviewUrl.value && props.panoramaOnHover)
shouldLoadPanorama.value = true
})
watch(
() => unref(panoramaPreviewUrl),
() => {
if (latestMouseEvent.value) {
calculatePanoramaStyle(latestMouseEvent.value)
}
}
)
if (import.meta.client) {
// Trigger transitions when preview image changes
watch(finalPreviewUrl, (newVal, oldVal) => {
if (newVal === oldVal) return
finalPreviewTransitioner.value?.triggerTransition()
})
}
</script>
<style>
/** https://loading.io/css/ */
.lds-ripple {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ripple div {
position: absolute;
border: 4px solid #cef;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
.lds-ripple div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes lds-ripple {
0% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 0;
}
4.9% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 0;
}
5% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0;
left: 0;
width: 72px;
height: 72px;
opacity: 0;
}
}
</style>