fcb924d3a5
* 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>
241 lines
6.1 KiB
Vue
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>
|