@onlynative/inertia 0.0.1-alpha.0 → 0.0.1-alpha.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.
Files changed (52) hide show
  1. package/README.md +7 -7
  2. package/dist/index.d.mts +5 -6
  3. package/dist/index.d.ts +5 -6
  4. package/dist/index.js +84 -10
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +85 -11
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/motion/Image.d.mts +1 -1
  9. package/dist/motion/Image.d.ts +1 -1
  10. package/dist/motion/Image.js +84 -10
  11. package/dist/motion/Image.js.map +1 -1
  12. package/dist/motion/Image.mjs +85 -11
  13. package/dist/motion/Image.mjs.map +1 -1
  14. package/dist/motion/Pressable.d.mts +1 -1
  15. package/dist/motion/Pressable.d.ts +1 -1
  16. package/dist/motion/Pressable.js +84 -10
  17. package/dist/motion/Pressable.js.map +1 -1
  18. package/dist/motion/Pressable.mjs +85 -11
  19. package/dist/motion/Pressable.mjs.map +1 -1
  20. package/dist/motion/ScrollView.d.mts +1 -1
  21. package/dist/motion/ScrollView.d.ts +1 -1
  22. package/dist/motion/ScrollView.js +84 -10
  23. package/dist/motion/ScrollView.js.map +1 -1
  24. package/dist/motion/ScrollView.mjs +85 -11
  25. package/dist/motion/ScrollView.mjs.map +1 -1
  26. package/dist/motion/Text.d.mts +1 -1
  27. package/dist/motion/Text.d.ts +1 -1
  28. package/dist/motion/Text.js +84 -10
  29. package/dist/motion/Text.js.map +1 -1
  30. package/dist/motion/Text.mjs +85 -11
  31. package/dist/motion/Text.mjs.map +1 -1
  32. package/dist/motion/View.d.mts +1 -1
  33. package/dist/motion/View.d.ts +1 -1
  34. package/dist/motion/View.js +84 -10
  35. package/dist/motion/View.js.map +1 -1
  36. package/dist/motion/View.mjs +85 -11
  37. package/dist/motion/View.mjs.map +1 -1
  38. package/dist/testing/index.d.mts +57 -0
  39. package/dist/testing/index.d.ts +57 -0
  40. package/dist/testing/index.js +19 -0
  41. package/dist/testing/index.js.map +1 -0
  42. package/dist/testing/index.mjs +16 -0
  43. package/dist/testing/index.mjs.map +1 -0
  44. package/dist/{types-CmbXx-G3.d.mts → types-DeZZzE_e.d.mts} +20 -3
  45. package/dist/{types-CmbXx-G3.d.ts → types-DeZZzE_e.d.ts} +20 -3
  46. package/llms.txt +5 -1
  47. package/package.json +19 -12
  48. package/src/gestures/focusVisibility.ts +61 -0
  49. package/src/gestures/index.ts +1 -0
  50. package/src/motion/createMotionComponent.tsx +132 -47
  51. package/src/testing/index.ts +78 -0
  52. package/src/types.ts +20 -3
package/README.md CHANGED
@@ -42,7 +42,7 @@ export function FadeIn() {
42
42
  - **Primitives** — `Motion.View`, `Motion.Text`, `Motion.Image`, `Motion.Pressable`, `Motion.ScrollView`. Per-primitive style inference (no shared `ViewStyle & TextStyle & ImageStyle` fallback).
43
43
  - **Sequences and keyframes** — `animate={{ x: [0, 100, 0] }}` with per-step transitions; unified `repeat: number | 'infinite' | { count, alternate }`.
44
44
  - **Variants** — `variants={{ open, closed }}` with `animate="open"`. Programmatic control via `useVariants` + `controller={...}`.
45
- - **Gestures** — single `gesture` prop on every primitive: `gesture={{ pressed, focused, hovered }}`. Zero overhead when omitted.
45
+ - **Gestures** — single `gesture` prop on every primitive: `gesture={{ pressed, focused, focusVisible, hovered }}`. `focusVisible` engages only on keyboard focus (W3C `:focus-visible`) so click-focus on web doesn't flash a ring; on native it tracks `focused`. Zero overhead when omitted.
46
46
  - **`<Presence>`** — mount/unmount transitions; exiting children automatically receive `pointerEvents: 'none'`.
47
47
  - **`<MotionConfig reducedMotion>`** — OS reduce-motion honored end-to-end (`'user' | 'never' | 'always'`).
48
48
  - **Per-primitive subpath imports** — `@onlynative/inertia/view`, `/text`, `/image`, `/pressable`, `/scroll-view`.
@@ -62,18 +62,18 @@ A `Motion.View`-only import currently bundles to ~3.2 kB brotlied (excluding pee
62
62
 
63
63
  ## Transitions
64
64
 
65
- | `type` | Public config keys | Maps to |
66
- | -------------------- | -------------------------------------------------------------------------------------------- | ---------------------------------- |
67
- | `'spring'` (default) | `tension`, `friction`, `mass`, `velocity`, `restSpeedThreshold`, `restDisplacementThreshold` | `withSpring` |
68
- | `'timing'` | `duration`, `easing`, `delay` | `withTiming` |
69
- | `'decay'` | `velocity`, `deceleration`, `clamp` | `withDecay` |
65
+ | `type` | Public config keys | Maps to |
66
+ | -------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------- |
67
+ | `'spring'` (default) | `tension`, `friction`, `mass`, `velocity`, `restSpeedThreshold`, `restDisplacementThreshold` | `withSpring` |
68
+ | `'timing'` | `duration`, `easing`, `delay` | `withTiming` |
69
+ | `'decay'` | `velocity`, `deceleration`, `clamp` | `withDecay` |
70
70
  | `'no-animation'` | — | direct assignment, no interpolation |
71
71
 
72
72
  Plus, on any transition: `delay`, `repeat`. Per-property transitions take precedence over the top-level transition. Spring config uses **react-spring vocabulary** (`tension`/`friction`); Reanimated's raw `stiffness`/`damping` is never on the public surface.
73
73
 
74
74
  ## Animatable properties
75
75
 
76
- `opacity`, `translateX`, `translateY`, `scale`, `scaleX`, `scaleY`, `rotate`, `rotateX`, `rotateY`, `backgroundColor`, `borderRadius`, `width`, `height`. Layout transforms via `transform: [...]`. Color interpolation uses Reanimated's color utilities.
76
+ Numeric: `opacity`, `translateX`, `translateY`, `scale`, `scaleX`, `scaleY`, `rotate`, `rotateX`, `rotateY`, `width`, `height`, `borderRadius`. Color: `backgroundColor`, `borderColor`, `color`, `tintColor` (Image only — `Motion.View` rejects it at compile time). Layout transforms via `transform: [...]`. Color targets are forwarded straight through `withSpring` / `withTiming`; Reanimated's value setter packs the string to RGBA and interpolates on the UI thread.
77
77
 
78
78
  Out of scope for `0.x`: SVG path morphing, gradient interpolation, shared-element transitions across screens.
79
79
 
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ReactNode } from 'react';
3
3
  import * as react_native from 'react-native';
4
- import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-CmbXx-G3.mjs';
5
- export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-CmbXx-G3.mjs';
4
+ import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-DeZZzE_e.mjs';
5
+ export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-DeZZzE_e.mjs';
6
6
  export { MotionImage } from './motion/Image.mjs';
7
7
  export { MotionPressable } from './motion/Pressable.mjs';
8
8
  export { MotionScrollView } from './motion/ScrollView.mjs';
@@ -16,10 +16,9 @@ export { MotionView } from './motion/View.mjs';
16
16
  * `exit` / `transition` all infer from `C`'s `style` prop. There is no
17
17
  * shared `ViewStyle & TextStyle & ImageStyle` fallback.
18
18
  *
19
- * Alpha scope: numeric properties listed in `ALL_KEYS`, applied via
20
- * Reanimated shared values + `useAnimatedStyle`. Sequences, variants,
21
- * gestures, color animation, and the cross-render memoization optimization
22
- * land in later phases.
19
+ * Alpha scope: numeric properties (transforms, opacity, width, height,
20
+ * borderRadius) and color properties (backgroundColor, borderColor, color,
21
+ * tintColor) applied via Reanimated shared values + `useAnimatedStyle`.
23
22
  */
24
23
  declare function createMotionComponent<C extends ComponentType<any>>(Component: C): MotionComponent<C>;
25
24
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react from 'react';
2
2
  import { ComponentType, ReactNode } from 'react';
3
3
  import * as react_native from 'react-native';
4
- import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-CmbXx-G3.js';
5
- export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-CmbXx-G3.js';
4
+ import { M as MotionComponent, A as AnimatableValue, T as TransitionConfig, V as VariantController } from './types-DeZZzE_e.js';
5
+ export { a as AnimateStyle, b as AnimationCallbackInfo, D as DecayTransition, G as GestureSubStates, c as MotionProps, N as NoAnimationTransition, P as PerPropertyTransition, R as RepeatConfig, S as SequenceStep, d as SpringTransition, e as TimingTransition, f as Transition, g as VariantsMap } from './types-DeZZzE_e.js';
6
6
  export { MotionImage } from './motion/Image.js';
7
7
  export { MotionPressable } from './motion/Pressable.js';
8
8
  export { MotionScrollView } from './motion/ScrollView.js';
@@ -16,10 +16,9 @@ export { MotionView } from './motion/View.js';
16
16
  * `exit` / `transition` all infer from `C`'s `style` prop. There is no
17
17
  * shared `ViewStyle & TextStyle & ImageStyle` fallback.
18
18
  *
19
- * Alpha scope: numeric properties listed in `ALL_KEYS`, applied via
20
- * Reanimated shared values + `useAnimatedStyle`. Sequences, variants,
21
- * gestures, color animation, and the cross-render memoization optimization
22
- * land in later phases.
19
+ * Alpha scope: numeric properties (transforms, opacity, width, height,
20
+ * borderRadius) and color properties (backgroundColor, borderColor, color,
21
+ * tintColor) applied via Reanimated shared values + `useAnimatedStyle`.
23
22
  */
24
23
  declare function createMotionComponent<C extends ComponentType<any>>(Component: C): MotionComponent<C>;
25
24
 
package/dist/index.js CHANGED
@@ -36,6 +36,29 @@ function MotionConfig({
36
36
  );
37
37
  return /* @__PURE__ */ jsxRuntime.jsx(MotionConfigContext.Provider, { value, children });
38
38
  }
39
+ var modality = "keyboard";
40
+ var installed = false;
41
+ function setKeyboard() {
42
+ modality = "keyboard";
43
+ }
44
+ function setPointer() {
45
+ modality = "pointer";
46
+ }
47
+ function ensureInstalled() {
48
+ if (installed) return;
49
+ if (reactNative.Platform.OS !== "web") return;
50
+ if (typeof document === "undefined") return;
51
+ document.addEventListener("keydown", setKeyboard, true);
52
+ document.addEventListener("mousedown", setPointer, true);
53
+ document.addEventListener("pointerdown", setPointer, true);
54
+ document.addEventListener("touchstart", setPointer, true);
55
+ installed = true;
56
+ }
57
+ function isFocusVisible() {
58
+ if (reactNative.Platform.OS !== "web") return true;
59
+ ensureInstalled();
60
+ return modality === "keyboard";
61
+ }
39
62
  var PresenceContext = react.createContext(null);
40
63
  function usePresence() {
41
64
  return react.useContext(PresenceContext);
@@ -270,8 +293,23 @@ var TRANSFORM_KEYS = [
270
293
  "scaleY",
271
294
  "rotate"
272
295
  ];
273
- var TOP_LEVEL_KEYS = ["opacity", "width", "height", "borderRadius"];
274
- var ALL_KEYS = [...TRANSFORM_KEYS, ...TOP_LEVEL_KEYS];
296
+ var NUMERIC_TOP_LEVEL_KEYS = [
297
+ "opacity",
298
+ "width",
299
+ "height",
300
+ "borderRadius"
301
+ ];
302
+ var COLOR_KEYS = [
303
+ "backgroundColor",
304
+ "borderColor",
305
+ "color",
306
+ "tintColor"
307
+ ];
308
+ var ALL_KEYS = [
309
+ ...TRANSFORM_KEYS,
310
+ ...NUMERIC_TOP_LEVEL_KEYS,
311
+ ...COLOR_KEYS
312
+ ];
275
313
  var TRANSFORM_KEY_SET = new Set(TRANSFORM_KEYS);
276
314
  var EXITING_POINTER_EVENTS_STYLE = { pointerEvents: "none" };
277
315
  var DEFAULT_RESTING = {
@@ -284,7 +322,15 @@ var DEFAULT_RESTING = {
284
322
  opacity: 1,
285
323
  width: 0,
286
324
  height: 0,
287
- borderRadius: 0
325
+ borderRadius: 0,
326
+ // 'transparent' is the only safe universal default for colors: it works as
327
+ // an initial seed for any color animation (no jarring opaque flash on mount
328
+ // when `initial` is omitted) and rgba(0,0,0,0) interpolates cleanly into
329
+ // any opaque target via Reanimated's color util.
330
+ backgroundColor: "transparent",
331
+ borderColor: "transparent",
332
+ color: "transparent",
333
+ tintColor: "transparent"
288
334
  };
289
335
  var TRANSITION_KEYS = /* @__PURE__ */ new Set([
290
336
  "type",
@@ -345,6 +391,7 @@ function createMotionComponent(Component) {
345
391
  const exitRecord = exit ? exit : void 0;
346
392
  const [pressed, setPressed] = react.useState(false);
347
393
  const [focused, setFocused] = react.useState(false);
394
+ const [focusVisible, setFocusVisible] = react.useState(false);
348
395
  const [hovered, setHovered] = react.useState(false);
349
396
  const activeKeysRef = react.useRef(null);
350
397
  if (activeKeysRef.current === null) {
@@ -365,6 +412,7 @@ function createMotionComponent(Component) {
365
412
  for (const subState of [
366
413
  gesture.pressed,
367
414
  gesture.focused,
415
+ gesture.focusVisible,
368
416
  gesture.hovered
369
417
  ]) {
370
418
  if (!subState) continue;
@@ -393,6 +441,7 @@ function createMotionComponent(Component) {
393
441
  const mergedRecord = isExiting && exitRecord ? { ...animateRecord, ...exitRecord } : mergeGestureTargets(animateRecord, gesture, {
394
442
  pressed,
395
443
  focused,
444
+ focusVisible,
396
445
  hovered
397
446
  });
398
447
  const mergedSig = stableSig(mergedRecord) + (isExiting ? "|exit" : "") + (shouldReduceMotion ? "|rm" : "");
@@ -475,6 +524,7 @@ function createMotionComponent(Component) {
475
524
  rest,
476
525
  setPressed,
477
526
  setFocused,
527
+ setFocusVisible,
478
528
  setHovered
479
529
  );
480
530
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -501,6 +551,12 @@ function useAnimatableSharedValues(init) {
501
551
  const width = Animated.useSharedValue(init("width"));
502
552
  const height = Animated.useSharedValue(init("height"));
503
553
  const borderRadius = Animated.useSharedValue(init("borderRadius"));
554
+ const backgroundColor = Animated.useSharedValue(
555
+ init("backgroundColor")
556
+ );
557
+ const borderColor = Animated.useSharedValue(init("borderColor"));
558
+ const color = Animated.useSharedValue(init("color"));
559
+ const tintColor = Animated.useSharedValue(init("tintColor"));
504
560
  const ref = react.useRef(null);
505
561
  if (ref.current === null) {
506
562
  ref.current = {
@@ -513,7 +569,11 @@ function useAnimatableSharedValues(init) {
513
569
  opacity,
514
570
  width,
515
571
  height,
516
- borderRadius
572
+ borderRadius,
573
+ backgroundColor,
574
+ borderColor,
575
+ color,
576
+ tintColor
517
577
  };
518
578
  }
519
579
  return ref.current;
@@ -632,13 +692,13 @@ function resolveAnimateInput(animate, variants, controllerKey) {
632
692
  }
633
693
  function restValue(v) {
634
694
  if (v === void 0) return void 0;
635
- if (typeof v === "number") return v;
695
+ if (typeof v === "number" || typeof v === "string") return v;
636
696
  if (Array.isArray(v)) {
637
697
  return v.length > 0 ? restValue(v[0]) : void 0;
638
698
  }
639
699
  if (typeof v === "object" && v !== null && "to" in v) {
640
700
  const to = v.to;
641
- return typeof to === "number" ? to : void 0;
701
+ return typeof to === "number" || typeof to === "string" ? to : void 0;
642
702
  }
643
703
  return void 0;
644
704
  }
@@ -670,6 +730,7 @@ function mergeGestureTargets(base, gesture, active) {
670
730
  const subStates = [
671
731
  gesture.hovered,
672
732
  gesture.focused,
733
+ gesture.focusVisible,
673
734
  gesture.pressed
674
735
  ];
675
736
  for (const sub of subStates) {
@@ -692,6 +753,12 @@ function mergeGestureTargets(base, gesture, active) {
692
753
  gesture.focused
693
754
  );
694
755
  }
756
+ if (active.focusVisible && gesture.focusVisible) {
757
+ Object.assign(
758
+ merged,
759
+ gesture.focusVisible
760
+ );
761
+ }
695
762
  if (active.pressed && gesture.pressed) {
696
763
  Object.assign(
697
764
  merged,
@@ -700,7 +767,7 @@ function mergeGestureTargets(base, gesture, active) {
700
767
  }
701
768
  return merged;
702
769
  }
703
- function useGestureHandlers(gesture, rest, setPressed, setFocused, setHovered) {
770
+ function useGestureHandlers(gesture, rest, setPressed, setFocused, setFocusVisible, setHovered) {
704
771
  return react.useMemo(() => {
705
772
  if (!gesture) return {};
706
773
  const handlers = {};
@@ -714,9 +781,15 @@ function useGestureHandlers(gesture, rest, setPressed, setFocused, setHovered) {
714
781
  handlers.onPressIn = compose(rest.onPressIn, () => setPressed(true));
715
782
  handlers.onPressOut = compose(rest.onPressOut, () => setPressed(false));
716
783
  }
717
- if (gesture.focused) {
718
- handlers.onFocus = compose(rest.onFocus, () => setFocused(true));
719
- handlers.onBlur = compose(rest.onBlur, () => setFocused(false));
784
+ if (gesture.focused || gesture.focusVisible) {
785
+ handlers.onFocus = compose(rest.onFocus, () => {
786
+ if (gesture.focused) setFocused(true);
787
+ if (gesture.focusVisible && isFocusVisible()) setFocusVisible(true);
788
+ });
789
+ handlers.onBlur = compose(rest.onBlur, () => {
790
+ if (gesture.focused) setFocused(false);
791
+ if (gesture.focusVisible) setFocusVisible(false);
792
+ });
720
793
  }
721
794
  if (gesture.hovered) {
722
795
  handlers.onMouseEnter = compose(rest.onMouseEnter, () => setHovered(true));
@@ -729,6 +802,7 @@ function useGestureHandlers(gesture, rest, setPressed, setFocused, setHovered) {
729
802
  }, [
730
803
  gesture?.pressed ? 1 : 0,
731
804
  gesture?.focused ? 1 : 0,
805
+ gesture?.focusVisible ? 1 : 0,
732
806
  gesture?.hovered ? 1 : 0,
733
807
  rest.onTouchStart,
734
808
  rest.onTouchEnd,