-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
KaTexBlockWrapper.vue
131 lines (110 loc) · 3.71 KB
/
KaTexBlockWrapper.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<!--
Line highlighting for KaTex blocks/
(auto transformed, you don't need to use this component directly)
Usage:
$$ {1|3|all}
\begin{array}{c}
\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} &
= \frac{4\pi}{c}\vec{\mathbf{j}} \nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
\nabla \cdot \vec{\mathbf{B}} & = 0
\end{array}
$$
Learn more: https://sli.dev/guide/syntax.html#latex-line-highlighting
-->
<script setup lang="ts">
import { computed, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import type { PropType } from 'vue'
import { parseRangeString } from '@slidev/parser'
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET, injectionClicksContext } from '../constants'
import { makeId, safeParseNumber } from '../logic/utils'
const props = defineProps({
ranges: {
type: Array as PropType<string[]>,
default: () => [],
},
finally: {
type: [String, Number],
default: 'last',
},
startLine: {
type: Number,
default: 1,
},
at: {
type: [String, Number],
default: '+1',
},
})
const clicks = inject(injectionClicksContext)?.value
const el = ref<HTMLDivElement>()
onMounted(() => {
if (!clicks || clicks.disabled)
return
const at = props.at
const atNum = safeParseNumber(at)
const isRelative = typeof at === 'string' && '+-'.includes(at[0])
const relativeDelta = isRelative
? atNum + props.ranges.length - 2
: 0
const start = isRelative
? clicks.currentOffset + atNum - 1
: atNum
const end = start + props.ranges.length - 1
// register to the page click map
const id = makeId()
clicks.register(id, { max: end, relativeDelta })
onUnmounted(() => clicks.unregister(id))
const index = computed(() => {
if (clicks.disabled)
return props.ranges.length - 1
return Math.max(0, clicks.current - start)
})
const finallyRange = computed(() => {
return props.finally === 'last' ? props.ranges.at(-1) : props.finally.toString()
})
watchEffect(() => {
if (!el.value)
return
let rangeStr = props.ranges[index.value] ?? finallyRange.value
const hide = rangeStr === 'hide'
el.value.classList.toggle(CLASS_VCLICK_HIDDEN, hide)
if (hide)
rangeStr = props.ranges[index.value + 1] ?? finallyRange.value
// KaTeX equations have col-align-XXX as parent
const equationParents = el.value.querySelectorAll('.mtable > [class*=col-align]')
if (!equationParents)
return
// For each row we extract the individual equation rows
const equationRowsOfEachParent = Array.from(equationParents)
.map(item => Array.from(item.querySelectorAll(':scope > .vlist-t > .vlist-r > .vlist > span > .mord')))
// This list maps rows from different parents to line them up
const lines: Element[][] = []
for (const equationRowParent of equationRowsOfEachParent) {
equationRowParent.forEach((equationRow, idx) => {
if (!equationRow)
return
if (Array.isArray(lines[idx]))
lines[idx].push(equationRow)
else
lines[idx] = [equationRow]
})
}
const startLine = props.startLine
const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr)
lines.forEach((line, idx) => {
const highlighted = highlights.includes(idx + startLine)
line.forEach((node) => {
node.classList.toggle(CLASS_VCLICK_TARGET, true)
node.classList.toggle('highlighted', highlighted)
node.classList.toggle('dishonored', !highlighted)
})
})
})
})
</script>
<template>
<div ref="el" class="slidev-katex-wrapper">
<slot />
</div>
</template>