diff --git a/.gitignore b/.gitignore index fa2dbf2..25312c6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ dist/ .*.swp yarn-error.log +.vscode/ diff --git a/demo/demos/AllDemo.tsx b/demo/demos/AllDemo.tsx index ea5f3ad..cb5460e 100644 --- a/demo/demos/AllDemo.tsx +++ b/demo/demos/AllDemo.tsx @@ -4,14 +4,14 @@ import { ChainableComponent } from '../../src/ChainableComponent'; import Step from '../Step'; export const AllDemo = - ChainableComponent.all([ + ChainableComponent.allT( withState('string value'), withState(1), withState(2), withState(3), withState(5), withState(8) - ]) + ) .render(([a, b, c, d, e, f]) => (
{/* a.value is inferred as a string */} diff --git a/demo/demos/FromNonStandardRenderPropDemo.tsx b/demo/demos/FromNonStandardRenderPropDemo.tsx new file mode 100644 index 0000000..9468c7e --- /dev/null +++ b/demo/demos/FromNonStandardRenderPropDemo.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import Step from '../Step'; +import { + fromNonStandardRenderProp, + ChainableComponent +} from '../../src/ChainableComponent'; + +type MyRenderPropProps = { + foo: string; + render?: (s: string) => React.ReactElement | null; + otherRender?: (n: number) => React.ReactElement | null; +}; + +const MyRenderProp = (props: MyRenderPropProps) => ( +
+ {props.render + ? props.render('string value') + : props.otherRender + ? props.otherRender(5) + : null} +
+); + +const nonStandardStr = fromNonStandardRenderProp('render', MyRenderProp, { + foo: 'asdf' +}); +const nonStandardNum = fromNonStandardRenderProp('otherRender', MyRenderProp, { + foo: 'asdf' +}); + +export const FromNonStandardRenderPropDemo = ChainableComponent.all([ + nonStandardStr, + nonStandardNum, + nonStandardNum +]).render(([foo, outer, inner]) => ( +
+ {foo} +
+ Outer: {outer} +
+
+ Inner: {inner} +
+
+)); + +export default () => ( + +
+      {`hmm
+`}
+    
+ {FromNonStandardRenderPropDemo} +
+); diff --git a/demo/demos/ReactRouterDemo.tsx b/demo/demos/ReactRouterDemo.tsx index 84141d6..0501d57 100644 --- a/demo/demos/ReactRouterDemo.tsx +++ b/demo/demos/ReactRouterDemo.tsx @@ -8,6 +8,9 @@ const customHistory = createBrowserHistory(); const withRoute = fromRenderProp(Route); +// how to use 'render' instead of 'children' (react router cares about this...) +// const withOtherRoute = fromNonStandardRenderProp('render', Route) + // Route doesn't have any required props, so we can just pass the empty object here const ReactRouterDemoInner: React.SFC = () => ( diff --git a/demo/index.tsx b/demo/index.tsx index 585c318..91e245f 100644 --- a/demo/index.tsx +++ b/demo/index.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { AppContainer } from 'react-hot-loader'; -import '../styles/index.less'; import './demo.less'; diff --git a/package.json b/package.json index 58568ac..e3d139f 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "1.6.5", "description": "", "license": "MIT", - "main": "lib/index.js", - "typings": "lib/index.d.ts", + "main": "dist/index.js", + "typings": "dist/index.d.ts", "author": { "name": "Paul Gray", "email": "pfbgray@gmail.com", @@ -15,7 +15,7 @@ "lint-fix": "tslint --project tsconfig-build.json --fix", "docs:gitbook": "gitbook build ./ docs", "docs:typedoc": "typedoc --module commonjs --out docs/typedoc/ src/", - "build": "npm-run-all clean build:ts build:dist docs:gitbook", + "build": "npm-run-all clean build:ts build:dist", "build:ts": "tsc -p tsconfig-build.json", "build:dist": "webpack --config webpack/bundle.ts", "clean": "rm -rf lib/", @@ -65,12 +65,15 @@ "ts-node": "^3.3.0", "tslint": "^5.7.0", "typedoc": "^0.11.1", - "typescript": "^2.5.3", + "typescript": "^3.4.0", "webpack": "^4.0.0", "webpack-cli": "^3.0.3", "webpack-dev-server": "^3.1.4" }, - "dependencies": {}, + "dependencies": { + "fp-ts": "^1.15.0", + "fp-ts-contrib": "^0.0.2" + }, "peerDependencies": { "react": "^15.0.0 || ^16.0.0" }, diff --git a/src/ChainableComponent.ts b/src/ChainableComponent.ts index 469b702..f1974b8 100644 --- a/src/ChainableComponent.ts +++ b/src/ChainableComponent.ts @@ -234,14 +234,9 @@ export function fromHigherOrderComponent

( } /** - * Converts a Render Props Component into a function that can be used to build a ChainableComponent - * Represents the type of a non standard render prop - * @template P the props of the render prop component - * @template A the type of the parameter of the render function - * @template C the string of the key of the render function + * Extracts a parameter type from the render prop function. */ -type NonStandardRenderPropProps = A & - { [K in C]: (a: P[K]) => React.ReactNode }; +type RenderPropContext = P extends {[K in S]?: (a: infer U) => React.ReactNode} ? U : never; /** * Converts a Render Prop Component into a function that can be used to build a ChainableComponent @@ -249,13 +244,13 @@ type NonStandardRenderPropProps = A & * A "non-standard" render prop is any render prop that does not use the 'children' prop as the render method. * @param Inner the render prop component */ -export function fromNonStandardRenderProp( +export function fromNonStandardRenderProp( renderMethod: S, - Inner: React.ComponentType>, + Inner: React.ComponentType

, parameters?: Omit -): ChainableComponent { +): ChainableComponent> { return fromRender(f => { - const apply = React.createFactory>( + const apply = React.createFactory

( Inner as any ); return apply({ @@ -266,7 +261,7 @@ export function fromNonStandardRenderProp( } /** - * Converts an apply function to a ChainableComponent + * Converts a render function to a ChainableComponent * @param render */ export function fromRender( @@ -308,6 +303,14 @@ export function fromRender( type CC = ChainableComponent; + +type Unchained = A extends ChainableComponent ? U : never; +type Unchainified = { [K in keyof A]: Unchained }; + +function allT[]>(...values: A): ChainableComponent> { + return all(values) as unknown as ChainableComponent>; +} + function all( values: [ CC, @@ -551,6 +554,7 @@ export const ChainableComponent = { 'fantasyland/of': of, 'fantasy-land/of': of, all, + allT, fork, Do, DoRender diff --git a/src/index.ts b/src/index.ts index 7dd6142..dc627c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ import { import { withPromise, WithPromise } from './lib/withPromise'; import { withState, WithState } from './lib/withState'; import { withLifecycle, WithLifecycle } from './lib/withLifecycle'; +import { chainableComponent, DoBuilder } from './lib/fpts'; export { ChainableComponent, @@ -24,5 +25,7 @@ export { withPromise, WithPromise, withLifecycle, - WithLifecycle + WithLifecycle, + chainableComponent, + DoBuilder }; diff --git a/src/lib/fpts.ts b/src/lib/fpts.ts new file mode 100644 index 0000000..b66407a --- /dev/null +++ b/src/lib/fpts.ts @@ -0,0 +1,31 @@ +import { Monad1 } from "fp-ts/lib/Monad"; +import { HKT } from 'fp-ts/lib/HKT'; +import { ChainableComponent } from ".."; +import { Do } from 'fp-ts-contrib/lib/Do'; + +export const URI = "ChainableComponent"; +export type URI = typeof URI; + +declare module "fp-ts/lib/HKT" { + interface URI2HKT { + ChainableComponent: ChainableComponent; + } +} + +export const chainableComponent: Monad1 = { + URI, + of(a: A) { + return ChainableComponent.of(a); + }, + map(fa: ChainableComponent, f: (a: A) => B) { + return fa.map(f); + }, + ap(fab: ChainableComponent<(a: A) => B>, fa: ChainableComponent) { + return fa.ap(fab) + }, + chain(fa: ChainableComponent, f: (a: A) => ChainableComponent) { + return fa.chain(f) + } +}; + +export const DoBuilder = Do(chainableComponent) diff --git a/styles/index.less b/styles/index.less deleted file mode 100644 index 8b13789..0000000 --- a/styles/index.less +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tsconfig.json b/tsconfig.json index fb11189..894f995 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,13 +2,12 @@ "compileOnSave": false, "compilerOptions": { "declaration": true, - "experimentalDecorators": true, "forceConsistentCasingInFileNames": true, "jsx": "react", "noEmit": true, "noFallthroughCasesInSwitch": true, "noImplicitReturns": true, - "noUnusedLocals": true, + "noUnusedLocals": false, "noUnusedParameters": true, "target": "es5", "sourceMap": true, diff --git a/yarn.lock b/yarn.lock index ce0675e..78f39b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,6 +81,10 @@ version "10.3.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.5.tgz#8423cdf6e6fb83433e489900d7600d3b61c8260c" +"@types/prop-types@*": + version "15.7.0" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.0.tgz#4c48fed958d6dcf9487195a0ef6456d5f6e0163a" + "@types/ramda@^0.25.36": version "0.25.36" resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.25.36.tgz#1ddaf3211c7cd7046fcaefe68c713469ccfc9504" @@ -105,12 +109,19 @@ "@types/history" "*" "@types/react" "*" -"@types/react@*", "@types/react@^16.0.10": +"@types/react@*": version "16.4.1" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.1.tgz#c53bbfb4a78933db587da085ac60dbf5fcf73f8f" dependencies: csstype "^2.2.0" +"@types/react@^16.0.10": + version "16.8.10" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.10.tgz#1ccb6fde17f71a62ef055382ec68bdc379d4d8d9" + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/recompose@^0.26.4": version "0.26.4" resolved "https://registry.yarnpkg.com/@types/recompose/-/recompose-0.26.4.tgz#93dd6c4e28857cd799e9a807a470f66a40c49c3f" @@ -2102,6 +2113,16 @@ forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" +fp-ts-contrib@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/fp-ts-contrib/-/fp-ts-contrib-0.0.2.tgz#95d3f5a3810020f8aea030bfcbd1efabbfb8124e" + dependencies: + fp-ts "^1.14.3" + +fp-ts@^1.14.3, fp-ts@^1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.15.0.tgz#7706d6761cc98ccbece91c2ff2e5a5b924f65f3d" + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" @@ -5540,9 +5561,9 @@ typescript@2.7.2: version "2.7.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" -typescript@^2.5.3: - version "2.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" +typescript@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6" ua-parser-js@^0.7.18: version "0.7.18"