Files
speckle-server/packages/frontend-2/components/viewer/Sidebar.vue
T

131 lines
3.9 KiB
Vue

<!-- eslint-disable vuejs-accessibility/no-static-element-interactions -->
<template>
<div
ref="resizableElement"
class="relative sm:absolute z-10 right-0 overflow-hidden w-screen sm:pr-3 sm:pb-3 sm:pt-0"
:style="!isSmallerOrEqualSm ? { maxWidth: width + 'px' } : {}"
:class="[
open ? '' : 'pointer-events-none',
isEmbedEnabled === true
? 'sm:top-2 sm:h-[calc(100dvh-3.8rem)]'
: 'sm:top-[3.7rem] sm:h-[calc(100dvh-3.8rem)]'
]"
>
<div
class="flex transition-all h-full"
:class="open ? '' : 'sm:translate-x-[100%]'"
>
<!-- Resize Handle -->
<div
ref="resizeHandle"
class="hidden sm:flex group relative z-30 hover:z-50 w-6 h-full items-center overflow-hidden -mr-1"
>
<div
class="w-7 h-8 mr-1 bg-primary group-hover:primary-focus rounded-l translate-x-1 group-hover:translate-x-0 opacity-0 group-hover:opacity-100 transition cursor-ew-resize flex items-center justify-center"
@mousedown="startResizing"
>
<ArrowsRightLeftIcon class="h-3 w-3 transition text-foundation" />
</div>
<div
class="relative z-30 w-1 h-full pt-[2.5rem] -ml-1 bg-transparent group-hover:bg-primary cursor-ew-resize transition rounded-l"
@mousedown="startResizing"
></div>
</div>
<div
class="flex flex-col w-full h-full relative z-20 overflow-hidden border border-outline-2 rounded-lg shadow"
>
<!-- Header -->
<div
class="h-[6.5rem] absolute z-10 top-0 w-full left-0 bg-foundation border-b border-outline-2 sm:rounded-t-md"
>
<div
class="flex items-center justify-between py-1.5 pl-3 pr-1 border-b border-outline-2"
>
<div v-if="$slots.title" class="text-body-xs text-foreground font-semibold">
<slot name="title"></slot>
</div>
<FormButton
hide-text
:icon-left="XMarkIcon"
size="sm"
color="subtle"
@click="onClose"
/>
</div>
<div v-if="$slots.actions" class="w-full">
<slot name="actions"></slot>
</div>
</div>
<div class="w-full" :class="$slots.actions ? 'h-[7rem]' : 'h-10'"></div>
<div
class="overflow-y-auto simple-scrollbar h-full bg-foundation w-full pt-2 sm:rounded-b-md max-h-[220px] sm:max-h-none"
>
<slot></slot>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useEventListener } from '@vueuse/core'
import { XMarkIcon, ArrowsRightLeftIcon } from '@heroicons/vue/24/outline'
import { useIsSmallerOrEqualThanBreakpoint } from '~~/composables/browser'
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
defineProps<{
open: boolean
}>()
const emit = defineEmits<{
(event: 'close'): void
}>()
const resizableElement = ref(null)
const resizeHandle = ref(null)
const isResizing = ref(false)
const width = ref(300)
let startWidth = 0
let startX = 0
const { isSmallerOrEqualSm } = useIsSmallerOrEqualThanBreakpoint()
const { isEnabled: isEmbedEnabled } = useEmbed()
const startResizing = (event: MouseEvent) => {
event.preventDefault()
isResizing.value = true
startX = event.clientX
startWidth = width.value
}
if (import.meta.client) {
useEventListener(resizeHandle, 'mousedown', startResizing)
useEventListener(document, 'mousemove', (event) => {
if (isResizing.value) {
const diffX = startX - event.clientX
width.value = Math.max(
300,
Math.min(startWidth + diffX, (parseInt('75vw') * window.innerWidth) / 100)
)
}
})
useEventListener(document, 'mouseup', () => {
if (isResizing.value) {
isResizing.value = false
}
})
}
const minimize = () => {
width.value = 300
}
const onClose = () => {
minimize()
emit('close')
}
</script>