| 1 | <script setup lang="ts"> |
| 2 | import type { SlidevMarkdown } from '@slidev/types' |
| 3 | import Center from '@slidev/client/layouts/center.vue' |
| 4 | import Default from '@slidev/client/layouts/default.vue' |
| 5 | import { parseSync } from '@slidev/parser' |
| 6 | import Cover from '@slidev/theme-default/layouts/cover.vue' |
| 7 | import Markdown from 'markdown-it' |
| 8 | import TypeIt from 'typeit' |
| 9 | import { onMounted, ref, watch } from 'vue' |
| 10 | import DemoEditor from './DemoEditor.vue' |
| 11 | import DemoSlide from './DemoSlide.vue' |
| 12 | import SlideContainer from './SlideContainer.vue' |
| 13 | |
| 14 | import '@slidev/client/styles/layouts-base.css' |
| 15 | import '@slidev/theme-default/styles/layouts.css' |
| 16 | |
| 17 | const page = ref(0) |
| 18 | const paused = ref(false) |
| 19 | const completed = ref(false) |
| 20 | const code = ref('') |
| 21 | const info = ref<SlidevMarkdown>() |
| 22 | const block = ref<HTMLPreElement>() |
| 23 | |
| 24 | const markdown = new Markdown() |
| 25 | |
| 26 | function getLayout(id: number) { |
| 27 | const name = info.value?.slides?.[id]?.frontmatter?.layout || 'default' |
| 28 | return name === 'cover' |
| 29 | ? Cover |
| 30 | : name === 'center' |
| 31 | ? Center |
| 32 | : Default |
| 33 | } |
| 34 | |
| 35 | watch([code, paused], () => { |
| 36 | if (paused.value) |
| 37 | return |
| 38 | try { |
| 39 | info.value = parseSync(code.value, '') |
| 40 | } |
| 41 | catch { |
| 42 | } |
| 43 | }) |
| 44 | |
| 45 | function getAttrs(id: number) { |
| 46 | return info.value?.slides?.[id]?.frontmatter |
| 47 | } |
| 48 | |
| 49 | function getHTML(id: number) { |
| 50 | const content = info?.value?.slides?.[id]?.content |
| 51 | if (!content) |
| 52 | return '' |
| 53 | return markdown.render(content) |
| 54 | } |
| 55 | |
| 56 | function pause() { |
| 57 | paused.value = true |
| 58 | } |
| 59 | function resume() { |
| 60 | paused.value = false |
| 61 | } |
| 62 | |
| 63 | const COVER_URL = 'https://sli.dev/demo-cover.png' |
| 64 | if (typeof window !== 'undefined') { |
| 65 | const img1 = new Image() |
| 66 | img1.src = COVER_URL |
| 67 | } |
| 68 | |
| 69 | function play() { |
| 70 | code.value = '' |
| 71 | block.value!.innerHTML = '' |
| 72 | completed.value = false |
| 73 | // @ts-expect-error wrong types provided by TypeIt |
| 74 | new TypeIt(block.value!, { |
| 75 | speed: 50, |
| 76 | startDelay: 900, |
| 77 | afterStep: () => { |
| 78 | code.value = JSON.parse(JSON.stringify(block.value!.innerText.replace('|', ''))) |
| 79 | }, |
| 80 | afterComplete: () => { |
| 81 | setTimeout(() => completed.value = true, 300) |
| 82 | }, |
| 83 | }) |
| 84 | .type('<br><span class="token title"># Welcome to Slidev!</span><br><br>', { delay: 400 }) |
| 85 | .type('Presentation Slides for Developers', { delay: 400 }) |
| 86 | .move(null, { to: 'START', speed: 0 }) |
| 87 | .type('<br>') |
| 88 | .move(null, { to: 'START' }) |
| 89 | .exec(pause) |
| 90 | .type('<span class="token punctuation">---<br><br>---</span>') |
| 91 | .move(-4) |
| 92 | .type('<span class="token tag">layout:</span> center') |
| 93 | .exec(resume) |
| 94 | .pause(1000) |
| 95 | .exec(pause) |
| 96 | .delete(6, { delay: 100, speed: 50 }) |
| 97 | .type('cover') |
| 98 | .exec(resume) |
| 99 | .exec(pause) |
| 100 | .pause(1000) |
| 101 | .type('<br>') |
| 102 | .type('<span class="token tag">background:</span> ', { delay: 200 }) |
| 103 | .type(COVER_URL, { speed: 0 }) |
| 104 | .exec(resume) |
| 105 | .pause(1000) |
| 106 | .move(null, { to: 'END', speed: 0 }) |
| 107 | .exec(pause) |
| 108 | .type('<br><br><span class="token punctuation">---</span><br><br>', { delay: 400 }) |
| 109 | .exec(resume) |
| 110 | .exec(() => setTimeout(() => page.value = 1)) |
| 111 | .type('<span class="token title"># Page 2</span><br><br>', { delay: 400 }) |
| 112 | .type('- 📄 Write slides in a single Markdown file<br>', { delay: 800 }) |
| 113 | .type('- 🌈 Themes, code blocks, interactive components<br>', { delay: 800 }) |
| 114 | .type('- 😎 Read the docs to learn more!', { delay: 800 }) |
| 115 | .exec(() => setTimeout(() => page.value = 0)) |
| 116 | .go() |
| 117 | } |
| 118 | |
| 119 | onMounted(play) |
| 120 | </script> |
| 121 | |
| 122 | <template> |
| 123 | <div> |
| 124 | <DemoEditor> |
| 125 | <div class="text-sm opacity-50 text-center"> |
| 126 | ./slides.md |
| 127 | </div> |
| 128 | |
| 129 | <div v-if="completed" class="absolute text-xs right-1 top-1 icon-btn opacity-50" title="Replay" @click="play()"> |
| 130 | <div class="i-carbon:reset" /> |
| 131 | </div> |
| 132 | |
| 133 | <div class="language-md !bg-transparent px4 py1"> |
| 134 | <pre ref="block" class="text-left whitespace-pre-wrap font-mono bg-transparent" /> |
| 135 | </div> |
| 136 | </DemoEditor> |
| 137 | |
| 138 | <DemoSlide class="text-left"> |
| 139 | <div |
| 140 | class="flex h-full dark:bg-[#181819] transition-transform transform duration-500" |
| 141 | style="width: 200%" |
| 142 | :class="page === 1 ? '-translate-x-1/2' : ''" |
| 143 | > |
| 144 | <SlideContainer class="w-full h-full"> |
| 145 | <component :is="getLayout(0)" v-bind="getAttrs(0)"> |
| 146 | <div v-html="getHTML(0)" /> |
| 147 | </component> |
| 148 | </SlideContainer> |
| 149 | <SlideContainer class="w-full h-full"> |
| 150 | <component :is="getLayout(1)" v-bind="getAttrs(1)"> |
| 151 | <div v-html="getHTML(1)" /> |
| 152 | </component> |
| 153 | </SlideContainer> |
| 154 | </div> |
| 155 | <div |
| 156 | class="absolute left-2 bottom-1 flex text-gray-200" |
| 157 | opacity="0 hover:100" |
| 158 | > |
| 159 | <div class="icon-btn" :class="{ disabled: page === 0 }" @click="page = 0"> |
| 160 | <div class="i-carbon:chevron-left" /> |
| 161 | </div> |
| 162 | <div class="icon-btn" :class="{ disabled: page === 1 }" @click="page = 1"> |
| 163 | <div class="i-carbon:chevron-right" /> |
| 164 | </div> |
| 165 | </div> |
| 166 | </DemoSlide> |
| 167 | </div> |
| 168 | </template> |
| 169 | |
| 170 | <style> |
| 171 | .slidev-layout ul { |
| 172 | padding: 0; |
| 173 | } |
| 174 | .slidev-layout li { |
| 175 | line-height: 2.4em; |
| 176 | } |
| 177 | </style> |
| 178 |