@slidev/client 0.47.5 → 0.48.0-beta.1

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.
@@ -2,7 +2,7 @@
2
2
  import { useHead } from '@unhead/vue'
3
3
  import { computed, onMounted, reactive, ref, shallowRef, watch } from 'vue'
4
4
  import { useMouse, useWindowFocus } from '@vueuse/core'
5
- import { clicks, clicksTotal, currentPage, currentRoute, hasNext, nextRoute, total, useSwipeControls } from '../logic/nav'
5
+ import { clicksContext, currentPage, currentRoute, hasNext, nextRoute, queryClicks, rawRoutes, total, useSwipeControls } from '../logic/nav'
6
6
  import { decreasePresenterFontSize, increasePresenterFontSize, presenterLayout, presenterNotesFontSize, showEditor, showOverview, showPresenterCursor } from '../state'
7
7
  import { configs, themeVars } from '../env'
8
8
  import { sharedState } from '../state/shared'
@@ -10,6 +10,7 @@ import { registerShortcuts } from '../logic/shortcuts'
10
10
  import { getSlideClass } from '../utils'
11
11
  import { useTimer } from '../logic/utils'
12
12
  import { isDrawing } from '../logic/drawings'
13
+ import { useFixedClicks } from '../composables/useClicks'
13
14
  import SlideContainer from './SlideContainer.vue'
14
15
  import NavControls from './NavControls.vue'
15
16
  import SlidesOverview from './SlidesOverview.vue'
@@ -19,7 +20,7 @@ import Goto from './Goto.vue'
19
20
  import SlidesShow from './SlidesShow.vue'
20
21
  import SlideWrapper from './SlideWrapper'
21
22
  import DrawingControls from './DrawingControls.vue'
22
- import HiddenText from './HiddenText.vue'
23
+ import IconButton from './IconButton.vue'
23
24
 
24
25
  const main = ref<HTMLDivElement>()
25
26
 
@@ -35,26 +36,21 @@ const notesEditing = ref(false)
35
36
 
36
37
  const { timer, resetTimer } = useTimer()
37
38
 
38
- const nextTabElements = ref([])
39
- const nextSlide = computed(() => {
40
- if (clicks.value < clicksTotal.value) {
41
- return {
42
- route: currentRoute.value,
43
- clicks: clicks.value + 1,
44
- }
45
- }
46
- else {
47
- if (hasNext.value) {
48
- return {
49
- route: nextRoute.value,
50
- clicks: 0,
51
- }
52
- }
53
- else {
54
- return null
55
- }
56
- }
39
+ const clicksCtxMap = rawRoutes.map(route => useFixedClicks(route))
40
+ const nextFrame = computed(() => {
41
+ if (clicksContext.value.current < clicksContext.value.total)
42
+ return [currentRoute.value!, clicksContext.value.current + 1] as const
43
+ else if (hasNext.value)
44
+ return [nextRoute.value!, 0] as const
45
+ else
46
+ return null
47
+ })
48
+ const nextFrameClicksCtx = computed(() => {
49
+ return nextFrame.value && clicksCtxMap[+nextFrame.value[0].path - 1]
57
50
  })
51
+ watch([currentRoute, queryClicks], () => {
52
+ nextFrameClicksCtx.value && (nextFrameClicksCtx.value[0].value = nextFrame.value![1])
53
+ }, { immediate: true })
58
54
 
59
55
  const Editor = shallowRef<any>()
60
56
  if (__DEV__ && __SLIDEV_FEATURE_EDITOR__)
@@ -120,17 +116,16 @@ onMounted(() => {
120
116
  </div>
121
117
  <div class="relative grid-section next flex flex-col p-2 lg:p-4" :style="themeVars">
122
118
  <SlideContainer
123
- v-if="nextSlide"
119
+ v-if="nextFrame && nextFrameClicksCtx"
124
120
  key="next"
125
121
  class="h-full w-full"
126
122
  >
127
123
  <SlideWrapper
128
- :is="nextSlide.route?.component as any"
129
- v-model:clicks-elements="nextTabElements"
130
- :clicks="nextSlide.clicks"
131
- :clicks-disabled="false"
132
- :class="getSlideClass(nextSlide.route)"
133
- :route="nextSlide.route"
124
+ :is="nextFrame[0].component as any"
125
+ :key="nextFrame[0].path"
126
+ :clicks-context="nextFrameClicksCtx[1]"
127
+ :class="getSlideClass(nextFrame[0])"
128
+ :route="nextFrame[0]"
134
129
  render-context="previewNext"
135
130
  />
136
131
  </SlideContainer>
@@ -155,21 +150,19 @@ onMounted(() => {
155
150
  :style="{ fontSize: `${presenterNotesFontSize}em` }"
156
151
  />
157
152
  <div class="border-t border-main py-1 px-2 text-sm">
158
- <button class="slidev-icon-btn" @click="increasePresenterFontSize">
159
- <HiddenText text="Increase font size" />
153
+ <IconButton title="Increase font size" @click="increasePresenterFontSize">
160
154
  <carbon:zoom-in />
161
- </button>
162
- <button class="slidev-icon-btn" @click="decreasePresenterFontSize">
163
- <HiddenText text="Decrease font size" />
155
+ </IconButton>
156
+ <IconButton title="Decrease font size" @click="decreasePresenterFontSize">
164
157
  <carbon:zoom-out />
165
- </button>
166
- <button
158
+ </IconButton>
159
+ <IconButton
167
160
  v-if="__DEV__"
168
- class="slidev-icon-btn" @click="notesEditing = !notesEditing"
161
+ title="Edit Notes"
162
+ @click="notesEditing = !notesEditing"
169
163
  >
170
- <HiddenText text="Edit Notes" />
171
164
  <carbon:edit />
172
- </button>
165
+ </IconButton>
173
166
  </div>
174
167
  </div>
175
168
  <div class="grid-section bottom">
@@ -1,22 +1,24 @@
1
1
  <script setup lang="ts">
2
2
  import type { RouteRecordRaw } from 'vue-router'
3
- import { computed, ref } from 'vue'
3
+ import { computed } from 'vue'
4
4
  import { useNav } from '../composables/useNav'
5
- import { isClicksDisabled } from '../logic/nav'
5
+ import { useFixedClicks } from '../composables/useClicks'
6
6
  import PrintSlideClick from './PrintSlideClick.vue'
7
7
 
8
8
  const props = defineProps<{ route: RouteRecordRaw }>()
9
9
 
10
- const clicksElements = ref(props.route.meta?.__clicksElements || [])
11
- const clicks = computed(() => props.route.meta?.clicks ?? clicksElements.value.length)
12
-
13
10
  const route = computed(() => props.route)
14
11
  const nav = useNav(route)
12
+ const clicks0 = useFixedClicks(route.value, 0)[1]
15
13
  </script>
16
14
 
17
15
  <template>
18
- <PrintSlideClick v-model:clicks-elements="clicksElements" :clicks="0" :nav="nav" :route="route" />
19
- <template v-if="!isClicksDisabled">
20
- <PrintSlideClick v-for="i of clicks" :key="i" :clicks="i" :nav="nav" :route="route" />
16
+ <PrintSlideClick
17
+ :clicks-context="clicks0"
18
+ :nav="nav"
19
+ :route="route"
20
+ />
21
+ <template v-if="!clicks0.disabled">
22
+ <PrintSlideClick v-for="i of clicks0.total" :key="i" :clicks-context="useFixedClicks(route, i)[1]" :nav="nav" :route="route" />
21
23
  </template>
22
24
  </template>
@@ -1,10 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import type { RouteRecordRaw } from 'vue-router'
3
3
  import { computed, provide, reactive, shallowRef } from 'vue'
4
- import { useVModel } from '@vueuse/core'
5
- import { useNavClicks } from '../composables/useNavClicks'
4
+ import type { ClicksContext } from '@slidev/types'
6
5
  import { injectionSlidevContext } from '../constants'
7
- import { isClicksDisabled } from '../logic/nav'
8
6
  import { configs, slideHeight, slideWidth } from '../env'
9
7
  import { getSlideClass } from '../utils'
10
8
  import type { SlidevContextNav } from '../modules/context'
@@ -17,16 +15,11 @@ import GlobalTop from '/@slidev/global-components/top'
17
15
  import GlobalBottom from '/@slidev/global-components/bottom'
18
16
 
19
17
  const props = defineProps<{
20
- clicks: number
21
- clicksElements?: HTMLElement[]
18
+ clicksContext: ClicksContext
22
19
  nav: SlidevContextNav
23
20
  route: RouteRecordRaw
24
21
  }>()
25
22
 
26
- const emit = defineEmits(['update:clicksElements'])
27
-
28
- const clicksElements = useVModel(props, 'clicksElements', emit)
29
-
30
23
  const style = computed(() => ({
31
24
  height: `${slideHeight}px`,
32
25
  width: `${slideWidth}px`,
@@ -36,12 +29,12 @@ const DrawingPreview = shallowRef<any>()
36
29
  if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
37
30
  import('./DrawingPreview.vue').then(v => (DrawingPreview.value = v.default))
38
31
 
39
- const clicks = computed(() => props.clicks)
40
- const navClicks = useNavClicks(clicks, props.nav.currentRoute, props.nav.currentPage)
41
- const id = computed(() => `${props.route.path.toString().padStart(3, '0')}-${(clicks.value + 1).toString().padStart(2, '0')}`)
32
+ const id = computed(() =>
33
+ `${props.route.path.toString().padStart(3, '0')}-${(props.nav.clicks.value + 1).toString().padStart(2, '0')}`,
34
+ )
42
35
 
43
36
  provide(injectionSlidevContext, reactive({
44
- nav: { ...props.nav, ...navClicks },
37
+ nav: props.nav,
45
38
  configs,
46
39
  themeConfigs: computed(() => configs.themeConfig),
47
40
  }))
@@ -53,9 +46,7 @@ provide(injectionSlidevContext, reactive({
53
46
 
54
47
  <SlideWrapper
55
48
  :is="route?.component!"
56
- v-model:clicks-elements="clicksElements"
57
- :clicks="isClicksDisabled ? undefined : clicks"
58
- :clicks-disabled="isClicksDisabled"
49
+ :clicks-context="clicksContext"
59
50
  :class="getSlideClass(route)"
60
51
  :route="route"
61
52
  />
@@ -5,7 +5,7 @@ import { recorder } from '../logic/recording'
5
5
  import { currentCamera, showRecordingDialog } from '../state'
6
6
  import DevicesList from './DevicesList.vue'
7
7
  import MenuButton from './MenuButton.vue'
8
- import HiddenText from './HiddenText.vue'
8
+ import IconButton from './IconButton.vue'
9
9
 
10
10
  const {
11
11
  recording,
@@ -34,33 +34,29 @@ onMounted(() => {
34
34
  </script>
35
35
 
36
36
  <template>
37
- <button
37
+ <IconButton
38
38
  v-if="currentCamera !== 'none'"
39
- class="slidev-icon-btn <md:hidden"
39
+ class="<md:hidden"
40
40
  :class="{ 'text-green-500': Boolean(showAvatar && streamCamera) }"
41
- title="Show camera view"
41
+ title="Toggle camera view"
42
42
  @click="toggleAvatar"
43
43
  >
44
- <HiddenText text="Toggle camera view" />
45
44
  <carbon:user-avatar />
46
- </button>
45
+ </IconButton>
47
46
 
48
- <button
49
- class="slidev-icon-btn"
47
+ <IconButton
50
48
  :class="{ 'text-red-500': recording }"
51
- title="Recording"
49
+ :title="recording ? 'Stop record video' : 'Record video'"
52
50
  @click="toggleRecording"
53
51
  >
54
- <HiddenText :text="recording ? 'Stop record video' : 'Record video'" />
55
52
  <carbon:stop-outline v-if="recording" />
56
53
  <carbon:video v-else />
57
- </button>
54
+ </IconButton>
58
55
  <MenuButton :disabled="recording">
59
56
  <template #button>
60
- <button class="slidev-icon-btn h-full !text-sm !px-0">
61
- <HiddenText text="Select recording device" />
57
+ <IconButton title="Select recording device" class="h-full !text-sm !px-0">
62
58
  <carbon:chevron-up class="opacity-50" />
63
- </button>
59
+ </IconButton>
64
60
  </template>
65
61
  <template #menu>
66
62
  <DevicesList />
@@ -1,26 +1,14 @@
1
- import { useVModel } from '@vueuse/core'
2
- import { computed, defineComponent, h, provide, ref, toRef } from 'vue'
3
- import type { RenderContext } from '@slidev/types'
4
- import { injectionActive, injectionClicks, injectionClicksDisabled, injectionClicksElements, injectionCurrentPage, injectionOrderMap, injectionRenderContext, injectionRoute } from '../constants'
1
+ import { defineComponent, h, provide, ref, toRef } from 'vue'
2
+ import type { PropType } from 'vue'
3
+ import type { ClicksContext, RenderContext } from '@slidev/types'
4
+ import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute } from '../constants'
5
5
 
6
6
  export default defineComponent({
7
7
  name: 'SlideWrapper',
8
8
  props: {
9
- clicks: {
10
- type: [Number, String],
11
- default: 0,
12
- },
13
- clicksElements: {
14
- type: Array,
15
- default: () => [] as Element[],
16
- },
17
- clicksOrderMap: {
18
- type: Map,
19
- default: () => new Map<number, HTMLElement[]>(),
20
- },
21
- clicksDisabled: {
22
- type: Boolean,
23
- default: false,
9
+ clicksContext: {
10
+ type: Object as PropType<ClicksContext>,
11
+ required: true,
24
12
  },
25
13
  renderContext: {
26
14
  type: String,
@@ -39,33 +27,12 @@ export default defineComponent({
39
27
  default: undefined,
40
28
  },
41
29
  },
42
- setup(props, { emit }) {
43
- const clicks = useVModel(props, 'clicks', emit)
44
- const clicksElements = useVModel(props, 'clicksElements', emit)
45
- const clicksDisabled = useVModel(props, 'clicksDisabled', emit)
46
- const clicksOrderMap = useVModel(props, 'clicksOrderMap', emit)
47
-
48
- clicksElements.value.length = 0
49
-
50
- const clicksWithDisable = computed({
51
- get() {
52
- if (clicksDisabled.value)
53
- return 9999999
54
- return +clicks.value
55
- },
56
- set(value) {
57
- clicks.value = value
58
- },
59
- })
60
-
30
+ setup(props) {
61
31
  provide(injectionRoute, props.route as any)
62
32
  provide(injectionCurrentPage, ref(+props.route?.path))
63
33
  provide(injectionRenderContext, ref(props.renderContext as RenderContext))
64
34
  provide(injectionActive, toRef(props, 'active'))
65
- provide(injectionClicks, clicksWithDisable)
66
- provide(injectionClicksDisabled, clicksDisabled)
67
- provide(injectionClicksElements, clicksElements as any)
68
- provide(injectionOrderMap, clicksOrderMap as any)
35
+ provide(injectionClicksContext, toRef(props, 'clicksContext'))
69
36
  },
70
37
  render() {
71
38
  if (this.$props.is)
@@ -5,11 +5,12 @@ import { themeVars } from '../env'
5
5
  import { breakpoints, showOverview, windowSize } from '../state'
6
6
  import { currentPage, go as goSlide, rawRoutes } from '../logic/nav'
7
7
  import { currentOverviewPage, overviewRowCount } from '../logic/overview'
8
+ import { useFixedClicks } from '../composables/useClicks'
8
9
  import { getSlideClass } from '../utils'
9
10
  import SlideContainer from './SlideContainer.vue'
10
11
  import SlideWrapper from './SlideWrapper'
11
12
  import DrawingPreview from './DrawingPreview.vue'
12
- import HiddenText from './HiddenText.vue'
13
+ import IconButton from './IconButton.vue'
13
14
 
14
15
  const props = defineProps<{ modelValue: boolean }>()
15
16
 
@@ -138,7 +139,7 @@ watchEffect(() => {
138
139
  <SlideWrapper
139
140
  :is="route.component"
140
141
  v-if="route?.component"
141
- :clicks-disabled="true"
142
+ :clicks-context="useFixedClicks(route, 99999)[1]"
142
143
  :class="getSlideClass(route)"
143
144
  :route="route"
144
145
  render-context="overview"
@@ -162,8 +163,7 @@ watchEffect(() => {
162
163
  </div>
163
164
  </div>
164
165
  </Transition>
165
- <button v-if="value" class="fixed text-2xl top-4 right-4 slidev-icon-btn text-gray-400" @click="close">
166
- <HiddenText text="Close" />
166
+ <IconButton v-if="value" title="Close" class="fixed text-2xl top-4 right-4 text-gray-400" @click="close">
167
167
  <carbon:close />
168
- </button>
168
+ </IconButton>
169
169
  </template>
@@ -1,9 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { TransitionGroup, computed, shallowRef, watch } from 'vue'
3
- import { clicks, currentRoute, isPresenter, nextRoute, rawRoutes, router, transition } from '../logic/nav'
3
+ import { currentRoute, isPresenter, nextRoute, rawRoutes, transition } from '../logic/nav'
4
4
  import { getSlideClass } from '../utils'
5
5
  import { useViewTransition } from '../composables/useViewTransition'
6
6
  import { skipTransition } from '../composables/hmr'
7
+ import { usePrimaryClicks } from '../composables/useClicks'
7
8
  import SlideWrapper from './SlideWrapper'
8
9
 
9
10
  // @ts-expect-error virtual module
@@ -27,12 +28,6 @@ watch(currentRoute, () => {
27
28
 
28
29
  const hasViewTransition = useViewTransition()
29
30
 
30
- // preserve the clicks count for previous slide to avoid flash on transition
31
- let previousClicks: [string | undefined, number] = [] as any
32
- router.beforeEach(() => {
33
- previousClicks = [currentRoute.value?.path, clicks.value]
34
- })
35
-
36
31
  const DrawingLayer = shallowRef<any>()
37
32
  if (__SLIDEV_FEATURE_DRAWINGS__ || __SLIDEV_FEATURE_DRAWINGS_PERSIST__)
38
33
  import('./DrawingLayer.vue').then(v => DrawingLayer.value = v.default)
@@ -62,9 +57,7 @@ function onAfterLeave() {
62
57
  <SlideWrapper
63
58
  :is="route?.component as any"
64
59
  v-show="route === currentRoute"
65
- :clicks="route === currentRoute ? clicks : route.path === previousClicks[0] ? previousClicks[1] : 0"
66
- :clicks-elements="route.meta?.__clicksElements || []"
67
- :clicks-disabled="false"
60
+ :clicks-context="usePrimaryClicks(route)"
68
61
  :class="getSlideClass(route)"
69
62
  :route="route"
70
63
  :render-context="renderContext"
package/logic/nav.ts CHANGED
@@ -6,19 +6,21 @@ import { timestamp, usePointerSwipe } from '@vueuse/core'
6
6
  import { rawRoutes, router } from '../routes'
7
7
  import { configs } from '../env'
8
8
  import { skipTransition } from '../composables/hmr'
9
+ import { usePrimaryClicks } from '../composables/useClicks'
9
10
  import { useRouteQuery } from './route'
10
11
  import { isDrawing } from './drawings'
11
12
 
12
13
  export { rawRoutes, router }
13
14
 
14
15
  // force update collected elements when the route is fully resolved
15
- const routeForceRefresh = ref(0)
16
+ export const routeForceRefresh = ref(0)
16
17
  nextTick(() => {
17
18
  router.afterEach(async () => {
18
19
  await nextTick()
19
20
  routeForceRefresh.value += 1
20
21
  })
21
22
  })
23
+
22
24
  export const navDirection = ref(0)
23
25
 
24
26
  export const route = computed(() => router.currentRoute.value)
@@ -28,11 +30,25 @@ export const isPrintWithClicks = computed(() => route.value.query.print === 'cli
28
30
  export const isEmbedded = computed(() => route.value.query.embedded !== undefined)
29
31
  export const isPresenter = computed(() => route.value.path.startsWith('/presenter'))
30
32
  export const isNotesViewer = computed(() => route.value.path.startsWith('/notes'))
31
- export const isClicksDisabled = computed(() => isPrintMode.value && !isPrintWithClicks.value)
32
33
  export const presenterPassword = computed(() => route.value.query.password)
33
34
  export const showPresenter = computed(() => !isPresenter.value && (!configs.remote || presenterPassword.value === configs.remote))
34
35
 
35
- export const queryClicks = useRouteQuery('clicks', '0')
36
+ const queryClicksRaw = useRouteQuery('clicks', '0')
37
+ export const queryClicks = computed({
38
+ get() {
39
+ // eslint-disable-next-line ts/no-use-before-define
40
+ if (clicksContext.value.disabled)
41
+ return 99999
42
+ let v = +(queryClicksRaw.value || 0)
43
+ if (Number.isNaN(v))
44
+ v = 0
45
+ return v
46
+ },
47
+ set(v) {
48
+ queryClicksRaw.value = v.toString()
49
+ },
50
+ })
51
+
36
52
  export const total = computed(() => rawRoutes.length)
37
53
  export const path = computed(() => route.value.path)
38
54
 
@@ -45,27 +61,9 @@ export const currentLayout = computed(() => currentRoute.value?.meta?.layout ||
45
61
  export const nextRoute = computed(() => rawRoutes.find(i => i.path === `${Math.min(rawRoutes.length, currentPage.value + 1)}`))
46
62
  export const prevRoute = computed(() => rawRoutes.find(i => i.path === `${Math.max(1, currentPage.value - 1)}`))
47
63
 
48
- export const clicksElements = computed<HTMLElement[]>(() => {
49
- // eslint-disable-next-line no-unused-expressions
50
- routeForceRefresh.value
51
- return currentRoute.value?.meta?.__clicksElements || []
52
- })
53
-
54
- export const clicks = computed<number>({
55
- get() {
56
- if (isClicksDisabled.value)
57
- return 99999
58
- let clicks = +(queryClicks.value || 0)
59
- if (Number.isNaN(clicks))
60
- clicks = 0
61
- return clicks
62
- },
63
- set(v) {
64
- queryClicks.value = v.toString()
65
- },
66
- })
67
-
68
- export const clicksTotal = computed(() => +(currentRoute.value?.meta?.clicks ?? clicksElements.value.length))
64
+ export const clicksContext = computed(() => usePrimaryClicks(currentRoute.value))
65
+ export const clicks = computed(() => clicksContext.value.current)
66
+ export const clicksTotal = computed(() => clicksContext.value.total)
69
67
 
70
68
  export const hasNext = computed(() => currentPage.value < rawRoutes.length || clicks.value < clicksTotal.value)
71
69
  export const hasPrev = computed(() => currentPage.value > 1 || clicks.value > 0)
@@ -85,27 +83,27 @@ watch(currentRoute, (next, prev) => {
85
83
  navDirection.value = Number(next?.path) - Number(prev?.path)
86
84
  })
87
85
 
88
- export function next() {
89
- if (clicksTotal.value <= clicks.value)
90
- nextSlide()
86
+ export async function next() {
87
+ if (clicksTotal.value <= queryClicks.value)
88
+ await nextSlide()
91
89
  else
92
- clicks.value += 1
90
+ queryClicks.value += 1
93
91
  }
94
92
 
95
93
  export async function prev() {
96
- if (clicks.value <= 0)
94
+ if (queryClicks.value <= 0)
97
95
  await prevSlide()
98
96
  else
99
- clicks.value -= 1
97
+ queryClicks.value -= 1
100
98
  }
101
99
 
102
100
  export function getPath(no: number | string) {
103
101
  return isPresenter.value ? `/presenter/${no}` : `/${no}`
104
102
  }
105
103
 
106
- export function nextSlide() {
107
- const next = Math.min(rawRoutes.length, currentPage.value + 1)
108
- return go(next)
104
+ export async function nextSlide() {
105
+ if (currentPage.value < rawRoutes.length)
106
+ await go(currentPage.value + 1)
109
107
  }
110
108
 
111
109
  export async function prevSlide(lastClicks = true) {
package/logic/utils.ts CHANGED
@@ -30,3 +30,21 @@ export function makeId(length = 5) {
30
30
  result.push(characters.charAt(Math.floor(Math.random() * charactersLength)))
31
31
  return result.join('')
32
32
  }
33
+
34
+ /**
35
+ * '+3' => '+3'
36
+ * '-3' => '-3'
37
+ * '3' => 3
38
+ * 3 => 3
39
+ */
40
+ export function normalizeAtProp(at: string | number = '+1'): [isRelative: boolean, value: number] {
41
+ let n = +at
42
+ if (Number.isNaN(n)) {
43
+ console.warn('[slidev] Invalid click position:', at)
44
+ n = 0
45
+ }
46
+ return [
47
+ typeof at === 'string' && '+-'.includes(at[0]),
48
+ n,
49
+ ]
50
+ }
@@ -4,21 +4,19 @@ import type { RouteLocationNormalizedLoaded, RouteRecordRaw } from 'vue-router'
4
4
  import type { ComputedRef } from '@vue/reactivity'
5
5
  import type { configs } from '../env'
6
6
  import * as nav from '../logic/nav'
7
- import { clicks, route } from '../logic/nav'
7
+ import { route } from '../logic/nav'
8
8
  import { isDark } from '../logic/dark'
9
- import { injectionClicks, injectionCurrentPage, injectionSlidevContext } from '../constants'
9
+ import { injectionCurrentPage, injectionSlidevContext } from '../constants'
10
10
  import { useContext } from '../composables/useContext'
11
11
 
12
- export type SlidevContextNavKey = 'path' | 'total' | 'currentPage' | 'currentPath' | 'currentRoute' | 'currentSlideId' | 'currentLayout' | 'nextRoute' | 'rawTree' | 'treeWithActiveStatuses' | 'tree' | 'downloadPDF' | 'next' | 'nextSlide' | 'openInEditor' | 'prev' | 'prevSlide' | 'rawRoutes' | 'go'
13
- export type SlidevContextNavClicksKey = 'clicks' | 'clicksElements' | 'clicksTotal' | 'hasNext' | 'hasPrev'
12
+ export type SlidevContextNavKey = 'path' | 'total' | 'clicksContext' | 'clicks' | 'clicksTotal' | 'currentPage' | 'currentPath' | 'currentRoute' | 'currentSlideId' | 'currentLayout' | 'nextRoute' | 'rawTree' | 'treeWithActiveStatuses' | 'tree' | 'downloadPDF' | 'next' | 'nextSlide' | 'openInEditor' | 'prev' | 'prevSlide' | 'rawRoutes' | 'go'
14
13
 
15
14
  export interface SlidevContextNav extends Pick<typeof nav, SlidevContextNavKey> {
16
15
  route: ComputedRef<RouteRecordRaw | RouteLocationNormalizedLoaded>
17
16
  }
18
- export type SlidevContextNavClicks = Pick<typeof nav, SlidevContextNavClicksKey>
19
17
 
20
18
  export interface SlidevContext {
21
- nav: SlidevContextNav & SlidevContextNavClicks
19
+ nav: SlidevContextNav
22
20
  configs: typeof configs
23
21
  themeConfigs: ComputedRef<typeof configs['themeConfig']>
24
22
  }
@@ -26,10 +24,9 @@ export interface SlidevContext {
26
24
  export default function createSlidevContext() {
27
25
  return {
28
26
  install(app: App) {
29
- const context = reactive(useContext(route, clicks))
27
+ const context = reactive(useContext(route))
30
28
  app.provide(injectionSlidevContext, context)
31
29
  app.provide(injectionCurrentPage, computed(() => context.nav.currentPage))
32
- app.provide(injectionClicks, computed(() => context.nav.clicks))
33
30
 
34
31
  // allows controls from postMessages
35
32
  if (__DEV__) {