@slidev/client 0.48.8 → 0.49.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.
@@ -0,0 +1,396 @@
1
+ <script setup lang="ts">
2
+ import { clamp } from '@antfu/utils'
3
+ import type { Pausable } from '@vueuse/core'
4
+ import { useIntervalFn } from '@vueuse/core'
5
+ import { computed, inject, ref, watchEffect } from 'vue'
6
+ import type { DragElementState } from '../composables/useDragElements'
7
+ import { useSlideBounds } from '../composables/useSlideBounds'
8
+ import { injectionSlideScale } from '../constants'
9
+ import { slideHeight, slideWidth } from '../env'
10
+ import { magicKeys } from '../state'
11
+
12
+ const { data } = defineProps<{ data: DragElementState }>()
13
+ const { id, zoom, autoHeight, x0, y0, width, height, rotate } = data
14
+
15
+ const slideScale = inject(injectionSlideScale, ref(1))
16
+ const scale = computed(() => slideScale.value * zoom.value)
17
+ const { left: slideLeft, top: slideTop } = useSlideBounds()
18
+
19
+ const ctrlSize = 10
20
+ const minSize = 40
21
+ const minRemain = 10
22
+
23
+ const rotateRad = computed(() => rotate.value * Math.PI / 180)
24
+ const rotateSin = computed(() => Math.sin(rotateRad.value))
25
+ const rotateCos = computed(() => Math.cos(rotateRad.value))
26
+
27
+ const boundingWidth = computed(() => width.value * rotateCos.value + height.value * rotateSin.value)
28
+ const boundingHeight = computed(() => width.value * rotateSin.value + height.value * rotateCos.value)
29
+
30
+ const boundingLeft = computed(() => x0.value - boundingWidth.value / 2)
31
+ const boundingTop = computed(() => y0.value - boundingHeight.value / 2)
32
+ const boundingRight = computed(() => x0.value + boundingWidth.value / 2)
33
+ const boundingBottom = computed(() => y0.value + boundingHeight.value / 2)
34
+
35
+ let currentDrag: {
36
+ x0: number
37
+ y0: number
38
+ width: number
39
+ height: number
40
+ rotate: number
41
+ dx0: number
42
+ dy0: number
43
+ ltx: number
44
+ lty: number
45
+ rtx: number
46
+ rty: number
47
+ lbx: number
48
+ lby: number
49
+ rbx: number
50
+ rby: number
51
+ } | null = null
52
+
53
+ function onPointerdown(ev: PointerEvent) {
54
+ if (ev.buttons !== 1)
55
+ return
56
+
57
+ ev.preventDefault()
58
+ ev.stopPropagation()
59
+ const el = ev.target as HTMLElement
60
+ const elBounds = el.getBoundingClientRect()
61
+
62
+ const cross1x = width.value * rotateCos.value - height.value * rotateSin.value
63
+ const cross1y = width.value * rotateSin.value + height.value * rotateCos.value
64
+ const cross2x = width.value * rotateCos.value + height.value * rotateSin.value
65
+ const cross2y = -width.value * rotateSin.value + height.value * rotateCos.value
66
+
67
+ currentDrag = {
68
+ x0: x0.value,
69
+ y0: y0.value,
70
+ width: width.value,
71
+ height: height.value,
72
+ rotate: rotate.value,
73
+ dx0: ev.clientX - (elBounds.left + elBounds.right) / 2,
74
+ dy0: ev.clientY - (elBounds.top + elBounds.bottom) / 2,
75
+ ltx: x0.value - cross1x / 2,
76
+ lty: y0.value - cross1y / 2,
77
+ rtx: x0.value + cross2x / 2,
78
+ rty: y0.value - cross2y / 2,
79
+ lbx: x0.value - cross2x / 2,
80
+ lby: y0.value + cross2y / 2,
81
+ rbx: x0.value + cross1x / 2,
82
+ rby: y0.value + cross1y / 2,
83
+ };
84
+
85
+ (ev.currentTarget as HTMLElement).setPointerCapture(ev.pointerId)
86
+ }
87
+
88
+ function onPointermove(ev: PointerEvent) {
89
+ if (!currentDrag || ev.buttons !== 1)
90
+ return
91
+
92
+ ev.preventDefault()
93
+ ev.stopPropagation()
94
+
95
+ const x = (ev.clientX - slideLeft.value - currentDrag.dx0) / scale.value
96
+ const y = (ev.clientY - slideTop.value - currentDrag.dy0) / scale.value
97
+
98
+ x0.value = clamp(x, -boundingWidth.value / 2 + minRemain, slideWidth.value + boundingWidth.value / 2 - minRemain)
99
+ y0.value = clamp(y, -boundingHeight.value / 2 + minRemain, slideHeight.value + boundingHeight.value / 2 - minRemain)
100
+ }
101
+
102
+ function onPointerup(ev: PointerEvent) {
103
+ if (!currentDrag)
104
+ return
105
+
106
+ ev.preventDefault()
107
+ ev.stopPropagation()
108
+
109
+ currentDrag = null
110
+ }
111
+
112
+ const ctrlClasses = `absolute border border-gray bg-gray dark:border-gray-500 dark:bg-gray-800 bg-opacity-30 `
113
+
114
+ function getCornerProps(isLeft: boolean, isTop: boolean) {
115
+ return {
116
+ onPointerdown,
117
+ onPointermove: (ev: PointerEvent) => {
118
+ if (!currentDrag || ev.buttons !== 1)
119
+ return
120
+
121
+ ev.preventDefault()
122
+ ev.stopPropagation()
123
+
124
+ let x = (ev.clientX - slideLeft.value) / scale.value
125
+ let y = (ev.clientY - slideTop.value) / scale.value
126
+
127
+ const { ltx, lty, rtx, rty, lbx, lby, rbx, rby } = currentDrag
128
+
129
+ const ratio = currentDrag.width / currentDrag.height
130
+ const wMin = Math.max(minSize, minSize * ratio)
131
+ function getSize(w1: number, h1: number) {
132
+ if (ev.shiftKey) {
133
+ const w = Math.max(w1, h1 * ratio, wMin)
134
+ const h = w / ratio
135
+ return { w, h }
136
+ }
137
+ else {
138
+ return { w: Math.max(w1, minSize), h: Math.max(h1, minSize) }
139
+ }
140
+ }
141
+
142
+ if (isLeft) {
143
+ if (isTop) {
144
+ const w1 = (rbx - x) * rotateCos.value + (rby - y) * rotateSin.value
145
+ const h1 = -(rbx - x) * rotateSin.value + (rby - y) * rotateCos.value
146
+ const { w, h } = getSize(w1, h1)
147
+ x = rbx - w * rotateCos.value + h * rotateSin.value
148
+ y = rby - w * rotateSin.value - h * rotateCos.value
149
+ }
150
+ else {
151
+ const w1 = (rtx - x) * rotateCos.value - (y - rty) * rotateSin.value
152
+ const h1 = (rtx - x) * rotateSin.value + (y - rty) * rotateCos.value
153
+ const { w, h } = getSize(w1, h1)
154
+ x = rtx - w * rotateCos.value - h * rotateSin.value
155
+ y = rty - w * rotateSin.value + h * rotateCos.value
156
+ }
157
+ }
158
+ else {
159
+ if (isTop) {
160
+ const w1 = (x - lbx) * rotateCos.value - (lby - y) * rotateSin.value
161
+ const h1 = (x - lbx) * rotateSin.value + (lby - y) * rotateCos.value
162
+ const { w, h } = getSize(w1, h1)
163
+ x = lbx + w * rotateCos.value + h * rotateSin.value
164
+ y = lby + w * rotateSin.value - h * rotateCos.value
165
+ }
166
+ else {
167
+ const w1 = (x - ltx) * rotateCos.value + (y - lty) * rotateSin.value
168
+ const h1 = -(x - ltx) * rotateSin.value + (y - lty) * rotateCos.value
169
+ const { w, h } = getSize(w1, h1)
170
+ x = ltx + w * rotateCos.value - h * rotateSin.value
171
+ y = lty + w * rotateSin.value + h * rotateCos.value
172
+ }
173
+ }
174
+
175
+ if (isLeft) {
176
+ if (isTop) {
177
+ x0.value = (x + rbx) / 2
178
+ y0.value = (y + rby) / 2
179
+ width.value = (rbx - x) * rotateCos.value + (rby - y) * rotateSin.value
180
+ height.value = -(rbx - x) * rotateSin.value + (rby - y) * rotateCos.value
181
+ }
182
+ else {
183
+ x0.value = (x + rtx) / 2
184
+ y0.value = (y + rty) / 2
185
+ width.value = (rtx - x) * rotateCos.value - (y - rty) * rotateSin.value
186
+ height.value = (rtx - x) * rotateSin.value + (y - rty) * rotateCos.value
187
+ }
188
+ }
189
+ else {
190
+ if (isTop) {
191
+ x0.value = (x + lbx) / 2
192
+ y0.value = (y + lby) / 2
193
+ width.value = (x - lbx) * rotateCos.value - (lby - y) * rotateSin.value
194
+ height.value = (x - lbx) * rotateSin.value + (lby - y) * rotateCos.value
195
+ }
196
+ else {
197
+ x0.value = (x + ltx) / 2
198
+ y0.value = (y + lty) / 2
199
+ width.value = (x - ltx) * rotateCos.value + (y - lty) * rotateSin.value
200
+ height.value = -(x - ltx) * rotateSin.value + (y - lty) * rotateCos.value
201
+ }
202
+ }
203
+ },
204
+ onPointerup,
205
+ style: {
206
+ width: `${ctrlSize}px`,
207
+ height: `${ctrlSize}px`,
208
+ margin: `-${ctrlSize / 2}px`,
209
+ left: isLeft ? '0' : undefined,
210
+ right: isLeft ? undefined : '0',
211
+ top: isTop ? '0' : undefined,
212
+ bottom: isTop ? undefined : '0',
213
+ cursor: +isLeft + +isTop === 1 ? 'nesw-resize' : 'nwse-resize',
214
+ },
215
+ class: ctrlClasses,
216
+ }
217
+ }
218
+
219
+ function getBorderProps(dir: 'l' | 'r' | 't' | 'b') {
220
+ return {
221
+ onPointerdown,
222
+ onPointermove: (ev: PointerEvent) => {
223
+ if (!currentDrag || ev.buttons !== 1)
224
+ return
225
+
226
+ ev.preventDefault()
227
+ ev.stopPropagation()
228
+
229
+ const x = (ev.clientX - slideLeft.value) / scale.value
230
+ const y = (ev.clientY - slideTop.value) / scale.value
231
+
232
+ const { ltx, lty, rtx, rty, lbx, lby, rbx, rby } = currentDrag
233
+
234
+ if (dir === 'l') {
235
+ const rx = (rtx + rbx) / 2
236
+ const ry = (rty + rby) / 2
237
+ width.value = Math.max((rx - x) * rotateCos.value + (ry - y) * rotateSin.value, minSize)
238
+ x0.value = rx - width.value * rotateCos.value / 2
239
+ y0.value = ry - width.value * rotateSin.value / 2
240
+ }
241
+ else if (dir === 'r') {
242
+ const lx = (ltx + lbx) / 2
243
+ const ly = (lty + lby) / 2
244
+ width.value = Math.max((x - lx) * rotateCos.value + (y - ly) * rotateSin.value, minSize)
245
+ x0.value = lx + width.value * rotateCos.value / 2
246
+ y0.value = ly + width.value * rotateSin.value / 2
247
+ }
248
+ else if (dir === 't') {
249
+ const bx = (lbx + rbx) / 2
250
+ const by = (lby + rby) / 2
251
+ height.value = Math.max((by - y) * rotateCos.value - (bx - x) * rotateSin.value, minSize)
252
+ x0.value = bx + height.value * rotateSin.value / 2
253
+ y0.value = by - height.value * rotateCos.value / 2
254
+ }
255
+ else if (dir === 'b') {
256
+ const tx = (ltx + rtx) / 2
257
+ const ty = (lty + rty) / 2
258
+ height.value = Math.max((y - ty) * rotateCos.value - (x - tx) * rotateSin.value, minSize)
259
+ x0.value = tx - height.value * rotateSin.value / 2
260
+ y0.value = ty + height.value * rotateCos.value / 2
261
+ }
262
+ },
263
+ onPointerup,
264
+ style: {
265
+ width: `${ctrlSize}px`,
266
+ height: `${ctrlSize}px`,
267
+ margin: `-${ctrlSize / 2}px`,
268
+ left: dir === 'l' ? '0' : dir === 'r' ? `100%` : `50%`,
269
+ top: dir === 't' ? '0' : dir === 'b' ? `100%` : `50%`,
270
+ cursor: 'lr'.includes(dir) ? 'ew-resize' : 'ns-resize',
271
+ borderRadius: '50%',
272
+ },
273
+ class: ctrlClasses,
274
+ }
275
+ }
276
+
277
+ function getRotateProps() {
278
+ return {
279
+ onPointerdown,
280
+ onPointermove: (ev: PointerEvent) => {
281
+ if (!currentDrag || ev.buttons !== 1)
282
+ return
283
+
284
+ ev.preventDefault()
285
+ ev.stopPropagation()
286
+
287
+ const x = (ev.clientX - slideLeft.value - currentDrag.dx0) / scale.value - ctrlSize / 4
288
+ const y = (ev.clientY - slideTop.value - currentDrag.dy0) / scale.value - ctrlSize / 4
289
+
290
+ let angle = Math.atan2(y - y0.value, x - x0.value) * 180 / Math.PI + 90
291
+
292
+ const commonAngles = [0, 90, 180, 270, 360]
293
+ for (const a of commonAngles) {
294
+ if (Math.abs(angle - a) < 5) {
295
+ angle = a % 360
296
+ break
297
+ }
298
+ }
299
+
300
+ rotate.value = angle
301
+ },
302
+ onPointerup,
303
+ style: {
304
+ width: `${ctrlSize}px`,
305
+ height: `${ctrlSize}px`,
306
+ margin: `-${ctrlSize / 2}px`,
307
+ left: '50%',
308
+ top: '-20px',
309
+ cursor: 'grab',
310
+ borderRadius: '50%',
311
+ },
312
+ class: ctrlClasses,
313
+ }
314
+ }
315
+
316
+ const moveInterval = 20
317
+ const intervalFnOptions = {
318
+ immediate: false,
319
+ immediateCallback: false,
320
+ }
321
+ const moveLeft = useIntervalFn(() => {
322
+ if (boundingRight.value <= minRemain)
323
+ return
324
+ x0.value--
325
+ }, moveInterval, intervalFnOptions)
326
+ const moveRight = useIntervalFn(() => {
327
+ if (boundingLeft.value >= slideWidth.value - minRemain)
328
+ return
329
+ x0.value++
330
+ }, moveInterval, intervalFnOptions)
331
+ const moveUp = useIntervalFn(() => {
332
+ if (boundingBottom.value <= minRemain)
333
+ return
334
+ y0.value--
335
+ }, moveInterval, intervalFnOptions)
336
+ const moveDown = useIntervalFn(() => {
337
+ if (boundingTop.value >= slideHeight.value - minRemain)
338
+ return
339
+ y0.value++
340
+ }, moveInterval, intervalFnOptions)
341
+
342
+ watchEffect(() => {
343
+ function shortcut(key: string, fn: Pausable) {
344
+ if (magicKeys[key].value)
345
+ fn.resume()
346
+ else fn.pause()
347
+ }
348
+ shortcut('left', moveLeft)
349
+ shortcut('right', moveRight)
350
+ shortcut('up', moveUp)
351
+ shortcut('down', moveDown)
352
+ })
353
+ </script>
354
+
355
+ <template>
356
+ <div
357
+ v-if="Number.isFinite(x0)"
358
+ :data-drag-id="id"
359
+ :style="{
360
+ position: 'absolute',
361
+ zIndex: 100,
362
+ left: `${zoom * (x0 - width / 2)}px`,
363
+ top: `${zoom * (y0 - height / 2)}px`,
364
+ width: `${zoom * width}px`,
365
+ height: `${zoom * height}px`,
366
+ transformOrigin: 'center center',
367
+ transform: `rotate(${rotate}deg)`,
368
+ }"
369
+ @pointerdown="onPointerdown"
370
+ @pointermove="onPointermove"
371
+ @pointerup="onPointerup"
372
+ >
373
+ <div class="absolute inset-0 z-100 b b-dark dark:b-gray-400">
374
+ <template v-if="!autoHeight">
375
+ <div v-bind="getCornerProps(true, true)" />
376
+ <div v-bind="getCornerProps(true, false)" />
377
+ <div v-bind="getCornerProps(false, true)" />
378
+ <div v-bind="getCornerProps(false, false)" />
379
+ </template>
380
+ <div v-bind="getBorderProps('l')" />
381
+ <div v-bind="getBorderProps('r')" />
382
+ <template v-if="!autoHeight">
383
+ <div v-bind="getBorderProps('t')" />
384
+ <div v-bind="getBorderProps('b')" />
385
+ </template>
386
+ <div v-bind="getRotateProps()" />
387
+ <div
388
+ class="absolute -top-15px w-0 b b-dashed b-dark dark:b-gray-400"
389
+ :style="{
390
+ left: 'calc(50% - 1px)',
391
+ height: autoHeight ? '14px' : '10px',
392
+ }"
393
+ />
394
+ </div>
395
+ </div>
396
+ </template>
@@ -2,7 +2,7 @@
2
2
  import { provideLocal, useElementSize, useStyleTag } from '@vueuse/core'
3
3
  import { computed, ref, watchEffect } from 'vue'
4
4
  import { configs, slideAspect, slideHeight, slideWidth } from '../env'
5
- import { injectionSlideScale } from '../constants'
5
+ import { injectionSlideElement, injectionSlideScale } from '../constants'
6
6
  import { useNav } from '../composables/useNav'
7
7
 
8
8
  const props = defineProps({
@@ -23,11 +23,12 @@ const props = defineProps({
23
23
 
24
24
  const { clicksDirection, isPrintMode } = useNav()
25
25
 
26
- const root = ref<HTMLDivElement>()
27
- const element = useElementSize(root)
26
+ const root = ref<HTMLDivElement | null>(null)
27
+ const rootSize = useElementSize(root)
28
+ const slideElement = ref<HTMLElement | null>(null)
28
29
 
29
- const width = computed(() => props.width ? props.width : element.width.value)
30
- const height = computed(() => props.width ? props.width / slideAspect.value : element.height.value)
30
+ const width = computed(() => props.width ? props.width : rootSize.width.value)
31
+ const height = computed(() => props.width ? props.width / slideAspect.value : rootSize.height.value)
31
32
 
32
33
  if (props.width) {
33
34
  watchEffect(() => {
@@ -42,7 +43,7 @@ const screenAspect = computed(() => width.value / height.value)
42
43
 
43
44
  const scale = computed(() => {
44
45
  if (props.scale && !isPrintMode.value)
45
- return props.scale
46
+ return +props.scale
46
47
  if (screenAspect.value < slideAspect.value)
47
48
  return width.value / slideWidth.value
48
49
  return height.value * slideAspect.value / slideWidth.value
@@ -69,12 +70,13 @@ if (props.isMain) {
69
70
  `))
70
71
  }
71
72
 
72
- provideLocal(injectionSlideScale, scale as any)
73
+ provideLocal(injectionSlideScale, scale)
74
+ provideLocal(injectionSlideElement, slideElement)
73
75
  </script>
74
76
 
75
77
  <template>
76
78
  <div id="slide-container" ref="root" class="slidev-slides-container" :class="className">
77
- <div id="slide-content" class="slidev-slide-content" :style="style">
79
+ <div id="slide-content" ref="slideElement" class="slidev-slide-content" :style="style">
78
80
  <slot />
79
81
  </div>
80
82
  <slot name="controls" />
@@ -4,6 +4,7 @@ import type { PropType } from 'vue'
4
4
  import { provideLocal } from '@vueuse/core'
5
5
  import type { ClicksContext, RenderContext, SlideRoute } from '@slidev/types'
6
6
  import { injectionActive, injectionClicksContext, injectionCurrentPage, injectionRenderContext, injectionRoute, injectionSlideZoom } from '../constants'
7
+ import { getSlideClass } from '../utils'
7
8
  import SlideLoading from './SlideLoading.vue'
8
9
 
9
10
  const props = defineProps({
@@ -67,16 +68,24 @@ const SlideComponent = defineAsyncComponent({
67
68
  </script>
68
69
 
69
70
  <template>
70
- <component
71
- :is="SlideComponent"
72
- :style="style"
73
- :data-slidev-no="props.route.no"
74
- :class="{ 'disable-view-transition': !['slide', 'presenter'].includes(props.renderContext) }"
75
- />
71
+ <div :class="getSlideClass(route)">
72
+ <component
73
+ :is="SlideComponent"
74
+ :style="style"
75
+ :data-slidev-no="props.route.no"
76
+ :class="{ 'disable-view-transition': !['slide', 'presenter'].includes(props.renderContext) }"
77
+ />
78
+ </div>
76
79
  </template>
77
80
 
78
81
  <style scoped>
79
82
  .disable-view-transition:deep(*) {
80
83
  view-transition-name: none !important;
81
84
  }
85
+
86
+ .slidev-page {
87
+ position: absolute;
88
+ width: 100%;
89
+ height: 100%;
90
+ }
82
91
  </style>
@@ -2,13 +2,14 @@
2
2
  import { TransitionGroup, computed, shallowRef, watch } from 'vue'
3
3
  import { recomputeAllPoppers } from 'floating-vue'
4
4
  import { useNav } from '../composables/useNav'
5
- import { getSlideClass } from '../utils'
6
5
  import { useViewTransition } from '../composables/useViewTransition'
7
6
  import { skipTransition } from '../logic/hmr'
8
7
  import { createFixedClicks } from '../composables/useClicks'
8
+ import { activeDragElement } from '../state'
9
9
  import { CLICKS_MAX } from '../constants'
10
10
  import SlideWrapper from './SlideWrapper.vue'
11
11
  import PresenterMouse from './PresenterMouse.vue'
12
+ import DragControl from './DragControl.vue'
12
13
 
13
14
  import GlobalTop from '#slidev/global-components/top'
14
15
  import GlobalBottom from '#slidev/global-components/bottom'
@@ -65,22 +66,19 @@ function onAfterLeave() {
65
66
  tag="div"
66
67
  @after-leave="onAfterLeave"
67
68
  >
68
- <div
69
+ <SlideWrapper
70
+ :is="route.component!"
69
71
  v-for="route of loadedRoutes"
70
72
  v-show="route === currentSlideRoute"
71
73
  :key="route.no"
72
- >
73
- <SlideWrapper
74
- :is="route.component!"
75
- :clicks-context="isPrintMode && !isPrintWithClicks ? createFixedClicks(route, CLICKS_MAX) : getPrimaryClicks(route)"
76
- :class="getSlideClass(route)"
77
- :route="route"
78
- :render-context="renderContext"
79
- class="overflow-hidden"
80
- />
81
- </div>
74
+ :clicks-context="isPrintMode && !isPrintWithClicks ? createFixedClicks(route, CLICKS_MAX) : getPrimaryClicks(route)"
75
+ :route="route"
76
+ :render-context="renderContext"
77
+ />
82
78
  </component>
83
79
 
80
+ <DragControl v-if="activeDragElement" :data="activeDragElement" />
81
+
84
82
  <div id="twoslash-container" />
85
83
 
86
84
  <!-- Global Top -->
@@ -96,10 +94,4 @@ function onAfterLeave() {
96
94
  #slideshow {
97
95
  height: 100%;
98
96
  }
99
-
100
- #slideshow > div {
101
- position: absolute;
102
- height: 100%;
103
- width: 100%;
104
- }
105
97
  </style>
@@ -1,5 +1,5 @@
1
1
  import type { ResolvedClicksInfo } from '@slidev/types'
2
- import type { App, DirectiveBinding, InjectionKey } from 'vue'
2
+ import type { App, DirectiveBinding } from 'vue'
3
3
  import { computed, watchEffect } from 'vue'
4
4
  import {
5
5
  CLASS_VCLICK_CURRENT,
@@ -8,14 +8,12 @@ import {
8
8
  CLASS_VCLICK_HIDDEN_EXP,
9
9
  CLASS_VCLICK_PRIOR,
10
10
  CLASS_VCLICK_TARGET,
11
+ injectionClickVisibility,
11
12
  injectionClicksContext,
12
13
  } from '../constants'
14
+ import { directiveInject, directiveProvide } from '../utils'
13
15
 
14
- export type VClickValue = string | [string | number, string | number] | boolean
15
-
16
- export function dirInject<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, defaultValue?: T): T | undefined {
17
- return (dir.instance?.$ as any).provides[key as any] ?? defaultValue
18
- }
16
+ export type VClickValue = undefined | string | number | [string | number, string | number] | boolean
19
17
 
20
18
  export function createVClickDirectives() {
21
19
  return {
@@ -25,7 +23,7 @@ export function createVClickDirectives() {
25
23
  name: 'v-click',
26
24
 
27
25
  mounted(el, dir) {
28
- const resolved = resolveClick(el, dir, dir.value)
26
+ const resolved = resolveClick(el, dir, dir.value, true)
29
27
  if (resolved == null)
30
28
  return
31
29
 
@@ -37,7 +35,8 @@ export function createVClickDirectives() {
37
35
  if (clicks[1] != null)
38
36
  el.dataset.slidevClicksEnd = String(clicks[1])
39
37
 
40
- watchEffect(() => {
38
+ // @ts-expect-error extra prop
39
+ el.watchStopHandle = watchEffect(() => {
41
40
  const active = resolved.isActive.value
42
41
  const current = resolved.isCurrent.value
43
42
  const prior = active && !current
@@ -62,13 +61,14 @@ export function createVClickDirectives() {
62
61
  name: 'v-after',
63
62
 
64
63
  mounted(el, dir) {
65
- const resolved = resolveClick(el, dir, dir.value, true)
64
+ const resolved = resolveClick(el, dir, dir.value, true, true)
66
65
  if (resolved == null)
67
66
  return
68
67
 
69
68
  el.classList.toggle(CLASS_VCLICK_TARGET, true)
70
69
 
71
- watchEffect(() => {
70
+ // @ts-expect-error extra prop
71
+ el.watchStopHandle = watchEffect(() => {
72
72
  const active = resolved.isActive.value
73
73
  const current = resolved.isCurrent.value
74
74
  const prior = active && !current
@@ -93,13 +93,14 @@ export function createVClickDirectives() {
93
93
  name: 'v-click-hide',
94
94
 
95
95
  mounted(el, dir) {
96
- const resolved = resolveClick(el, dir, dir.value, false, true)
96
+ const resolved = resolveClick(el, dir, dir.value, true, false, true)
97
97
  if (resolved == null)
98
98
  return
99
99
 
100
100
  el.classList.toggle(CLASS_VCLICK_TARGET, true)
101
101
 
102
- watchEffect(() => {
102
+ // @ts-expect-error extra prop
103
+ el.watchStopHandle = watchEffect(() => {
103
104
  const active = resolved.isActive.value
104
105
  const current = resolved.isCurrent.value
105
106
  const prior = active && !current
@@ -117,20 +118,20 @@ export function createVClickDirectives() {
117
118
  }
118
119
  }
119
120
 
120
- function isActive(thisClick: number | [number, number], clicks: number) {
121
+ function isClickActive(thisClick: number | [number, number], clicks: number) {
121
122
  return Array.isArray(thisClick)
122
123
  ? thisClick[0] <= clicks && clicks < thisClick[1]
123
124
  : thisClick <= clicks
124
125
  }
125
126
 
126
- function isCurrent(thisClick: number | [number, number], clicks: number) {
127
+ function isClickCurrent(thisClick: number | [number, number], clicks: number) {
127
128
  return Array.isArray(thisClick)
128
129
  ? thisClick[0] === clicks
129
130
  : thisClick === clicks
130
131
  }
131
132
 
132
- export function resolveClick(el: Element, dir: DirectiveBinding<any>, value: VClickValue, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
133
- const ctx = dirInject(dir, injectionClicksContext)?.value
133
+ export function resolveClick(el: Element | string, dir: DirectiveBinding<any>, value: VClickValue, provideVisibility = false, clickAfter = false, flagHide = false): ResolvedClicksInfo | null {
134
+ const ctx = directiveInject(dir, injectionClicksContext)?.value
134
135
 
135
136
  if (!el || !ctx)
136
137
  return null
@@ -152,29 +153,47 @@ export function resolveClick(el: Element, dir: DirectiveBinding<any>, value: VCl
152
153
  if (Array.isArray(value)) {
153
154
  // range (absolute)
154
155
  delta = 0
155
- thisClick = value as [number, number]
156
+ thisClick = [+value[0], +value[1]]
156
157
  maxClick = +value[1]
157
158
  }
158
159
  else {
159
160
  ({ start: thisClick, end: maxClick, delta } = ctx.resolve(value))
160
161
  }
161
162
 
163
+ const isActive = computed(() => isClickActive(thisClick, ctx.current))
164
+ const isCurrent = computed(() => isClickCurrent(thisClick, ctx.current))
165
+ const isShown = computed(() => flagHide ? !isActive.value : isActive.value)
166
+
162
167
  const resolved: ResolvedClicksInfo = {
163
168
  max: maxClick,
164
169
  clicks: thisClick,
165
170
  delta,
166
- isActive: computed(() => isActive(thisClick, ctx.current)),
167
- isCurrent: computed(() => isCurrent(thisClick, ctx.current)),
168
- isShown: computed(() => flagHide ? !isActive(thisClick, ctx.current) : isActive(thisClick, ctx.current)),
171
+ isActive,
172
+ isCurrent,
173
+ isShown,
169
174
  flagFade,
170
175
  flagHide,
171
176
  }
172
177
  ctx.register(el, resolved)
178
+
179
+ if (provideVisibility) {
180
+ directiveProvide(dir, injectionClickVisibility, computed(() => {
181
+ if (isShown.value)
182
+ return true
183
+ if (Array.isArray(thisClick))
184
+ return ctx.current < thisClick[0] ? 'before' : 'after'
185
+ else
186
+ return flagHide ? 'after' : 'before'
187
+ }))
188
+ }
189
+
173
190
  return resolved
174
191
  }
175
192
 
176
193
  function unmounted(el: HTMLElement, dir: DirectiveBinding<any>) {
177
194
  el.classList.toggle(CLASS_VCLICK_TARGET, false)
178
- const ctx = dirInject(dir, injectionClicksContext)?.value
195
+ const ctx = directiveInject(dir, injectionClicksContext)?.value
179
196
  ctx?.unregister(el)
197
+ // @ts-expect-error extra prop
198
+ el.watchStopHandle?.()
180
199
  }