feat(fe2): presentations ui polish
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-foundation border border-outline-3 rounded-xl shadow-md h-10 flex items-center"
|
||||
class="bg-foundation border border-outline-3 rounded-xl shadow-md flex items-center h-10"
|
||||
>
|
||||
<div class="flex items-center justify-between space-x-1 p-1">
|
||||
<FormButton>Share</FormButton>
|
||||
@@ -9,21 +9,32 @@
|
||||
class="hidden md:flex touch:hidden"
|
||||
@click="toggleFullscreen"
|
||||
>
|
||||
<LucideFullscreen class="size-4" />
|
||||
<LucideMinimize
|
||||
v-if="isFullscreen"
|
||||
:size="16"
|
||||
:stroke-width="1.5"
|
||||
:absolute-stroke-width="true"
|
||||
/>
|
||||
<LucideMaximize
|
||||
v-else
|
||||
:size="16"
|
||||
:stroke-width="1.5"
|
||||
:absolute-stroke-width="true"
|
||||
/>
|
||||
</PresentationFloatingPanelButton>
|
||||
|
||||
<PresentationFloatingPanelButton
|
||||
:is-active="isSidebarOpen"
|
||||
@click="emit('toggleSidebar')"
|
||||
>
|
||||
<LucideInfo class="size-4" />
|
||||
<LucideInfo :size="16" :stroke-width="1.5" :absolute-stroke-width="true" />
|
||||
</PresentationFloatingPanelButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LucideInfo, LucideFullscreen } from 'lucide-vue-next'
|
||||
import { LucideInfo, LucideMaximize, LucideMinimize } from 'lucide-vue-next'
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggleSidebar'): void
|
||||
@@ -31,6 +42,8 @@ const emit = defineEmits<{
|
||||
|
||||
const isSidebarOpen = defineModel<boolean>('is-sidebar-open')
|
||||
|
||||
const isFullscreen = ref(false)
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen()
|
||||
@@ -38,4 +51,17 @@ const toggleFullscreen = () => {
|
||||
document.exitFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for fullscreen changes
|
||||
const handleFullscreenChange = () => {
|
||||
isFullscreen.value = !!document.fullscreenElement
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<template>
|
||||
<PresentationFloatingPanel>
|
||||
<div class="flex items-center justify-between space-x-1">
|
||||
<div class="flex items-center justify-between space-x-2">
|
||||
<PresentationFloatingPanelButton
|
||||
:active="isSidebarOpen"
|
||||
@click="emit('toggleSidebar')"
|
||||
>
|
||||
<LucideArrowLeftToLine v-if="isSidebarOpen" class="size-4" />
|
||||
<LucidePanelLeft v-else class="size-4" />
|
||||
<LucideArrowLeftToLine
|
||||
v-if="isSidebarOpen"
|
||||
:size="16"
|
||||
:stroke-width="1.5"
|
||||
:absolute-stroke-width="true"
|
||||
/>
|
||||
<LucidePanelLeft
|
||||
v-else
|
||||
:size="16"
|
||||
:stroke-width="1.5"
|
||||
:absolute-stroke-width="true"
|
||||
/>
|
||||
</PresentationFloatingPanelButton>
|
||||
<h1
|
||||
v-if="presentation?.title"
|
||||
class="hidden sm:block text-body-xs font-medium text-foreground leading-none sm:pr-1.5"
|
||||
class="hidden sm:block text-body-xs font-medium text-foreground leading-none sm:pr-3 max-w-64 truncate"
|
||||
>
|
||||
{{ presentation?.title }}
|
||||
</h1>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<aside
|
||||
class="bg-foundation h-48 lg:h-dvh w-full lg:w-64 xl:w-80 border-t lg:border-t-0 lg:border-l border-outline-3 py-5 px-4"
|
||||
class="bg-foundation h-[196px] lg:h-dvh w-full lg:w-[260px] xl:w-[324px] border-t lg:border-t-0 lg:border-l border-outline-3 p-4"
|
||||
>
|
||||
<div class="hidden lg:flex items-center justify-end space-x-0.5">
|
||||
<div class="hidden lg:flex items-center justify-end space-x-1">
|
||||
<div
|
||||
v-tippy="
|
||||
canUpdateSlide ? undefined : 'You do not have permission to edit this slide'
|
||||
@@ -24,9 +24,12 @@
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
<section class="pt-2 flex flex-col gap-4">
|
||||
<div class="flex items-center justify-between gap-x-2">
|
||||
<h1 v-if="currentSlide?.name" class="text-xl font-medium text-foreground px-2">
|
||||
<section class="lg:pt-4 lg:px-1 flex flex-col gap-3">
|
||||
<div class="flex items-start justify-between gap-x-2">
|
||||
<h1
|
||||
v-if="currentSlide?.name"
|
||||
class="text-xl/7 xl:text-[26px]/8 tracking-[-0.399px] xl:tracking-[-0.494px] font-medium text-foreground px-1 lg:px-2 xl:px-3 py-0.5 lg:py-1.5"
|
||||
>
|
||||
{{ currentSlide?.name }}
|
||||
</h1>
|
||||
<div class="lg:hidden flex items-center gap-x-1">
|
||||
@@ -37,18 +40,12 @@
|
||||
hide-text
|
||||
@click="isSlideEditDialogOpen = true"
|
||||
/>
|
||||
<FormButton
|
||||
:icon-left="LucideX"
|
||||
color="subtle"
|
||||
hide-text
|
||||
@click="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p
|
||||
v-if="currentSlide?.description"
|
||||
class="text-body-sm text-foreground whitespace-pre-wrap px-2"
|
||||
class="text-body-sm xl:text-body text-foreground whitespace-pre-wrap px-1 lg:px-2 xl:px-3 lg:py-1"
|
||||
>
|
||||
{{ currentSlide?.description }}
|
||||
</p>
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
<template>
|
||||
<div class="w-full sm:w-auto">
|
||||
<div class="fixed inset-0 z-10 md:hidden">
|
||||
<div class="absolute inset-0 bg-black/50" />
|
||||
<div class="absolute inset-0 bg-black/20" />
|
||||
</div>
|
||||
|
||||
<aside
|
||||
class="relative z-20 bg-foundation h-dvh w-52 md:w-60 border-r border-outline-3 pt-3"
|
||||
class="relative z-20 bg-foundation h-dvh w-[212px] md:w-60 border-r border-outline-3"
|
||||
>
|
||||
<div class="flex flex-col h-full">
|
||||
<section class="flex-shrink-0 flex items-center gap-3 px-3">
|
||||
<section
|
||||
class="flex-shrink-0 flex items-center gap-3 absolute bg-foundation/70 backdrop-blur-lg w-full p-3 pb-2.5 border-b border-white/80 dark:border-gray-900/30"
|
||||
>
|
||||
<NuxtLink
|
||||
class="flex items-center gap-2 min-w-0 flex-1"
|
||||
class="flex items-center gap-2.5 min-w-0 flex-1"
|
||||
:to="workspaceRoute(workspace?.slug)"
|
||||
>
|
||||
<WorkspaceAvatar :name="workspace?.name" :logo="workspace?.logo" />
|
||||
<WorkspaceAvatar
|
||||
:name="workspace?.name"
|
||||
:logo="workspace?.logo"
|
||||
size="lg"
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-body-xs text-foreground truncate">
|
||||
<p class="text-body-xs font-medium text-foreground truncate">
|
||||
{{ workspace?.name }}
|
||||
</p>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</section>
|
||||
<section
|
||||
class="flex-1 flex justify-center simple-scrollbar overflow-y-auto mt-3 pb-3 px-3"
|
||||
class="flex-1 flex justify-center simple-scrollbar overflow-y-auto pt-16 pb-3 px-3"
|
||||
>
|
||||
<PresentationSlideList />
|
||||
</section>
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
<PresentationHeader
|
||||
v-if="!hideUi"
|
||||
v-model:is-sidebar-open="isLeftSidebarOpen"
|
||||
class="absolute top-4 z-40"
|
||||
:class="[isLeftSidebarOpen ? 'left-56 md:left-[15.75rem]' : 'left-4']"
|
||||
class="absolute top-3 z-40"
|
||||
:class="[isLeftSidebarOpen ? 'left-56 md:left-[15.75rem]' : 'left-3']"
|
||||
@toggle-sidebar="isLeftSidebarOpen = !isLeftSidebarOpen"
|
||||
/>
|
||||
|
||||
<PresentationActions
|
||||
v-if="!hideUi"
|
||||
v-model:is-sidebar-open="isInfoSidebarOpen"
|
||||
class="absolute bottom-4 lg:top-4 right-4 z-20"
|
||||
class="absolute bottom-3 lg:top-3 right-3 z-20"
|
||||
:class="{
|
||||
'bottom-52 lg:bottom-auto lg:right-[17rem] xl:right-[21rem]':
|
||||
isInfoSidebarOpen
|
||||
@@ -28,12 +28,12 @@
|
||||
isInfoSidebarOpen
|
||||
? 'translate-y-[calc(-50%+25px-6rem)] lg:translate-y-[-50%]'
|
||||
: 'translate-y-[-50%]',
|
||||
isLeftSidebarOpen ? 'lg:left-[14.75rem] hidden md:block' : 'left-0'
|
||||
isLeftSidebarOpen ? 'lg:left-60 hidden md:block' : 'left-0'
|
||||
]"
|
||||
/>
|
||||
|
||||
<PresentationSpeckleLogo
|
||||
class="absolute right-4 z-30 top-4 lg:top-auto lg:bottom-4"
|
||||
class="absolute right-3 z-30 top-3 lg:top-auto lg:bottom-3"
|
||||
:class="[isInfoSidebarOpen ? '' : '']"
|
||||
/>
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
|
||||
<PresentationControls
|
||||
:hide-ui="hideUi"
|
||||
class="absolute left-4 lg:left-1/2 lg:-translate-x-1/2"
|
||||
class="absolute left-3 lg:left-1/2 lg:-translate-x-1/2"
|
||||
:class="[
|
||||
isInfoSidebarOpen ? 'bottom-52 lg:bottom-4' : 'bottom-4',
|
||||
isLeftSidebarOpen ? 'hidden md:flex md:left-64' : ''
|
||||
isInfoSidebarOpen ? 'bottom-52 lg:bottom-3' : 'bottom-3',
|
||||
isLeftSidebarOpen ? 'hidden md:flex md:left-[252px]' : ''
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
@@ -101,7 +101,7 @@ const onLoadingChange = (loading: boolean) => {
|
||||
if (!loading) {
|
||||
hideUi.value = false
|
||||
|
||||
isLeftSidebarOpen.value = !isMobile.value
|
||||
isLeftSidebarOpen.value = false
|
||||
isInfoSidebarOpen.value = !isMobile.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="p-4 absolute group">
|
||||
<div class="p-5 absolute group">
|
||||
<ul class="flex flex-col space-y-2">
|
||||
<li v-for="slide in visibleSlides" :key="slide.id">
|
||||
<div
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<div
|
||||
v-if="showSlideList"
|
||||
class="hidden lg:block absolute top-[calc(50%+25px)] -translate-y-1/2 max-h-[75vh] overflow-y-auto w-56 simple-scrollbar bg-foundation border border-outline-3 rounded-xl p-3 shadow-md transition-all duration-300 ease-out opacity-0 invisible group-hover:opacity-100 group-hover:visible -translate-x-5 group-hover:translate-x-0"
|
||||
class="hidden lg:block absolute top-[calc(50%+25px)] -translate-y-1/2 max-h-[75vh] overflow-y-auto w-56 simple-scrollbar bg-foundation border border-outline-3 rounded-2xl p-2 shadow-md transition-all duration-300 ease-out opacity-0 invisible group-hover:opacity-100 group-hover:visible -translate-x-5 group-hover:translate-x-0"
|
||||
>
|
||||
<PresentationSlideList class="w-full" hide-title />
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
<template>
|
||||
<PresentationFloatingPanel class="flex items-center shrink-0 select-none px-3 h-10">
|
||||
<span class="text-body-2xs text-foreground-2">Made with</span>
|
||||
<img
|
||||
class="size-5 block mr-0.5 ml-1"
|
||||
src="~~/assets/images/speckle_logo_big.png"
|
||||
alt="Speckle"
|
||||
/>
|
||||
<PresentationFloatingPanel
|
||||
class="flex items-center shrink-0 select-none px-[3px] h-10"
|
||||
>
|
||||
<NuxtLink
|
||||
to="https://speckle.systems/"
|
||||
external
|
||||
target="_blank"
|
||||
class="flex items-center gap-1 hover:bg-highlight-1 rounded-md h-8 px-2.5"
|
||||
>
|
||||
<span class="text-body-xs text-foreground-2">Made with</span>
|
||||
<img
|
||||
class="size-6 block mr-[0.5px] ml-0.5"
|
||||
src="~~/assets/images/speckle_logo_big.png"
|
||||
alt="Speckle"
|
||||
/>
|
||||
|
||||
<div class="text-body-xs mt-0 font-medium">Speckle</div>
|
||||
<div class="text-body-xs mt-0 font-medium">Speckle</div>
|
||||
</NuxtLink>
|
||||
</PresentationFloatingPanel>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<button
|
||||
:disabled="disabled"
|
||||
class="bg-foundation size-9 flex items-center justify-center hover:bg-primary-muted disabled:hover:bg-transparent text-foreground disabled:text-foreground-3"
|
||||
class="bg-foundation size-8 rounded-md flex items-center justify-center hover:bg-primary-muted disabled:hover:bg-transparent text-foreground disabled:text-foreground-3"
|
||||
>
|
||||
<component :is="icon" class="size-4" />
|
||||
<slot />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center rounded-xl bg-foundation border border-outline-3 shadow-md overflow-hidden divide-x divide-outline-3"
|
||||
class="flex items-center rounded-xl bg-foundation border border-outline-3 shadow-md overflow-hidden h-10 p-[3px]"
|
||||
:class="{ hidden: hideUi }"
|
||||
>
|
||||
<PresentationControlsButton
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<template>
|
||||
<button
|
||||
class="size-8 flex items-center justify-center bg-foundation rounded-xl hover:bg-info-lighter hover:text-primary-focus dark:hover:text-foreground-on-primary"
|
||||
class="size-8 flex items-center text-foreground justify-center bg-foundation rounded-md hover:bg-primary-muted dark:hover:text-foreground-on-primary"
|
||||
:class="{
|
||||
'bg-info-lighter text-primary-focus dark:text-foreground-on-primary': isActive
|
||||
'bg-info-lighter hover:!bg-info-lighter text-primary-focus dark:text-foreground-on-primary':
|
||||
isActive
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="bg-foundation border border-outline-3 rounded-xl p-1 shadow-md h-10 flex items-center"
|
||||
class="bg-foundation border border-collapse border-outline-3 rounded-xl p-1 p-x-[3px] shadow-md flex items-center h-10"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
<template>
|
||||
<li class="w-full" :class="{ 'pb-1 last:pb-0': hideTitle }">
|
||||
<li class="w-full" :class="{ 'pb-0': hideTitle }">
|
||||
<button
|
||||
class="bg-foundation-page rounded-xl overflow-hidden border border-outline-3 transition-all duration-200 hover:!border-outline-4 w-full"
|
||||
class="bg-foundation-page rounded-md overflow-hidden border border-outline-3 transition-all duration-200 hover:!border-outline-4 w-full"
|
||||
:class="[isCurrentSlide ? '!border-outline-5' : '']"
|
||||
@click="onSelectSlide"
|
||||
>
|
||||
<img
|
||||
:src="slide.screenshot"
|
||||
:alt="slide.name"
|
||||
class="w-full h-28 object-contain"
|
||||
class="w-full aspect-[3/2] md:aspect-video object-cover"
|
||||
/>
|
||||
</button>
|
||||
<p v-if="!hideTitle" class="text-body-3xs leading-none text-foreground my-2">
|
||||
{{ slideIndex }}. {{ slide.name }}
|
||||
|
||||
<p v-if="!hideTitle" class="text-body-3xs font-medium text-foreground mt-1.5 mb-2">
|
||||
<span class="font-semibold mr-1">{{ slideIndex }}.</span>
|
||||
{{ slide.name }}
|
||||
</p>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div class="px-3 pt-3">
|
||||
<div class="px-2 pt-2">
|
||||
<ViewerButtonGroup>
|
||||
<ViewerButtonGroupButton
|
||||
v-for="viewsType in Object.values(ViewsType)"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
v-if="!hasGroups || !project"
|
||||
:type="emptyStateType"
|
||||
/>
|
||||
<div v-else class="p-1.5 pt-2">
|
||||
<div v-else class="p-2 pt-2">
|
||||
<ViewerSavedViewsPanelViewsGroup
|
||||
v-for="group in groups"
|
||||
:key="group.id"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col min-w-0 grow">
|
||||
<div class="flex flex-col min-w-0 grow gap-y-0.5">
|
||||
<div class="text-body-2xs font-medium text-foreground truncate grow-0">
|
||||
{{ view.name }}
|
||||
</div>
|
||||
@@ -279,7 +279,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 relative transition-all'
|
||||
'flex items-center gap-2 p-2 w-full group rounded-md cursor-pointer relative transition-all'
|
||||
]
|
||||
|
||||
if (isActive.value) {
|
||||
|
||||
Reference in New Issue
Block a user