Skip to content

Commit

Permalink
feat: animations for code highlights
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed May 4, 2021
1 parent 31a8b0a commit 94e158d
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 25 deletions.
28 changes: 28 additions & 0 deletions docs/guide/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,34 @@ console.log('HelloWorld')
```
~~~

To highlight specific lines, simply add line numbers within bracket `{}`. Line numbers start count from 1.

~~~md
```ts {2,3}
function add(
a: Ref<number> | number,
b: Ref<number> | number
) {
return computed(() => unref(a) + unref(b))
}
```
~~~

To change the highlight in multiple steps, you can use `|` to separate them. For example

~~~md
```ts {2-3|5|all}
function add(
a: Ref<number> | number,
b: Ref<number> | number
) {
return computed(() => unref(a) + unref(b))
}
```
~~~

This will highlight on `a` and `b` first, then goes to the `computed` line after one click, and then, the whole block. Learn more about the [clicks animations here](/guide/animations).

Whenever you want to do some modification in the presentation, simply add `{monaco}` after the language id, it turns the block into a full-featured Monaco editor!

~~~md
Expand Down
42 changes: 22 additions & 20 deletions packages/client/builtin/CodeHighlightController.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { range, remove } from '@antfu/utils'
import { parseRangeString } from '@slidev/parser/core'
import { computed, defineProps, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import { computed, defineProps, getCurrentInstance, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import { injectionClicks, injectionClicksElements, injectionClicksDisabled } from '../modules/directives'
const props = defineProps({
Expand All @@ -25,36 +25,38 @@ function makeid(length = 5) {
return result.join('')
}
const prev = elements.value.length
const index = computed(() => {
if (disabled.value)
return props.ranges.length - 1
return Math.min(Math.max(0, clicks.value - prev), props.ranges.length - 1)
})
const rangeStr = computed(() => props.ranges[index.value] || '')
if (props.ranges.length >= 2 && !disabled.value) {
const id = makeid()
const ids = range(props.ranges.length - 1).map(i => id + i)
elements.value.push(...ids)
onUnmounted(() => {
ids.forEach(i => remove(elements.value, i))
})
}
const el = ref<HTMLDivElement>()
const vm = getCurrentInstance()
onMounted(() => {
const prev = elements.value.length
const index = computed(() => {
if (disabled.value)
return props.ranges.length - 1
return Math.min(Math.max(0, clicks.value - prev), props.ranges.length - 1)
})
const rangeStr = computed(() => props.ranges[index.value] || '')
if (props.ranges.length >= 2 && !disabled.value) {
const id = makeid()
const ids = range(props.ranges.length - 1).map(i => id + i)
elements.value.push(...ids)
onUnmounted(() => {
ids.forEach(i => remove(elements.value, i))
}, vm)
}
watchEffect(() => {
if (!el.value)
return
const duoTone = el.value.querySelector('.shiki-dark')
const targets = duoTone ? Array.from(el.value.querySelectorAll('.shiki')) : [el.value]
for (const target of targets) {
const lines = Array.from(target.querySelectorAll('.line'))
const hightlights: number[] = parseRangeString(lines.length, rangeStr.value)
// console.log(hightlights, rangeStr.value)
const highlights: number[] = parseRangeString(lines.length, rangeStr.value)
lines.forEach((line, idx) => {
line.classList.toggle('opacity-30', !hightlights.includes(idx))
const highlighted = highlights.includes(idx)
line.classList.toggle('highlighted', highlighted)
line.classList.toggle('dishonored', !highlighted)
})
}
})
Expand Down
6 changes: 6 additions & 0 deletions packages/client/styles/code.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ html:not(.dark) .shiki-dark {
overflow: auto;
}

.slidev-code .line.highlighted {
}
.slidev-code .line.dishonored {
opacity: 0.3;
}

.shiki-container {
@apply relative flex flex-col;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/template/slides.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ image: 'https://source.unsplash.com/collection/94734566/1920x1080'

Use code snippets and get the highlighting directly!

```ts
```ts {all|2|1-6|9|all}
interface User {
id: number
firstName: string
Expand Down
2 changes: 1 addition & 1 deletion packages/parser/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export function parse(
* 1,3-5,8 => [1, 3, 4, 5, 8]
*/
export function parseRangeString(total: number, rangeStr?: string) {
if (!rangeStr || rangeStr === 'all')
if (!rangeStr || rangeStr === 'all' || rangeStr === '*')
return range(total)

const pages: number[] = []
Expand Down
6 changes: 3 additions & 3 deletions packages/slidev/node/plugins/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ export function truncateMancoMark(code: string) {

export function transformHighlighter(md: string) {
// transform monaco
return md.replace(/\n```(\w+?)\s*{([\w,\|-]+)}[\s\n]*([\s\S]+?)\n```/mg, (full, lang = '', rangeStr: string, code: string) => {
const ranges = rangeStr.split('|').map(i => i.trim())
return `<CodeHighlightController :ranges='${JSON.stringify(ranges)}'>\n\n\`\`\`${lang}\n${code}\n\`\`\`\n\n</CodeHighlightController>`
return md.replace(/\n```(\w+?)\s*{([\d\w*,\|-]+)}[\s\n]*([\s\S]+?)\n```/mg, (full, lang = '', rangeStr: string, code: string) => {
const ranges = rangeStr.split(/\|/g).map(i => i.trim())
return `\n<CodeHighlightController :ranges='${JSON.stringify(ranges)}'>\n\n\`\`\`${lang}\n${code}\n\`\`\`\n\n</CodeHighlightController>`
})
}

0 comments on commit 94e158d

Please sign in to comment.