Skip to content

Commit

Permalink
feat(v-click): use order map to order v-click (#277)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
WingLim and antfu committed Jun 23, 2021
1 parent 49042c7 commit 3aa61a2
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 7 deletions.
3 changes: 2 additions & 1 deletion cypress.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"baseUrl": "http://localhost:3030"
"baseUrl": "http://localhost:3030",
"chromeWebSecurity": false
}
12 changes: 12 additions & 0 deletions cypress/fixtures/basic/slides.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,15 @@ layout: two-cols
# Left

Left

---

# Page 9

<div class="cy-content">
<div v-click="3">A</div>
<div v-click="2">B</div>
<div v-click="1">C</div>
<div v-click.hide="4">D</div>
<v-click hide><div>E</div></v-click>
</div>
50 changes: 50 additions & 0 deletions cypress/integration/examples/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ context('Basic', () => {
.should('have.text', '<div>{{$slidev.nav.currentPage}}</div>')
.get('#page-root > #slide-container > #slide-content > .slidev-page-5 > p')
.should('have.text', 'Current Page: 5')
})

it('should nav correctly', () => {
goPage(5)

cy.get('body')
.type('{DownArrow}')
Expand Down Expand Up @@ -69,10 +73,56 @@ context('Basic', () => {
.type('{UpArrow}')
.url()
.should('eq', 'http://localhost:3030/6')
})

it('named slots', () => {
goPage(8)

cy.get('.col-right')
.contains('Right')
})

it('clicks map', () => {
goPage(9)

cy
.url()
.should('eq', 'http://localhost:3030/9')

cy.get('body')
.type('{RightArrow}')

cy
.url()
.should('eq', 'http://localhost:3030/9?clicks=1')

cy.get('.cy-content .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'CDE')

cy.get('body')
.type('{RightArrow}')
.type('{RightArrow}')

cy.get('.cy-content .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'ABCDE')

// v-click.hide
cy.get('body')
.type('{RightArrow}')
.type('{RightArrow}')

cy.get('.cy-content .slidev-vclick-target:not(.slidev-vclick-hidden)')
.should('have.text', 'ABC')

cy
.url()
.should('eq', 'http://localhost:3030/9?clicks=5')

cy.get('body')
.type('{RightArrow}')

cy
.url()
.should('eq', 'http://localhost:3030/10')
})
})
6 changes: 5 additions & 1 deletion packages/client/builtin/VClick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ export default defineComponent({
type: [Number, String],
default: null,
},
hide: {
type: Boolean,
default: false,
},
},
render() {
return createVNode(VClicks, { every: 99999, at: this.at }, { default: this.$slots.default })
return createVNode(VClicks, { every: 99999, at: this.at, hide: this.hide }, { default: this.$slots.default })
},
})
8 changes: 6 additions & 2 deletions packages/client/builtin/VClicks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ export default defineComponent({
type: [Number, String],
default: null,
},
hide: {
type: Boolean,
default: null,
},
},
render() {
const click = resolveDirective('click')!
const after = resolveDirective('after')!

const applyDirective = (node: VNode, directive: Directive, delta: number) => {
if (this.at != null)
return withDirectives(node, [[directive, +this.at + delta]])
return withDirectives(node, [[directive]])
return withDirectives(node, [[directive, +this.at + delta, '', { hide: this.hide }]])
return withDirectives(node, [[directive, null, '', { hide: this.hide }]])
}

let defaults = this.$slots.default?.()
Expand Down
8 changes: 7 additions & 1 deletion packages/client/internals/SlideWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useVModel } from '@vueuse/core'
import { provide, defineComponent, h } from 'vue'
import { injectionClicks, injectionClicksDisabled, injectionClicksElements } from '../modules/directives'
import { injectionClicks, injectionClicksDisabled, injectionClicksElements, injectionOrderMap } from '../modules/directives'

export default defineComponent({
props: {
Expand All @@ -12,6 +12,10 @@ export default defineComponent({
type: Array,
default: () => [] as Element[],
},
clicksOrderMap: {
type: Map,
default: () => new Map<number, HTMLElement[]>(),
},
clicksDisabled: {
type: Boolean,
default: false,
Expand All @@ -25,12 +29,14 @@ export default defineComponent({
const clicks = useVModel(props, 'clicks', emit)
const clicksElements = useVModel(props, 'clicksElements', emit)
const clicksDisabled = useVModel(props, 'clicksDisabled', emit)
const clicksOrderMap = useVModel(props, 'clicksOrderMap', emit)

clicksElements.value.length = 0

provide(injectionClicks, clicks)
provide(injectionClicksDisabled, clicksDisabled)
provide(injectionClicksElements, clicksElements)
provide(injectionOrderMap, clicksOrderMap)
},
render() {
if (this.$props.is)
Expand Down
16 changes: 14 additions & 2 deletions packages/client/logic/nav.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { computed, Ref, ref } from 'vue'
import { computed, Ref, ref, nextTick } from 'vue'
import { isString, SwipeDirection, timestamp, useSwipe } from '@vueuse/core'
import { rawRoutes, router } from '../routes'
import { configs } from '../env'
import { useRouteQuery } from './route'

export { rawRoutes }

// force update collected elements when the route is fully resolved
const routeForceRefresh = ref(0)
router.afterEach(async() => {
await nextTick()
routeForceRefresh.value += 1
})

export const route = computed(() => router.currentRoute.value)

export const isPrintMode = computed(() => route.value.query.print !== undefined)
Expand All @@ -25,7 +32,12 @@ export const currentLayout = computed(() => currentRoute.value?.meta?.layout)

export const nextRoute = computed(() => rawRoutes.find(i => i.path === `${Math.min(rawRoutes.length, currentPage.value + 1)}`))

export const clicksElements = computed<HTMLElement[]>(() => currentRoute.value?.meta?.__clicksElements || [])
export const clicksElements = computed<HTMLElement[]>(() => {
// eslint-disable-next-line no-unused-expressions
routeForceRefresh.value
return currentRoute.value?.meta?.__clicksElements || []
})

export const clicks = computed<number>({
get() {
let clicks = +(queryClicks.value || 0)
Expand Down
63 changes: 63 additions & 0 deletions packages/client/modules/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { isPrintMode, isPrintWithClicks } from '../logic/nav'

export const injectionClicks: InjectionKey<Ref<number>> = Symbol('v-click-clicks')
export const injectionClicksElements: InjectionKey<Ref<(Element | string)[]>> = Symbol('v-click-clicks-elements')
export const injectionOrderMap: InjectionKey<Ref<Map<number, HTMLElement[]>>> = Symbol('v-click-clicks-order-map')
export const injectionClicksDisabled: InjectionKey<Ref<boolean>> = Symbol('v-click-clicks-disabled')

export const CLASS_VCLICK_TARGET = 'slidev-vclick-target'
export const CLASS_VCLICK_HIDDEN = 'slidev-vclick-hidden'
export const CLASS_VCLICK_GONE = 'slidev-vclick-gone'
export const CLASS_VCLICK_HIDDEN_EXP = 'slidev-vclick-hidden-explicitly'
export const CLASS_VCLICK_CURRENT = 'slidev-vclick-current'
export const CLASS_VCLICK_PRIOR = 'slidev-vclick-prior'

function dirInject<T = unknown>(dir: DirectiveBinding<any>, key: InjectionKey<T> | string, defaultValue?: T): T | undefined {
return (dir.instance?.$ as any).provides[key as any] ?? defaultValue
Expand All @@ -28,12 +31,32 @@ export default function createDirectives() {

const elements = dirInject(dir, injectionClicksElements)
const clicks = dirInject(dir, injectionClicks)
const orderMap = dirInject(dir, injectionOrderMap)

const hide = dir.modifiers.hide

const prev = elements?.value?.length || 0

if (elements && !elements?.value?.includes(el))
elements.value.push(el)

// Set default dir.value
if (dir.value === null)
dir.value = elements?.value.length

// If orderMap didn't have dir.value aka the order key, then initialize it.
// If key exists, then move current element to the first of order array to
// make sure the v-after set correct CLASS_VCLICK_CURRENT state.
if (!orderMap?.value.has(dir.value)) {
orderMap?.value.set(dir.value, [el])
}
else {
if (!orderMap?.value.get(dir.value)?.includes(el)) {
const afterClicks = orderMap?.value.get(dir.value) || []
orderMap?.value.set(dir.value, [el].concat(afterClicks))
}
}

el?.classList.toggle(CLASS_VCLICK_TARGET, true)

if (clicks) {
Expand All @@ -46,6 +69,27 @@ export default function createDirectives() {
: c > prev
if (!el.classList.contains(CLASS_VCLICK_HIDDEN_EXP))
el.classList.toggle(CLASS_VCLICK_HIDDEN, !show)

if (hide !== false && hide !== undefined)
el.classList.toggle(CLASS_VCLICK_HIDDEN, show)

// Reset CLASS_VCLICK_CURRENT to false.
el.classList.toggle(CLASS_VCLICK_CURRENT, false)

const currentElArray = orderMap?.value.get(c)
currentElArray?.forEach((cEl, idx) => {
// Reset CLASS_VCLICK_PRIOR to false
cEl.classList.toggle(CLASS_VCLICK_PRIOR, false)
// If the element is the last of order array, then set it as
// CLASS_VCLICK_CURRENT state, others set as CLASS_VCLICK_PRIOR state.
if (idx === currentElArray.length - 1)
cEl.classList.toggle(CLASS_VCLICK_CURRENT, true)
else
cEl.classList.toggle(CLASS_VCLICK_PRIOR, true)
})

if (!el.classList.contains(CLASS_VCLICK_CURRENT))
el.classList.toggle(CLASS_VCLICK_PRIOR, show)
},
{ immediate: true },
)
Expand All @@ -69,9 +113,22 @@ export default function createDirectives() {

const elements = dirInject(dir, injectionClicksElements)
const clicks = dirInject(dir, injectionClicks)
const orderMap = dirInject(dir, injectionOrderMap)

const prev = elements?.value.length

// Set default dir.value
if (dir.value === undefined)
dir.value = elements?.value.length

// If a v-click order before v-after is lower than v-after, the order map will
// not contain the key for v-after, so we need to set it first, then move v-after
// to the last of order array.
if (orderMap?.value.has(dir.value))
orderMap?.value.get(dir.value)?.push(el)
else
orderMap?.value.set(dir.value, [el])

el?.classList.toggle(CLASS_VCLICK_TARGET, true)

if (clicks) {
Expand All @@ -81,6 +138,12 @@ export default function createDirectives() {
const show = (clicks.value ?? 0) >= (dir.value ?? prev ?? 0)
if (!el.classList.contains(CLASS_VCLICK_HIDDEN_EXP))
el.classList.toggle(CLASS_VCLICK_HIDDEN, !show)

// Reset CLASS_VCLICK_CURRENT to false.
el.classList.toggle(CLASS_VCLICK_CURRENT, false)

if (!el.classList.contains(CLASS_VCLICK_CURRENT))
el.classList.toggle(CLASS_VCLICK_PRIOR, show)
},
{ immediate: true },
)
Expand Down

0 comments on commit 3aa61a2

Please sign in to comment.