Skip to content

Commit 1a5034d

Browse files
authored
Merge pull request #111 from motiondivision/fix/tabindex
Fix/tabindex
2 parents 5e17ae4 + b6da3c3 commit 1a5034d

9 files changed

Lines changed: 108 additions & 55 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-motion",
3-
"version": "0.11.3",
3+
"version": "0.12.0-beta.2",
44
"private": true,
55
"packageManager": "pnpm@9.15.0+sha1.8bfdb6d72b4d5fdf87d21d27f2bfbe2b21dd2629",
66
"description": "",

packages/motion/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "motion-v",
3-
"version": "0.11.3",
3+
"version": "0.12.0-beta.2",
44
"description": "",
55
"author": "",
66
"license": "MIT",
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { render } from '@testing-library/vue'
3+
import { Motion } from '@/components'
4+
import { nextTick } from 'vue'
5+
6+
// Mock framer-motion press
7+
vi.mock('framer-motion/dom', async () => {
8+
const actual = await vi.importActual('framer-motion/dom')
9+
return {
10+
...actual,
11+
press: vi.fn((element) => {
12+
// 模拟 press 的行为
13+
element.setAttribute('tabindex', '0')
14+
return (actual.press as any)(element)
15+
}),
16+
}
17+
})
18+
describe('press gesture', () => {
19+
it('adds tabindex=0 when whilePress is set', async () => {
20+
const wrapper = render(Motion, {
21+
props: {
22+
whilePress: { scale: 0.9 },
23+
},
24+
attrs: {
25+
'data-testid': 'motion',
26+
},
27+
})
28+
await nextTick()
29+
const motion = wrapper.getByTestId('motion')
30+
expect(motion.tabIndex).toBe(0)
31+
})
32+
33+
it('does not add tabindex when whilePress is not set', () => {
34+
const wrapper = render(Motion, {
35+
props: {},
36+
attrs: {
37+
'data-testid': 'motion',
38+
},
39+
})
40+
41+
expect(wrapper.getByTestId('motion').tabIndex).toBe(-1)
42+
})
43+
})

packages/motion/src/features/gestures/press/index.ts

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { MotionState } from '@/state/motion-state'
22
import { Feature } from '@/features'
33
import { frame, press } from 'framer-motion/dom'
44
import type { EventInfo } from 'framer-motion'
5+
import type { Options } from '@/types'
56

67
export function extractEventInfo(event: PointerEvent): EventInfo {
78
return {
@@ -35,30 +36,46 @@ function handlePressEvent(
3536

3637
export class PressGesture extends Feature {
3738
isActive() {
38-
return Boolean(this.state.options.press)
39+
return Boolean(this.state.options.whilePress)
3940
}
4041

4142
constructor(state: MotionState) {
4243
super(state)
4344
}
4445

4546
mount() {
46-
const element = this.state.element
47-
if (!element)
48-
return
49-
this.unmount = press(
50-
element,
51-
(el, startEvent) => {
52-
handlePressEvent(this.state, startEvent, 'Start')
47+
this.register()
48+
}
49+
50+
update() {
51+
const preProps = this.state.visualElement.prevProps as unknown as Options
52+
// Re-register if whilePress changes
53+
if (preProps.whilePress !== this.state.options.whilePress) {
54+
this.register()
55+
}
56+
}
57+
58+
register() {
59+
// Unmount previous press handler
60+
this.unmount()
61+
if (this.isActive()) {
62+
const element = this.state.element
63+
if (!element)
64+
return
65+
this.unmount = press(
66+
element,
67+
(el, startEvent) => {
68+
handlePressEvent(this.state, startEvent, 'Start')
5369

54-
return (endEvent, { success }) =>
55-
handlePressEvent(
56-
this.state,
57-
endEvent,
58-
success ? 'End' : 'Cancel',
59-
)
60-
},
61-
{ useGlobalTarget: this.state.options.globalPressTarget },
62-
)
70+
return (endEvent, { success }) =>
71+
handlePressEvent(
72+
this.state,
73+
endEvent,
74+
success ? 'End' : 'Cancel',
75+
)
76+
},
77+
{ useGlobalTarget: this.state.options.globalPressTarget },
78+
)
79+
}
6380
}
6481
}

packages/motion/src/state/animate-updates.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function animateUpdates(
4848
else
4949
animationOptions = resolveStateAnimation.call(this, controlActiveState)
5050
const factories = createAnimationFactories.call(this, prevTarget, animationOptions, controlDelay)
51-
const { getChildAnimations, childAnimations } = setupChildAnimations.call(this, transition, this.activeStates, isFallback)
51+
const { getChildAnimations, childAnimations } = setupChildAnimations.call(this, animationOptions, this.activeStates, isFallback)
5252

5353
return executeAnimations.call(this, {
5454
factories,
@@ -134,12 +134,13 @@ function createAnimationFactories(
134134
return
135135
this.baseTarget[key] ??= style.get(this.element, key) as string
136136
const keyValue = (this.target[key] === 'none' && isDef(transformResetValue[key])) ? transformResetValue[key] : this.target[key]
137+
// console.log(key, keyValue, (animationOptions?.[key]?.delay || animationOptions?.delay || 0) + controlDelay, controlDelay)
137138
factories.push(() => animate(
138139
this.element,
139140
{ [key]: keyValue },
140141
{
141142
...animationOptions,
142-
delay: (animationOptions[key]?.delay || animationOptions?.delay || 0) + controlDelay,
143+
delay: (animationOptions?.[key]?.delay || animationOptions?.delay || 0) + controlDelay,
143144
} as any,
144145
))
145146
})

packages/motion/src/state/motion-state.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,10 @@ export class MotionState {
168168
// Mount features in parent-to-child order
169169
this.featureManager.mount()
170170
if (!notAnimate && this.options.animate) {
171-
/**
172-
* Immediately animate updates on mount to avoid transform-origin issues with SVG elements
173-
*/
174-
this.animateUpdates()
171+
if (this.visualElement.type === 'svg') {
172+
(this.visualElement as any).updateDimensions()
173+
}
174+
this.startAnimation()
175175
}
176176
if (this.options.layoutId) {
177177
mountedLayoutIds.add(this.options.layoutId)
@@ -267,7 +267,7 @@ export class MotionState {
267267
if (isAnimate) {
268268
this.animateUpdates({
269269
isFallback: !isActive && name !== 'exit' && this.visualElement.isControllingVariants,
270-
isExit: this.activeStates.exit,
270+
isExit: name === 'exit' && this.activeStates.exit,
271271
})
272272
}
273273
}

packages/motion/vitest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import path from 'node:path'
66
export default defineConfig({
77
plugins: [vue(), vueJsx()],
88
test: {
9-
environment: 'happy-dom',
9+
environment: 'jsdom',
1010
globals: true,
1111
coverage: {
1212
provider: 'v8',

packages/plugins/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "plugins",
3-
"version": "0.11.3",
3+
"version": "0.12.0-beta.2",
44
"private": true,
55
"description": "",
66
"author": "",

pnpm-lock.yaml

Lines changed: 19 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)