返回 slidev
KaTexBlockWrapper.vue
根目录 / packages / client / builtin / KaTexBlockWrapper.vue
1 <!--
2 Line highlighting for KaTex blocks/
3 (auto transformed, you don't need to use this component directly)
4
5 Usage:
6 $$ {1|3|all}
7 \begin{array}{c}
8
9 \nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &
10 = \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
11
12 \nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
13
14 \nabla \cdot \vec{\mathbf{B}} & = 0
15
16 \end{array}
17 $$
18
19 Learn more: https://sli.dev/guide/syntax.html#latex-line-highlighting
20 -->
21
22 <script setup lang="ts">
23 import type { PropType } from 'vue'
24 import { parseRangeString } from '@slidev/parser/utils'
25 import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
26 import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET, CLICKS_MAX } from '../constants'
27 import { useSlideContext } from '../context'
28 import { makeId } from '../logic/utils'
29 import { collectKatexEquationLines } from './katex-lines'
30
31 const props = defineProps({
32 ranges: {
33 type: Array as PropType<string[]>,
34 default: () => [],
35 },
36 finally: {
37 type: [String, Number],
38 default: 'last',
39 },
40 startLine: {
41 type: Number,
42 default: 1,
43 },
44 at: {
45 type: [String, Number],
46 default: '+1',
47 },
48 })
49
50 const { $clicksContext: clicks } = useSlideContext()
51 const el = ref<HTMLDivElement>()
52 const id = makeId()
53
54 onUnmounted(() => {
55 clicks!.unregister(id)
56 })
57
58 onMounted(() => {
59 if (!clicks || !props.ranges?.length)
60 return
61
62 const clicksInfo = clicks.calculateSince(props.at, props.ranges.length - 1)
63 clicks.register(id, clicksInfo)
64
65 const index = computed(() => clicksInfo ? Math.max(0, clicks.current - clicksInfo.start + 1) : CLICKS_MAX)
66
67 const finallyRange = computed(() => {
68 return props.finally === 'last' ? props.ranges.at(-1) : props.finally.toString()
69 })
70
71 watchEffect(() => {
72 if (!el.value)
73 return
74
75 let rangeStr = props.ranges[index.value] ?? finallyRange.value
76 const hide = rangeStr === 'hide'
77 el.value.classList.toggle(CLASS_VCLICK_HIDDEN, hide)
78 if (hide)
79 rangeStr = props.ranges[index.value + 1] ?? finallyRange.value
80
81 // This list maps rows from different parents to line them up
82 const lines = collectKatexEquationLines(el.value)
83 if (!lines.length)
84 return
85
86 const startLine = props.startLine
87 const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr)
88 lines.forEach((line, idx) => {
89 const highlighted = highlights.includes(idx + startLine)
90 line.forEach((node) => {
91 node.classList.toggle(CLASS_VCLICK_TARGET, true)
92 node.classList.toggle('highlighted', highlighted)
93 node.classList.toggle('dishonored', !highlighted)
94 })
95 })
96 })
97 })
98 </script>
99
100 <template>
101 <div ref="el" class="slidev-katex-wrapper">
102 <slot />
103 </div>
104 </template>
105
105 lines Plain Text