@rootnative/inertia 0.0.0-alpha.5 → 0.0.0-alpha.7
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/CHANGELOG.md +6 -0
- package/dist/gestureLayer/index.d.mts +2 -2
- package/dist/gestureLayer/index.d.ts +2 -2
- package/dist/index.d.mts +63 -5
- package/dist/index.d.ts +63 -5
- package/dist/index.js +150 -0
- package/dist/index.mjs +150 -0
- package/dist/motion/Image.d.mts +1 -1
- package/dist/motion/Image.d.ts +1 -1
- package/dist/motion/Pressable.d.mts +1 -1
- package/dist/motion/Pressable.d.ts +1 -1
- package/dist/motion/ScrollView.d.mts +1 -1
- package/dist/motion/ScrollView.d.ts +1 -1
- package/dist/motion/Text.d.mts +1 -1
- package/dist/motion/Text.d.ts +1 -1
- package/dist/motion/View.d.mts +1 -1
- package/dist/motion/View.d.ts +1 -1
- package/dist/reanimated/index.d.mts +1 -0
- package/dist/reanimated/index.d.ts +1 -0
- package/dist/reanimated/index.js +46 -0
- package/dist/reanimated/index.mjs +1 -0
- package/dist/touch/index.d.mts +1 -1
- package/dist/touch/index.d.ts +1 -1
- package/dist/{types-BzEgiUdJ.d.mts → types-G1dBU2IQ.d.mts} +1 -1
- package/dist/{types-BzEgiUdJ.d.ts → types-G1dBU2IQ.d.ts} +1 -1
- package/dist/{useGesture-BRLgiNOC.d.ts → useGesture-D1e4ixJo.d.ts} +1 -1
- package/dist/{useGesture-C0GBS7d2.d.mts → useGesture-RFY6kTL3.d.mts} +1 -1
- package/llms.txt +5 -1
- package/package.json +8 -1
- package/src/index.ts +6 -0
- package/src/internal/boxShadow.ts +204 -0
- package/src/reanimated/index.ts +43 -0
- package/src/values/index.ts +1 -0
- package/src/values/useShadow.ts +76 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `@rootnative/inertia` are documented here. The format fol
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`useShadow` now interpolates CSS `boxShadow`.** The classic `shadow*`/`elevation` keys never reach the web renderer, so a `useShadow` elevation crossfade silently dropped its shadow on web — the platform where hover, its most common driver, matters most (gap surfaced by the RootNative UI Card migration). `ShadowConfig` gains `boxShadow?: string | BoxShadowLayer[]`: pass the CSS string form design systems store elevation tokens in (px lengths only; parsed once on the JS thread, `cubicBezier`-style — malformed tokens throw at setup rather than warning) or structured layers mirroring RN's `BoxShadowValue`. Multi-layer shadows interpolate per layer with CSS-transition padding semantics (shorter side padded with invisible layers; a genuine `inset` mismatch throws); blur clamps at 0 under springy overshoot. Emitted as a `boxShadow` style string — passed through as CSS by react-native-web and rendered natively on RN 0.76+ new architecture; keep `shadow*`/`elevation` alongside it for old-arch native. New `BoxShadowLayer` type exported from the root.
|
|
10
|
+
|
|
11
|
+
## [0.0.0-alpha.5] - 2026-07-21
|
|
12
|
+
|
|
7
13
|
### Changed
|
|
8
14
|
|
|
9
15
|
- **Non-worklet transformers and easings now dev-warn.** `useTransform`'s transformer overload and custom `timing.easing` functions must be worklets (`'worklet'` directive as the first statement). The previous "plain functions are auto-wrapped" promise was unfulfillable: the directive-wrapped fallback closes over the opaque function reference, not the shared values read inside it, so Reanimated cannot extract dependencies (a `useTransform` derived value silently only refreshed on React re-renders — found via a frozen TextField label float in the UI library) and native builds reject the plain function when the closure is serialized to the UI thread. The fallback wrapper remains as a web-only best effort, but both sites now `console.warn` once in dev (suppressed under Jest, where the shared stubs report every function as non-worklet). Docs (`transitions.md`, `layout.md`, `api/hooks.md`) and docstrings corrected to state the real contract.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SharedValue, AnimatedStyle } from 'react-native-reanimated';
|
|
2
|
-
import { U as UseGestureHandlers } from '../useGesture-
|
|
3
|
-
import {
|
|
2
|
+
import { U as UseGestureHandlers } from '../useGesture-RFY6kTL3.mjs';
|
|
3
|
+
import { a as TransitionInput, G as GestureLayerTransitions } from '../types-G1dBU2IQ.mjs';
|
|
4
4
|
import 'react';
|
|
5
5
|
import 'react-native';
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SharedValue, AnimatedStyle } from 'react-native-reanimated';
|
|
2
|
-
import { U as UseGestureHandlers } from '../useGesture-
|
|
3
|
-
import {
|
|
2
|
+
import { U as UseGestureHandlers } from '../useGesture-D1e4ixJo.js';
|
|
3
|
+
import { a as TransitionInput, G as GestureLayerTransitions } from '../types-G1dBU2IQ.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
import 'react-native';
|
|
6
6
|
|
package/dist/index.d.mts
CHANGED
|
@@ -2,10 +2,11 @@ import * as react from 'react';
|
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import * as react_native from 'react-native';
|
|
4
4
|
import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
|
|
5
|
-
import { M as MotionComponent, N as NamedTransitions,
|
|
6
|
-
export { c as AnimateStyle, d as AnimationCallbackInfo, D as DecayTransition, e as EasingFunction, f as EasingFunctionFactory,
|
|
5
|
+
import { M as MotionComponent, N as NamedTransitions, a as TransitionInput, T as TransitionConfig, A as AnimatableValue, E as EasingInput, S as SpringTransition, b as TransitionName, V as VariantController } from './types-G1dBU2IQ.mjs';
|
|
6
|
+
export { c as AnimateStyle, d as AnimationCallbackInfo, D as DecayTransition, e as EasingFunction, f as EasingFunctionFactory, g as GestureSubStates, h as MotionProps, i as NoAnimationTransition, P as PerPropertyTransition, R as RegisteredTransitions, j as RepeatConfig, k as SequenceStep, l as TimingTransition, m as Transition, n as VariantsMap } from './types-G1dBU2IQ.mjs';
|
|
7
7
|
import { SharedValue, useAnimatedStyle } from 'react-native-reanimated';
|
|
8
|
-
export {
|
|
8
|
+
export { SharedValue } from 'react-native-reanimated';
|
|
9
|
+
export { U as UseGestureHandlers, a as UseGestureResult, u as useGesture } from './useGesture-RFY6kTL3.mjs';
|
|
9
10
|
export { MotionImage } from './motion/Image.mjs';
|
|
10
11
|
export { MotionPressable } from './motion/Pressable.mjs';
|
|
11
12
|
export { MotionScrollView } from './motion/ScrollView.mjs';
|
|
@@ -555,11 +556,42 @@ interface UseScrollResult {
|
|
|
555
556
|
*/
|
|
556
557
|
declare function useScroll(): UseScrollResult;
|
|
557
558
|
|
|
559
|
+
/**
|
|
560
|
+
* CSS `box-shadow` parsing + pairing for `useShadow`.
|
|
561
|
+
*
|
|
562
|
+
* Design systems store web elevation tokens as CSS `box-shadow` strings
|
|
563
|
+
* (`'0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'`).
|
|
564
|
+
* Like `cubicBezier`, this module makes those tokens directly consumable:
|
|
565
|
+
* strings are parsed once on the JS thread into flat layer records the
|
|
566
|
+
* `useShadow` worklet can interpolate without any frame-time string work.
|
|
567
|
+
*
|
|
568
|
+
* Invalid input **throws** rather than warning — shadow tokens are
|
|
569
|
+
* constructed at theme/module setup, and a malformed token should fail
|
|
570
|
+
* loudly there, not silently render the wrong elevation.
|
|
571
|
+
*/
|
|
572
|
+
/**
|
|
573
|
+
* One layer of a `box-shadow`, structurally mirroring React Native's
|
|
574
|
+
* `BoxShadowValue` (RN 0.76+ `boxShadow` style). Lengths are px numbers.
|
|
575
|
+
*/
|
|
576
|
+
interface BoxShadowLayer {
|
|
577
|
+
offsetX: number;
|
|
578
|
+
offsetY: number;
|
|
579
|
+
/** Must be >= 0, per CSS. @default 0 */
|
|
580
|
+
blurRadius?: number;
|
|
581
|
+
/** @default 0 */
|
|
582
|
+
spreadDistance?: number;
|
|
583
|
+
/** Any color string Reanimated's `interpolateColor` accepts. @default 'black' */
|
|
584
|
+
color?: string;
|
|
585
|
+
/** @default false */
|
|
586
|
+
inset?: boolean;
|
|
587
|
+
}
|
|
588
|
+
|
|
558
589
|
/**
|
|
559
590
|
* Shape accepted on either end of a `useShadow` tween. Every field is
|
|
560
591
|
* optional — only keys present on at least one side participate in the
|
|
561
592
|
* output style. Mirrors the flat shadow keys on `Motion.View`'s `animate`
|
|
562
|
-
* surface, plus the nested `shadowOffset` source
|
|
593
|
+
* surface, plus the nested `shadowOffset` source and the CSS `boxShadow`
|
|
594
|
+
* surface.
|
|
563
595
|
*/
|
|
564
596
|
interface ShadowConfig {
|
|
565
597
|
shadowOpacity?: number;
|
|
@@ -571,6 +603,19 @@ interface ShadowConfig {
|
|
|
571
603
|
/** Android elevation. iOS shadow consumers can leave this off. */
|
|
572
604
|
elevation?: number;
|
|
573
605
|
shadowColor?: string;
|
|
606
|
+
/**
|
|
607
|
+
* CSS `box-shadow` — the shadow surface on web (react-native-web passes
|
|
608
|
+
* it through as CSS) and on React Native 0.76+ new-architecture native.
|
|
609
|
+
* Accepts the CSS string form design systems store elevation tokens in
|
|
610
|
+
* (`'0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'`;
|
|
611
|
+
* px lengths only) or structured layers. Multi-layer shadows interpolate
|
|
612
|
+
* per layer; when one side has fewer layers, it is padded with invisible
|
|
613
|
+
* layers, CSS-transition style. A malformed string **throws** at render
|
|
614
|
+
* (like `cubicBezier` — token mistakes should fail loudly at setup).
|
|
615
|
+
* The classic `shadow*`/`elevation` keys don't reach the web renderer —
|
|
616
|
+
* provide `boxShadow` alongside them when the tween must show up there.
|
|
617
|
+
*/
|
|
618
|
+
boxShadow?: string | readonly BoxShadowLayer[];
|
|
574
619
|
}
|
|
575
620
|
interface UseShadowOptions {
|
|
576
621
|
/** Shadow state at `progress === 0`. */
|
|
@@ -607,6 +652,19 @@ interface UseShadowOptions {
|
|
|
607
652
|
* `shadowColor`, `{ width: 0, height: 0 }` for `shadowOffset`). This is a
|
|
608
653
|
* pure interpolator — to "animate" the shadow, drive `progress` with a
|
|
609
654
|
* spring, timing, or gesture upstream.
|
|
655
|
+
*
|
|
656
|
+
* The classic `shadow*`/`elevation` keys don't render on web. When the
|
|
657
|
+
* tween must show up there (or on RN 0.76+ new-arch native via the CSS
|
|
658
|
+
* shadow model), provide `boxShadow` on both ends — CSS string tokens or
|
|
659
|
+
* structured layers; multi-layer shadows interpolate per layer:
|
|
660
|
+
*
|
|
661
|
+
* ```tsx
|
|
662
|
+
* const shadowStyle = useShadow({
|
|
663
|
+
* from: { boxShadow: theme.elevation.level1 }, // '0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'
|
|
664
|
+
* to: { boxShadow: theme.elevation.level2 },
|
|
665
|
+
* progress,
|
|
666
|
+
* })
|
|
667
|
+
* ```
|
|
610
668
|
*/
|
|
611
669
|
declare function useShadow({ from, to, progress, }: UseShadowOptions): ReturnType<typeof useAnimatedStyle>;
|
|
612
670
|
|
|
@@ -623,4 +681,4 @@ declare function useShadow({ from, to, progress, }: UseShadowOptions): ReturnTyp
|
|
|
623
681
|
*/
|
|
624
682
|
declare function useVariants<V extends Readonly<Record<string, object>>>(variants: V, initial?: keyof V & string): VariantController<keyof V & string>;
|
|
625
683
|
|
|
626
|
-
export { AnimatableValue, type ColorStyleKey, EasingInput, type ExtrapolationMode, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, NamedTransitions, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, TransitionConfig, TransitionInput, TransitionName, type UseColorTransitionOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useBooleanSpring, useColorTransition, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useTransform, useVariants };
|
|
684
|
+
export { AnimatableValue, type BoxShadowLayer, type ColorStyleKey, EasingInput, type ExtrapolationMode, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, NamedTransitions, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, TransitionConfig, TransitionInput, TransitionName, type UseColorTransitionOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useBooleanSpring, useColorTransition, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useTransform, useVariants };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import * as react from 'react';
|
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import * as react_native from 'react-native';
|
|
4
4
|
import { NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
|
|
5
|
-
import { M as MotionComponent, N as NamedTransitions,
|
|
6
|
-
export { c as AnimateStyle, d as AnimationCallbackInfo, D as DecayTransition, e as EasingFunction, f as EasingFunctionFactory,
|
|
5
|
+
import { M as MotionComponent, N as NamedTransitions, a as TransitionInput, T as TransitionConfig, A as AnimatableValue, E as EasingInput, S as SpringTransition, b as TransitionName, V as VariantController } from './types-G1dBU2IQ.js';
|
|
6
|
+
export { c as AnimateStyle, d as AnimationCallbackInfo, D as DecayTransition, e as EasingFunction, f as EasingFunctionFactory, g as GestureSubStates, h as MotionProps, i as NoAnimationTransition, P as PerPropertyTransition, R as RegisteredTransitions, j as RepeatConfig, k as SequenceStep, l as TimingTransition, m as Transition, n as VariantsMap } from './types-G1dBU2IQ.js';
|
|
7
7
|
import { SharedValue, useAnimatedStyle } from 'react-native-reanimated';
|
|
8
|
-
export {
|
|
8
|
+
export { SharedValue } from 'react-native-reanimated';
|
|
9
|
+
export { U as UseGestureHandlers, a as UseGestureResult, u as useGesture } from './useGesture-D1e4ixJo.js';
|
|
9
10
|
export { MotionImage } from './motion/Image.js';
|
|
10
11
|
export { MotionPressable } from './motion/Pressable.js';
|
|
11
12
|
export { MotionScrollView } from './motion/ScrollView.js';
|
|
@@ -555,11 +556,42 @@ interface UseScrollResult {
|
|
|
555
556
|
*/
|
|
556
557
|
declare function useScroll(): UseScrollResult;
|
|
557
558
|
|
|
559
|
+
/**
|
|
560
|
+
* CSS `box-shadow` parsing + pairing for `useShadow`.
|
|
561
|
+
*
|
|
562
|
+
* Design systems store web elevation tokens as CSS `box-shadow` strings
|
|
563
|
+
* (`'0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'`).
|
|
564
|
+
* Like `cubicBezier`, this module makes those tokens directly consumable:
|
|
565
|
+
* strings are parsed once on the JS thread into flat layer records the
|
|
566
|
+
* `useShadow` worklet can interpolate without any frame-time string work.
|
|
567
|
+
*
|
|
568
|
+
* Invalid input **throws** rather than warning — shadow tokens are
|
|
569
|
+
* constructed at theme/module setup, and a malformed token should fail
|
|
570
|
+
* loudly there, not silently render the wrong elevation.
|
|
571
|
+
*/
|
|
572
|
+
/**
|
|
573
|
+
* One layer of a `box-shadow`, structurally mirroring React Native's
|
|
574
|
+
* `BoxShadowValue` (RN 0.76+ `boxShadow` style). Lengths are px numbers.
|
|
575
|
+
*/
|
|
576
|
+
interface BoxShadowLayer {
|
|
577
|
+
offsetX: number;
|
|
578
|
+
offsetY: number;
|
|
579
|
+
/** Must be >= 0, per CSS. @default 0 */
|
|
580
|
+
blurRadius?: number;
|
|
581
|
+
/** @default 0 */
|
|
582
|
+
spreadDistance?: number;
|
|
583
|
+
/** Any color string Reanimated's `interpolateColor` accepts. @default 'black' */
|
|
584
|
+
color?: string;
|
|
585
|
+
/** @default false */
|
|
586
|
+
inset?: boolean;
|
|
587
|
+
}
|
|
588
|
+
|
|
558
589
|
/**
|
|
559
590
|
* Shape accepted on either end of a `useShadow` tween. Every field is
|
|
560
591
|
* optional — only keys present on at least one side participate in the
|
|
561
592
|
* output style. Mirrors the flat shadow keys on `Motion.View`'s `animate`
|
|
562
|
-
* surface, plus the nested `shadowOffset` source
|
|
593
|
+
* surface, plus the nested `shadowOffset` source and the CSS `boxShadow`
|
|
594
|
+
* surface.
|
|
563
595
|
*/
|
|
564
596
|
interface ShadowConfig {
|
|
565
597
|
shadowOpacity?: number;
|
|
@@ -571,6 +603,19 @@ interface ShadowConfig {
|
|
|
571
603
|
/** Android elevation. iOS shadow consumers can leave this off. */
|
|
572
604
|
elevation?: number;
|
|
573
605
|
shadowColor?: string;
|
|
606
|
+
/**
|
|
607
|
+
* CSS `box-shadow` — the shadow surface on web (react-native-web passes
|
|
608
|
+
* it through as CSS) and on React Native 0.76+ new-architecture native.
|
|
609
|
+
* Accepts the CSS string form design systems store elevation tokens in
|
|
610
|
+
* (`'0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'`;
|
|
611
|
+
* px lengths only) or structured layers. Multi-layer shadows interpolate
|
|
612
|
+
* per layer; when one side has fewer layers, it is padded with invisible
|
|
613
|
+
* layers, CSS-transition style. A malformed string **throws** at render
|
|
614
|
+
* (like `cubicBezier` — token mistakes should fail loudly at setup).
|
|
615
|
+
* The classic `shadow*`/`elevation` keys don't reach the web renderer —
|
|
616
|
+
* provide `boxShadow` alongside them when the tween must show up there.
|
|
617
|
+
*/
|
|
618
|
+
boxShadow?: string | readonly BoxShadowLayer[];
|
|
574
619
|
}
|
|
575
620
|
interface UseShadowOptions {
|
|
576
621
|
/** Shadow state at `progress === 0`. */
|
|
@@ -607,6 +652,19 @@ interface UseShadowOptions {
|
|
|
607
652
|
* `shadowColor`, `{ width: 0, height: 0 }` for `shadowOffset`). This is a
|
|
608
653
|
* pure interpolator — to "animate" the shadow, drive `progress` with a
|
|
609
654
|
* spring, timing, or gesture upstream.
|
|
655
|
+
*
|
|
656
|
+
* The classic `shadow*`/`elevation` keys don't render on web. When the
|
|
657
|
+
* tween must show up there (or on RN 0.76+ new-arch native via the CSS
|
|
658
|
+
* shadow model), provide `boxShadow` on both ends — CSS string tokens or
|
|
659
|
+
* structured layers; multi-layer shadows interpolate per layer:
|
|
660
|
+
*
|
|
661
|
+
* ```tsx
|
|
662
|
+
* const shadowStyle = useShadow({
|
|
663
|
+
* from: { boxShadow: theme.elevation.level1 }, // '0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'
|
|
664
|
+
* to: { boxShadow: theme.elevation.level2 },
|
|
665
|
+
* progress,
|
|
666
|
+
* })
|
|
667
|
+
* ```
|
|
610
668
|
*/
|
|
611
669
|
declare function useShadow({ from, to, progress, }: UseShadowOptions): ReturnType<typeof useAnimatedStyle>;
|
|
612
670
|
|
|
@@ -623,4 +681,4 @@ declare function useShadow({ from, to, progress, }: UseShadowOptions): ReturnTyp
|
|
|
623
681
|
*/
|
|
624
682
|
declare function useVariants<V extends Readonly<Record<string, object>>>(variants: V, initial?: keyof V & string): VariantController<keyof V & string>;
|
|
625
683
|
|
|
626
|
-
export { AnimatableValue, type ColorStyleKey, EasingInput, type ExtrapolationMode, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, NamedTransitions, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, TransitionConfig, TransitionInput, TransitionName, type UseColorTransitionOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useBooleanSpring, useColorTransition, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useTransform, useVariants };
|
|
684
|
+
export { AnimatableValue, type BoxShadowLayer, type ColorStyleKey, EasingInput, type ExtrapolationMode, Motion, MotionComponent, MotionConfig, type MotionConfigProps, type MotionConfigValue, NamedTransitions, Presence, type PresenceContextValue, type ReducedMotion, type ShadowConfig, SpringTransition, TransitionConfig, TransitionInput, TransitionName, type UseColorTransitionOptions, type UseScrollResult, type UseShadowOptions, type UseTransformOptions, VariantController, buildReleaseAnimation, createMotionComponent, cubicBezier, ensureWorkletEasing, resolveAnimatableValue, resolveNamedTransition, resolveTransition, useAnimation, useBooleanSpring, useColorTransition, useMotionConfig, useMotionValue, useNamedTransitions, usePresence, useScroll, useShadow, useShouldReduceMotion, useSpring, useTransform, useVariants };
|
package/dist/index.js
CHANGED
|
@@ -166,6 +166,127 @@ function useScroll() {
|
|
|
166
166
|
onScroll: handler
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
|
+
|
|
170
|
+
// src/internal/boxShadow.ts
|
|
171
|
+
var LENGTH = /^[+-]?(\d+\.?\d*|\.\d+)(px)?$/i;
|
|
172
|
+
var UNIT_LIKE = /^[+-]?(\d+\.?\d*|\.\d+)[a-z%]+$/i;
|
|
173
|
+
function invalid(input, reason) {
|
|
174
|
+
return new Error(
|
|
175
|
+
`[inertia] parseBoxShadow: ${reason} in ${JSON.stringify(input)}. Expected CSS box-shadow syntax with px lengths: '[inset] <offset-x> <offset-y> [blur] [spread] [color], ...'`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
function splitLayers(input) {
|
|
179
|
+
const layers = [];
|
|
180
|
+
let depth = 0;
|
|
181
|
+
let start = 0;
|
|
182
|
+
for (let i = 0; i < input.length; i++) {
|
|
183
|
+
const ch = input[i];
|
|
184
|
+
if (ch === "(") depth++;
|
|
185
|
+
else if (ch === ")") depth--;
|
|
186
|
+
else if (ch === "," && depth === 0) {
|
|
187
|
+
layers.push(input.slice(start, i));
|
|
188
|
+
start = i + 1;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
layers.push(input.slice(start));
|
|
192
|
+
return layers;
|
|
193
|
+
}
|
|
194
|
+
function tokenize(layer) {
|
|
195
|
+
const tokens = [];
|
|
196
|
+
let depth = 0;
|
|
197
|
+
let current = "";
|
|
198
|
+
for (const ch of layer) {
|
|
199
|
+
if (ch === "(") depth++;
|
|
200
|
+
else if (ch === ")") depth--;
|
|
201
|
+
if (depth === 0 && /\s/.test(ch)) {
|
|
202
|
+
if (current) tokens.push(current);
|
|
203
|
+
current = "";
|
|
204
|
+
} else {
|
|
205
|
+
current += ch;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (current) tokens.push(current);
|
|
209
|
+
return tokens;
|
|
210
|
+
}
|
|
211
|
+
function parseBoxShadow(input) {
|
|
212
|
+
const trimmed = input.trim();
|
|
213
|
+
if (trimmed === "") throw invalid(input, "empty string");
|
|
214
|
+
if (trimmed.toLowerCase() === "none") return [];
|
|
215
|
+
return splitLayers(trimmed).map((layerString) => {
|
|
216
|
+
const tokens = tokenize(layerString.trim());
|
|
217
|
+
if (tokens.length === 0) throw invalid(input, "empty layer");
|
|
218
|
+
const lengths = [];
|
|
219
|
+
let color;
|
|
220
|
+
let inset = false;
|
|
221
|
+
for (const token of tokens) {
|
|
222
|
+
if (token.toLowerCase() === "inset") {
|
|
223
|
+
if (inset) throw invalid(input, "duplicate 'inset'");
|
|
224
|
+
inset = true;
|
|
225
|
+
} else if (LENGTH.test(token)) {
|
|
226
|
+
lengths.push(parseFloat(token));
|
|
227
|
+
} else if (UNIT_LIKE.test(token)) {
|
|
228
|
+
throw invalid(input, `unsupported unit in ${JSON.stringify(token)}`);
|
|
229
|
+
} else {
|
|
230
|
+
if (color !== void 0) throw invalid(input, "multiple colors");
|
|
231
|
+
color = token;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (lengths.length < 2 || lengths.length > 4) {
|
|
235
|
+
throw invalid(input, `expected 2-4 lengths, got ${lengths.length}`);
|
|
236
|
+
}
|
|
237
|
+
const [offsetX = 0, offsetY = 0, blurRadius = 0, spreadDistance = 0] = lengths;
|
|
238
|
+
if (blurRadius < 0) throw invalid(input, "negative blur radius");
|
|
239
|
+
return {
|
|
240
|
+
offsetX,
|
|
241
|
+
offsetY,
|
|
242
|
+
blurRadius,
|
|
243
|
+
spreadDistance,
|
|
244
|
+
color: color ?? "black",
|
|
245
|
+
inset
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function resolveBoxShadowInput(input) {
|
|
250
|
+
if (input === void 0) return [];
|
|
251
|
+
if (typeof input === "string") return parseBoxShadow(input);
|
|
252
|
+
return input.map((layer) => ({
|
|
253
|
+
offsetX: layer.offsetX,
|
|
254
|
+
offsetY: layer.offsetY,
|
|
255
|
+
blurRadius: layer.blurRadius ?? 0,
|
|
256
|
+
spreadDistance: layer.spreadDistance ?? 0,
|
|
257
|
+
color: layer.color ?? "black",
|
|
258
|
+
inset: layer.inset ?? false
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
function pairBoxShadowLayers(from, to) {
|
|
262
|
+
const count = Math.max(from.length, to.length);
|
|
263
|
+
const pairs = [];
|
|
264
|
+
for (let i = 0; i < count; i++) {
|
|
265
|
+
const a = from[i];
|
|
266
|
+
const b = to[i];
|
|
267
|
+
const fromLayer = a ?? invisibleLayer(b.inset);
|
|
268
|
+
const toLayer = b ?? invisibleLayer(a.inset);
|
|
269
|
+
if (fromLayer.inset !== toLayer.inset) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`[inertia] useShadow: boxShadow layer ${i} is 'inset' on one side but not the other \u2014 inset cannot be interpolated. Give both sides the same inset-ness (pad with a transparent layer if needed).`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
pairs.push({ from: fromLayer, to: toLayer });
|
|
275
|
+
}
|
|
276
|
+
return pairs;
|
|
277
|
+
}
|
|
278
|
+
function invisibleLayer(inset) {
|
|
279
|
+
return {
|
|
280
|
+
offsetX: 0,
|
|
281
|
+
offsetY: 0,
|
|
282
|
+
blurRadius: 0,
|
|
283
|
+
spreadDistance: 0,
|
|
284
|
+
color: "transparent",
|
|
285
|
+
inset
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/values/useShadow.ts
|
|
169
290
|
function useShadow({
|
|
170
291
|
from,
|
|
171
292
|
to,
|
|
@@ -176,6 +297,10 @@ function useShadow({
|
|
|
176
297
|
const hasElevation = from.elevation !== void 0 || to.elevation !== void 0;
|
|
177
298
|
const hasColor = from.shadowColor !== void 0 || to.shadowColor !== void 0;
|
|
178
299
|
const hasOffset = from.shadowOffset !== void 0 || to.shadowOffset !== void 0;
|
|
300
|
+
const boxShadowPairs = from.boxShadow !== void 0 || to.boxShadow !== void 0 ? pairBoxShadowLayers(
|
|
301
|
+
resolveBoxShadowInput(from.boxShadow),
|
|
302
|
+
resolveBoxShadowInput(to.boxShadow)
|
|
303
|
+
) : [];
|
|
179
304
|
const opacityFrom = from.shadowOpacity ?? 0;
|
|
180
305
|
const opacityTo = to.shadowOpacity ?? 0;
|
|
181
306
|
const radiusFrom = from.shadowRadius ?? 0;
|
|
@@ -210,6 +335,31 @@ function useShadow({
|
|
|
210
335
|
height: reactNativeReanimated.interpolate(t, [0, 1], [offsetHFrom, offsetHTo])
|
|
211
336
|
};
|
|
212
337
|
}
|
|
338
|
+
if (boxShadowPairs.length > 0) {
|
|
339
|
+
let css = "";
|
|
340
|
+
let first = true;
|
|
341
|
+
for (const pair of boxShadowPairs) {
|
|
342
|
+
const x = reactNativeReanimated.interpolate(t, [0, 1], [pair.from.offsetX, pair.to.offsetX]);
|
|
343
|
+
const y = reactNativeReanimated.interpolate(t, [0, 1], [pair.from.offsetY, pair.to.offsetY]);
|
|
344
|
+
const blur = Math.max(
|
|
345
|
+
0,
|
|
346
|
+
reactNativeReanimated.interpolate(t, [0, 1], [pair.from.blurRadius, pair.to.blurRadius])
|
|
347
|
+
);
|
|
348
|
+
const spread = reactNativeReanimated.interpolate(
|
|
349
|
+
t,
|
|
350
|
+
[0, 1],
|
|
351
|
+
[pair.from.spreadDistance, pair.to.spreadDistance]
|
|
352
|
+
);
|
|
353
|
+
const color = reactNativeReanimated.interpolateColor(
|
|
354
|
+
t,
|
|
355
|
+
[0, 1],
|
|
356
|
+
[pair.from.color, pair.to.color]
|
|
357
|
+
);
|
|
358
|
+
css += (first ? "" : ", ") + (pair.from.inset ? "inset " : "") + `${x}px ${y}px ${blur}px ${spread}px ${color}`;
|
|
359
|
+
first = false;
|
|
360
|
+
}
|
|
361
|
+
out.boxShadow = css;
|
|
362
|
+
}
|
|
213
363
|
return out;
|
|
214
364
|
});
|
|
215
365
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -171,6 +171,127 @@ function useScroll() {
|
|
|
171
171
|
onScroll: handler
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
+
|
|
175
|
+
// src/internal/boxShadow.ts
|
|
176
|
+
var LENGTH = /^[+-]?(\d+\.?\d*|\.\d+)(px)?$/i;
|
|
177
|
+
var UNIT_LIKE = /^[+-]?(\d+\.?\d*|\.\d+)[a-z%]+$/i;
|
|
178
|
+
function invalid(input, reason) {
|
|
179
|
+
return new Error(
|
|
180
|
+
`[inertia] parseBoxShadow: ${reason} in ${JSON.stringify(input)}. Expected CSS box-shadow syntax with px lengths: '[inset] <offset-x> <offset-y> [blur] [spread] [color], ...'`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
function splitLayers(input) {
|
|
184
|
+
const layers = [];
|
|
185
|
+
let depth = 0;
|
|
186
|
+
let start = 0;
|
|
187
|
+
for (let i = 0; i < input.length; i++) {
|
|
188
|
+
const ch = input[i];
|
|
189
|
+
if (ch === "(") depth++;
|
|
190
|
+
else if (ch === ")") depth--;
|
|
191
|
+
else if (ch === "," && depth === 0) {
|
|
192
|
+
layers.push(input.slice(start, i));
|
|
193
|
+
start = i + 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
layers.push(input.slice(start));
|
|
197
|
+
return layers;
|
|
198
|
+
}
|
|
199
|
+
function tokenize(layer) {
|
|
200
|
+
const tokens = [];
|
|
201
|
+
let depth = 0;
|
|
202
|
+
let current = "";
|
|
203
|
+
for (const ch of layer) {
|
|
204
|
+
if (ch === "(") depth++;
|
|
205
|
+
else if (ch === ")") depth--;
|
|
206
|
+
if (depth === 0 && /\s/.test(ch)) {
|
|
207
|
+
if (current) tokens.push(current);
|
|
208
|
+
current = "";
|
|
209
|
+
} else {
|
|
210
|
+
current += ch;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (current) tokens.push(current);
|
|
214
|
+
return tokens;
|
|
215
|
+
}
|
|
216
|
+
function parseBoxShadow(input) {
|
|
217
|
+
const trimmed = input.trim();
|
|
218
|
+
if (trimmed === "") throw invalid(input, "empty string");
|
|
219
|
+
if (trimmed.toLowerCase() === "none") return [];
|
|
220
|
+
return splitLayers(trimmed).map((layerString) => {
|
|
221
|
+
const tokens = tokenize(layerString.trim());
|
|
222
|
+
if (tokens.length === 0) throw invalid(input, "empty layer");
|
|
223
|
+
const lengths = [];
|
|
224
|
+
let color;
|
|
225
|
+
let inset = false;
|
|
226
|
+
for (const token of tokens) {
|
|
227
|
+
if (token.toLowerCase() === "inset") {
|
|
228
|
+
if (inset) throw invalid(input, "duplicate 'inset'");
|
|
229
|
+
inset = true;
|
|
230
|
+
} else if (LENGTH.test(token)) {
|
|
231
|
+
lengths.push(parseFloat(token));
|
|
232
|
+
} else if (UNIT_LIKE.test(token)) {
|
|
233
|
+
throw invalid(input, `unsupported unit in ${JSON.stringify(token)}`);
|
|
234
|
+
} else {
|
|
235
|
+
if (color !== void 0) throw invalid(input, "multiple colors");
|
|
236
|
+
color = token;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (lengths.length < 2 || lengths.length > 4) {
|
|
240
|
+
throw invalid(input, `expected 2-4 lengths, got ${lengths.length}`);
|
|
241
|
+
}
|
|
242
|
+
const [offsetX = 0, offsetY = 0, blurRadius = 0, spreadDistance = 0] = lengths;
|
|
243
|
+
if (blurRadius < 0) throw invalid(input, "negative blur radius");
|
|
244
|
+
return {
|
|
245
|
+
offsetX,
|
|
246
|
+
offsetY,
|
|
247
|
+
blurRadius,
|
|
248
|
+
spreadDistance,
|
|
249
|
+
color: color ?? "black",
|
|
250
|
+
inset
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function resolveBoxShadowInput(input) {
|
|
255
|
+
if (input === void 0) return [];
|
|
256
|
+
if (typeof input === "string") return parseBoxShadow(input);
|
|
257
|
+
return input.map((layer) => ({
|
|
258
|
+
offsetX: layer.offsetX,
|
|
259
|
+
offsetY: layer.offsetY,
|
|
260
|
+
blurRadius: layer.blurRadius ?? 0,
|
|
261
|
+
spreadDistance: layer.spreadDistance ?? 0,
|
|
262
|
+
color: layer.color ?? "black",
|
|
263
|
+
inset: layer.inset ?? false
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
function pairBoxShadowLayers(from, to) {
|
|
267
|
+
const count = Math.max(from.length, to.length);
|
|
268
|
+
const pairs = [];
|
|
269
|
+
for (let i = 0; i < count; i++) {
|
|
270
|
+
const a = from[i];
|
|
271
|
+
const b = to[i];
|
|
272
|
+
const fromLayer = a ?? invisibleLayer(b.inset);
|
|
273
|
+
const toLayer = b ?? invisibleLayer(a.inset);
|
|
274
|
+
if (fromLayer.inset !== toLayer.inset) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
`[inertia] useShadow: boxShadow layer ${i} is 'inset' on one side but not the other \u2014 inset cannot be interpolated. Give both sides the same inset-ness (pad with a transparent layer if needed).`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
pairs.push({ from: fromLayer, to: toLayer });
|
|
280
|
+
}
|
|
281
|
+
return pairs;
|
|
282
|
+
}
|
|
283
|
+
function invisibleLayer(inset) {
|
|
284
|
+
return {
|
|
285
|
+
offsetX: 0,
|
|
286
|
+
offsetY: 0,
|
|
287
|
+
blurRadius: 0,
|
|
288
|
+
spreadDistance: 0,
|
|
289
|
+
color: "transparent",
|
|
290
|
+
inset
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/values/useShadow.ts
|
|
174
295
|
function useShadow({
|
|
175
296
|
from,
|
|
176
297
|
to,
|
|
@@ -181,6 +302,10 @@ function useShadow({
|
|
|
181
302
|
const hasElevation = from.elevation !== void 0 || to.elevation !== void 0;
|
|
182
303
|
const hasColor = from.shadowColor !== void 0 || to.shadowColor !== void 0;
|
|
183
304
|
const hasOffset = from.shadowOffset !== void 0 || to.shadowOffset !== void 0;
|
|
305
|
+
const boxShadowPairs = from.boxShadow !== void 0 || to.boxShadow !== void 0 ? pairBoxShadowLayers(
|
|
306
|
+
resolveBoxShadowInput(from.boxShadow),
|
|
307
|
+
resolveBoxShadowInput(to.boxShadow)
|
|
308
|
+
) : [];
|
|
184
309
|
const opacityFrom = from.shadowOpacity ?? 0;
|
|
185
310
|
const opacityTo = to.shadowOpacity ?? 0;
|
|
186
311
|
const radiusFrom = from.shadowRadius ?? 0;
|
|
@@ -215,6 +340,31 @@ function useShadow({
|
|
|
215
340
|
height: interpolate(t, [0, 1], [offsetHFrom, offsetHTo])
|
|
216
341
|
};
|
|
217
342
|
}
|
|
343
|
+
if (boxShadowPairs.length > 0) {
|
|
344
|
+
let css = "";
|
|
345
|
+
let first = true;
|
|
346
|
+
for (const pair of boxShadowPairs) {
|
|
347
|
+
const x = interpolate(t, [0, 1], [pair.from.offsetX, pair.to.offsetX]);
|
|
348
|
+
const y = interpolate(t, [0, 1], [pair.from.offsetY, pair.to.offsetY]);
|
|
349
|
+
const blur = Math.max(
|
|
350
|
+
0,
|
|
351
|
+
interpolate(t, [0, 1], [pair.from.blurRadius, pair.to.blurRadius])
|
|
352
|
+
);
|
|
353
|
+
const spread = interpolate(
|
|
354
|
+
t,
|
|
355
|
+
[0, 1],
|
|
356
|
+
[pair.from.spreadDistance, pair.to.spreadDistance]
|
|
357
|
+
);
|
|
358
|
+
const color = interpolateColor(
|
|
359
|
+
t,
|
|
360
|
+
[0, 1],
|
|
361
|
+
[pair.from.color, pair.to.color]
|
|
362
|
+
);
|
|
363
|
+
css += (first ? "" : ", ") + (pair.from.inset ? "inset " : "") + `${x}px ${y}px ${blur}px ${spread}px ${color}`;
|
|
364
|
+
first = false;
|
|
365
|
+
}
|
|
366
|
+
out.boxShadow = css;
|
|
367
|
+
}
|
|
218
368
|
return out;
|
|
219
369
|
});
|
|
220
370
|
}
|
package/dist/motion/Image.d.mts
CHANGED
package/dist/motion/Image.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import * as react_native from 'react-native';
|
|
3
|
-
import { M as MotionComponent } from '../types-
|
|
3
|
+
import { M as MotionComponent } from '../types-G1dBU2IQ.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Animatable `Pressable`. The `gesture` prop's `pressed` sub-state hooks into
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import * as react_native from 'react-native';
|
|
3
|
-
import { M as MotionComponent } from '../types-
|
|
3
|
+
import { M as MotionComponent } from '../types-G1dBU2IQ.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Animatable `Pressable`. The `gesture` prop's `pressed` sub-state hooks into
|
package/dist/motion/Text.d.mts
CHANGED
package/dist/motion/Text.d.ts
CHANGED
package/dist/motion/View.d.mts
CHANGED
package/dist/motion/View.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Animated, AnimatedProps, AnimatedStyle, DerivedValue, Extrapolation, SharedValue, cancelAnimation, createAnimatedComponent, interpolate, interpolateColor, useAnimatedProps, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Animated, AnimatedProps, AnimatedStyle, DerivedValue, Extrapolation, SharedValue, cancelAnimation, createAnimatedComponent, interpolate, interpolateColor, useAnimatedProps, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactNativeReanimated = require('react-native-reanimated');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var reactNativeReanimated__default = /*#__PURE__*/_interopDefault(reactNativeReanimated);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, "Animated", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return reactNativeReanimated__default.default; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "Extrapolation", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return reactNativeReanimated.Extrapolation; }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "cancelAnimation", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return reactNativeReanimated.cancelAnimation; }
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports, "createAnimatedComponent", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return reactNativeReanimated.createAnimatedComponent; }
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(exports, "interpolate", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return reactNativeReanimated.interpolate; }
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(exports, "interpolateColor", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return reactNativeReanimated.interpolateColor; }
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(exports, "useAnimatedProps", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
get: function () { return reactNativeReanimated.useAnimatedProps; }
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(exports, "useAnimatedStyle", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function () { return reactNativeReanimated.useAnimatedStyle; }
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(exports, "useDerivedValue", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function () { return reactNativeReanimated.useDerivedValue; }
|
|
46
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Animated, Extrapolation, cancelAnimation, createAnimatedComponent, interpolate, interpolateColor, useAnimatedProps, useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
|
package/dist/touch/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PanResponderInstance } from 'react-native';
|
|
2
2
|
import { useAnimatedStyle, SharedValue } from 'react-native-reanimated';
|
|
3
|
-
import {
|
|
3
|
+
import { T as TransitionConfig } from '../types-G1dBU2IQ.mjs';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/touch/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PanResponderInstance } from 'react-native';
|
|
2
2
|
import { useAnimatedStyle, SharedValue } from 'react-native-reanimated';
|
|
3
|
-
import {
|
|
3
|
+
import { T as TransitionConfig } from '../types-G1dBU2IQ.js';
|
|
4
4
|
import 'react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -385,4 +385,4 @@ interface MotionComponent<C extends ComponentType<any>> {
|
|
|
385
385
|
displayName?: string;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
export type { AnimatableValue as A, DecayTransition as D, EasingInput as E,
|
|
388
|
+
export type { AnimatableValue as A, DecayTransition as D, EasingInput as E, GestureLayerTransitions as G, MotionComponent as M, NamedTransitions as N, PerPropertyTransition as P, RegisteredTransitions as R, SpringTransition as S, TransitionConfig as T, VariantController as V, TransitionInput as a, TransitionName as b, AnimateStyle as c, AnimationCallbackInfo as d, EasingFunction as e, EasingFunctionFactory as f, GestureSubStates as g, MotionProps as h, NoAnimationTransition as i, RepeatConfig as j, SequenceStep as k, TimingTransition as l, Transition as m, VariantsMap as n };
|
|
@@ -385,4 +385,4 @@ interface MotionComponent<C extends ComponentType<any>> {
|
|
|
385
385
|
displayName?: string;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
export type { AnimatableValue as A, DecayTransition as D, EasingInput as E,
|
|
388
|
+
export type { AnimatableValue as A, DecayTransition as D, EasingInput as E, GestureLayerTransitions as G, MotionComponent as M, NamedTransitions as N, PerPropertyTransition as P, RegisteredTransitions as R, SpringTransition as S, TransitionConfig as T, VariantController as V, TransitionInput as a, TransitionName as b, AnimateStyle as c, AnimationCallbackInfo as d, EasingFunction as e, EasingFunctionFactory as f, GestureSubStates as g, MotionProps as h, NoAnimationTransition as i, RepeatConfig as j, SequenceStep as k, TimingTransition as l, Transition as m, VariantsMap as n };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SharedValue } from 'react-native-reanimated';
|
|
2
|
-
import {
|
|
2
|
+
import { a as TransitionInput, G as GestureLayerTransitions } from './types-G1dBU2IQ.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Handler bag returned by `useGesture`. Spread on a `Pressable` to drive the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SharedValue } from 'react-native-reanimated';
|
|
2
|
-
import {
|
|
2
|
+
import { a as TransitionInput, G as GestureLayerTransitions } from './types-G1dBU2IQ.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Handler bag returned by `useGesture`. Spread on a `Pressable` to drive the
|
package/llms.txt
CHANGED
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
} from '@rootnative/inertia'
|
|
31
31
|
// Opt-in MD3 / iOS state-layer overlay helper:
|
|
32
32
|
import { useGestureLayer } from '@rootnative/inertia/gesture-layer'
|
|
33
|
+
// Render-layer Reanimated interop for custom animated components:
|
|
34
|
+
import { Animated, useAnimatedStyle } from '@rootnative/inertia/reanimated'
|
|
33
35
|
// or for tree-shaking the primitives:
|
|
34
36
|
import { MotionView } from '@rootnative/inertia/view'
|
|
35
37
|
import { MotionText } from '@rootnative/inertia/text'
|
|
@@ -51,7 +53,7 @@ import { MotionScrollView } from '@rootnative/inertia/scroll-view'
|
|
|
51
53
|
- `useSpring(target, config?)` — spring-only shorthand. Animates a `SharedValue<number>` toward `target` with react-spring vocab (or a spring-typed registered `TransitionName`; non-spring names warn and fall back to the default spring). `target` may be a plain number (effect-driven) or a `SharedValue<number>` (UI-thread reaction); the latter is the gesture-smoothing path.
|
|
52
54
|
- `useBooleanSpring(active, config?)` — sugar over `useSpring` for the recurring "spring 0↔1 progress from a boolean" shape (checkbox checks, accordion expansions, drawer open/closed). Identical mechanics to `useSpring(active ? 1 : 0, config)` — the named form so the call site reads as the boolean it represents.
|
|
53
55
|
- `useTransform(value, inputRange, outputRange, options?)` / `useTransform(transformer)` — interpolate a numeric shared value onto a number or color range, or derive any value from any number of shared values via a worklet. Non-worklet transformers are auto-wrapped.
|
|
54
|
-
- `useShadow({ from, to, progress })` — pure value-layer interpolator between two `ShadowConfig`s (`shadowOpacity` / `shadowRadius` / `shadowOffset` / `elevation` / `shadowColor`) driven by a `SharedValue<number>` (0→1). Returns an animated style fragment to spread onto `style`; only emits keys present on either side, absent sides default to natural zero. The hook does not animate on its own — drive `progress` with a spring, a scroll-derived `useTransform`, or any other shared value source.
|
|
56
|
+
- `useShadow({ from, to, progress })` — pure value-layer interpolator between two `ShadowConfig`s (`shadowOpacity` / `shadowRadius` / `shadowOffset` / `elevation` / `shadowColor`, plus `boxShadow` as a CSS string or `BoxShadowLayer[]` — the shadow surface on web and RN 0.76+ new-arch; multi-layer, CSS-transition padding semantics, malformed strings throw at setup) driven by a `SharedValue<number>` (0→1). Returns an animated style fragment to spread onto `style`; only emits keys present on either side, absent sides default to natural zero. The hook does not animate on its own — drive `progress` with a spring, a scroll-derived `useTransform`, or any other shared value source.
|
|
55
57
|
- `useColorTransition(progress, [from, to], options?)` — pure value-layer interpolator for a single color channel, driven by a `SharedValue<number>` (0→1). Returns an animated style fragment with one color key (default `backgroundColor`; configurable via `options.key` to `color` / `borderColor` / `tintColor` / `shadowColor` / per-side border colors). For raw `SharedValue<string>` output, use `useTransform(progress, [0, 1], [from, to])` instead.
|
|
56
58
|
- `useScroll()` — returns `{ scrollX, scrollY, onScroll }` for use with `Motion.ScrollView`. Scroll events fire on the UI thread.
|
|
57
59
|
- `createMotionComponent<C>(C)` — wrap any component with the same Motion prop surface, inferring style from `C`.
|
|
@@ -60,6 +62,8 @@ import { MotionScrollView } from '@rootnative/inertia/scroll-view'
|
|
|
60
62
|
- `resolveAnimatableValue(value, base, factory?)` — one level up: resolves a full per-property `animate` value (plain target, `{ to, ...override }` step, or keyframe array → `withSequence` with sequence-level `repeat`) into one animation. Gives custom components the complete `animate` grammar.
|
|
61
63
|
- `cubicBezier(x1, y1, x2, y2)` / `cubicBezier(css)` — builds a `timing.easing` value from cubic-bezier control points: four numbers, a W3C CSS `cubic-bezier(0.2, 0, 0, 1)` string (design-token form), or a CSS keyword (`'linear'` | `'ease'` | `'ease-in'` | `'ease-out'` | `'ease-in-out'`). Makes CSS-format easing tokens directly consumable — pair with named transitions to feed a theme's motion tokens into the registry. Invalid input throws (`x1`/`x2` must be within [0, 1]; `step-*` and `linear(...)` unsupported).
|
|
62
64
|
- `ensureWorkletEasing(easing)` — normalizes any accepted easing input (plain function, worklet, or Reanimated 4 `EasingFunctionFactory`) into a worklet `withTiming` can consume. Only needed for manual `withTiming` calls — the resolvers and primitives apply it automatically.
|
|
65
|
+
- `SharedValue<T>` (type) — re-exported from the root entry so props/refs that carry a value-layer hook result can be typed without importing from `react-native-reanimated`.
|
|
66
|
+
- `@rootnative/inertia/reanimated` — render-layer interop subpath: pure re-exports of `Animated`, `createAnimatedComponent`, `useAnimatedStyle`, `useAnimatedProps`, `useDerivedValue`, `interpolate`, `interpolateColor`, `Extrapolation`, `cancelAnimation` (plus `SharedValue` / `DerivedValue` / `AnimatedStyle` / `AnimatedProps` types) under their original Reanimated names, for components that own their own `useAnimatedStyle` worklet over Inertia-driven shared values. Names must stay original — the Reanimated Babel plugin auto-workletizes callbacks by callee name. The `with*` animation factories are deliberately not re-exported; start animations with `useAnimation` / `resolveTransition` / `buildReleaseAnimation`.
|
|
63
67
|
|
|
64
68
|
## Motion props
|
|
65
69
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootnative/inertia",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.7",
|
|
4
4
|
"description": "Declarative animation primitives for React Native, built on react-native-reanimated.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "RootNative",
|
|
@@ -93,6 +93,13 @@
|
|
|
93
93
|
"import": "./dist/gestureLayer/index.mjs",
|
|
94
94
|
"require": "./dist/gestureLayer/index.js"
|
|
95
95
|
},
|
|
96
|
+
"./reanimated": {
|
|
97
|
+
"types": "./dist/reanimated/index.d.ts",
|
|
98
|
+
"react-native": "./src/reanimated/index.ts",
|
|
99
|
+
"source": "./src/reanimated/index.ts",
|
|
100
|
+
"import": "./dist/reanimated/index.mjs",
|
|
101
|
+
"require": "./dist/reanimated/index.js"
|
|
102
|
+
},
|
|
96
103
|
"./jest-preset": "./jest-preset.js",
|
|
97
104
|
"./jest-setup": "./jest-setup.js",
|
|
98
105
|
"./package.json": "./package.json"
|
package/src/index.ts
CHANGED
|
@@ -51,7 +51,13 @@ export {
|
|
|
51
51
|
useTransform,
|
|
52
52
|
useVariants,
|
|
53
53
|
} from './values'
|
|
54
|
+
// The value-layer hooks above all return Reanimated `SharedValue`s — export
|
|
55
|
+
// the type so consumers can annotate props/refs that carry one (e.g. a
|
|
56
|
+
// scroll-offset prop) without importing from `react-native-reanimated`.
|
|
57
|
+
// Runtime render-layer interop lives in `@rootnative/inertia/reanimated`.
|
|
58
|
+
export type { SharedValue } from 'react-native-reanimated'
|
|
54
59
|
export type {
|
|
60
|
+
BoxShadowLayer,
|
|
55
61
|
ColorStyleKey,
|
|
56
62
|
ExtrapolationMode,
|
|
57
63
|
ShadowConfig,
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS `box-shadow` parsing + pairing for `useShadow`.
|
|
3
|
+
*
|
|
4
|
+
* Design systems store web elevation tokens as CSS `box-shadow` strings
|
|
5
|
+
* (`'0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'`).
|
|
6
|
+
* Like `cubicBezier`, this module makes those tokens directly consumable:
|
|
7
|
+
* strings are parsed once on the JS thread into flat layer records the
|
|
8
|
+
* `useShadow` worklet can interpolate without any frame-time string work.
|
|
9
|
+
*
|
|
10
|
+
* Invalid input **throws** rather than warning — shadow tokens are
|
|
11
|
+
* constructed at theme/module setup, and a malformed token should fail
|
|
12
|
+
* loudly there, not silently render the wrong elevation.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One layer of a `box-shadow`, structurally mirroring React Native's
|
|
17
|
+
* `BoxShadowValue` (RN 0.76+ `boxShadow` style). Lengths are px numbers.
|
|
18
|
+
*/
|
|
19
|
+
export interface BoxShadowLayer {
|
|
20
|
+
offsetX: number
|
|
21
|
+
offsetY: number
|
|
22
|
+
/** Must be >= 0, per CSS. @default 0 */
|
|
23
|
+
blurRadius?: number
|
|
24
|
+
/** @default 0 */
|
|
25
|
+
spreadDistance?: number
|
|
26
|
+
/** Any color string Reanimated's `interpolateColor` accepts. @default 'black' */
|
|
27
|
+
color?: string
|
|
28
|
+
/** @default false */
|
|
29
|
+
inset?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A layer with every field resolved to a concrete value. */
|
|
33
|
+
export interface ResolvedBoxShadowLayer {
|
|
34
|
+
offsetX: number
|
|
35
|
+
offsetY: number
|
|
36
|
+
blurRadius: number
|
|
37
|
+
spreadDistance: number
|
|
38
|
+
color: string
|
|
39
|
+
inset: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const LENGTH = /^[+-]?(\d+\.?\d*|\.\d+)(px)?$/i
|
|
43
|
+
const UNIT_LIKE = /^[+-]?(\d+\.?\d*|\.\d+)[a-z%]+$/i
|
|
44
|
+
|
|
45
|
+
function invalid(input: string, reason: string): Error {
|
|
46
|
+
return new Error(
|
|
47
|
+
`[inertia] parseBoxShadow: ${reason} in ${JSON.stringify(input)}. ` +
|
|
48
|
+
'Expected CSS box-shadow syntax with px lengths: ' +
|
|
49
|
+
"'[inset] <offset-x> <offset-y> [blur] [spread] [color], ...'",
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Split a box-shadow string into layer strings on top-level commas —
|
|
55
|
+
* commas inside color functions (`rgba(0, 0, 0, 0.3)`) don't split.
|
|
56
|
+
*/
|
|
57
|
+
function splitLayers(input: string): string[] {
|
|
58
|
+
const layers: string[] = []
|
|
59
|
+
let depth = 0
|
|
60
|
+
let start = 0
|
|
61
|
+
for (let i = 0; i < input.length; i++) {
|
|
62
|
+
const ch = input[i]
|
|
63
|
+
if (ch === '(') depth++
|
|
64
|
+
else if (ch === ')') depth--
|
|
65
|
+
else if (ch === ',' && depth === 0) {
|
|
66
|
+
layers.push(input.slice(start, i))
|
|
67
|
+
start = i + 1
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
layers.push(input.slice(start))
|
|
71
|
+
return layers
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Split one layer into whitespace-separated tokens, keeping color
|
|
76
|
+
* functions (which may contain spaces: `rgb(0 0 0 / 40%)`) as one token.
|
|
77
|
+
*/
|
|
78
|
+
function tokenize(layer: string): string[] {
|
|
79
|
+
const tokens: string[] = []
|
|
80
|
+
let depth = 0
|
|
81
|
+
let current = ''
|
|
82
|
+
for (const ch of layer) {
|
|
83
|
+
if (ch === '(') depth++
|
|
84
|
+
else if (ch === ')') depth--
|
|
85
|
+
if (depth === 0 && /\s/.test(ch)) {
|
|
86
|
+
if (current) tokens.push(current)
|
|
87
|
+
current = ''
|
|
88
|
+
} else {
|
|
89
|
+
current += ch
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (current) tokens.push(current)
|
|
93
|
+
return tokens
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Parse a CSS `box-shadow` string into resolved layers. `'none'` parses to
|
|
98
|
+
* an empty list. Only px (and unitless) lengths are supported — other units
|
|
99
|
+
* depend on font/viewport context that a style value can't resolve.
|
|
100
|
+
*/
|
|
101
|
+
export function parseBoxShadow(input: string): ResolvedBoxShadowLayer[] {
|
|
102
|
+
const trimmed = input.trim()
|
|
103
|
+
if (trimmed === '') throw invalid(input, 'empty string')
|
|
104
|
+
if (trimmed.toLowerCase() === 'none') return []
|
|
105
|
+
|
|
106
|
+
return splitLayers(trimmed).map((layerString) => {
|
|
107
|
+
const tokens = tokenize(layerString.trim())
|
|
108
|
+
if (tokens.length === 0) throw invalid(input, 'empty layer')
|
|
109
|
+
|
|
110
|
+
const lengths: number[] = []
|
|
111
|
+
let color: string | undefined
|
|
112
|
+
let inset = false
|
|
113
|
+
for (const token of tokens) {
|
|
114
|
+
if (token.toLowerCase() === 'inset') {
|
|
115
|
+
if (inset) throw invalid(input, "duplicate 'inset'")
|
|
116
|
+
inset = true
|
|
117
|
+
} else if (LENGTH.test(token)) {
|
|
118
|
+
lengths.push(parseFloat(token))
|
|
119
|
+
} else if (UNIT_LIKE.test(token)) {
|
|
120
|
+
throw invalid(input, `unsupported unit in ${JSON.stringify(token)}`)
|
|
121
|
+
} else {
|
|
122
|
+
if (color !== undefined) throw invalid(input, 'multiple colors')
|
|
123
|
+
color = token
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (lengths.length < 2 || lengths.length > 4) {
|
|
128
|
+
throw invalid(input, `expected 2-4 lengths, got ${lengths.length}`)
|
|
129
|
+
}
|
|
130
|
+
// The `= 0` on the offsets never fires (length >= 2 is validated
|
|
131
|
+
// above); it's here for noUncheckedIndexedAccess.
|
|
132
|
+
const [offsetX = 0, offsetY = 0, blurRadius = 0, spreadDistance = 0] =
|
|
133
|
+
lengths
|
|
134
|
+
if (blurRadius < 0) throw invalid(input, 'negative blur radius')
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
offsetX,
|
|
138
|
+
offsetY,
|
|
139
|
+
blurRadius,
|
|
140
|
+
spreadDistance,
|
|
141
|
+
color: color ?? 'black',
|
|
142
|
+
inset,
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Resolve either input form (CSS string or structured layers) to layers. */
|
|
148
|
+
export function resolveBoxShadowInput(
|
|
149
|
+
input: string | readonly BoxShadowLayer[] | undefined,
|
|
150
|
+
): ResolvedBoxShadowLayer[] {
|
|
151
|
+
if (input === undefined) return []
|
|
152
|
+
if (typeof input === 'string') return parseBoxShadow(input)
|
|
153
|
+
return input.map((layer) => ({
|
|
154
|
+
offsetX: layer.offsetX,
|
|
155
|
+
offsetY: layer.offsetY,
|
|
156
|
+
blurRadius: layer.blurRadius ?? 0,
|
|
157
|
+
spreadDistance: layer.spreadDistance ?? 0,
|
|
158
|
+
color: layer.color ?? 'black',
|
|
159
|
+
inset: layer.inset ?? false,
|
|
160
|
+
}))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Pair up `from`/`to` layer lists for interpolation, CSS-transition style:
|
|
165
|
+
* the shorter list is padded with an invisible layer (all lengths 0,
|
|
166
|
+
* transparent color) matching the counterpart's `inset` flag. A genuine
|
|
167
|
+
* `inset` mismatch between paired layers is not interpolable and throws.
|
|
168
|
+
*/
|
|
169
|
+
export function pairBoxShadowLayers(
|
|
170
|
+
from: ResolvedBoxShadowLayer[],
|
|
171
|
+
to: ResolvedBoxShadowLayer[],
|
|
172
|
+
): Array<{ from: ResolvedBoxShadowLayer; to: ResolvedBoxShadowLayer }> {
|
|
173
|
+
const count = Math.max(from.length, to.length)
|
|
174
|
+
const pairs: Array<{
|
|
175
|
+
from: ResolvedBoxShadowLayer
|
|
176
|
+
to: ResolvedBoxShadowLayer
|
|
177
|
+
}> = []
|
|
178
|
+
for (let i = 0; i < count; i++) {
|
|
179
|
+
const a = from[i]
|
|
180
|
+
const b = to[i]
|
|
181
|
+
const fromLayer = a ?? invisibleLayer(b!.inset)
|
|
182
|
+
const toLayer = b ?? invisibleLayer(a!.inset)
|
|
183
|
+
if (fromLayer.inset !== toLayer.inset) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`[inertia] useShadow: boxShadow layer ${i} is 'inset' on one side ` +
|
|
186
|
+
'but not the other — inset cannot be interpolated. Give both ' +
|
|
187
|
+
'sides the same inset-ness (pad with a transparent layer if needed).',
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
pairs.push({ from: fromLayer, to: toLayer })
|
|
191
|
+
}
|
|
192
|
+
return pairs
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function invisibleLayer(inset: boolean): ResolvedBoxShadowLayer {
|
|
196
|
+
return {
|
|
197
|
+
offsetX: 0,
|
|
198
|
+
offsetY: 0,
|
|
199
|
+
blurRadius: 0,
|
|
200
|
+
spreadDistance: 0,
|
|
201
|
+
color: 'transparent',
|
|
202
|
+
inset,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@rootnative/inertia/reanimated` — the render-layer interop surface for
|
|
3
|
+
* building custom animated components on top of Inertia's value layer
|
|
4
|
+
* (`useMotionValue` / `useSpring` / `useGesture` / `useScroll` / …).
|
|
5
|
+
*
|
|
6
|
+
* Inertia's contract is that consumers should not need to import from
|
|
7
|
+
* `react-native-reanimated` directly. Most of the time the declarative
|
|
8
|
+
* `Motion.*` primitives cover that; when they don't — a component that owns
|
|
9
|
+
* its own `useAnimatedStyle` worklet over Inertia-driven shared values —
|
|
10
|
+
* this subpath provides the remaining rendering primitives so the consumer's
|
|
11
|
+
* animation dependency stays `@rootnative/inertia` alone.
|
|
12
|
+
*
|
|
13
|
+
* Scope is deliberately render-layer only: animated host components, the
|
|
14
|
+
* animated style/props hooks, worklet-safe interpolators, and cancellation.
|
|
15
|
+
* Starting animations stays in Inertia's transition vocabulary —
|
|
16
|
+
* `useAnimation` / `useSpring` declaratively, `resolveTransition` /
|
|
17
|
+
* `buildReleaseAnimation` imperatively — so the `with*` animation factories
|
|
18
|
+
* are intentionally not re-exported here.
|
|
19
|
+
*
|
|
20
|
+
* Everything is re-exported under its original Reanimated name. That is a
|
|
21
|
+
* hard constraint, not a style choice: the Reanimated Babel plugin
|
|
22
|
+
* auto-workletizes the callbacks passed to `useAnimatedStyle` /
|
|
23
|
+
* `useAnimatedProps` / `useDerivedValue` by callee *name*, so a renamed
|
|
24
|
+
* wrapper would silently stop workletizing consumer callbacks. Re-exports
|
|
25
|
+
* keep the name and the behavior from any import path.
|
|
26
|
+
*/
|
|
27
|
+
export {
|
|
28
|
+
default as Animated,
|
|
29
|
+
Extrapolation,
|
|
30
|
+
cancelAnimation,
|
|
31
|
+
createAnimatedComponent,
|
|
32
|
+
interpolate,
|
|
33
|
+
interpolateColor,
|
|
34
|
+
useAnimatedProps,
|
|
35
|
+
useAnimatedStyle,
|
|
36
|
+
useDerivedValue,
|
|
37
|
+
} from 'react-native-reanimated'
|
|
38
|
+
export type {
|
|
39
|
+
AnimatedProps,
|
|
40
|
+
AnimatedStyle,
|
|
41
|
+
DerivedValue,
|
|
42
|
+
SharedValue,
|
|
43
|
+
} from 'react-native-reanimated'
|
package/src/values/index.ts
CHANGED
package/src/values/useShadow.ts
CHANGED
|
@@ -4,12 +4,20 @@ import {
|
|
|
4
4
|
useAnimatedStyle,
|
|
5
5
|
type SharedValue,
|
|
6
6
|
} from 'react-native-reanimated'
|
|
7
|
+
import {
|
|
8
|
+
pairBoxShadowLayers,
|
|
9
|
+
resolveBoxShadowInput,
|
|
10
|
+
type BoxShadowLayer,
|
|
11
|
+
} from '../internal/boxShadow'
|
|
12
|
+
|
|
13
|
+
export type { BoxShadowLayer }
|
|
7
14
|
|
|
8
15
|
/**
|
|
9
16
|
* Shape accepted on either end of a `useShadow` tween. Every field is
|
|
10
17
|
* optional — only keys present on at least one side participate in the
|
|
11
18
|
* output style. Mirrors the flat shadow keys on `Motion.View`'s `animate`
|
|
12
|
-
* surface, plus the nested `shadowOffset` source
|
|
19
|
+
* surface, plus the nested `shadowOffset` source and the CSS `boxShadow`
|
|
20
|
+
* surface.
|
|
13
21
|
*/
|
|
14
22
|
export interface ShadowConfig {
|
|
15
23
|
shadowOpacity?: number
|
|
@@ -18,6 +26,19 @@ export interface ShadowConfig {
|
|
|
18
26
|
/** Android elevation. iOS shadow consumers can leave this off. */
|
|
19
27
|
elevation?: number
|
|
20
28
|
shadowColor?: string
|
|
29
|
+
/**
|
|
30
|
+
* CSS `box-shadow` — the shadow surface on web (react-native-web passes
|
|
31
|
+
* it through as CSS) and on React Native 0.76+ new-architecture native.
|
|
32
|
+
* Accepts the CSS string form design systems store elevation tokens in
|
|
33
|
+
* (`'0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'`;
|
|
34
|
+
* px lengths only) or structured layers. Multi-layer shadows interpolate
|
|
35
|
+
* per layer; when one side has fewer layers, it is padded with invisible
|
|
36
|
+
* layers, CSS-transition style. A malformed string **throws** at render
|
|
37
|
+
* (like `cubicBezier` — token mistakes should fail loudly at setup).
|
|
38
|
+
* The classic `shadow*`/`elevation` keys don't reach the web renderer —
|
|
39
|
+
* provide `boxShadow` alongside them when the tween must show up there.
|
|
40
|
+
*/
|
|
41
|
+
boxShadow?: string | readonly BoxShadowLayer[]
|
|
21
42
|
}
|
|
22
43
|
|
|
23
44
|
export interface UseShadowOptions {
|
|
@@ -56,6 +77,19 @@ export interface UseShadowOptions {
|
|
|
56
77
|
* `shadowColor`, `{ width: 0, height: 0 }` for `shadowOffset`). This is a
|
|
57
78
|
* pure interpolator — to "animate" the shadow, drive `progress` with a
|
|
58
79
|
* spring, timing, or gesture upstream.
|
|
80
|
+
*
|
|
81
|
+
* The classic `shadow*`/`elevation` keys don't render on web. When the
|
|
82
|
+
* tween must show up there (or on RN 0.76+ new-arch native via the CSS
|
|
83
|
+
* shadow model), provide `boxShadow` on both ends — CSS string tokens or
|
|
84
|
+
* structured layers; multi-layer shadows interpolate per layer:
|
|
85
|
+
*
|
|
86
|
+
* ```tsx
|
|
87
|
+
* const shadowStyle = useShadow({
|
|
88
|
+
* from: { boxShadow: theme.elevation.level1 }, // '0px 1px 2px rgba(0,0,0,0.3), 0px 1px 3px 1px rgba(0,0,0,0.15)'
|
|
89
|
+
* to: { boxShadow: theme.elevation.level2 },
|
|
90
|
+
* progress,
|
|
91
|
+
* })
|
|
92
|
+
* ```
|
|
59
93
|
*/
|
|
60
94
|
export function useShadow({
|
|
61
95
|
from,
|
|
@@ -76,6 +110,17 @@ export function useShadow({
|
|
|
76
110
|
const hasOffset =
|
|
77
111
|
from.shadowOffset !== undefined || to.shadowOffset !== undefined
|
|
78
112
|
|
|
113
|
+
// boxShadow layers: parse/pair once on the JS thread into flat records so
|
|
114
|
+
// the worklet only interpolates numbers/colors and concatenates — no
|
|
115
|
+
// frame-time parsing. `[]` when neither side provides the key.
|
|
116
|
+
const boxShadowPairs =
|
|
117
|
+
from.boxShadow !== undefined || to.boxShadow !== undefined
|
|
118
|
+
? pairBoxShadowLayers(
|
|
119
|
+
resolveBoxShadowInput(from.boxShadow),
|
|
120
|
+
resolveBoxShadowInput(to.boxShadow),
|
|
121
|
+
)
|
|
122
|
+
: []
|
|
123
|
+
|
|
79
124
|
const opacityFrom = from.shadowOpacity ?? 0
|
|
80
125
|
const opacityTo = to.shadowOpacity ?? 0
|
|
81
126
|
const radiusFrom = from.shadowRadius ?? 0
|
|
@@ -111,6 +156,36 @@ export function useShadow({
|
|
|
111
156
|
height: interpolate(t, [0, 1], [offsetHFrom, offsetHTo]),
|
|
112
157
|
}
|
|
113
158
|
}
|
|
159
|
+
if (boxShadowPairs.length > 0) {
|
|
160
|
+
let css = ''
|
|
161
|
+
let first = true
|
|
162
|
+
for (const pair of boxShadowPairs) {
|
|
163
|
+
const x = interpolate(t, [0, 1], [pair.from.offsetX, pair.to.offsetX])
|
|
164
|
+
const y = interpolate(t, [0, 1], [pair.from.offsetY, pair.to.offsetY])
|
|
165
|
+
// Blur can't go negative (invalid CSS) even if a springy driver
|
|
166
|
+
// overshoots below 0.
|
|
167
|
+
const blur = Math.max(
|
|
168
|
+
0,
|
|
169
|
+
interpolate(t, [0, 1], [pair.from.blurRadius, pair.to.blurRadius]),
|
|
170
|
+
)
|
|
171
|
+
const spread = interpolate(
|
|
172
|
+
t,
|
|
173
|
+
[0, 1],
|
|
174
|
+
[pair.from.spreadDistance, pair.to.spreadDistance],
|
|
175
|
+
)
|
|
176
|
+
const color = interpolateColor(
|
|
177
|
+
t,
|
|
178
|
+
[0, 1],
|
|
179
|
+
[pair.from.color, pair.to.color],
|
|
180
|
+
)
|
|
181
|
+
css +=
|
|
182
|
+
(first ? '' : ', ') +
|
|
183
|
+
(pair.from.inset ? 'inset ' : '') +
|
|
184
|
+
`${x}px ${y}px ${blur}px ${spread}px ${color}`
|
|
185
|
+
first = false
|
|
186
|
+
}
|
|
187
|
+
out.boxShadow = css
|
|
188
|
+
}
|
|
114
189
|
return out
|
|
115
190
|
})
|
|
116
191
|
}
|