@humanspeak/svelte-motion 0.1.1 → 0.1.2
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/README.md +8 -0
- package/dist/html/_MotionContainer.svelte +49 -49
- package/dist/types.d.ts +12 -2
- package/dist/utils/a11y.d.ts +2 -0
- package/dist/utils/a11y.js +20 -0
- package/dist/utils/interaction.d.ts +8 -1
- package/dist/utils/interaction.js +140 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,6 +79,7 @@ This package carefully selects its dependencies to provide a robust and maintain
|
|
|
79
79
|
| [React - Enter Animation](https://examples.motion.dev/react/enter-animation) | `/tests/motion/enter-animation` | [View Example](https://svelte.dev/playground/7f60c347729f4ea48b1a4590c9dedc02?version=5.38.10) |
|
|
80
80
|
| [HTML Content (0→100 counter)](https://examples.motion.dev/react/html-content) | `/tests/motion/html-content` | [View Example](https://svelte.dev/playground/31cd72df4a3242b4b4589501a25e774f?version=5.38.10) |
|
|
81
81
|
| [Aspect Ratio](https://examples.motion.dev/react/aspect-ratio) | `/tests/motion/aspect-ratio` | [View Example](https://svelte.dev/playground/1bf60e745fae44f5becb4c830fde9b6e?version=5.38.10) |
|
|
82
|
+
| [Hover + Tap (whileHover + whileTap)](https://motion.dev/docs/react?platform=react#hover-tap-animation) | `/tests/motion/hover-and-tap` | [View Example](https://svelte.dev/playground/674c7d58f2c740baa4886b01340a97ea?version=5.38.10) |
|
|
82
83
|
| [Random - Shiny Button](https://www.youtube.com/watch?v=jcpLprT5F0I) by [@verse\_](https://x.com/verse_) | `/tests/random/shiny-button` | [View Example](https://svelte.dev/playground/96f9e0bf624f4396adaf06c519147450?version=5.38.10) |
|
|
83
84
|
| [Fancy Like Button](https://github.com/DRlFTER/fancyLikeButton) | `/tests/random/fancy-like-button` | [View Example](https://svelte.dev/playground/c34b7e53d41c48b0ab1eaf21ca120c6e?version=5.38.10) |
|
|
84
85
|
| [Keyframes (square → circle → square; scale 1→2→1)](https://motion.dev/docs/react-animation#keyframes) | `/tests/motion/keyframes` | [View Example](https://svelte.dev/playground/05595ce0db124c1cbbe4e74fda68d717?version=5.38.10) |
|
|
@@ -111,6 +112,13 @@ Svelte Motion now supports hover interactions via the `whileHover` prop, similar
|
|
|
111
112
|
<motion.button whileTap={{ scale: 0.95 }} />
|
|
112
113
|
```
|
|
113
114
|
|
|
115
|
+
- Callbacks: `onTapStart`, `onTap`, `onTapCancel` are supported.
|
|
116
|
+
- Accessibility: Elements with `whileTap` are keyboard-accessible (Enter and Space).
|
|
117
|
+
- Enter or Space down → fires `onTapStart` and applies `whileTap` (Space prevents default scrolling)
|
|
118
|
+
- Enter or Space up → fires `onTap`
|
|
119
|
+
- Blur while key is held → fires `onTapCancel`
|
|
120
|
+
- `MotionContainer` sets `tabindex="0"` automatically when `whileTap` is present and no `tabindex`/`tabIndex` is provided.
|
|
121
|
+
|
|
114
122
|
### Animation lifecycle
|
|
115
123
|
|
|
116
124
|
```svelte
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { MotionProps, MotionTransition } from '../types.js'
|
|
4
4
|
import { isNotEmpty } from '../utils/objects.js'
|
|
5
5
|
import { sleep } from '../utils/testing.js'
|
|
6
|
-
import { animate } from 'motion'
|
|
6
|
+
import { animate, type AnimationOptions, type DOMKeyframesDefinition } from 'motion'
|
|
7
7
|
import { type Snippet } from 'svelte'
|
|
8
8
|
import { VOID_TAGS } from '../utils/constants.js'
|
|
9
9
|
import { mergeTransitions, animateWithLifecycle } from '../utils/animation.js'
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
} from '../utils/layout.js'
|
|
19
19
|
import type { SvelteHTMLElements } from 'svelte/elements'
|
|
20
20
|
import { mergeInlineStyles } from '../utils/style.js'
|
|
21
|
+
import { isNativelyFocusable } from '../utils/a11y.js'
|
|
21
22
|
|
|
22
23
|
type Props = MotionProps & {
|
|
23
24
|
children?: Snippet
|
|
@@ -37,10 +38,13 @@
|
|
|
37
38
|
class: classProp,
|
|
38
39
|
whileTap: whileTapProp,
|
|
39
40
|
whileHover: whileHoverProp,
|
|
40
|
-
ref: element = $bindable(null),
|
|
41
41
|
onHoverStart: onHoverStartProp,
|
|
42
42
|
onHoverEnd: onHoverEndProp,
|
|
43
|
+
onTapStart: onTapStartProp,
|
|
44
|
+
onTap: onTapProp,
|
|
45
|
+
onTapCancel: onTapCancelProp,
|
|
43
46
|
layout: layoutProp,
|
|
47
|
+
ref: element = $bindable(null),
|
|
44
48
|
...rest
|
|
45
49
|
}: Props = $props()
|
|
46
50
|
let isLoaded = $state<'mounting' | 'initial' | 'ready' | 'animated'>('mounting')
|
|
@@ -54,26 +58,45 @@
|
|
|
54
58
|
const isVoidTag = $derived(VOID_TAGS.has(tag as string))
|
|
55
59
|
|
|
56
60
|
// Compute merged transition without mutating props to avoid effect write loops
|
|
57
|
-
|
|
61
|
+
const mergedTransition = $derived<MotionTransition>(
|
|
58
62
|
mergeTransitions(motionConfig?.transition, transitionProp)
|
|
59
63
|
)
|
|
60
64
|
|
|
65
|
+
// Derived attributes to keep both branches in sync (focusability, data flags, style, class)
|
|
66
|
+
const derivedAttrs = $derived<Record<string, unknown>>({
|
|
67
|
+
...(rest as Record<string, unknown>),
|
|
68
|
+
...(whileTapProp &&
|
|
69
|
+
!isNativelyFocusable(tag, rest as Record<string, unknown>) &&
|
|
70
|
+
((rest as Record<string, unknown>)?.tabindex ??
|
|
71
|
+
(rest as Record<string, unknown>)?.tabIndex ??
|
|
72
|
+
undefined) === undefined
|
|
73
|
+
? { tabindex: 0 }
|
|
74
|
+
: {}),
|
|
75
|
+
...(isPlaywright
|
|
76
|
+
? {
|
|
77
|
+
'data-playwright': isPlaywright,
|
|
78
|
+
'data-is-loaded': isLoaded,
|
|
79
|
+
'data-path': dataPath
|
|
80
|
+
}
|
|
81
|
+
: {}),
|
|
82
|
+
style: mergeInlineStyles(
|
|
83
|
+
styleProp,
|
|
84
|
+
initialProp as unknown as Record<string, unknown>,
|
|
85
|
+
animateProp as unknown as Record<string, unknown>
|
|
86
|
+
),
|
|
87
|
+
class: classProp
|
|
88
|
+
})
|
|
89
|
+
|
|
61
90
|
const runAnimation = () => {
|
|
62
91
|
if (!element || !animateProp) return
|
|
63
|
-
const
|
|
92
|
+
const transitionAnimate: MotionTransition = mergedTransition ?? {}
|
|
64
93
|
const payload = $state.snapshot(animateProp)
|
|
65
94
|
animateWithLifecycle(
|
|
66
95
|
element,
|
|
67
|
-
payload as unknown as
|
|
68
|
-
|
|
69
|
-
(def) =>
|
|
70
|
-
|
|
71
|
-
def as unknown as import('motion').DOMKeyframesDefinition | undefined
|
|
72
|
-
),
|
|
73
|
-
(def) =>
|
|
74
|
-
onAnimationCompleteProp?.(
|
|
75
|
-
def as unknown as import('motion').DOMKeyframesDefinition | undefined
|
|
76
|
-
)
|
|
96
|
+
payload as unknown as DOMKeyframesDefinition,
|
|
97
|
+
transitionAnimate as unknown as AnimationOptions,
|
|
98
|
+
(def) => onAnimationStartProp?.(def as unknown as DOMKeyframesDefinition | undefined),
|
|
99
|
+
(def) => onAnimationCompleteProp?.(def as unknown as DOMKeyframesDefinition | undefined)
|
|
77
100
|
)
|
|
78
101
|
}
|
|
79
102
|
|
|
@@ -97,11 +120,7 @@
|
|
|
97
120
|
}
|
|
98
121
|
const next = measureRect(element!)
|
|
99
122
|
const transforms = computeFlipTransforms(lastRect, next, layoutProp ?? false)
|
|
100
|
-
runFlipAnimation(
|
|
101
|
-
element!,
|
|
102
|
-
transforms,
|
|
103
|
-
(mergedTransition ?? {}) as import('motion').AnimationOptions
|
|
104
|
-
)
|
|
123
|
+
runFlipAnimation(element!, transforms, (mergedTransition ?? {}) as AnimationOptions)
|
|
105
124
|
lastRect = next
|
|
106
125
|
}
|
|
107
126
|
|
|
@@ -134,7 +153,14 @@
|
|
|
134
153
|
element!,
|
|
135
154
|
(whileTapProp ?? {}) as Record<string, unknown>,
|
|
136
155
|
(initialProp ?? {}) as Record<string, unknown>,
|
|
137
|
-
(animateProp ?? {}) as Record<string, unknown
|
|
156
|
+
(animateProp ?? {}) as Record<string, unknown>,
|
|
157
|
+
{
|
|
158
|
+
onTapStart: onTapStartProp,
|
|
159
|
+
onTap: onTapProp,
|
|
160
|
+
onTapCancel: onTapCancelProp,
|
|
161
|
+
hoverDef: (whileHoverProp ?? {}) as Record<string, unknown>,
|
|
162
|
+
hoverFallbackTransition: (mergedTransition ?? {}) as AnimationOptions
|
|
163
|
+
}
|
|
138
164
|
)
|
|
139
165
|
})
|
|
140
166
|
|
|
@@ -144,7 +170,7 @@
|
|
|
144
170
|
return attachWhileHover(
|
|
145
171
|
element!,
|
|
146
172
|
(whileHoverProp ?? {}) as Record<string, unknown>,
|
|
147
|
-
(mergedTransition ?? {}) as
|
|
173
|
+
(mergedTransition ?? {}) as AnimationOptions,
|
|
148
174
|
{ onStart: onHoverStartProp, onEnd: onHoverEndProp },
|
|
149
175
|
undefined,
|
|
150
176
|
{
|
|
@@ -202,35 +228,9 @@
|
|
|
202
228
|
</script>
|
|
203
229
|
|
|
204
230
|
{#if isVoidTag}
|
|
205
|
-
<svelte:element
|
|
206
|
-
this={tag}
|
|
207
|
-
bind:this={element}
|
|
208
|
-
{...rest}
|
|
209
|
-
data-playwright={isPlaywright ? isPlaywright : undefined}
|
|
210
|
-
data-is-loaded={isPlaywright ? isLoaded : undefined}
|
|
211
|
-
data-path={isPlaywright ? dataPath : undefined}
|
|
212
|
-
style={mergeInlineStyles(
|
|
213
|
-
styleProp,
|
|
214
|
-
initialProp as unknown as Record<string, unknown>,
|
|
215
|
-
animateProp as unknown as Record<string, unknown>
|
|
216
|
-
)}
|
|
217
|
-
class={classProp}
|
|
218
|
-
/>
|
|
231
|
+
<svelte:element this={tag} bind:this={element} {...derivedAttrs} />
|
|
219
232
|
{:else}
|
|
220
|
-
<svelte:element
|
|
221
|
-
this={tag}
|
|
222
|
-
bind:this={element}
|
|
223
|
-
{...rest}
|
|
224
|
-
data-playwright={isPlaywright ? isPlaywright : undefined}
|
|
225
|
-
data-is-loaded={isPlaywright ? isLoaded : undefined}
|
|
226
|
-
data-path={isPlaywright ? dataPath : undefined}
|
|
227
|
-
style={mergeInlineStyles(
|
|
228
|
-
styleProp,
|
|
229
|
-
initialProp as unknown as Record<string, unknown>,
|
|
230
|
-
animateProp as unknown as Record<string, unknown>
|
|
231
|
-
)}
|
|
232
|
-
class={classProp}
|
|
233
|
-
>
|
|
233
|
+
<svelte:element this={tag} bind:this={element} {...derivedAttrs}>
|
|
234
234
|
{#if isLoaded === 'ready'}
|
|
235
235
|
{@render children?.()}
|
|
236
236
|
{/if}
|
package/dist/types.d.ts
CHANGED
|
@@ -58,11 +58,15 @@ export type MotionWhileHover = (Record<string, unknown> & {
|
|
|
58
58
|
/**
|
|
59
59
|
* Animation lifecycle callbacks for motion components.
|
|
60
60
|
*/
|
|
61
|
-
export type MotionAnimationStart = ((
|
|
62
|
-
export type MotionAnimationComplete = ((
|
|
61
|
+
export type MotionAnimationStart = ((_definition: DOMKeyframesDefinition | undefined) => void) | undefined;
|
|
62
|
+
export type MotionAnimationComplete = ((_definition: DOMKeyframesDefinition | undefined) => void) | undefined;
|
|
63
63
|
/** Hover lifecycle callbacks */
|
|
64
64
|
export type MotionOnHoverStart = (() => void) | undefined;
|
|
65
65
|
export type MotionOnHoverEnd = (() => void) | undefined;
|
|
66
|
+
/** Tap lifecycle callbacks */
|
|
67
|
+
export type MotionOnTapStart = (() => void) | undefined;
|
|
68
|
+
export type MotionOnTap = (() => void) | undefined;
|
|
69
|
+
export type MotionOnTapCancel = (() => void) | undefined;
|
|
66
70
|
/**
|
|
67
71
|
* Base motion props shared by all motion components.
|
|
68
72
|
*/
|
|
@@ -85,6 +89,12 @@ export type MotionProps = {
|
|
|
85
89
|
onHoverStart?: MotionOnHoverStart;
|
|
86
90
|
/** Called when a true hover gesture ends */
|
|
87
91
|
onHoverEnd?: MotionOnHoverEnd;
|
|
92
|
+
/** Called when a tap gesture starts (pointerdown recognized) */
|
|
93
|
+
onTapStart?: MotionOnTapStart;
|
|
94
|
+
/** Called when a tap gesture ends successfully (pointerup) */
|
|
95
|
+
onTap?: MotionOnTap;
|
|
96
|
+
/** Called when a tap gesture is cancelled (pointercancel) */
|
|
97
|
+
onTapCancel?: MotionOnTapCancel;
|
|
88
98
|
/** Inline styles */
|
|
89
99
|
style?: string;
|
|
90
100
|
/** CSS classes */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const isNativelyFocusable = (tag, attrs = {}) => {
|
|
2
|
+
if (attrs.tabindex != null)
|
|
3
|
+
return true;
|
|
4
|
+
if (attrs.tabIndex != null)
|
|
5
|
+
return true;
|
|
6
|
+
if (attrs.contenteditable != null)
|
|
7
|
+
return true;
|
|
8
|
+
switch (tag) {
|
|
9
|
+
case 'a':
|
|
10
|
+
return Boolean(attrs.href);
|
|
11
|
+
case 'button':
|
|
12
|
+
case 'input':
|
|
13
|
+
case 'select':
|
|
14
|
+
case 'textarea':
|
|
15
|
+
case 'summary':
|
|
16
|
+
return true;
|
|
17
|
+
default:
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AnimationOptions } from 'motion';
|
|
1
2
|
/**
|
|
2
3
|
* Build a reset record for whileTap on pointerup.
|
|
3
4
|
*
|
|
@@ -24,4 +25,10 @@ export declare const buildTapResetRecord: (initial: Record<string, unknown>, ani
|
|
|
24
25
|
* @param animateDef Animate keyframe record.
|
|
25
26
|
* @return Cleanup function to remove listeners.
|
|
26
27
|
*/
|
|
27
|
-
export declare const attachWhileTap: (el: HTMLElement, whileTap: Record<string, unknown> | undefined, initial?: Record<string, unknown>, animateDef?: Record<string, unknown
|
|
28
|
+
export declare const attachWhileTap: (el: HTMLElement, whileTap: Record<string, unknown> | undefined, initial?: Record<string, unknown>, animateDef?: Record<string, unknown>, callbacks?: {
|
|
29
|
+
onTapStart?: () => void;
|
|
30
|
+
onTap?: () => void;
|
|
31
|
+
onTapCancel?: () => void;
|
|
32
|
+
hoverDef?: Record<string, unknown> | undefined;
|
|
33
|
+
hoverFallbackTransition?: AnimationOptions | undefined;
|
|
34
|
+
}) => (() => void);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isHoverCapable, splitHoverDefinition } from './hover';
|
|
1
2
|
import { animate } from 'motion';
|
|
2
3
|
/**
|
|
3
4
|
* Build a reset record for whileTap on pointerup.
|
|
@@ -38,15 +39,139 @@ export const buildTapResetRecord = (initial, animateDef, whileTap) => {
|
|
|
38
39
|
* @param animateDef Animate keyframe record.
|
|
39
40
|
* @return Cleanup function to remove listeners.
|
|
40
41
|
*/
|
|
41
|
-
export const attachWhileTap = (el, whileTap, initial, animateDef) => {
|
|
42
|
+
export const attachWhileTap = (el, whileTap, initial, animateDef, callbacks) => {
|
|
42
43
|
if (!whileTap)
|
|
43
44
|
return () => { };
|
|
44
|
-
|
|
45
|
+
let keyboardActive = false;
|
|
46
|
+
let activePointerId = null;
|
|
47
|
+
const handlePointerDown = (event) => {
|
|
48
|
+
// Capture pointer so we receive up/cancel even if pointer leaves the element
|
|
49
|
+
if (typeof event.pointerId === 'number') {
|
|
50
|
+
try {
|
|
51
|
+
if ('setPointerCapture' in el) {
|
|
52
|
+
el.setPointerCapture(event.pointerId);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// noop if not supported
|
|
57
|
+
}
|
|
58
|
+
activePointerId = event.pointerId;
|
|
59
|
+
// Attach global listeners to catch off-element releases (even if capture unsupported)
|
|
60
|
+
window.addEventListener('pointerup', handlePointerUp);
|
|
61
|
+
window.addEventListener('pointercancel', handlePointerCancel);
|
|
62
|
+
document.addEventListener('pointerup', handlePointerUp);
|
|
63
|
+
document.addEventListener('pointercancel', handlePointerCancel);
|
|
64
|
+
}
|
|
65
|
+
callbacks?.onTapStart?.();
|
|
45
66
|
animate(el, whileTap);
|
|
46
67
|
};
|
|
47
|
-
const
|
|
68
|
+
const reapplyHoverIfActive = () => {
|
|
69
|
+
if (!callbacks?.hoverDef)
|
|
70
|
+
return false;
|
|
71
|
+
if (!isHoverCapable())
|
|
72
|
+
return false;
|
|
73
|
+
try {
|
|
74
|
+
if (!el.matches(':hover'))
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const { keyframes, transition } = splitHoverDefinition(callbacks.hoverDef);
|
|
81
|
+
animate(el, keyframes, (transition ?? callbacks.hoverFallbackTransition));
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
const handlePointerUp = (event) => {
|
|
85
|
+
if (typeof event.pointerId === 'number' && activePointerId !== null) {
|
|
86
|
+
if (event.pointerId !== activePointerId)
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
if ('releasePointerCapture' in el)
|
|
90
|
+
el.releasePointerCapture(event.pointerId);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// noop
|
|
94
|
+
}
|
|
95
|
+
activePointerId = null;
|
|
96
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
97
|
+
window.removeEventListener('pointercancel', handlePointerCancel);
|
|
98
|
+
document.removeEventListener('pointerup', handlePointerUp);
|
|
99
|
+
document.removeEventListener('pointercancel', handlePointerCancel);
|
|
100
|
+
}
|
|
101
|
+
callbacks?.onTap?.();
|
|
48
102
|
if (!whileTap)
|
|
49
103
|
return;
|
|
104
|
+
if (reapplyHoverIfActive())
|
|
105
|
+
return;
|
|
106
|
+
if (initial || animateDef) {
|
|
107
|
+
const resetRecord = buildTapResetRecord(initial ?? {}, animateDef ?? {}, whileTap ?? {});
|
|
108
|
+
if (Object.keys(resetRecord).length > 0) {
|
|
109
|
+
animate(el, resetRecord);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const handlePointerCancel = (event) => {
|
|
114
|
+
if (typeof event.pointerId === 'number' && activePointerId !== null) {
|
|
115
|
+
if (event.pointerId !== activePointerId)
|
|
116
|
+
return;
|
|
117
|
+
try {
|
|
118
|
+
if ('releasePointerCapture' in el)
|
|
119
|
+
el.releasePointerCapture(event.pointerId);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// noop
|
|
123
|
+
}
|
|
124
|
+
activePointerId = null;
|
|
125
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
126
|
+
window.removeEventListener('pointercancel', handlePointerCancel);
|
|
127
|
+
document.removeEventListener('pointerup', handlePointerUp);
|
|
128
|
+
document.removeEventListener('pointercancel', handlePointerCancel);
|
|
129
|
+
}
|
|
130
|
+
callbacks?.onTapCancel?.();
|
|
131
|
+
// On cancel, also restore baseline if available
|
|
132
|
+
if (initial || animateDef) {
|
|
133
|
+
const resetRecord = buildTapResetRecord(initial ?? {}, animateDef ?? {}, whileTap ?? {});
|
|
134
|
+
if (Object.keys(resetRecord).length > 0) {
|
|
135
|
+
animate(el, resetRecord);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const handleKeyDown = (e) => {
|
|
140
|
+
if (!(e.key === 'Enter' || e.key === ' ' || e.key === 'Space'))
|
|
141
|
+
return;
|
|
142
|
+
// Prevent page scroll/activation for Space
|
|
143
|
+
if (e.key === ' ' || e.key === 'Space')
|
|
144
|
+
e.preventDefault?.();
|
|
145
|
+
if (keyboardActive)
|
|
146
|
+
return;
|
|
147
|
+
keyboardActive = true;
|
|
148
|
+
callbacks?.onTapStart?.();
|
|
149
|
+
animate(el, whileTap);
|
|
150
|
+
};
|
|
151
|
+
const handleKeyUp = (e) => {
|
|
152
|
+
if (!(e.key === 'Enter' || e.key === ' ' || e.key === 'Space'))
|
|
153
|
+
return;
|
|
154
|
+
// Prevent page scroll/activation for Space
|
|
155
|
+
if (e.key === ' ' || e.key === 'Space')
|
|
156
|
+
e.preventDefault?.();
|
|
157
|
+
if (!keyboardActive)
|
|
158
|
+
return;
|
|
159
|
+
keyboardActive = false;
|
|
160
|
+
callbacks?.onTap?.();
|
|
161
|
+
if (reapplyHoverIfActive())
|
|
162
|
+
return;
|
|
163
|
+
if (initial || animateDef) {
|
|
164
|
+
const resetRecord = buildTapResetRecord(initial ?? {}, animateDef ?? {}, whileTap ?? {});
|
|
165
|
+
if (Object.keys(resetRecord).length > 0) {
|
|
166
|
+
animate(el, resetRecord);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const handleBlur = () => {
|
|
171
|
+
if (!keyboardActive)
|
|
172
|
+
return;
|
|
173
|
+
keyboardActive = false;
|
|
174
|
+
callbacks?.onTapCancel?.();
|
|
50
175
|
if (initial || animateDef) {
|
|
51
176
|
const resetRecord = buildTapResetRecord(initial ?? {}, animateDef ?? {}, whileTap ?? {});
|
|
52
177
|
if (Object.keys(resetRecord).length > 0) {
|
|
@@ -56,10 +181,20 @@ export const attachWhileTap = (el, whileTap, initial, animateDef) => {
|
|
|
56
181
|
};
|
|
57
182
|
el.addEventListener('pointerdown', handlePointerDown);
|
|
58
183
|
el.addEventListener('pointerup', handlePointerUp);
|
|
59
|
-
el.addEventListener('pointercancel',
|
|
184
|
+
el.addEventListener('pointercancel', handlePointerCancel);
|
|
185
|
+
el.addEventListener('keydown', handleKeyDown);
|
|
186
|
+
el.addEventListener('keyup', handleKeyUp);
|
|
187
|
+
el.addEventListener('blur', handleBlur);
|
|
60
188
|
return () => {
|
|
61
189
|
el.removeEventListener('pointerdown', handlePointerDown);
|
|
62
190
|
el.removeEventListener('pointerup', handlePointerUp);
|
|
63
|
-
el.removeEventListener('pointercancel',
|
|
191
|
+
el.removeEventListener('pointercancel', handlePointerCancel);
|
|
192
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
193
|
+
window.removeEventListener('pointercancel', handlePointerCancel);
|
|
194
|
+
document.removeEventListener('pointerup', handlePointerUp);
|
|
195
|
+
document.removeEventListener('pointercancel', handlePointerCancel);
|
|
196
|
+
el.removeEventListener('keydown', handleKeyDown);
|
|
197
|
+
el.removeEventListener('keyup', handleKeyUp);
|
|
198
|
+
el.removeEventListener('blur', handleBlur);
|
|
64
199
|
};
|
|
65
200
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanspeak/svelte-motion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A lightweight animation library for Svelte 5 that provides smooth, hardware-accelerated animations. Features include spring physics, custom easing, and fluid transitions. Built on top of the motion library, it offers a simple API for creating complex animations with minimal code. Perfect for interactive UIs, micro-interactions, and engaging user experiences.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|