diff --git a/package.json b/package.json index c9481d5408..52bcf40111 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,12 @@ "main": "web.cjs.js", "module": "web.js", "react-native": "native.js", - "typings": "index.d.ts", "private": true, "sideEffects": false, "scripts": { "prebuild": "rimraf dist", "build": "npm-run-all --parallel copy rollup", - "copy": "copyfiles package.json readme.md LICENSE.md index.d.ts dist && json -I -f dist/package.json -e \"this.private=false; this.devDependencies=undefined; this.scripts=undefined; this.husky=undefined; this.prettier=undefined; this.jest=undefined; this['lint-staged'] = undefined;\"", + "copy": "copyfiles -f package.json readme.md LICENSE.md \"types/*\" dist && json -I -f dist/package.json -e \"this.private=false; this.devDependencies=undefined; this.scripts=undefined; this.husky=undefined; this.prettier=undefined; this.jest=undefined; this['lint-staged'] = undefined;\"", "rollup": "rollup -c", "prepare": "npm run build", "test": "jest", @@ -72,7 +71,7 @@ "@babel/preset-react": "^7.0.0", "@storybook/addon-knobs": "^3.4.11", "@storybook/react": "^3.4.11", - "@types/react": "^16.7.8", + "@types/react": "^16.7.17", "babel-core": "7.0.0-bridge.0", "babel-jest": "^23.6.0", "babel-plugin-react-docgen": "^2.0.0", @@ -89,6 +88,7 @@ "immer-wieder": "^1.1.5", "jest": "^23.6.0", "json": "^9.0.6", + "konva": "^2.6.0", "lint-staged": "^7.3.0", "lorem-ipsum": "^1.0.6", "mock-raf": "^1.0.0", @@ -99,6 +99,7 @@ "react-dom": "16.7.0-alpha.2", "react-feather": "^1.1.4", "react-helmet": "^5.2.0", + "react-konva": "^16.6.31", "react-test-renderer": "^16.5.2", "react-testing-library": "^5.2.0", "react-transition-group": "^2.5.0", diff --git a/tsconfig.json b/tsconfig.json index c72143b4d5..f7c3ca46d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,8 @@ "noFallthroughCasesInSwitch": true, "noEmitOnError": true, "forceConsistentCasingInFileNames": true, - "lib": ["es2016", "dom"] + "lib": ["es2016", "dom"], + "skipLibCheck": false }, - "include": ["tests/**/*.tsx"] + "include": ["tests/**/*.tsx", "types/**/*.ts"] } diff --git a/types/addons.d.ts b/types/addons.d.ts new file mode 100644 index 0000000000..eed606c1bb --- /dev/null +++ b/types/addons.d.ts @@ -0,0 +1,28 @@ +import { Ref, PureComponent } from 'react' +import { SpringConfig } from './universal' + +interface ParallaxProps { + pages: number + + config?: SpringConfig | ((key: string) => SpringConfig) + + scrolling?: boolean + + horizontal?: boolean + + ref?: Ref +} + +export class Parallax extends PureComponent { + scrollTo: (offset: number) => void +} + +interface ParallaxLayerProps { + factor?: number + + offset?: number + + speed?: number +} + +export class ParallaxLayer extends PureComponent {} diff --git a/types/hooks.d.ts b/types/hooks.d.ts new file mode 100644 index 0000000000..b9e80fd06d --- /dev/null +++ b/types/hooks.d.ts @@ -0,0 +1,231 @@ +import { + SpringConfig, + SpringBaseProps, + TransitionKeyProps, + State, +} from './universal' +export { SpringConfig, SpringBaseProps, TransitionKeyProps, State } + +/** List from `function getForwardProps` in `src/shared/helpers` */ +type ExcludedProps = + | 'to' + | 'from' + | 'config' + | 'native' + | 'onStart' + | 'onRest' + | 'onFrame' + | 'children' + | 'reset' + | 'reverse' + | 'force' + | 'immediate' + | 'impl' + | 'inject' + | 'delay' + | 'attach' + | 'destroyed' + | 'track' + | 'interpolateTo' + | 'autoStart' + | 'ref' +export type ForwardedProps = Pick> +// NOTE: because of the Partial, this makes a weak type, which can have excess props +type InferFrom = T extends { to: infer TTo } + ? Partial + : Partial> + +// This is similar to "Omit & B", +// but with a delayed evaluation that still allows A to be inferrable +type Merge = { [K in keyof A]: K extends keyof B ? B[K] : A[K] } & B + +export type SetUpdateFn = (ds: DS) => void + +// The hooks do emulate React's 'ref' by accepting { ref?: React.RefObject } and +// updating it. However, there are no types for Controller, and I assume it is intentionally so. +export interface HooksBaseProps + extends Pick> { + // there is an undocumented onKeyframesHalt which passes the controller instance, + // so it also cannot be typed unless Controller types are written +} + +export interface UseSpringBaseProps extends HooksBaseProps { + config?: SpringBaseProps['config'] +} + +export type UseSpringProps = Merge< + DS, + { + from?: InferFrom + /** + * Callback when the animation comes to a still-stand + */ + onRest?(ds: InferFrom): void + } +> + +// there's a third value in the tuple but it's not public API (?) +export function useSpring( + getProps: () => UseSpringProps +): [ForwardedProps, SetUpdateFn] +export function useSpring( + values: UseSpringProps +): ForwardedProps + +// there's a third value in the tuple but it's not public API (?) +export function useTrail( + count: number, + getProps: () => UseSpringProps +): [ForwardedProps[], SetUpdateFn] +export function useTrail( + count: number, + values: UseSpringProps +): ForwardedProps[] // safe to modify (result of .map) + +export interface UseTransitionProps + extends HooksBaseProps { + items: ReadonlyArray | null | undefined + /** + * Spring config, or for individual items: fn(item => config) + * @default config.default + */ + config?: SpringConfig | ((item: TItem) => SpringConfig) + /** + * The same keys you would normally hand over to React in a list. Keys can be specified as a key-accessor function, an array of keys, or a single value + */ + keys?: + | ((item: TItem) => TransitionKeyProps) + | ReadonlyArray + | TransitionKeyProps + + /** + * When true enforces that an item can only occur once instead of allowing two or more items with the same key to co-exist in a stack + * @default false + */ + unique?: boolean + /** + * Trailing delay in ms + */ + trail?: number + + from?: InferFrom + /** + * Values that apply to new elements, or: item => values + * @default {} + */ + enter?: InferFrom | ((item: TItem) => InferFrom) + /** + * Values that apply to leaving elements, or: item => values + * @default {} + */ + leave?: InferFrom | ((item: TItem) => InferFrom) + /** + * Values that apply to elements that are neither entering nor leaving (you can use this to update present elements), or: item => values + */ + update?: InferFrom | ((item: TItem) => InferFrom) +} + +export interface UseTransitionResult { + item: TItem + key: string + state: State + props: ForwardedProps +} + +export function useTransition( + values: Merge> +): UseTransitionResult>[] // result array is safe to modify + +// higher order hooks 🤯 +export namespace useKeyframes { + // this kind of higher order hook requires higher kinded types to type correctly at all + // this is the best approximation I can get to....... and there sure are anys around 🤯 + + // the docs says it receives a 3rd argument with the component's own props, but it's only + // ever called with two (https://github.com/react-spring/react-spring/blob/v7.1.3/src/hooks/KeyframesHook.js#L25) + export type KeyframeFn = ( + next: (props: UseSpringProps) => Promise, + cancel: () => void + ) => Promise + export interface SpringKeyframeSlotsConfig extends HooksBaseProps { + // this is way too self-referential to type correctly + // both onRest and config have to have vague types... 😭 + + /** + * Callback when the animation comes to a still-stand + */ + onRest?(ds: any): void + config?: + | SpringConfig + | ReadonlyArray + | ((slot: string) => SpringConfig | ReadonlyArray) + } + export interface TrailKeyframeSlotsConfig + extends SpringKeyframeSlotsConfig { + items: ReadonlyArray + } + + export type ResolveKeyframeSlotValue = T extends KeyframeFn + ? V + : T extends ReadonlyArray + ? U extends KeyframeFn + ? V + : U extends Function // guard against accidentally putting in a KeyframeFn function + ? never + : U + : T extends Function // same guard, but when the function is not in an array + ? never + : T + + // fun fact: the state is literally named "undefined" if you're using this overload + // the docs are vague but it seems this just loops the one animation forever? + export function spring( + animation: + | ReadonlyArray + | ReadonlyArray> + | KeyframeFn, + initialProps?: UseSpringProps + ): UseSpringKeyframes + // unfortunately, it's not possible to infer the type of the callback functions (if any are given) + // while also remaining possible to infer the slot names. Callback functions have to be cast with + // `as useKeyframes.KeyframeFn<{ ... }>`. + // it's also not possible to specify the types of the values inside TSlots. This is a mess. + export function spring( + slots: TSlots & SpringKeyframeSlotsConfig, + // also unfortunately not possible to strongly type this either + initialProps?: UseSpringProps + ): UseSpringKeyframesWithSlots + + export function trail( + animation: + | ReadonlyArray + | ReadonlyArray> + | KeyframeFn, + initialProps?: UseSpringProps + ): UseTrailKeyframes + export function trail( + slots: TSlots & TrailKeyframeSlotsConfig, + initialProps?: UseSpringProps + ): UseTrailKeyframesWithSlots + + export type UseSpringKeyframesWithSlots = + // there's a bug in the implementation that actually should cause a crash + // because there is no second argument, but because it is built with babel on loose mode, + // it doesn't... + >( + slot: TSlot + ) => ForwardedProps> + // this one crashes "even more" because both values are undefined + export type UseSpringKeyframes = () => ForwardedProps + + export type UseTrailKeyframesWithSlots = < + TSlot extends Exclude> + >( + count: number, + slot: TSlot + ) => ForwardedProps>[] + + export type UseTrailKeyframes = ( + count: number + ) => ForwardedProps[] +} diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000000..a1d37f4420 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,5 @@ +// This is the file TypeScript will see when it looks inside the module without any sub-paths +// This should just reexport the package.json's "main". +export * from './web' +// Add this (and to all other entry points) if a default export is ever added +// export { default } from './web' diff --git a/types/konva.d.ts b/types/konva.d.ts new file mode 100644 index 0000000000..ce5e23b021 --- /dev/null +++ b/types/konva.d.ts @@ -0,0 +1,25 @@ +import { + ForwardRefExoticComponent, + ComponentPropsWithRef, + ReactType, +} from 'react' +import { animated } from './universal' +export * from './universal' + +import * as konva from 'react-konva' + +type KonvaComponents = Pick< + typeof konva, + { + [K in keyof typeof konva]: typeof konva[K] extends ReactType ? K : never + }[keyof typeof konva] +> + +declare const augmentedAnimated: typeof animated & + { + [Tag in keyof KonvaComponents]: ForwardRefExoticComponent< + ComponentPropsWithRef + > + } + +export { augmentedAnimated as animated } diff --git a/types/native.d.ts b/types/native.d.ts new file mode 100644 index 0000000000..93fe93623d --- /dev/null +++ b/types/native.d.ts @@ -0,0 +1 @@ +export * from './universal' diff --git a/index.d.ts b/types/universal.d.ts similarity index 81% rename from index.d.ts rename to types/universal.d.ts index 4b43d462fc..90377e82f5 100644 --- a/index.d.ts +++ b/types/universal.d.ts @@ -1,10 +1,16 @@ +// This is where the bulk of the type definitions should go. +// The other entry points (except for hooks) all just expose this but with some customization. + import { Component, PureComponent, ReactNode, ComponentClass, ComponentType, + ReactType, Ref, + ForwardRefExoticComponent, + ComponentPropsWithRef, } from 'react' export type SpringEasingFunc = (t: number) => number @@ -23,7 +29,7 @@ export interface SpringConfig { type SpringRendererFunc = (params: DS) => ReactNode -interface SpringProps { +export interface SpringBaseProps { /** * Spring config, or for individual keys: fn(key => config) * @default config.default @@ -34,6 +40,31 @@ interface SpringProps { * @default false */ native?: boolean + /** + * When true it literally resets: from -> to + * @default false + */ + reset?: boolean + /** + * Prevents animation if true + * @default false + */ + immediate?: boolean | ((key: string) => boolean) + /** + * Animation start delay, optional + */ + delay?: number + /** + * reverse the animation + */ + reverse?: boolean + /** + * Callback when the animation starts to animate + */ + onStart?(): void +} + +export interface SpringProps { /** * Base styles * @default {} @@ -44,10 +75,6 @@ interface SpringProps { * @default {} */ to: DS - /** - * Callback when the animation starts to animate - */ - onStart?: () => void /** * Callback when the animation comes to a still-stand */ @@ -65,28 +92,15 @@ interface SpringProps { * @default false */ immediate?: boolean | string[] | ((key: string) => boolean) - /** - * When true it literally resets: from -> to - * @default false - */ - reset?: boolean /** * Inject props * @default undefined */ inject?: any - /** - * Animation start delay, optional - */ - delay?: number /** * Inject props after animation is ended */ after?: Partial - /** - * reverse the animation - */ - reverse?: boolean /** * Escape hatch to force the spring to render */ @@ -115,19 +129,15 @@ export function interpolate( config: (...args: number[]) => any ): any -export const animated: { -

(comp: ComponentType

): ComponentType

-} & { - [Tag in keyof JSX.IntrinsicElements]: ComponentType< - JSX.IntrinsicElements[Tag] - > -} +export function animated( + comp: T +): ForwardRefExoticComponent> -type TransitionKeyProps = string | number +export type TransitionKeyProps = string | number -type State = 'enter' | 'update' | 'leave' +export type State = 'enter' | 'update' | 'leave' -interface TransitionProps< +export interface TransitionProps< TItem, TInit extends object = {}, TFrom extends object = {}, @@ -136,21 +146,11 @@ interface TransitionProps< TUpdate extends object = {}, SpringProps extends object = {}, DS extends object = {} -> { +> extends SpringBaseProps { /** * First-render initial values, if present overrides "from" on the first render pass. It can be "null" to skip first mounting transition. Otherwise it can take an object or a function (item => object) */ initial?: TInit | null - /** - * Will skip rendering the component if true and write to the dom directly - * @default false - */ - native?: boolean - /** - * Spring config ({ tension, friction }) - * @default config.default - */ - config?: SpringConfig | ((key: string) => SpringConfig) /** * Base values (from -> enter), or: item => values * @default {} @@ -166,10 +166,6 @@ interface TransitionProps< * @default {} */ leave?: TLeave - /** - * Callback when the animation starts to animate - */ - onStart?: () => void /** * Callback when the animation comes to a still-stand */ @@ -235,17 +231,7 @@ export class Transition< type TrailKeyProps = string | number -interface TrailProps { - /** - * Will skip rendering the component if true and write to the dom directly - * @default false - */ - native?: boolean - /** - * Spring config, or for individual keys: fn(key => config) - * @default config.default - */ - config?: SpringConfig | ((key: string) => SpringConfig) +interface TrailProps extends SpringBaseProps { /** * Base values, optional */ @@ -267,42 +253,12 @@ interface TrailProps { * A single function-child that receives the individual item and return a functional component (item, index) => props => view) */ children?: (item: TItem, index: number) => SpringRendererFunc - /** - * When true the trailing order is switched, it will then trail bottom to top - */ - reverse?: boolean } export class Trail extends PureComponent< TrailProps > {} -interface ParallaxProps { - pages: number - - config?: SpringConfig | ((key: string) => SpringConfig) - - scrolling?: boolean - - horizontal?: boolean - - ref?: Ref -} - -export class Parallax extends PureComponent { - scrollTo: (offset: number) => void -} - -interface ParallaxLayerProps { - factor?: number - - offset?: number - - speed?: number -} - -export class ParallaxLayer extends PureComponent {} - interface KeyframesProps { state?: string } diff --git a/types/web.d.ts b/types/web.d.ts new file mode 100644 index 0000000000..ab1087b772 --- /dev/null +++ b/types/web.d.ts @@ -0,0 +1,12 @@ +import { ForwardRefExoticComponent, ComponentPropsWithRef } from 'react' +import { animated } from './universal' +export * from './universal' + +declare const augmentedAnimated: typeof animated & + { + [Tag in keyof JSX.IntrinsicElements]: ForwardRefExoticComponent< + ComponentPropsWithRef + > + } + +export { augmentedAnimated as animated } diff --git a/yarn.lock b/yarn.lock index 85f253d2a2..91a3e7e361 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1781,10 +1781,10 @@ version "15.5.6" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c" -"@types/react@^16.7.8": - version "16.7.8" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.8.tgz#ca24ba280f79debfc86578278a503c6d917d402e" - integrity sha512-7Vua2IYokMiPBk0WWXaDt4Cg3XVcwU6HpkuPhvwhBrVEy4SafpcvEfGYByoY9jxyFMiegYQPaSOT+H+AY00CPw== +"@types/react@^16.7.17": + version "16.7.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.7.17.tgz#3242e796a1ffbba4f49eae5915a67f4c079504e9" + integrity sha512-YcXcaoXaxo7A76mBCGlKlN2aZu3REQfF0DTrhiyXVJLA7PDdxVCr+wiQOrkVNn44D/zLlIyDSn3U918Ve0AaEA== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -8739,6 +8739,11 @@ koa@^2.5.2, koa@^2.6.2: type-is "^1.6.16" vary "^1.1.2" +konva@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/konva/-/konva-2.6.0.tgz#43165b95e32a4378ce532d9113c914f4998409c3" + integrity sha512-LCOoavICTD9PYoAqtWo8sbxYtCiXdgEeY7vj/Sq8b2bwFmrQr9Ak0RkD4/jxAf5fcUQRL5e1zPLyfRpVndp20A== + last-call-webpack-plugin@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" @@ -11724,6 +11729,14 @@ react-is@^16.3.1, react-is@^16.5.2, react-is@^16.6.0: version "16.6.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.0.tgz#456645144581a6e99f6816ae2bd24ee94bdd0c01" +react-konva@^16.6.31: + version "16.6.31" + resolved "https://registry.yarnpkg.com/react-konva/-/react-konva-16.6.31.tgz#e6e715f1c4085f725d70fb3938695d3ddc54f2ec" + integrity sha512-SKVbf9uLii2qJrbPKl+yKKkfgpbd4ehWS7de2/I+nCWcrS5Ktz75O0oU4f3aklL+vrtcAtse94GuGK/Bufz/kw== + dependencies: + react-reconciler "^0.17.2" + scheduler "^0.11.2" + react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -11769,6 +11782,16 @@ react-powerplug@^1.0.0: dependencies: "@babel/runtime" "^7.0.0" +react-reconciler@^0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.17.2.tgz#cabe8cb403793f6462842e07b525c3b61c4f5cde" + integrity sha512-V3WjUlYgMjWWRQH5lzMMsy1jMDL28pInmwfCsKoOGqNQ0TN2DQHKBzycgFnZsiL3NoIb52GQtQgkYGeDDDoN+g== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.11.2" + react-router-dom@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6"