@slidev/client 0.47.4 → 0.48.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builtin/CodeBlockWrapper.vue +38 -27
- package/builtin/KaTexBlockWrapper.vue +37 -26
- package/builtin/Mermaid.vue +18 -10
- package/builtin/SlidevVideo.vue +4 -7
- package/builtin/VClick.ts +16 -4
- package/builtin/VClickGap.vue +38 -0
- package/builtin/VClicks.ts +44 -47
- package/composables/useClicks.ts +83 -0
- package/composables/useContext.ts +2 -8
- package/composables/useNav.ts +5 -2
- package/constants.ts +2 -5
- package/internals/Editor.vue +1 -1
- package/internals/Presenter.vue +22 -27
- package/internals/PrintSlide.vue +10 -8
- package/internals/PrintSlideClick.vue +7 -16
- package/internals/SlideWrapper.ts +9 -42
- package/internals/SlidesOverview.vue +2 -1
- package/internals/SlidesShow.vue +3 -10
- package/logic/nav.ts +26 -28
- package/logic/note.ts +4 -0
- package/logic/utils.ts +18 -0
- package/modules/context.ts +5 -8
- package/modules/directives.ts +124 -167
- package/package.json +7 -7
- package/routes.ts +2 -1
- package/setup/codemirror.ts +3 -1
- package/setup/root.ts +5 -5
- package/composables/useNavClicks.ts +0 -38
|
@@ -12,18 +12,23 @@ Learn more: https://sli.dev/guide/syntax.html#line-highlighting
|
|
|
12
12
|
-->
|
|
13
13
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
|
-
import { range, remove } from '@antfu/utils'
|
|
16
15
|
import { parseRangeString } from '@slidev/parser/core'
|
|
17
16
|
import { useClipboard } from '@vueuse/core'
|
|
18
|
-
import { computed,
|
|
17
|
+
import { computed, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
18
|
+
import type { PropType } from 'vue'
|
|
19
19
|
import { configs } from '../env'
|
|
20
|
-
import { CLASS_VCLICK_TARGET, injectionClicks, injectionClicksDisabled, injectionClicksElements } from '../constants'
|
|
21
20
|
import { makeId } from '../logic/utils'
|
|
21
|
+
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET, injectionClicksContext } from '../constants'
|
|
22
22
|
|
|
23
23
|
const props = defineProps({
|
|
24
24
|
ranges: {
|
|
25
|
+
type: Array as PropType<string[]>,
|
|
25
26
|
default: () => [],
|
|
26
27
|
},
|
|
28
|
+
finally: {
|
|
29
|
+
type: [String, Number],
|
|
30
|
+
default: 'last',
|
|
31
|
+
},
|
|
27
32
|
startLine: {
|
|
28
33
|
type: Number,
|
|
29
34
|
default: 1,
|
|
@@ -33,8 +38,8 @@ const props = defineProps({
|
|
|
33
38
|
default: configs.lineNumbers,
|
|
34
39
|
},
|
|
35
40
|
at: {
|
|
36
|
-
type: Number,
|
|
37
|
-
default:
|
|
41
|
+
type: [String, Number],
|
|
42
|
+
default: '+1',
|
|
38
43
|
},
|
|
39
44
|
maxHeight: {
|
|
40
45
|
type: String,
|
|
@@ -42,39 +47,47 @@ const props = defineProps({
|
|
|
42
47
|
},
|
|
43
48
|
})
|
|
44
49
|
|
|
45
|
-
const clicks = inject(
|
|
46
|
-
const elements = inject(injectionClicksElements)
|
|
47
|
-
const disabled = inject(injectionClicksDisabled)
|
|
48
|
-
|
|
50
|
+
const clicks = inject(injectionClicksContext)?.value
|
|
49
51
|
const el = ref<HTMLDivElement>()
|
|
50
|
-
const
|
|
52
|
+
const id = makeId()
|
|
53
|
+
|
|
54
|
+
onUnmounted(() => {
|
|
55
|
+
clicks!.unregister(id)
|
|
56
|
+
})
|
|
51
57
|
|
|
52
58
|
onMounted(() => {
|
|
53
|
-
|
|
59
|
+
if (!clicks || clicks.disabled)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
const { start, end, delta } = clicks.resolve(props.at, props.ranges.length - 1)
|
|
63
|
+
clicks.register(id, { max: end, delta })
|
|
64
|
+
|
|
54
65
|
const index = computed(() => {
|
|
55
|
-
if (disabled
|
|
66
|
+
if (clicks.disabled)
|
|
56
67
|
return props.ranges.length - 1
|
|
57
|
-
return Math.
|
|
68
|
+
return Math.max(0, clicks.current - start + 1)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const finallyRange = computed(() => {
|
|
72
|
+
return props.finally === 'last' ? props.ranges.at(-1) : props.finally.toString()
|
|
58
73
|
})
|
|
59
|
-
const rangeStr = computed(() => props.ranges[index.value] || '')
|
|
60
|
-
if (props.ranges.length >= 2 && !disabled?.value) {
|
|
61
|
-
const id = makeId()
|
|
62
|
-
const ids = range(props.ranges.length - 1).map(i => id + i)
|
|
63
|
-
if (elements?.value) {
|
|
64
|
-
elements.value.push(...ids)
|
|
65
|
-
onUnmounted(() => ids.forEach(i => remove(elements.value, i)), vm)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
74
|
|
|
69
75
|
watchEffect(() => {
|
|
70
76
|
if (!el.value)
|
|
71
77
|
return
|
|
78
|
+
|
|
79
|
+
let rangeStr = props.ranges[index.value] ?? finallyRange.value
|
|
80
|
+
const hide = rangeStr === 'hide'
|
|
81
|
+
el.value.classList.toggle(CLASS_VCLICK_HIDDEN, hide)
|
|
82
|
+
if (hide)
|
|
83
|
+
rangeStr = props.ranges[index.value + 1] ?? finallyRange.value
|
|
84
|
+
|
|
72
85
|
const isDuoTone = el.value.querySelector('.shiki-dark')
|
|
73
86
|
const targets = isDuoTone ? Array.from(el.value.querySelectorAll('.shiki')) : [el.value]
|
|
74
87
|
const startLine = props.startLine
|
|
75
88
|
for (const target of targets) {
|
|
76
89
|
const lines = Array.from(target.querySelectorAll('code > .line'))
|
|
77
|
-
const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr
|
|
90
|
+
const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr)
|
|
78
91
|
lines.forEach((line, idx) => {
|
|
79
92
|
const highlighted = highlights.includes(idx + startLine)
|
|
80
93
|
line.classList.toggle(CLASS_VCLICK_TARGET, true)
|
|
@@ -107,11 +120,9 @@ function copyCode() {
|
|
|
107
120
|
|
|
108
121
|
<template>
|
|
109
122
|
<div
|
|
110
|
-
ref="el" class="slidev-code-wrapper relative group"
|
|
111
|
-
:class="{
|
|
123
|
+
ref="el" class="slidev-code-wrapper relative group" :class="{
|
|
112
124
|
'slidev-code-line-numbers': props.lines,
|
|
113
|
-
}"
|
|
114
|
-
:style="{
|
|
125
|
+
}" :style="{
|
|
115
126
|
'max-height': props.maxHeight,
|
|
116
127
|
'overflow-y': props.maxHeight ? 'scroll' : undefined,
|
|
117
128
|
'--start': props.startLine,
|
|
@@ -20,53 +20,66 @@ Learn more: https://sli.dev/guide/syntax.html#latex-line-highlighting
|
|
|
20
20
|
-->
|
|
21
21
|
|
|
22
22
|
<script setup lang="ts">
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
23
|
+
import { computed, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
24
|
+
import type { PropType } from 'vue'
|
|
25
25
|
import { parseRangeString } from '@slidev/parser'
|
|
26
|
-
import {
|
|
26
|
+
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET, injectionClicksContext } from '../constants'
|
|
27
27
|
import { makeId } from '../logic/utils'
|
|
28
28
|
|
|
29
29
|
const props = defineProps({
|
|
30
30
|
ranges: {
|
|
31
|
+
type: Array as PropType<string[]>,
|
|
31
32
|
default: () => [],
|
|
32
33
|
},
|
|
34
|
+
finally: {
|
|
35
|
+
type: [String, Number],
|
|
36
|
+
default: 'last',
|
|
37
|
+
},
|
|
33
38
|
startLine: {
|
|
34
39
|
type: Number,
|
|
35
40
|
default: 1,
|
|
36
41
|
},
|
|
37
42
|
at: {
|
|
38
|
-
type: Number,
|
|
39
|
-
default:
|
|
43
|
+
type: [String, Number],
|
|
44
|
+
default: '+1',
|
|
40
45
|
},
|
|
41
46
|
})
|
|
42
47
|
|
|
43
|
-
const clicks = inject(
|
|
44
|
-
const elements = inject(injectionClicksElements)
|
|
45
|
-
const disabled = inject(injectionClicksDisabled)
|
|
46
|
-
|
|
48
|
+
const clicks = inject(injectionClicksContext)?.value
|
|
47
49
|
const el = ref<HTMLDivElement>()
|
|
48
|
-
const
|
|
50
|
+
const id = makeId()
|
|
51
|
+
|
|
52
|
+
onUnmounted(() => {
|
|
53
|
+
clicks!.unregister(id)
|
|
54
|
+
})
|
|
49
55
|
|
|
50
56
|
onMounted(() => {
|
|
51
|
-
|
|
57
|
+
if (!clicks || clicks.disabled)
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
const { start, end, delta } = clicks.resolve(props.at, props.ranges.length - 1)
|
|
61
|
+
clicks.register(id, { max: end, delta })
|
|
62
|
+
|
|
52
63
|
const index = computed(() => {
|
|
53
|
-
if (disabled
|
|
64
|
+
if (clicks.disabled)
|
|
54
65
|
return props.ranges.length - 1
|
|
55
|
-
return Math.
|
|
66
|
+
return Math.max(0, clicks.current - start + 1)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const finallyRange = computed(() => {
|
|
70
|
+
return props.finally === 'last' ? props.ranges.at(-1) : props.finally.toString()
|
|
56
71
|
})
|
|
57
|
-
const rangeStr = computed(() => props.ranges[index.value] || '')
|
|
58
|
-
if (props.ranges.length >= 2 && !disabled?.value) {
|
|
59
|
-
const id = makeId()
|
|
60
|
-
const ids = range(props.ranges.length - 1).map(i => id + i)
|
|
61
|
-
if (elements?.value) {
|
|
62
|
-
elements.value.push(...ids)
|
|
63
|
-
onUnmounted(() => ids.forEach(i => remove(elements.value, i)), vm)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
72
|
|
|
67
73
|
watchEffect(() => {
|
|
68
74
|
if (!el.value)
|
|
69
75
|
return
|
|
76
|
+
|
|
77
|
+
let rangeStr = props.ranges[index.value] ?? finallyRange.value
|
|
78
|
+
const hide = rangeStr === 'hide'
|
|
79
|
+
el.value.classList.toggle(CLASS_VCLICK_HIDDEN, hide)
|
|
80
|
+
if (hide)
|
|
81
|
+
rangeStr = props.ranges[index.value + 1] ?? finallyRange.value
|
|
82
|
+
|
|
70
83
|
// KaTeX equations have col-align-XXX as parent
|
|
71
84
|
const equationParents = el.value.querySelectorAll('.mtable > [class*=col-align]')
|
|
72
85
|
if (!equationParents)
|
|
@@ -89,7 +102,7 @@ onMounted(() => {
|
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
const startLine = props.startLine
|
|
92
|
-
const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr
|
|
105
|
+
const highlights: number[] = parseRangeString(lines.length + startLine - 1, rangeStr)
|
|
93
106
|
lines.forEach((line, idx) => {
|
|
94
107
|
const highlighted = highlights.includes(idx + startLine)
|
|
95
108
|
line.forEach((node) => {
|
|
@@ -103,9 +116,7 @@ onMounted(() => {
|
|
|
103
116
|
</script>
|
|
104
117
|
|
|
105
118
|
<template>
|
|
106
|
-
<div
|
|
107
|
-
ref="el" class="slidev-katex-wrapper"
|
|
108
|
-
>
|
|
119
|
+
<div ref="el" class="slidev-katex-wrapper">
|
|
109
120
|
<slot />
|
|
110
121
|
</div>
|
|
111
122
|
</template>
|
package/builtin/Mermaid.vue
CHANGED
|
@@ -26,6 +26,7 @@ const props = defineProps<{
|
|
|
26
26
|
|
|
27
27
|
const vm = getCurrentInstance()
|
|
28
28
|
const el = ref<ShadowRoot>()
|
|
29
|
+
const error = ref<string | null>(null)
|
|
29
30
|
const html = ref('')
|
|
30
31
|
|
|
31
32
|
watchEffect(async (onCleanup) => {
|
|
@@ -33,15 +34,21 @@ watchEffect(async (onCleanup) => {
|
|
|
33
34
|
onCleanup(() => {
|
|
34
35
|
disposed = true
|
|
35
36
|
})
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
error.value = null
|
|
38
|
+
try {
|
|
39
|
+
const svg = await renderMermaid(
|
|
40
|
+
props.code || '',
|
|
41
|
+
{
|
|
42
|
+
theme: props.theme || (isDark.value ? 'dark' : undefined),
|
|
43
|
+
...vm!.attrs,
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
if (!disposed)
|
|
47
|
+
html.value = svg
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
error.value = `${e}`
|
|
51
|
+
}
|
|
45
52
|
})
|
|
46
53
|
|
|
47
54
|
const actualHeight = ref<number>()
|
|
@@ -69,5 +76,6 @@ watchEffect(() => {
|
|
|
69
76
|
</script>
|
|
70
77
|
|
|
71
78
|
<template>
|
|
72
|
-
<
|
|
79
|
+
<pre v-if="error" border="1 red rounded" class="pa-3">{{ error }}</pre>
|
|
80
|
+
<ShadowRoot v-else class="mermaid" :inner-html="html" @shadow="el = $event" />
|
|
73
81
|
</template>
|
package/builtin/SlidevVideo.vue
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
3
|
-
|
|
4
|
-
import { injectionClicks, injectionClicksDisabled, injectionClicksElements, injectionRenderContext, injectionRoute, injectionSlidevContext } from '../constants'
|
|
3
|
+
import { injectionClicksContext, injectionRenderContext, injectionRoute, injectionSlidevContext } from '../constants'
|
|
5
4
|
|
|
6
5
|
const props = defineProps<{
|
|
7
6
|
autoPlay?: boolean | 'once' | 'resume' | 'resumeOnce'
|
|
@@ -12,9 +11,7 @@ const props = defineProps<{
|
|
|
12
11
|
const $slidev = inject(injectionSlidevContext)
|
|
13
12
|
const route = inject(injectionRoute)
|
|
14
13
|
const currentContext = inject(injectionRenderContext)
|
|
15
|
-
const clicks = inject(
|
|
16
|
-
const clicksDisabled = inject(injectionClicksDisabled)
|
|
17
|
-
const clicksElements = inject(injectionClicksElements)
|
|
14
|
+
const clicks = inject(injectionClicksContext)?.value
|
|
18
15
|
|
|
19
16
|
const video = ref<HTMLMediaElement>()
|
|
20
17
|
const played = ref(false)
|
|
@@ -27,9 +24,9 @@ const matchRoute = computed(() => {
|
|
|
27
24
|
})
|
|
28
25
|
|
|
29
26
|
const matchClick = computed(() => {
|
|
30
|
-
if (!video.value || currentContext?.value !== 'slide' || clicks?.
|
|
27
|
+
if (!video.value || currentContext?.value !== 'slide' || clicks?.disabled || clicks?.current === undefined)
|
|
31
28
|
return false
|
|
32
|
-
return
|
|
29
|
+
return clicks.map.get(video.value)?.isShown?.value ?? true
|
|
33
30
|
})
|
|
34
31
|
|
|
35
32
|
const matchRouteAndClick = computed(() => matchRoute.value && matchClick.value)
|
package/builtin/VClick.ts
CHANGED
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* Learn more: https://sli.dev/guide/animations.html#click-animations
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import type { PropType, VNode } from 'vue'
|
|
8
|
+
import { Text, defineComponent, h } from 'vue'
|
|
8
9
|
import VClicks from './VClicks'
|
|
9
10
|
|
|
10
11
|
export default defineComponent({
|
|
11
12
|
props: {
|
|
12
13
|
at: {
|
|
13
14
|
type: [Number, String],
|
|
14
|
-
default:
|
|
15
|
+
default: '+1',
|
|
15
16
|
},
|
|
16
17
|
hide: {
|
|
17
18
|
type: Boolean,
|
|
@@ -21,9 +22,13 @@ export default defineComponent({
|
|
|
21
22
|
type: Boolean,
|
|
22
23
|
default: false,
|
|
23
24
|
},
|
|
25
|
+
wrapText: {
|
|
26
|
+
type: Function as PropType<(text: VNode) => VNode>,
|
|
27
|
+
default: (text: VNode) => h('span', text),
|
|
28
|
+
},
|
|
24
29
|
},
|
|
25
30
|
render() {
|
|
26
|
-
return
|
|
31
|
+
return h(
|
|
27
32
|
VClicks,
|
|
28
33
|
{
|
|
29
34
|
every: 99999,
|
|
@@ -31,7 +36,14 @@ export default defineComponent({
|
|
|
31
36
|
hide: this.hide,
|
|
32
37
|
fade: this.fade,
|
|
33
38
|
},
|
|
34
|
-
{
|
|
39
|
+
{
|
|
40
|
+
default: () =>
|
|
41
|
+
this.$slots.default?.().map(v =>
|
|
42
|
+
v.type === Text
|
|
43
|
+
? this.wrapText(v)
|
|
44
|
+
: v,
|
|
45
|
+
),
|
|
46
|
+
},
|
|
35
47
|
)
|
|
36
48
|
},
|
|
37
49
|
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Fragment, inject, onMounted, watchEffect } from 'vue'
|
|
3
|
+
import { injectionClicksContext } from '../constants'
|
|
4
|
+
import { makeId } from '../logic/utils'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
size: {
|
|
8
|
+
type: [String, Number],
|
|
9
|
+
default: 1,
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const clicksRef = inject(injectionClicksContext)
|
|
14
|
+
|
|
15
|
+
onMounted(() => {
|
|
16
|
+
watchEffect((onCleanup) => {
|
|
17
|
+
const clicks = clicksRef?.value
|
|
18
|
+
|
|
19
|
+
if (!clicks || clicks.disabled)
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
let delta = +props.size
|
|
23
|
+
if (Number.isNaN(delta)) {
|
|
24
|
+
console.warn(`[slidev] Invalid size for VClickGap: ${props.size}`)
|
|
25
|
+
delta = 1
|
|
26
|
+
}
|
|
27
|
+
const max = clicks.currentOffset + delta - 1
|
|
28
|
+
|
|
29
|
+
const id = makeId()
|
|
30
|
+
clicks.register(id, { max, delta })
|
|
31
|
+
onCleanup(() => clicks.unregister(id))
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<Fragment />
|
|
38
|
+
</template>
|
package/builtin/VClicks.ts
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { toArray } from '@antfu/utils'
|
|
8
|
-
import type {
|
|
9
|
-
|
|
10
|
-
import {
|
|
8
|
+
import type { VNode, VNodeArrayChildren } from 'vue'
|
|
9
|
+
import { Comment, createVNode, defineComponent, h, isVNode, resolveDirective, withDirectives } from 'vue'
|
|
10
|
+
import { normalizeAtProp } from '../logic/utils'
|
|
11
|
+
import VClickGap from './VClickGap.vue'
|
|
11
12
|
|
|
12
13
|
const listTags = ['ul', 'ol']
|
|
13
14
|
|
|
@@ -18,12 +19,12 @@ export default defineComponent({
|
|
|
18
19
|
default: 1,
|
|
19
20
|
},
|
|
20
21
|
every: {
|
|
21
|
-
type: Number,
|
|
22
|
+
type: [Number, String],
|
|
22
23
|
default: 1,
|
|
23
24
|
},
|
|
24
25
|
at: {
|
|
25
26
|
type: [Number, String],
|
|
26
|
-
default:
|
|
27
|
+
default: '+1',
|
|
27
28
|
},
|
|
28
29
|
hide: {
|
|
29
30
|
type: Boolean,
|
|
@@ -35,13 +36,15 @@ export default defineComponent({
|
|
|
35
36
|
},
|
|
36
37
|
},
|
|
37
38
|
render() {
|
|
39
|
+
const every = +this.every
|
|
40
|
+
const [isRelative, at] = normalizeAtProp(this.at)
|
|
41
|
+
|
|
38
42
|
const click = resolveDirective('click')!
|
|
39
|
-
const after = resolveDirective('after')!
|
|
40
43
|
|
|
41
|
-
const applyDirective = (node: VNode,
|
|
44
|
+
const applyDirective = (node: VNode, value: number | string) => {
|
|
42
45
|
return withDirectives(node, [[
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
click,
|
|
47
|
+
value,
|
|
45
48
|
'',
|
|
46
49
|
{
|
|
47
50
|
hide: this.hide,
|
|
@@ -66,76 +69,70 @@ export default defineComponent({
|
|
|
66
69
|
|
|
67
70
|
defaults = openAllTopLevelSlots(toArray(defaults))
|
|
68
71
|
|
|
69
|
-
const mapSubList = (children: VNodeArrayChildren, depth = 1):
|
|
70
|
-
let idx = 0
|
|
72
|
+
const mapSubList = (children: VNodeArrayChildren, depth = 1): VNodeArrayChildren => {
|
|
71
73
|
const vNodes = openAllTopLevelSlots(children).map((i) => {
|
|
72
74
|
if (!isVNode(i))
|
|
73
75
|
return i
|
|
74
76
|
if (listTags.includes(i.type as string) && Array.isArray(i.children)) {
|
|
75
77
|
// eslint-disable-next-line ts/no-use-before-define
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
return h(i, {}, [vNodes])
|
|
78
|
+
const vNodes = mapChildren(i.children, depth + 1)
|
|
79
|
+
return h(i, {}, vNodes)
|
|
79
80
|
}
|
|
80
81
|
return h(i)
|
|
81
82
|
})
|
|
82
|
-
return
|
|
83
|
+
return vNodes
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
let globalIdx =
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
let globalIdx = 1
|
|
87
|
+
let execIdx = 0
|
|
88
|
+
const mapChildren = (children: VNodeArrayChildren, depth = 1): VNodeArrayChildren => {
|
|
88
89
|
const vNodes = openAllTopLevelSlots(children).map((i) => {
|
|
89
90
|
if (!isVNode(i) || i.type === Comment)
|
|
90
91
|
return i
|
|
91
|
-
|
|
92
|
+
|
|
93
|
+
const thisShowIdx = +at + Math.ceil(globalIdx++ / every) - 1
|
|
94
|
+
|
|
92
95
|
let vNode
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
vNode = h(i, {}, [vNodes])
|
|
97
|
-
childCount = total
|
|
98
|
-
idx += total + 1
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
96
|
+
if (depth < +this.depth && Array.isArray(i.children))
|
|
97
|
+
vNode = h(i, {}, mapSubList(i.children, depth))
|
|
98
|
+
else
|
|
101
99
|
vNode = h(i)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
: (depth - 1 - childCount).toString()
|
|
107
|
-
globalIdx++
|
|
100
|
+
|
|
101
|
+
const delta = thisShowIdx - execIdx
|
|
102
|
+
execIdx = thisShowIdx
|
|
103
|
+
|
|
108
104
|
return applyDirective(
|
|
109
105
|
vNode,
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
isRelative
|
|
107
|
+
? delta >= 0 ? `+${delta}` : `${delta}`
|
|
108
|
+
: thisShowIdx,
|
|
112
109
|
)
|
|
113
110
|
})
|
|
114
|
-
return
|
|
111
|
+
return vNodes
|
|
115
112
|
}
|
|
116
113
|
|
|
114
|
+
const lastGap = () => createVNode(VClickGap, {
|
|
115
|
+
size: +at + Math.ceil((globalIdx - 1) / every) - 1 - execIdx,
|
|
116
|
+
})
|
|
117
|
+
|
|
117
118
|
// handle ul, ol list
|
|
118
119
|
if (defaults.length === 1 && listTags.includes(defaults[0].type as string) && Array.isArray(defaults[0].children))
|
|
119
|
-
return h(defaults[0], {}, [mapChildren(defaults[0].children)
|
|
120
|
+
return h(defaults[0], {}, [mapChildren(defaults[0].children), lastGap()])
|
|
120
121
|
|
|
121
122
|
if (defaults.length === 1 && defaults[0].type as string === 'table') {
|
|
122
123
|
const tableNode = defaults[0]
|
|
123
124
|
if (Array.isArray(tableNode.children)) {
|
|
124
125
|
return h(tableNode, {}, tableNode.children.map((i) => {
|
|
125
|
-
if (!isVNode(i))
|
|
126
|
+
if (!isVNode(i))
|
|
126
127
|
return i
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
else
|
|
133
|
-
return h(i)
|
|
134
|
-
}
|
|
128
|
+
else if (i.type === 'tbody' && Array.isArray(i.children))
|
|
129
|
+
return h(i, {}, [mapChildren(i.children), lastGap()])
|
|
130
|
+
else
|
|
131
|
+
return h(i)
|
|
135
132
|
}))
|
|
136
133
|
}
|
|
137
134
|
}
|
|
138
135
|
|
|
139
|
-
return mapChildren(defaults)[0]
|
|
136
|
+
return [mapChildren(defaults)[0], lastGap()]
|
|
140
137
|
},
|
|
141
138
|
})
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { sum } from '@antfu/utils'
|
|
2
|
+
import type { ClicksContext } from '@slidev/types'
|
|
3
|
+
import type { Ref } from 'vue'
|
|
4
|
+
import { ref, shallowReactive } from 'vue'
|
|
5
|
+
import type { RouteRecordRaw } from 'vue-router'
|
|
6
|
+
import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
|
|
7
|
+
import { normalizeAtProp } from '../logic/utils'
|
|
8
|
+
|
|
9
|
+
function useClicksContextBase(route: RouteRecordRaw | undefined, getCurrent: () => number): ClicksContext {
|
|
10
|
+
const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
|
|
11
|
+
const map: ClicksContext['map'] = shallowReactive(new Map())
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
get disabled() {
|
|
15
|
+
return isPrintMode.value && !isPrintWithClicks.value
|
|
16
|
+
},
|
|
17
|
+
get current() {
|
|
18
|
+
return getCurrent()
|
|
19
|
+
},
|
|
20
|
+
relativeOffsets,
|
|
21
|
+
map,
|
|
22
|
+
resolve(at, size = 1) {
|
|
23
|
+
const [isRelative, value] = normalizeAtProp(at)
|
|
24
|
+
if (isRelative) {
|
|
25
|
+
const offset = this.currentOffset
|
|
26
|
+
return {
|
|
27
|
+
start: offset + value,
|
|
28
|
+
end: offset + value + size - 1,
|
|
29
|
+
delta: value + size - 1,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return {
|
|
34
|
+
start: value,
|
|
35
|
+
end: value + size - 1,
|
|
36
|
+
delta: 0,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
register(el, resolved) {
|
|
41
|
+
relativeOffsets.set(el, resolved.delta)
|
|
42
|
+
map.set(el, resolved)
|
|
43
|
+
},
|
|
44
|
+
unregister(el) {
|
|
45
|
+
relativeOffsets.delete(el)
|
|
46
|
+
map.delete(el)
|
|
47
|
+
},
|
|
48
|
+
get currentOffset() {
|
|
49
|
+
// eslint-disable-next-line no-unused-expressions
|
|
50
|
+
routeForceRefresh.value
|
|
51
|
+
return sum(...relativeOffsets.values())
|
|
52
|
+
},
|
|
53
|
+
get total() {
|
|
54
|
+
// eslint-disable-next-line no-unused-expressions
|
|
55
|
+
routeForceRefresh.value
|
|
56
|
+
return route?.meta?.clicks
|
|
57
|
+
?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksContext {
|
|
63
|
+
if (route?.meta?.__clicksContext)
|
|
64
|
+
return route.meta.__clicksContext
|
|
65
|
+
const thisPath = +(route?.path ?? 99999)
|
|
66
|
+
const context = useClicksContextBase(route, () => {
|
|
67
|
+
const currentPath = +(currentRoute.value?.path ?? 99999)
|
|
68
|
+
if (currentPath === thisPath)
|
|
69
|
+
return queryClicks.value
|
|
70
|
+
else if (currentPath > thisPath)
|
|
71
|
+
return 99999
|
|
72
|
+
else
|
|
73
|
+
return 0
|
|
74
|
+
})
|
|
75
|
+
if (route?.meta)
|
|
76
|
+
route.meta.__clicksContext = context
|
|
77
|
+
return context
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function useFixedClicks(route: RouteRecordRaw | undefined, currentInit = 0): [Ref<number>, ClicksContext] {
|
|
81
|
+
const current = ref(currentInit)
|
|
82
|
+
return [current, useClicksContextBase(route, () => current.value)]
|
|
83
|
+
}
|
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
import type { ComputedRef
|
|
1
|
+
import type { ComputedRef } from 'vue'
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
|
4
4
|
import type { SlidevContext } from '../modules/context'
|
|
5
5
|
import { configs } from '../env'
|
|
6
6
|
import { useNav } from './useNav'
|
|
7
|
-
import { useNavClicks } from './useNavClicks'
|
|
8
7
|
|
|
9
8
|
export function useContext(
|
|
10
9
|
route: ComputedRef<RouteLocationNormalizedLoaded>,
|
|
11
|
-
clicks: WritableComputedRef<number>,
|
|
12
10
|
): SlidevContext {
|
|
13
11
|
const nav = useNav(route)
|
|
14
|
-
const navClicks = useNavClicks(clicks, nav.currentRoute, nav.currentPage)
|
|
15
12
|
return {
|
|
16
|
-
nav
|
|
17
|
-
...nav,
|
|
18
|
-
...navClicks,
|
|
19
|
-
},
|
|
13
|
+
nav,
|
|
20
14
|
configs,
|
|
21
15
|
themeConfigs: computed(() => configs.themeConfig),
|
|
22
16
|
}
|