| 1 | <script setup lang="ts"> |
| 2 | import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types' |
| 3 | import type { CSSProperties, PropType } from 'vue' |
| 4 | import { provideLocal } from '@vueuse/core' |
| 5 | import { computed, ref, toRef } from 'vue' |
| 6 | import { SlideBottom, SlideTop } from '#slidev/global-layers' |
| 7 | import { injectionClicksContext, injectionCurrentPage, injectionFrontmatter, injectionRenderContext, injectionRoute, injectionSlideZoom } from '../constants' |
| 8 | import { configs } from '../env' |
| 9 | import { getSlideClass } from '../utils' |
| 10 | |
| 11 | const props = defineProps({ |
| 12 | clicksContext: { |
| 13 | type: Object as PropType<ClicksContext>, |
| 14 | required: true, |
| 15 | }, |
| 16 | renderContext: { |
| 17 | type: String as PropType<RenderContext>, |
| 18 | default: 'slide', |
| 19 | }, |
| 20 | route: { |
| 21 | type: Object as PropType<SlideRoute>, |
| 22 | required: true, |
| 23 | }, |
| 24 | }) |
| 25 | |
| 26 | const zoom = computed(() => props.route.meta?.slide?.frontmatter.zoom ?? 1) |
| 27 | |
| 28 | provideLocal(injectionRoute, props.route) |
| 29 | provideLocal(injectionFrontmatter, props.route.meta.slide.frontmatter) |
| 30 | provideLocal(injectionCurrentPage, ref(props.route.no)) |
| 31 | provideLocal(injectionRenderContext, ref(props.renderContext)) |
| 32 | provideLocal(injectionClicksContext, toRef(props, 'clicksContext')) |
| 33 | provideLocal(injectionSlideZoom, zoom) |
| 34 | |
| 35 | const style = computed<CSSProperties>(() => ({ |
| 36 | 'user-select': configs.selectable ? undefined : 'none', |
| 37 | '--slidev-slide-zoom-scale': zoom.value === 1 ? undefined : zoom.value, |
| 38 | })) |
| 39 | </script> |
| 40 | |
| 41 | <template> |
| 42 | <div |
| 43 | :data-slidev-no="props.route.no" |
| 44 | :class="getSlideClass(route, ['slide', 'presenter'].includes(props.renderContext) ? '' : 'disable-view-transition')" |
| 45 | :style="style" |
| 46 | :lang="props.route.meta.slide.frontmatter.lang" |
| 47 | > |
| 48 | <SlideBottom /> |
| 49 | <component :is="props.route.component" /> |
| 50 | <SlideTop /> |
| 51 | </div> |
| 52 | </template> |
| 53 | |
| 54 | <style scoped> |
| 55 | .disable-view-transition:deep(*) { |
| 56 | view-transition-name: none !important; |
| 57 | } |
| 58 | |
| 59 | .slidev-page { |
| 60 | position: absolute; |
| 61 | inset: 0; |
| 62 | |
| 63 | /* Zoom handling */ |
| 64 | --slidev-slide-zoom-scale: 1; |
| 65 | width: calc(100% / var(--slidev-slide-zoom-scale)); |
| 66 | height: calc(100% / var(--slidev-slide-zoom-scale)); |
| 67 | transform-origin: top left; |
| 68 | scale: var(--slidev-slide-zoom-scale); |
| 69 | /* slide scale = container scale * zoom scale */ |
| 70 | --slidev-slide-scale: calc(var(--slidev-slide-container-scale) * var(--slidev-slide-zoom-scale)); |
| 71 | } |
| 72 | </style> |
| 73 |