Skip to content

Commit d6b45f0

Browse files
committed
createHost
1 parent d2bcc82 commit d6b45f0

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed

spring/src/createHost.ts

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// @ts-ignore
2+
3+
import type { JSX } from "solid-js";
4+
import { Animated } from "./animated";
5+
import { AnimatedObject as AnimatedObjectClass } from "./AnimatedObject";
6+
import { FluidProps, FluidValue } from "./fluids";
7+
import { is, Lookup, eachProp, Merge, NonObject } from "./utils";
8+
import { AnimatableComponent, withAnimated } from "./withAnimated";
9+
10+
export interface HostConfig {
11+
/** Provide custom logic for native updates */
12+
applyAnimatedValues: (node: any, props: Lookup) => boolean | void;
13+
/** Wrap the `style` prop with an animated node */
14+
createAnimatedStyle: (style: Lookup) => Animated;
15+
/** Intercept props before they're passed to an animated component */
16+
getComponentProps: (props: Lookup) => typeof props;
17+
}
18+
type Angle = number | string;
19+
type Length = number | string;
20+
21+
type TransformProps = {
22+
transform?: string;
23+
x?: Length;
24+
y?: Length;
25+
z?: Length;
26+
translate?: Length | readonly [Length, Length];
27+
translateX?: Length;
28+
translateY?: Length;
29+
translateZ?: Length;
30+
translate3d?: readonly [Length, Length, Length];
31+
rotate?: Angle;
32+
rotateX?: Angle;
33+
rotateY?: Angle;
34+
rotateZ?: Angle;
35+
rotate3d?: readonly [number, number, number, Angle];
36+
// Note: "string" is not really supported by "scale", but this lets us
37+
// spread React.CSSProperties into an animated style object.
38+
scale?: number | readonly [number, number] | string;
39+
scaleX?: number;
40+
scaleY?: number;
41+
scaleZ?: number;
42+
scale3d?: readonly [number, number, number];
43+
skew?: Angle | readonly [Angle, Angle];
44+
skewX?: Angle;
45+
skewY?: Angle;
46+
matrix?: readonly [number, number, number, number, number, number];
47+
matrix3d?: readonly [
48+
number, // a1
49+
number,
50+
number,
51+
number,
52+
number, // a2
53+
number,
54+
number,
55+
number,
56+
number, // a3
57+
number,
58+
number,
59+
number,
60+
number, // a4
61+
number,
62+
number,
63+
number
64+
];
65+
};
66+
67+
type CSSProperties = JSX.IntrinsicElements["a"]["style"];
68+
type StyleProps = Merge<CSSProperties, TransformProps>;
69+
70+
// A stub type that gets replaced by @react-spring/web and others.
71+
export type WithAnimated = ((Component: any) => any) &
72+
{
73+
// (Component: AnimatableComponent): any;
74+
[K in keyof JSX.IntrinsicElements]: (
75+
props: AnimatedProps<
76+
Merge<JSX.IntrinsicElements[K], { style?: StyleProps }>
77+
> &
78+
FluidProps<{
79+
scrollTop?: number;
80+
scrollLeft?: number;
81+
}>
82+
) => JSX.Element;
83+
};
84+
/** The props of an `animated()` component */
85+
export type AnimatedProps<Props extends object> = {
86+
[P in keyof Props]: P extends "ref" | "key"
87+
? Props[P]
88+
: AnimatedProp<Props[P]>;
89+
};
90+
// The animated prop value of a React element
91+
type AnimatedProp<T> = [T, T] extends [infer T, infer DT]
92+
? [DT] extends [never]
93+
? never
94+
: DT extends void
95+
? undefined
96+
: DT extends string | number
97+
? DT | AnimatedLeaf<T>
98+
: DT extends object
99+
? [ValidStyleProps<DT>] extends [never]
100+
? DT extends ReadonlyArray<any>
101+
? AnimatedStyles<DT>
102+
: DT
103+
: AnimatedStyle<T>
104+
: DT | AnimatedLeaf<T>
105+
: never;
106+
107+
// An animated object of style attributes
108+
type AnimatedStyle<T> = [T, T] extends [infer T, infer DT]
109+
? DT extends void
110+
? undefined
111+
: [DT] extends [never]
112+
? never
113+
: DT extends string | number
114+
? DT | AnimatedLeaf<T>
115+
: DT extends object
116+
? AnimatedObject<DT>
117+
: DT | AnimatedLeaf<T>
118+
: never;
119+
120+
type AnimatedObject<T extends object> =
121+
| { [P in keyof T]: AnimatedStyle<T[P]> }
122+
| (T extends ReadonlyArray<number | string>
123+
? FluidValue<Readonly<T>>
124+
: never);
125+
126+
// An animated array of style objects
127+
type AnimatedStyles<T extends ReadonlyArray<any>> = {
128+
[P in keyof T]: [T[P]] extends [infer DT]
129+
? DT extends object
130+
? [ValidStyleProps<DT>] extends [never]
131+
? DT extends ReadonlyArray<any>
132+
? AnimatedStyles<DT>
133+
: DT
134+
: { [P in keyof DT]: AnimatedProp<DT[P]> }
135+
: DT
136+
: never;
137+
};
138+
// An animated primitive (or an array of them)
139+
type AnimatedLeaf<T> = NonObject<T> extends infer U
140+
? [U] extends [never]
141+
? never
142+
: FluidValue<U>
143+
: never;
144+
145+
type StylePropKeys = keyof StyleProps;
146+
147+
type ValidStyleProps<T extends object> = {
148+
[P in keyof T & StylePropKeys]: T[P] extends StyleProps[P] ? P : never;
149+
}[keyof T & StylePropKeys];
150+
151+
// For storing the animated version on the original component
152+
const cacheKey = Symbol.for("AnimatedComponent");
153+
154+
export const createHost = (
155+
components: AnimatableComponent[] | { [key: string]: AnimatableComponent },
156+
{
157+
applyAnimatedValues = () => false,
158+
createAnimatedStyle = (style) => new AnimatedObjectClass(style),
159+
getComponentProps = (props) => props,
160+
}: Partial<HostConfig> = {}
161+
) => {
162+
const hostConfig: HostConfig = {
163+
applyAnimatedValues,
164+
createAnimatedStyle,
165+
getComponentProps,
166+
};
167+
168+
const animated: any = (Component: any): any => {
169+
if (is.str(Component)) {
170+
Component =
171+
animated[Component] ||
172+
(animated[Component] = withAnimated(Component, hostConfig));
173+
} else {
174+
Component =
175+
Component[cacheKey] ||
176+
(Component[cacheKey] = withAnimated(Component, hostConfig));
177+
}
178+
179+
return Component;
180+
};
181+
182+
eachProp(components, (Component, key) => {
183+
if (is.arr(components)) {
184+
key = Component;
185+
}
186+
animated[key] = animated(Component);
187+
});
188+
189+
return {
190+
animated,
191+
} as { animated: WithAnimated };
192+
};

0 commit comments

Comments
 (0)