Skip to content

Commit

Permalink
feat: slide layers (#1634)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <github@antfu.me>
  • Loading branch information
KermanX and antfu committed May 31, 2024
1 parent 8f49c19 commit ad9e80a
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 104 deletions.
34 changes: 20 additions & 14 deletions docs/custom/global-layers.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
# Global Layers

> Available since v0.17
Global layers allow you to have custom components that **persist** across slides. This could be useful for having footers, cross-slide animations, global effects, etc.

Slidev provides three layers for this usage, create `global-top.vue`, `global-bottom.vue` or `custom-nav-controls.vue` under your project root and it will pick up automatically.

Layers relationship:
There are also layers for **each** slide: `layouts/slide-top.vue` and `layouts/slide-bottom.vue`. The usage is similar to the global layers, but they are applied to every slide, so there may be more than one instance of them.

::: tip
When exporting, the `--per-slide` option should be used to ensure the global layers are applied to each slide correctly.
:::

## Layers relationship

At z-axis, from top to bottom:

- Global Top (`global-top.vue`)
- Slides
- Global Bottom (`global-bottom.vue`)
- NavControls
- Customized Navigation Controls (`custom-nav-controls.vue`)

Note that this uses the terms "top" and "bottom" in the context of depth, not the screen position.
- Global Top (`global-top.vue`) - single instance
- Slide Top (`slide-top.vue`) - instance per slide
- Slide Content
- Slide Bottom (`slide-bottom.vue`) - instance per slide
- Global Bottom (`global-bottom.vue`) - single instance

## Example

Expand All @@ -30,7 +36,7 @@ The text `Your Name` will appear on all your slides.
```html
<!-- custom-nav-controls -->
<template>
<button class="icon-btn" title="Next" @click="$slidev.nav.next">
<button class="icon-btn" title="Next" @click="$nav.next">
<carbon:arrow-right />
</button>
</template>
Expand All @@ -44,7 +50,7 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- hide the footer from Page 4 -->
<template>
<footer
v-if="$slidev.nav.currentPage !== 4"
v-if="$nav.currentPage !== 4"
class="absolute bottom-0 left-0 right-0 p-2"
>
Your Name
Expand All @@ -56,7 +62,7 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- hide the footer from "cover" layout -->
<template>
<footer
v-if="$slidev.nav.currentLayout !== 'cover'"
v-if="$nav.currentLayout !== 'cover'"
class="absolute bottom-0 left-0 right-0 p-2"
>
Your Name
Expand All @@ -68,10 +74,10 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- an example footer for pages -->
<template>
<footer
v-if="$slidev.nav.currentLayout !== 'cover'"
v-if="$nav.currentLayout !== 'cover'"
class="absolute bottom-0 left-0 right-0 p-2"
>
{{ $slidev.nav.currentPage }} / {{ $slidev.nav.total }}
{{ $nav.currentPage }} / {{ $nav.total }}
</footer>
</template>
```
Expand All @@ -80,7 +86,7 @@ To enable it conditionally, you can apply it with the [Vue Global Context](/cust
<!-- custom-nav-controls -->
<!-- hide the button in Presenter model -->
<template>
<button v-if="!$slidev.nav.isPresenter" class="icon-btn" title="Next" @click="$slidev.nav.next">
<button v-if="!$nav.isPresenter" class="icon-btn" title="Next" @click="$nav.next">
<carbon:arrow-right />
</button>
</template>
Expand Down
4 changes: 1 addition & 3 deletions packages/client/internals/PrintSlideClick.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import { configs, slideHeight, slideWidth } from '../env'
import { getSlideClass } from '../utils'
import type { SlidevContextNav } from '../composables/useNav'
import SlideWrapper from './SlideWrapper.vue'
import GlobalTop from '#slidev/global-components/top'
import GlobalBottom from '#slidev/global-components/bottom'
import { GlobalBottom, GlobalTop } from '#slidev/global-layers'
const { nav } = defineProps<{
nav: SlidevContextNav
Expand Down
7 changes: 7 additions & 0 deletions packages/client/internals/SlideWrapper.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { injectionClicksContext, injectionCurrentPage, injectionRenderContext, i
import { getSlideClass } from '../utils'
import { configs } from '../env'
import SlideLoading from './SlideLoading.vue'
import { SlideBottom, SlideTop } from '#slidev/global-layers'
const props = defineProps({
clicksContext: {
Expand Down Expand Up @@ -67,7 +68,13 @@ const SlideComponent = computed(() => props.route && defineAsyncComponent({
:class="getSlideClass(route, ['slide', 'presenter'].includes(props.renderContext) ? '' : 'disable-view-transition')"
:style="style"
>
<div v-if="SlideBottom" class="absolute inset-0">
<SlideBottom />
</div>
<SlideComponent />
<div v-if="SlideTop" class="absolute inset-0">
<SlideTop />
</div>
</div>
</template>

Expand Down
4 changes: 1 addition & 3 deletions packages/client/internals/SlidesShow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import { activeDragElement } from '../state'
import { CLICKS_MAX } from '../constants'
import SlideWrapper from './SlideWrapper.vue'
import DragControl from './DragControl.vue'
import GlobalTop from '#slidev/global-components/top'
import GlobalBottom from '#slidev/global-components/bottom'
import { GlobalBottom, GlobalTop } from '#slidev/global-layers'
defineProps<{
renderContext: 'slide' | 'presenter'
Expand Down
72 changes: 0 additions & 72 deletions packages/slidev/node/virtual/global-components.ts

This file was deleted.

39 changes: 39 additions & 0 deletions packages/slidev/node/virtual/global-layers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { existsSync } from 'node:fs'
import { join } from 'node:path'
import { toAtFS } from '../resolver'
import type { VirtualModuleTemplate } from './types'

export const templateGlobalLayers: VirtualModuleTemplate = {
id: `/@slidev/global-layers`,
getContent({ roots }) {
const imports: string[] = []

let n = 0
function getComponent(names: string[]) {
const components = roots
.flatMap(root => names.map(name => join(root, name)))
.filter(i => existsSync(i))

imports.push(components.map((path, i) => `import __n${n}_${i} from '${toAtFS(path)}'`).join('\n'))
const render = components.map((_, i) => `h(__n${n}_${i})`).join(',')

n++

return `{ render: () => [${render}] }`
}

const globalTop = getComponent(['global.vue', 'global-top.vue', 'GlobalTop.vue'])
const globalBottom = getComponent(['global-bottom.vue', 'GlobalBottom.vue'])
const slideTop = getComponent(['slide-top.vue', 'SlideTop.vue'])
const slideBottom = getComponent(['slide-bottom.vue', 'SlideBottom.vue'])

return [
imports.join('\n'),
`import { h } from 'vue'`,
`export const GlobalTop = ${globalTop}`,
`export const GlobalBottom = ${globalBottom}`,
`export const SlideTop = ${slideTop}`,
`export const SlideBottom = ${slideBottom}`,
].join('\n')
},
}
6 changes: 3 additions & 3 deletions packages/slidev/node/virtual/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { templateConfigs } from './configs'
import { templateLegacyRoutes, templateLegacyTitles } from './deprecated'
import { templateGlobalBottom, templateGlobalTop, templateNavControls } from './global-components'
import { templateGlobalLayers } from './global-layers'
import { templateNavControls } from './nav-controls'
import { templateLayouts } from './layouts'
import { templateMonacoRunDeps } from './monaco-deps'
import { templateMonacoTypes } from './monaco-types'
Expand All @@ -16,8 +17,7 @@ export const templates = [
templateMonacoRunDeps,
templateConfigs,
templateStyle,
templateGlobalBottom,
templateGlobalTop,
templateGlobalLayers,
templateNavControls,
templateSlides,
templateLayouts,
Expand Down
27 changes: 27 additions & 0 deletions packages/slidev/node/virtual/nav-controls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { existsSync } from 'node:fs'
import { join } from 'node:path'
import { toAtFS } from '../resolver'
import type { VirtualModuleTemplate } from './types'

export const templateNavControls: VirtualModuleTemplate = {
id: '/@slidev/custom-nav-controls',
getContent({ roots }) {
const components = roots
.flatMap((root) => {
return [
join(root, 'custom-nav-controls.vue'),
join(root, 'CustomNavControls.vue'),
]
})
.filter(i => existsSync(i))

const imports = components.map((i, idx) => `import __n${idx} from '${toAtFS(i)}'`).join('\n')
const render = components.map((i, idx) => `h(__n${idx})`).join(',')

return `${imports}
import { h } from 'vue'
export default {
render: () => [${render}],
}`
},
}
14 changes: 5 additions & 9 deletions packages/types/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ declare module '#slidev/configs' {
export default configs
}

declare module '#slidev/global-components/top' {
declare module '#slidev/global-layers' {
import type { ComponentOptions } from 'vue'

const component: ComponentOptions
export default component
}
export const GlobalTop: ComponentOptions
export const GlobalBottom: ComponentOptions

declare module '#slidev/global-components/bottom' {
import type { ComponentOptions } from 'vue'

const component: ComponentOptions
export default component
export const SlideTop: ComponentOptions
export const SlideBottom: ComponentOptions
}

declare module '#slidev/slides' {
Expand Down

0 comments on commit ad9e80a

Please sign in to comment.