@slidev/client 0.48.0-beta.0 → 0.48.0-beta.10
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 +4 -3
- package/builtin/KaTexBlockWrapper.vue +4 -3
- package/builtin/RenderWhen.vue +3 -3
- package/builtin/SlideCurrentNo.vue +2 -3
- package/builtin/SlidesTotal.vue +3 -4
- package/builtin/SlidevVideo.vue +8 -6
- package/builtin/Toc.vue +3 -3
- package/builtin/Tweet.vue +4 -15
- package/builtin/VClickGap.vue +3 -5
- package/builtin/VClicks.ts +9 -9
- package/composables/useClicks.ts +16 -13
- package/composables/useTweetScript.ts +17 -0
- package/constants.ts +56 -8
- package/context.ts +70 -0
- package/internals/DrawingControls.vue +66 -46
- package/internals/DrawingLayer.vue +3 -2
- package/internals/Editor.vue +68 -34
- package/internals/IconButton.vue +15 -0
- package/internals/InfoDialog.vue +1 -1
- package/internals/Modal.vue +1 -1
- package/internals/NavControls.vue +37 -68
- package/internals/NoteDisplay.vue +1 -1
- package/internals/NoteEditor.vue +10 -6
- package/internals/NoteStatic.vue +5 -6
- package/internals/PrintContainer.vue +3 -2
- package/internals/PrintSlideClick.vue +3 -2
- package/internals/RecordingControls.vue +10 -14
- package/internals/RecordingDialog.vue +2 -3
- package/internals/SlideContainer.vue +7 -6
- package/internals/SlideWrapper.ts +28 -12
- package/internals/SlidesOverview.vue +19 -9
- package/logic/drawings.ts +6 -3
- package/logic/nav.ts +6 -6
- package/logic/note.ts +7 -7
- package/main.ts +5 -3
- package/modules/context.ts +4 -3
- package/modules/{directives.ts → v-click.ts} +15 -15
- package/modules/v-mark.ts +159 -0
- package/package.json +21 -13
- package/{internals/EntrySelect.vue → pages/entry.vue} +7 -0
- package/{internals/NotesView.vue → pages/notes.vue} +9 -12
- package/pages/overview.vue +157 -0
- package/{internals/Play.vue → pages/play.vue} +10 -10
- package/{internals/PresenterPrint.vue → pages/presenter/print.vue} +7 -5
- package/{internals/Presenter.vue → pages/presenter.vue} +24 -22
- package/{internals/Print.vue → pages/print.vue} +2 -2
- package/routes.ts +25 -19
- package/setup/codemirror.ts +7 -0
- package/state/index.ts +11 -9
- package/styles/index.css +5 -0
- package/styles/layouts-base.css +6 -4
- package/styles/vars.css +1 -0
- package/uno.config.ts +6 -2
- package/internals/HiddenText.vue +0 -9
|
@@ -14,11 +14,12 @@ Learn more: https://sli.dev/guide/syntax.html#line-highlighting
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
15
|
import { parseRangeString } from '@slidev/parser/core'
|
|
16
16
|
import { useClipboard } from '@vueuse/core'
|
|
17
|
-
import { computed,
|
|
17
|
+
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
18
18
|
import type { PropType } from 'vue'
|
|
19
19
|
import { configs } from '../env'
|
|
20
20
|
import { makeId } from '../logic/utils'
|
|
21
|
-
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET
|
|
21
|
+
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET } from '../constants'
|
|
22
|
+
import { useSlideContext } from '../context'
|
|
22
23
|
|
|
23
24
|
const props = defineProps({
|
|
24
25
|
ranges: {
|
|
@@ -47,7 +48,7 @@ const props = defineProps({
|
|
|
47
48
|
},
|
|
48
49
|
})
|
|
49
50
|
|
|
50
|
-
const clicks =
|
|
51
|
+
const { $clicksContext: clicks } = useSlideContext()
|
|
51
52
|
const el = ref<HTMLDivElement>()
|
|
52
53
|
const id = makeId()
|
|
53
54
|
|
|
@@ -20,11 +20,12 @@ Learn more: https://sli.dev/guide/syntax.html#latex-line-highlighting
|
|
|
20
20
|
-->
|
|
21
21
|
|
|
22
22
|
<script setup lang="ts">
|
|
23
|
-
import { computed,
|
|
23
|
+
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
|
|
24
24
|
import type { PropType } from 'vue'
|
|
25
25
|
import { parseRangeString } from '@slidev/parser'
|
|
26
|
-
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET
|
|
26
|
+
import { CLASS_VCLICK_HIDDEN, CLASS_VCLICK_TARGET } from '../constants'
|
|
27
27
|
import { makeId } from '../logic/utils'
|
|
28
|
+
import { useSlideContext } from '../context'
|
|
28
29
|
|
|
29
30
|
const props = defineProps({
|
|
30
31
|
ranges: {
|
|
@@ -45,7 +46,7 @@ const props = defineProps({
|
|
|
45
46
|
},
|
|
46
47
|
})
|
|
47
48
|
|
|
48
|
-
const clicks =
|
|
49
|
+
const { $clicksContext: clicks } = useSlideContext()
|
|
49
50
|
const el = ref<HTMLDivElement>()
|
|
50
51
|
const id = makeId()
|
|
51
52
|
|
package/builtin/RenderWhen.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { RenderContext } from '@slidev/types'
|
|
3
|
-
import { computed,
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
4
|
import { useElementVisibility } from '@vueuse/core'
|
|
5
|
-
import {
|
|
5
|
+
import { useSlideContext } from '../context'
|
|
6
6
|
|
|
7
7
|
type Context = 'main' | 'visible' | RenderContext
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ const targetVisible = useElementVisibility(target)
|
|
|
16
16
|
// When context has `visible`, we need to wrap the content with a div to track the visibility
|
|
17
17
|
const needsDomWrapper = Array.isArray(context) ? context.includes('visible') : context === 'visible'
|
|
18
18
|
|
|
19
|
-
const currentContext =
|
|
19
|
+
const { $renderContext: currentContext } = useSlideContext()
|
|
20
20
|
const shouldRender = computed(() => {
|
|
21
21
|
const anyContext = Array.isArray(context) ? context.some(contextMatch) : contextMatch(context)
|
|
22
22
|
const allConditions = Array.isArray(context) ? context.every(conditionsMatch) : conditionsMatch(context)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { injectionCurrentPage } from '../constants'
|
|
2
|
+
import { useSlideContext } from '../context'
|
|
4
3
|
|
|
5
|
-
const $page =
|
|
4
|
+
const { $page } = useSlideContext()
|
|
6
5
|
</script>
|
|
7
6
|
|
|
8
7
|
<template>
|
package/builtin/SlidesTotal.vue
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
import { injectionSlidevContext } from '../constants'
|
|
2
|
+
import { useSlideContext } from '../context'
|
|
4
3
|
|
|
5
|
-
const $
|
|
4
|
+
const { $nav } = useSlideContext()
|
|
6
5
|
</script>
|
|
7
6
|
|
|
8
7
|
<template>
|
|
9
|
-
<span>{{ $
|
|
8
|
+
<span>{{ $nav.total }}</span>
|
|
10
9
|
</template>
|
package/builtin/SlidevVideo.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed,
|
|
3
|
-
import {
|
|
2
|
+
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
3
|
+
import { useSlideContext } from '../context'
|
|
4
4
|
|
|
5
5
|
const props = defineProps<{
|
|
6
6
|
autoPlay?: boolean | 'once' | 'resume' | 'resumeOnce'
|
|
@@ -8,10 +8,12 @@ const props = defineProps<{
|
|
|
8
8
|
autoReset?: 'slide' | 'click'
|
|
9
9
|
}>()
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const {
|
|
12
|
+
$slidev,
|
|
13
|
+
$clicksContext: clicks,
|
|
14
|
+
$renderContext: currentContext,
|
|
15
|
+
$route: route,
|
|
16
|
+
} = useSlideContext()
|
|
15
17
|
|
|
16
18
|
const video = ref<HTMLMediaElement>()
|
|
17
19
|
const played = ref(false)
|
package/builtin/Toc.vue
CHANGED
|
@@ -8,9 +8,9 @@ Usage:
|
|
|
8
8
|
<Toc columns='2' maxDepth='3' mode='onlySiblings'/>
|
|
9
9
|
-->
|
|
10
10
|
<script setup lang='ts'>
|
|
11
|
-
import { computed
|
|
11
|
+
import { computed } from 'vue'
|
|
12
12
|
import type { TocItem } from '@slidev/types'
|
|
13
|
-
import {
|
|
13
|
+
import { useSlideContext } from '../context'
|
|
14
14
|
|
|
15
15
|
const props = withDefaults(
|
|
16
16
|
defineProps<{
|
|
@@ -33,7 +33,7 @@ const props = withDefaults(
|
|
|
33
33
|
},
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
const $slidev =
|
|
36
|
+
const { $slidev } = useSlideContext()
|
|
37
37
|
|
|
38
38
|
function filterTreeDepth(tree: TocItem[], level = 1): TocItem[] {
|
|
39
39
|
if (level > Number(props.maxDepth)) {
|
package/builtin/Tweet.vue
CHANGED
|
@@ -7,9 +7,9 @@ Usage:
|
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
<script setup lang="ts">
|
|
10
|
-
import { useScriptTag } from '@vueuse/core'
|
|
11
10
|
import { getCurrentInstance, onMounted, ref } from 'vue'
|
|
12
11
|
import { isDark } from '../logic/dark'
|
|
12
|
+
import { useTweetScript } from '../composables/useTweetScript'
|
|
13
13
|
|
|
14
14
|
const props = defineProps<{
|
|
15
15
|
id: string | number
|
|
@@ -41,21 +41,10 @@ async function create() {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// @ts-expect-error global
|
|
44
|
-
if (window?.twttr?.widgets)
|
|
44
|
+
if (window?.twttr?.widgets)
|
|
45
45
|
onMounted(create)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
useScriptTag(
|
|
49
|
-
'https://platform.twitter.com/widgets.js',
|
|
50
|
-
() => {
|
|
51
|
-
if (vm.isMounted)
|
|
52
|
-
create()
|
|
53
|
-
else
|
|
54
|
-
onMounted(create, vm)
|
|
55
|
-
},
|
|
56
|
-
{ async: true },
|
|
57
|
-
)
|
|
58
|
-
}
|
|
46
|
+
else
|
|
47
|
+
useTweetScript(vm, create)
|
|
59
48
|
</script>
|
|
60
49
|
|
|
61
50
|
<template>
|
package/builtin/VClickGap.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { Fragment,
|
|
3
|
-
import { injectionClicksContext } from '../constants'
|
|
2
|
+
import { Fragment, onMounted, watchEffect } from 'vue'
|
|
4
3
|
import { makeId } from '../logic/utils'
|
|
4
|
+
import { useSlideContext } from '../context'
|
|
5
5
|
|
|
6
6
|
const props = defineProps({
|
|
7
7
|
size: {
|
|
@@ -10,12 +10,10 @@ const props = defineProps({
|
|
|
10
10
|
},
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
const
|
|
13
|
+
const { $clicksContext: clicks } = useSlideContext()
|
|
14
14
|
|
|
15
15
|
onMounted(() => {
|
|
16
16
|
watchEffect((onCleanup) => {
|
|
17
|
-
const clicks = clicksRef?.value
|
|
18
|
-
|
|
19
17
|
if (!clicks || clicks.disabled)
|
|
20
18
|
return
|
|
21
19
|
|
package/builtin/VClicks.ts
CHANGED
|
@@ -62,12 +62,12 @@ export default defineComponent({
|
|
|
62
62
|
}) as T
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
let
|
|
65
|
+
let elements = this.$slots.default?.()
|
|
66
66
|
|
|
67
|
-
if (!
|
|
67
|
+
if (!elements)
|
|
68
68
|
return
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
elements = openAllTopLevelSlots(toArray(elements))
|
|
71
71
|
|
|
72
72
|
const mapSubList = (children: VNodeArrayChildren, depth = 1): VNodeArrayChildren => {
|
|
73
73
|
const vNodes = openAllTopLevelSlots(children).map((i) => {
|
|
@@ -116,23 +116,23 @@ export default defineComponent({
|
|
|
116
116
|
})
|
|
117
117
|
|
|
118
118
|
// handle ul, ol list
|
|
119
|
-
if (
|
|
120
|
-
return h(
|
|
119
|
+
if (elements.length === 1 && listTags.includes(elements[0].type as string) && Array.isArray(elements[0].children))
|
|
120
|
+
return h(elements[0], {}, [...mapChildren(elements[0].children), lastGap()])
|
|
121
121
|
|
|
122
|
-
if (
|
|
123
|
-
const tableNode =
|
|
122
|
+
if (elements.length === 1 && elements[0].type as string === 'table') {
|
|
123
|
+
const tableNode = elements[0]
|
|
124
124
|
if (Array.isArray(tableNode.children)) {
|
|
125
125
|
return h(tableNode, {}, tableNode.children.map((i) => {
|
|
126
126
|
if (!isVNode(i))
|
|
127
127
|
return i
|
|
128
128
|
else if (i.type === 'tbody' && Array.isArray(i.children))
|
|
129
|
-
return h(i, {}, [mapChildren(i.children), lastGap()])
|
|
129
|
+
return h(i, {}, [...mapChildren(i.children), lastGap()])
|
|
130
130
|
else
|
|
131
131
|
return h(i)
|
|
132
132
|
}))
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
return [mapChildren(
|
|
136
|
+
return [...mapChildren(elements), lastGap()]
|
|
137
137
|
},
|
|
138
138
|
})
|
package/composables/useClicks.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { RouteRecordRaw } from 'vue-router'
|
|
|
6
6
|
import { currentRoute, isPrintMode, isPrintWithClicks, queryClicks, routeForceRefresh } from '../logic/nav'
|
|
7
7
|
import { normalizeAtProp } from '../logic/utils'
|
|
8
8
|
|
|
9
|
-
function useClicksContextBase(
|
|
9
|
+
function useClicksContextBase(getCurrent: () => number, clicksOverrides?: number): ClicksContext {
|
|
10
10
|
const relativeOffsets: ClicksContext['relativeOffsets'] = new Map()
|
|
11
11
|
const map: ClicksContext['map'] = shallowReactive(new Map())
|
|
12
12
|
|
|
@@ -53,7 +53,7 @@ function useClicksContextBase(route: RouteRecordRaw | undefined, getCurrent: ()
|
|
|
53
53
|
get total() {
|
|
54
54
|
// eslint-disable-next-line no-unused-expressions
|
|
55
55
|
routeForceRefresh.value
|
|
56
|
-
return
|
|
56
|
+
return clicksOverrides
|
|
57
57
|
?? Math.max(0, ...[...map.values()].map(v => v.max || 0))
|
|
58
58
|
},
|
|
59
59
|
}
|
|
@@ -63,21 +63,24 @@ export function usePrimaryClicks(route: RouteRecordRaw | undefined): ClicksConte
|
|
|
63
63
|
if (route?.meta?.__clicksContext)
|
|
64
64
|
return route.meta.__clicksContext
|
|
65
65
|
const thisPath = +(route?.path ?? 99999)
|
|
66
|
-
const context = useClicksContextBase(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
const context = useClicksContextBase(
|
|
67
|
+
() => {
|
|
68
|
+
const currentPath = +(currentRoute.value?.path ?? 99999)
|
|
69
|
+
if (currentPath === thisPath)
|
|
70
|
+
return queryClicks.value
|
|
71
|
+
else if (currentPath > thisPath)
|
|
72
|
+
return 99999
|
|
73
|
+
else
|
|
74
|
+
return 0
|
|
75
|
+
},
|
|
76
|
+
route?.meta?.clicks,
|
|
77
|
+
)
|
|
75
78
|
if (route?.meta)
|
|
76
79
|
route.meta.__clicksContext = context
|
|
77
80
|
return context
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
export function useFixedClicks(route
|
|
83
|
+
export function useFixedClicks(route?: RouteRecordRaw | undefined, currentInit = 0): [Ref<number>, ClicksContext] {
|
|
81
84
|
const current = ref(currentInit)
|
|
82
|
-
return [current, useClicksContextBase(
|
|
85
|
+
return [current, useClicksContextBase(() => current.value, route?.meta?.clicks)]
|
|
83
86
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createSharedComposable, useScriptTag } from '@vueuse/core'
|
|
2
|
+
import type { ComponentInternalInstance } from 'vue'
|
|
3
|
+
import { onMounted } from 'vue'
|
|
4
|
+
|
|
5
|
+
export const useTweetScript = createSharedComposable(
|
|
6
|
+
(vm: ComponentInternalInstance, create: () => void) =>
|
|
7
|
+
useScriptTag(
|
|
8
|
+
'https://platform.twitter.com/widgets.js',
|
|
9
|
+
() => {
|
|
10
|
+
if (vm.isMounted)
|
|
11
|
+
create()
|
|
12
|
+
else
|
|
13
|
+
onMounted(create, vm)
|
|
14
|
+
},
|
|
15
|
+
{ async: true },
|
|
16
|
+
),
|
|
17
|
+
)
|
package/constants.ts
CHANGED
|
@@ -3,14 +3,16 @@ import type { RouteRecordRaw } from 'vue-router'
|
|
|
3
3
|
import type { ClicksContext, RenderContext } from '@slidev/types'
|
|
4
4
|
import type { SlidevContext } from './modules/context'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
12
|
-
export const
|
|
13
|
-
export const
|
|
6
|
+
// Here we use string literal instead of symbols to make HMR more stable
|
|
7
|
+
// The value of the injections keys are implementation details, you should always use them with the reference to the constant instead of the value
|
|
8
|
+
export const injectionClicksContext = '$$slidev-clicks-context' as unknown as InjectionKey<Ref<ClicksContext>>
|
|
9
|
+
export const injectionCurrentPage = '$$slidev-page' as unknown as InjectionKey<Ref<number>>
|
|
10
|
+
export const injectionSlideScale = '$$slidev-slide-scale' as unknown as InjectionKey<ComputedRef<number>>
|
|
11
|
+
export const injectionSlidevContext = '$$slidev-context' as unknown as InjectionKey<UnwrapNestedRefs<SlidevContext>>
|
|
12
|
+
export const injectionRoute = '$$slidev-route' as unknown as InjectionKey<RouteRecordRaw>
|
|
13
|
+
export const injectionRenderContext = '$$slidev-render-context' as unknown as InjectionKey<Ref<RenderContext>>
|
|
14
|
+
export const injectionActive = '$$slidev-active' as unknown as InjectionKey<Ref<boolean>>
|
|
15
|
+
export const injectionFrontmatter = '$$slidev-fontmatter' as unknown as InjectionKey<Record<string, any>>
|
|
14
16
|
|
|
15
17
|
export const CLASS_VCLICK_TARGET = 'slidev-vclick-target'
|
|
16
18
|
export const CLASS_VCLICK_HIDDEN = 'slidev-vclick-hidden'
|
|
@@ -24,3 +26,49 @@ export const TRUST_ORIGINS = [
|
|
|
24
26
|
'localhost',
|
|
25
27
|
'127.0.0.1',
|
|
26
28
|
]
|
|
29
|
+
|
|
30
|
+
export const FRONTMATTER_FIELDS = [
|
|
31
|
+
'clicks',
|
|
32
|
+
'disabled',
|
|
33
|
+
'hide',
|
|
34
|
+
'hideInToc',
|
|
35
|
+
'layout',
|
|
36
|
+
'level',
|
|
37
|
+
'preload',
|
|
38
|
+
'routeAlias',
|
|
39
|
+
'src',
|
|
40
|
+
'title',
|
|
41
|
+
'transition',
|
|
42
|
+
'zoom',
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
export const HEADMATTER_FIELDS = [
|
|
46
|
+
...FRONTMATTER_FIELDS,
|
|
47
|
+
'theme',
|
|
48
|
+
'titleTemplate',
|
|
49
|
+
'info',
|
|
50
|
+
'author',
|
|
51
|
+
'keywords',
|
|
52
|
+
'presenter',
|
|
53
|
+
'download',
|
|
54
|
+
'exportFilename',
|
|
55
|
+
'export',
|
|
56
|
+
'highlighter',
|
|
57
|
+
'lineNumbers',
|
|
58
|
+
'monaco',
|
|
59
|
+
'remoteAssets',
|
|
60
|
+
'selectable',
|
|
61
|
+
'record',
|
|
62
|
+
'colorSchema',
|
|
63
|
+
'routerMode',
|
|
64
|
+
'aspectRatio',
|
|
65
|
+
'canvasWidth',
|
|
66
|
+
'themeConfig',
|
|
67
|
+
'favicon',
|
|
68
|
+
'plantUmlServer',
|
|
69
|
+
'fonts',
|
|
70
|
+
'defaults',
|
|
71
|
+
'drawings',
|
|
72
|
+
'htmlAttrs',
|
|
73
|
+
'mdc',
|
|
74
|
+
]
|
package/context.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { shallowRef, toRef } from 'vue'
|
|
2
|
+
import { injectLocal, objectOmit, provideLocal } from '@vueuse/core'
|
|
3
|
+
import { useFixedClicks } from './composables/useClicks'
|
|
4
|
+
import {
|
|
5
|
+
FRONTMATTER_FIELDS,
|
|
6
|
+
HEADMATTER_FIELDS,
|
|
7
|
+
injectionClicksContext,
|
|
8
|
+
injectionCurrentPage,
|
|
9
|
+
injectionFrontmatter,
|
|
10
|
+
injectionRenderContext,
|
|
11
|
+
injectionRoute,
|
|
12
|
+
injectionSlidevContext,
|
|
13
|
+
} from './constants'
|
|
14
|
+
|
|
15
|
+
const clicksContextFallback = shallowRef(useFixedClicks()[1])
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the current slide context, should be called inside the setup function of a component inside slide
|
|
19
|
+
*/
|
|
20
|
+
export function useSlideContext() {
|
|
21
|
+
const $slidev = injectLocal(injectionSlidevContext)!
|
|
22
|
+
const $nav = toRef($slidev, 'nav')
|
|
23
|
+
const $clicksContext = injectLocal(injectionClicksContext, clicksContextFallback)!.value
|
|
24
|
+
const $clicks = toRef($clicksContext, 'current')
|
|
25
|
+
const $page = injectLocal(injectionCurrentPage)!
|
|
26
|
+
const $renderContext = injectLocal(injectionRenderContext)!
|
|
27
|
+
const $frontmatter = injectLocal(injectionFrontmatter, {})
|
|
28
|
+
const $route = injectLocal(injectionRoute, undefined)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
$slidev,
|
|
32
|
+
$nav,
|
|
33
|
+
$clicksContext,
|
|
34
|
+
$clicks,
|
|
35
|
+
$page,
|
|
36
|
+
$route,
|
|
37
|
+
$renderContext,
|
|
38
|
+
$frontmatter,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function provideFrontmatter(frontmatter: Record<string, any>) {
|
|
43
|
+
provideLocal(injectionFrontmatter, frontmatter)
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
$slidev,
|
|
47
|
+
$page,
|
|
48
|
+
} = useSlideContext()
|
|
49
|
+
|
|
50
|
+
// update frontmatter in router to make HMR work better
|
|
51
|
+
const route = $slidev.nav.rawRoutes.find(i => i.path === String($page.value))
|
|
52
|
+
if (route?.meta?.slide?.frontmatter) {
|
|
53
|
+
for (const key of Object.keys(route.meta.slide.frontmatter)) {
|
|
54
|
+
if (!(key in frontmatter))
|
|
55
|
+
delete route.meta.slide.frontmatter[key]
|
|
56
|
+
}
|
|
57
|
+
Object.assign(route.meta.slide.frontmatter, frontmatter)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convert frontmatter options to props for v-bind
|
|
63
|
+
* It removes known options fields, and expose an extra `frontmatter` field that contains full frontmatter
|
|
64
|
+
*/
|
|
65
|
+
export function frontmatterToProps(frontmatter: Record<string, any>, pageNo: number) {
|
|
66
|
+
return {
|
|
67
|
+
...objectOmit(frontmatter, pageNo === 0 ? HEADMATTER_FIELDS : FRONTMATTER_FIELDS),
|
|
68
|
+
frontmatter,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { Menu } from 'floating-vue'
|
|
3
|
+
import 'floating-vue/dist/style.css'
|
|
2
4
|
import {
|
|
3
5
|
brush,
|
|
4
6
|
brushColors,
|
|
@@ -13,7 +15,7 @@ import {
|
|
|
13
15
|
} from '../logic/drawings'
|
|
14
16
|
import VerticalDivider from './VerticalDivider.vue'
|
|
15
17
|
import Draggable from './Draggable.vue'
|
|
16
|
-
import
|
|
18
|
+
import IconButton from './IconButton.vue'
|
|
17
19
|
|
|
18
20
|
function undo() {
|
|
19
21
|
drauu.undo()
|
|
@@ -21,100 +23,118 @@ function undo() {
|
|
|
21
23
|
function redo() {
|
|
22
24
|
drauu.redo()
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
let lastDrawingMode: typeof drawingMode.value = 'stylus'
|
|
24
28
|
function setDrawingMode(mode: typeof drawingMode.value) {
|
|
25
29
|
drawingMode.value = mode
|
|
26
30
|
drawingEnabled.value = true
|
|
31
|
+
if (mode !== 'eraseLine')
|
|
32
|
+
lastDrawingMode = mode
|
|
27
33
|
}
|
|
28
34
|
function setBrushColor(color: typeof brush.color) {
|
|
29
35
|
brush.color = color
|
|
30
36
|
drawingEnabled.value = true
|
|
37
|
+
drawingMode.value = lastDrawingMode
|
|
31
38
|
}
|
|
32
39
|
</script>
|
|
33
40
|
|
|
34
41
|
<template>
|
|
35
42
|
<Draggable
|
|
36
|
-
class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200"
|
|
37
|
-
dark="border border-gray-400 border-opacity-10"
|
|
43
|
+
class="flex flex-wrap text-xl p-2 gap-1 rounded-md bg-main shadow transition-opacity duration-200 z-20 border border-main"
|
|
38
44
|
:class="drawingEnabled ? '' : drawingPinned ? 'opacity-40 hover:opacity-90' : 'opacity-0 pointer-events-none'"
|
|
39
45
|
storage-key="slidev-drawing-pos"
|
|
40
46
|
:initial-x="10"
|
|
41
47
|
:initial-y="10"
|
|
42
48
|
>
|
|
43
|
-
<
|
|
44
|
-
<HiddenText text="Draw with stylus" />
|
|
49
|
+
<IconButton title="Draw with stylus" :class="{ shallow: drawingMode !== 'stylus' }" @click="setDrawingMode('stylus')">
|
|
45
50
|
<carbon:pen />
|
|
46
|
-
</
|
|
47
|
-
<
|
|
48
|
-
<HiddenText text="Draw a line" />
|
|
51
|
+
</IconButton>
|
|
52
|
+
<IconButton title="Draw a line" :class="{ shallow: drawingMode !== 'line' }" @click="setDrawingMode('line')">
|
|
49
53
|
<svg width="1em" height="1em" class="-mt-0.5" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">
|
|
50
54
|
<path d="M21.71 3.29a1 1 0 0 0-1.42 0l-18 18a1 1 0 0 0 0 1.42a1 1 0 0 0 1.42 0l18-18a1 1 0 0 0 0-1.42z" fill="currentColor" />
|
|
51
55
|
</svg>
|
|
52
|
-
</
|
|
53
|
-
<
|
|
54
|
-
<HiddenText text="Draw an arrow" />
|
|
56
|
+
</IconButton>
|
|
57
|
+
<IconButton title="Draw an arrow" :class="{ shallow: drawingMode !== 'arrow' }" @click="setDrawingMode('arrow')">
|
|
55
58
|
<carbon:arrow-up-right />
|
|
56
|
-
</
|
|
57
|
-
<
|
|
58
|
-
<HiddenText text="Draw an ellipse" />
|
|
59
|
+
</IconButton>
|
|
60
|
+
<IconButton title="Draw an ellipse" :class="{ shallow: drawingMode !== 'ellipse' }" @click="setDrawingMode('ellipse')">
|
|
59
61
|
<carbon:radio-button />
|
|
60
|
-
</
|
|
61
|
-
<
|
|
62
|
-
<HiddenText text="Draw a rectangle" />
|
|
62
|
+
</IconButton>
|
|
63
|
+
<IconButton title="Draw a rectangle" :class="{ shallow: drawingMode !== 'rectangle' }" @click="setDrawingMode('rectangle')">
|
|
63
64
|
<carbon:checkbox />
|
|
64
|
-
</
|
|
65
|
-
|
|
66
|
-
<!-- <button class="slidev-icon-btn" :class="{ shallow: drawingMode != 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
67
|
-
<HiddenText text="Erase" />
|
|
65
|
+
</IconButton>
|
|
66
|
+
<IconButton title="Erase" :class="{ shallow: drawingMode !== 'eraseLine' }" @click="setDrawingMode('eraseLine')">
|
|
68
67
|
<carbon:erase />
|
|
69
|
-
</
|
|
68
|
+
</IconButton>
|
|
70
69
|
|
|
71
70
|
<VerticalDivider />
|
|
72
71
|
|
|
73
|
-
<
|
|
72
|
+
<Menu>
|
|
73
|
+
<IconButton title="Adjust stroke width" :class="{ shallow: drawingMode === 'eraseLine' }">
|
|
74
|
+
<svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
|
|
75
|
+
<line x1="2" y1="15" x2="22" y2="4" stroke="currentColor" stroke-width="1" stroke-linecap="round" />
|
|
76
|
+
<line x1="2" y1="24" x2="28" y2="10" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
|
77
|
+
<line x1="7" y1="31" x2="29" y2="19" stroke="currentColor" stroke-width="3" stroke-linecap="round" />
|
|
78
|
+
</svg>
|
|
79
|
+
</IconButton>
|
|
80
|
+
<template #popper>
|
|
81
|
+
<div class="flex bg-main p-2">
|
|
82
|
+
<div class="inline-block w-7 text-center">
|
|
83
|
+
{{ brush.size }}
|
|
84
|
+
</div>
|
|
85
|
+
<div class="pt-.5">
|
|
86
|
+
<input v-model="brush.size" type="range" min="1" max="15" @change="drawingMode = lastDrawingMode">
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
90
|
+
</Menu>
|
|
91
|
+
<IconButton
|
|
74
92
|
v-for="color of brushColors"
|
|
75
93
|
:key="color"
|
|
76
|
-
|
|
77
|
-
:class="brush.color === color ? 'active' : 'shallow'"
|
|
94
|
+
title="Set brush color"
|
|
95
|
+
:class="brush.color === color && drawingMode !== 'eraseLine' ? 'active' : 'shallow'"
|
|
78
96
|
@click="setBrushColor(color)"
|
|
79
97
|
>
|
|
80
|
-
<HiddenText text="Set brush color" />
|
|
81
98
|
<div
|
|
82
|
-
class="w-6 h-6 transition-all transform border
|
|
83
|
-
:class="brush.color !== color ? 'rounded-1/2 scale-85' : 'rounded-md'"
|
|
99
|
+
class="w-6 h-6 transition-all transform border"
|
|
100
|
+
:class="brush.color !== color ? 'rounded-1/2 scale-85 border-white' : 'rounded-md border-gray-300/50'"
|
|
84
101
|
:style="drawingEnabled ? { background: color } : { borderColor: color }"
|
|
85
102
|
/>
|
|
86
|
-
</
|
|
103
|
+
</IconButton>
|
|
87
104
|
|
|
88
105
|
<VerticalDivider />
|
|
89
106
|
|
|
90
|
-
<
|
|
91
|
-
<HiddenText text="Undo" />
|
|
107
|
+
<IconButton title="Undo" :class="{ disabled: !canUndo }" @click="undo()">
|
|
92
108
|
<carbon:undo />
|
|
93
|
-
</
|
|
94
|
-
<
|
|
95
|
-
<HiddenText text="Redo" />
|
|
109
|
+
</IconButton>
|
|
110
|
+
<IconButton title="Redo" :class="{ disabled: !canRedo }" @click="redo()">
|
|
96
111
|
<carbon:redo />
|
|
97
|
-
</
|
|
98
|
-
<
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
</button>
|
|
112
|
+
</IconButton>
|
|
113
|
+
<IconButton title="Delete" :class="{ disabled: !canClear }" @click="clearDrauu()">
|
|
114
|
+
<carbon:trash-can />
|
|
115
|
+
</IconButton>
|
|
102
116
|
|
|
103
117
|
<VerticalDivider />
|
|
104
|
-
<
|
|
105
|
-
<HiddenText :text="drawingPinned ? 'Unpin drawing' : 'Pin drawing'" />
|
|
118
|
+
<IconButton :title="drawingPinned ? 'Unpin drawing' : 'Pin drawing'" :class="{ shallow: !drawingPinned }" @click="drawingPinned = !drawingPinned">
|
|
106
119
|
<carbon:pin-filled v-show="drawingPinned" class="transform -rotate-45" />
|
|
107
120
|
<carbon:pin v-show="!drawingPinned" />
|
|
108
|
-
</
|
|
109
|
-
<
|
|
121
|
+
</IconButton>
|
|
122
|
+
<IconButton
|
|
110
123
|
v-if="drawingEnabled"
|
|
111
|
-
|
|
124
|
+
:title="drawingPinned ? 'Drawing pinned' : 'Drawing unpinned'"
|
|
112
125
|
:class="{ shallow: !drawingEnabled }"
|
|
113
126
|
@click="drawingEnabled = !drawingEnabled"
|
|
114
127
|
>
|
|
115
|
-
<HiddenText :text="drawingPinned ? 'Drawing pinned' : 'Drawing unpinned'" />
|
|
116
128
|
<carbon:error v-show="drawingPinned" />
|
|
117
129
|
<carbon:close-outline v-show="!drawingPinned" />
|
|
118
|
-
</
|
|
130
|
+
</IconButton>
|
|
119
131
|
</Draggable>
|
|
120
132
|
</template>
|
|
133
|
+
|
|
134
|
+
<style lang="postcss">
|
|
135
|
+
.v-popper--theme-menu {
|
|
136
|
+
.v-popper__arrow-inner {
|
|
137
|
+
@apply border-main;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
</style>
|