├── .editorconfig
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CNAME
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── MAINTAINERS.md
├── Makefile
├── README.md
├── examples
├── preact-playground
│ ├── README.md
│ ├── package.json
│ └── src
│ │ ├── config.js
│ │ ├── files
│ │ ├── .gitignore
│ │ ├── assets
│ │ │ ├── favicon.ico
│ │ │ └── icons
│ │ │ │ ├── android-chrome-192x192.png
│ │ │ │ ├── android-chrome-512x512.png
│ │ │ │ ├── apple-touch-icon.png
│ │ │ │ ├── favicon-16x16.png
│ │ │ │ ├── favicon-32x32.png
│ │ │ │ └── mstile-150x150.png
│ │ └── manifest.json
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── proppy-redux
│ │ ├── actions
│ │ │ └── counter.js
│ │ ├── components
│ │ │ └── PlayWithStore.js
│ │ ├── constants
│ │ │ └── index.js
│ │ ├── reducers
│ │ │ ├── counter.js
│ │ │ └── index.js
│ │ └── store
│ │ │ └── index.js
│ │ ├── proppy-rx
│ │ └── PlayWithStream.js
│ │ ├── proppy
│ │ ├── PlayCompose.js
│ │ ├── PlayDidSubscribe.js
│ │ ├── PlayEmit.js
│ │ ├── PlayMap.js
│ │ ├── PlayOnChange.js
│ │ ├── PlayShouldUpdate.js
│ │ ├── PlayWithHandlers.js
│ │ ├── PlayWithObservable.js
│ │ ├── PlayWithProps.js
│ │ ├── PlayWithReducer.js
│ │ ├── PlayWithState.js
│ │ ├── PlayWithStateHandlers.js
│ │ └── PlayWithTimer.js
│ │ └── style
│ │ └── index.scss
└── react-playground
│ ├── README.md
│ ├── package.json
│ ├── public
│ └── index.html
│ └── src
│ ├── config.js
│ ├── index.js
│ ├── proppy-redux
│ ├── actions
│ │ └── counter.js
│ ├── components
│ │ └── PlayWithStore.js
│ ├── constants
│ │ └── index.js
│ ├── reducers
│ │ ├── counter.js
│ │ └── index.js
│ └── store
│ │ └── index.js
│ ├── proppy-rx
│ └── PlayWithStream.js
│ └── proppy
│ ├── PlayCompose.js
│ ├── PlayDidSubscribe.js
│ ├── PlayEmit.js
│ ├── PlayMap.js
│ ├── PlayOnChange.js
│ ├── PlayShouldUpdate.js
│ ├── PlayWithHandlers.js
│ ├── PlayWithObservable.js
│ ├── PlayWithProps.js
│ ├── PlayWithReducer.js
│ ├── PlayWithState.js
│ ├── PlayWithStateHandlers.js
│ └── PlayWithTimer.js
├── lerna.json
├── package-lock.json
├── package.json
├── packages
├── proppy-frint-react
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── ProppyProvider.tsx
│ │ ├── index.spec.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── webpack.config.js
├── proppy-preact
│ ├── .gitignore
│ ├── .npmignore
│ ├── ProppyProvider.js
│ ├── README.md
│ ├── attach.js
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── ProppyContext.ts
│ │ ├── ProppyProvider.tsx
│ │ ├── ProppySubscription.tsx
│ │ ├── attach.spec.tsx
│ │ ├── attach.tsx
│ │ ├── index.spec.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── webpack.config.js
├── proppy-react
│ ├── .gitignore
│ ├── .npmignore
│ ├── ProppyProvider.js
│ ├── README.md
│ ├── attach.js
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── ProppyContext.ts
│ │ ├── ProppyProvider.tsx
│ │ ├── ProppySubscription.tsx
│ │ ├── attach.spec.tsx
│ │ ├── attach.tsx
│ │ └── index.ts
│ ├── tsconfig.json
│ └── webpack.config.js
├── proppy-redux
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── index.ts
│ │ ├── withStore.spec.ts
│ │ └── withStore.ts
│ ├── tsconfig.json
│ ├── webpack.config.js
│ └── withStore.js
├── proppy-rx
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── from.js
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── from.spec.ts
│ │ ├── from.ts
│ │ ├── index.ts
│ │ ├── withStream.spec.ts
│ │ └── withStream.ts
│ ├── tsconfig.json
│ ├── webpack.config.js
│ └── withStream.js
├── proppy-vue
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── attach.js
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── attach.spec.ts
│ │ ├── attach.ts
│ │ └── index.ts
│ ├── tsconfig.json
│ └── webpack.config.js
└── proppy
│ ├── .gitignore
│ ├── .npmignore
│ ├── README.md
│ ├── compose.js
│ ├── create.js
│ ├── didSubscribe.js
│ ├── emit.js
│ ├── handleReceivedProps.js
│ ├── jest.config.js
│ ├── map.js
│ ├── onChange.js
│ ├── package.json
│ ├── shouldUpdate.js
│ ├── src
│ ├── compose.spec.ts
│ ├── compose.ts
│ ├── create.ts
│ ├── didSubscribe.spec.ts
│ ├── didSubscribe.ts
│ ├── emit.spec.ts
│ ├── emit.ts
│ ├── handleReceivedProps.spec.ts
│ ├── handleReceivedProps.ts
│ ├── index.ts
│ ├── map.spec.ts
│ ├── map.ts
│ ├── onChange.spec.ts
│ ├── onChange.ts
│ ├── shouldUpdate.spec.ts
│ ├── shouldUpdate.ts
│ ├── types.ts
│ ├── willDestroy.spec.ts
│ ├── willDestroy.ts
│ ├── withHandlers.spec.ts
│ ├── withHandlers.ts
│ ├── withObservable.spec.ts
│ ├── withObservable.ts
│ ├── withProps.spec.ts
│ ├── withProps.ts
│ ├── withReducer.spec.ts
│ ├── withReducer.ts
│ ├── withState.spec.ts
│ ├── withState.ts
│ ├── withStateHandlers.spec.ts
│ ├── withStateHandlers.ts
│ ├── withTimer.spec.ts
│ └── withTimer.ts
│ ├── tsconfig.json
│ ├── webpack.config.js
│ ├── willDestroy.js
│ ├── withHandlers.js
│ ├── withObservable.js
│ ├── withProps.js
│ ├── withReducer.js
│ ├── withState.js
│ ├── withStateHandlers.js
│ └── withTimer.js
├── site
├── assets
│ ├── css
│ │ ├── content.scss
│ │ ├── flow.scss
│ │ ├── footer.scss
│ │ ├── global.scss
│ │ ├── header.scss
│ │ └── main.scss
│ ├── img
│ │ ├── banner-trimmed-900.png
│ │ ├── banner-trimmed.png
│ │ ├── banner.png
│ │ ├── js-logo.png
│ │ ├── logo-text-80.png
│ │ ├── logo-text.png
│ │ ├── logo-transparent-128.png
│ │ ├── logo-transparent.png
│ │ ├── preact-logo.png
│ │ ├── proppy-flow.gif
│ │ ├── proppy-og.png
│ │ ├── proppy-square-128.png
│ │ ├── proppy-square.png
│ │ ├── react-logo.svg
│ │ ├── redux-logo.png
│ │ ├── rxjs-logo.png
│ │ └── vue-logo.png
│ └── js
│ │ └── site.js
├── bin
│ └── build.js
├── content
│ ├── README.md
│ └── docs
│ │ ├── README.md
│ │ ├── api.md
│ │ ├── bundling.md
│ │ ├── changelog.md
│ │ ├── contributing.md
│ │ ├── examples
│ │ ├── react-counter.md
│ │ ├── react-fetch-request.md
│ │ ├── react-redux.md
│ │ ├── react-rxjs.md
│ │ └── vue-counter.md
│ │ ├── factory-instance.md
│ │ ├── faq.md
│ │ ├── introduction.md
│ │ ├── lifecycle.md
│ │ ├── packages
│ │ ├── proppy-preact.md
│ │ ├── proppy-react.md
│ │ ├── proppy-redux.md
│ │ ├── proppy-rx.md
│ │ ├── proppy-vue.md
│ │ └── proppy.md
│ │ ├── playground.md
│ │ ├── props.md
│ │ ├── providers.md
│ │ ├── quickstart.md
│ │ └── testing.md
├── data
│ ├── defaults.json
│ └── sidebarLinks.json
├── layouts
│ ├── default.html
│ └── home.html
└── partials
│ ├── assets_body.html
│ ├── assets_head.html
│ ├── docsSidebar.html
│ ├── footer.html
│ └── navLinks.html
└── tslint.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | ; This file is for unifying the coding style for different editors and IDEs.
2 | ; More information at http://editorconfig.org
3 |
4 | root = true
5 |
6 | [*]
7 | charset = utf-8
8 | indent_style = space
9 | indent_size = 2
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [Makefile]
14 | indent_style = tab
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib
3 | dist
4 | coverage
5 | npm-debug.log
6 | lerna-debug.log
7 | yarn.lock
8 | _site
9 | .changelog
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - 'lts/*'
5 |
6 | script:
7 | - 'npm run bootstrap'
8 | - 'set -e'
9 | - 'npm run lint'
10 | - 'npm run test'
11 |
12 | after_success:
13 | - 'npm run cover'
14 |
15 | notifications:
16 | email: false
17 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | proppyjs.com
2 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at proppyjs-conduct@googlegroups.com
59 | All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
60 | Further details of specific enforcement policies may be posted separately.
61 |
62 | Project maintainers who do not follow or enforce the Code of Conduct in good
63 | faith may face temporary or permanent repercussions as determined by other
64 | members of the project's leadership.
65 |
66 | ## Attribution
67 |
68 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
69 | available at [http://contributor-covenant.org/version/1/4][version]
70 |
71 | [homepage]: http://contributor-covenant.org
72 | [version]: http://contributor-covenant.org/version/1/4/
73 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to ProppyJS
2 |
3 |
4 |
5 | - [Code of Conduct](#code-of-conduct)
6 | - [Open Development](#open-development)
7 | - [Bugs and Issues](#bugs-and-issues)
8 | - [Proposals](#proposals)
9 | - [Sending a pull request](#sending-a-pull-request)
10 | - [Get in Touch](#get-in-touch)
11 | - [License](#license)
12 |
13 |
14 |
15 | ## Code of Conduct
16 |
17 | We have adopted a Code of Conduct, that we expect all the participants of our project to adhere to. You can find the full text [here](https://github.com/fahad19/proppy/blob/master/CODE_OF_CONDUCT.md).
18 |
19 | ## Open Development
20 |
21 | Development of the project is done publicly on [GitHub](https://github.com/fahad19/proppy). Core team members and also other external contributors send Pull Requests and they all go through the same review process.
22 |
23 | ## Bugs and Issues
24 |
25 | Bugs can be reported as [GitHub Issues](https://github.com/fahad19/proppy/issues).
26 |
27 | Before submitting any issue, please make sure they are not a duplicate of any other [existing issues](https://github.com/fahad19/proppy/issues).
28 |
29 | ## Proposals
30 |
31 | For every new feature, package or a change, we post [proposals](https://github.com/fahad19/proppy/issues?q=is%3Aissue+is%3Aopen+label%3Aproposal) under GitHub Issues publicly.
32 |
33 | Members from the project and other interested parties can then comment on the proposal, discussing about the strengths and weaknesses, and the proposal gets refined/updated based on feedback.
34 |
35 | Once the spec is ready and finalized, the development begins.
36 |
37 | In your Proposal description, make sure you answer these questions:
38 |
39 | * What is the current situation?
40 | * What is the proposed change?
41 |
42 | ## Sending a pull request
43 |
44 | If you are looking for ways to contribute with code or documentation to the project directly, you can help us out by contributing to issues with [`help wanted`](https://github.com/fahad19/proppy/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) label on GitHub.
45 |
46 | We already have automated checks for tests and linting for all Pull Requests, but you can always run these commands locally to make sure everything is working as expected:
47 |
48 | ```
49 | $ npm install
50 |
51 | $ npm run bootstrap
52 | $ npm run lint
53 | $ npm test
54 | ```
55 |
56 | If your Pull Request results in a change in the API, then please update the [examples](https://github.com/fahad19/proppy/tree/master/examples) in the same repository accordingly, as well as the API documentation (`README.md` of every package).
57 |
58 | ## Get in Touch
59 |
60 | * [Twitter](https://twitter.com/fahad19)
61 | * [GitHub](https://github.com/fahad19/proppy)
62 |
63 | ## License
64 |
65 | By contributing to ProppyJS, you agree that your contributions will be licensed under its MIT license.
66 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018-present Fahad Ibnay Heylaal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | FROM_TAG := ""
2 | TO_TAG := ""
3 | GITHUB_API_TOKEN := ""
4 |
5 | ##
6 | # Releases
7 | release:
8 | npm run dist
9 | ./node_modules/.bin/lerna publish --force-publish=*
10 |
11 | release-canary:
12 | npm run dist
13 | ./node_modules/.bin/lerna publish --canary
14 |
15 | ##
16 | # Changelog
17 | changelog:
18 | git checkout master
19 | git pull origin master
20 | GITHUB_AUTH=$(GITHUB_API_TOKEN) ./node_modules/.bin/lerna-changelog --tag-from $(FROM_TAG) --tag-to $(TO_TAG)
21 |
22 | push-changelog:
23 | git checkout master
24 | git pull origin master
25 | git add CHANGELOG.md
26 | git commit -m 'changelog updated.'
27 | git push origin master
28 |
29 | ##
30 | # Packages
31 | list-packages:
32 | ./node_modules/.bin/lerna ls
33 |
34 | list-updated:
35 | ./node_modules/.bin/lerna updated
36 |
37 | list-dists:
38 | @echo "bytes \\t kilobytes \\t file"
39 | @echo "--- \\t\\t --- \\t\\t ---"
40 | @ls -alh ./packages/proppy*/dist/*.min.js.gz | awk '{print $$9 }' | while read LINE; do\
41 | SIZE="$$(cat $${LINE} | wc -c | bc)";\
42 | SIZE_IN_KB=$$(echo "scale=1; $${SIZE} / 1024" | bc);\
43 | echo "$${SIZE}K \\t\\t $${SIZE_IN_KB}K \\t\\t $${LINE}";\
44 | done
45 |
46 | ##
47 | # Site
48 | site-build:
49 | node ./site/bin/build.js
50 |
51 | mkdir -p ./_site/css
52 | ./node_modules/.bin/node-sass --include-path ./node_modules ./site/assets/css/main.scss ./_site/css/site.css
53 |
54 | mkdir -p ./_site/js
55 | cp -r ./site/assets/js/ ./_site/js/
56 | cp -rf ./site/assets/img ./_site/img
57 |
58 | site-watch:
59 | make site-build
60 | fswatch -or './site' | xargs -I{} make site-build
61 |
62 | site-serve-only:
63 | echo "Starting server at http://localhost:6001"
64 | ./node_modules/.bin/live-server --port=6001 ./_site/
65 |
66 | site-serve:
67 | make site-build
68 | make site-serve-only
69 |
70 | site-publish:
71 | rm -rf ./_site
72 | make site-build
73 | make site-publish-only
74 |
75 | site-publish-only:
76 | rm -rf ./_site/.git
77 |
78 | cp -f CNAME ./_site/CNAME
79 |
80 | (cd ./_site && git init)
81 | (cd ./_site && git commit --allow-empty -m 'update site')
82 | (cd ./_site && git checkout -b gh-pages)
83 | (cd ./_site && touch .nojekyll)
84 | (cd ./_site && git add .)
85 | (cd ./_site && git commit -am 'update site')
86 | (cd ./_site && git push git@github.com:fahad19/proppy gh-pages --force)
87 |
--------------------------------------------------------------------------------
/examples/preact-playground/README.md:
--------------------------------------------------------------------------------
1 | # Preact Playground
2 |
3 | Examples of ProppyJS using Preact.
4 |
5 | ## Demo
6 |
7 | You can access this example on [CodeSandbox](https://codesandbox.io/s/github/fahad19/proppy/tree/master/examples/preact-playground).
8 |
9 | ## Local development
10 |
11 | ```
12 | $ npm install
13 | $ npm start
14 | ```
15 |
--------------------------------------------------------------------------------
/examples/preact-playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppyjs-preact-playground-wip",
3 | "description": null,
4 | "version": "0.0.16",
5 | "dependencies": {
6 | "history": "^4.7.0",
7 | "preact": "8.2.5",
8 | "preact-compat": "3.17.0",
9 | "proppy": "^1.2.1",
10 | "proppy-preact": "^1.0.0",
11 | "proppy-redux": "^1.0.0",
12 | "proppy-rx": "^1.0.0",
13 | "react-router": "^4.0.0",
14 | "react-router-dom": "^4.0.0",
15 | "redux": "^4.0.0",
16 | "rxjs": "^6.0.0"
17 | },
18 | "devDependencies": {
19 | "eslint": "^4.5.0",
20 | "eslint-config-synacor": "^1.1.0",
21 | "if-env": "^1.0.0",
22 | "less": "^2.7.2",
23 | "less-loader": "^4.0.5",
24 | "node-sass": "^4.5.3",
25 | "preact-cli": "^1.4.1",
26 | "sass-loader": "^6.0.6",
27 | "stylus": "^0.54.5",
28 | "stylus-loader": "^3.0.1"
29 | },
30 | "scripts": {
31 | "test": "eslint . && preact test",
32 | "start": "if-env NODE_ENV=production && npm run -s serve || npm run -s dev",
33 | "build": "preact build",
34 | "serve": "preact build && preact serve",
35 | "dev": "preact watch"
36 | },
37 | "eslintConfig": {
38 | "extends": "eslint-config-synacor"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/config.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 |
3 | // proppy
4 | import PlayWithState from "./proppy/PlayWithState";
5 | import PlayWithProps from "./proppy/PlayWithProps";
6 | import PlayCompose from "./proppy/PlayCompose";
7 | import PlayWithReducer from "./proppy/PlayWithReducer";
8 | import PlayWithHandlers from "./proppy/PlayWithHandlers";
9 | import PlayWithStateHandlers from "./proppy/PlayWithStateHandlers";
10 | import PlayWithObservable from "./proppy/PlayWithObservable";
11 | import PlayWithTimer from "./proppy/PlayWithTimer";
12 | import PlayEmit from "./proppy/PlayEmit";
13 | import PlayOnChange from "./proppy/PlayOnChange";
14 | import PlayMap from "./proppy/PlayMap";
15 | import PlayShouldUpdate from "./proppy/PlayShouldUpdate";
16 | import PlayDidSubscribe from "./proppy/PlayDidSubscribe";
17 |
18 | // proppy-redux
19 | import PlayWithStore from "./proppy-redux/components/PlayWithStore";
20 |
21 | // proppy-rx
22 | import PlayWithStream from "./proppy-rx/PlayWithStream";
23 |
24 | export const routesConfig = [
25 | {
26 | name: "proppy",
27 | routes: [
28 | { path: "/withProps", render: () => },
29 | { path: "/withState", render: () => },
30 | { path: "/compose", render: () => },
31 | { path: "/withReducer", render: () => },
32 | { path: "/withHandlers", render: () => },
33 | { path: "/withStateHandlers", render: () => },
34 | { path: "/withObservable", render: () => },
35 | { path: "/withTimer", render: () => },
36 | { path: "/emit", render: () => },
37 | { path: "/onChange", render: () => },
38 | { path: "/map", render: () => },
39 | { path: "/shouldUpdate", render: () => },
40 | { path: "/didSubscribe", render: () => }
41 | ]
42 | },
43 | {
44 | name: "proppy-redux",
45 | routes: [{ path: "/withStore", render: () => }]
46 | },
47 | {
48 | name: "proppy-rx",
49 | routes: [
50 | { path: "/withStream", render: props => }
51 | ]
52 | }
53 | ];
54 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | gh-pages
3 |
4 | # Esdoc dirs
5 | out
6 | gh-pages
7 |
8 | # Docker
9 | *.dockerfile
10 | *.dockerignore
11 |
12 | # NPM config
13 | .npmrc
14 |
15 | # Avoid lock files in libraries
16 | yarn.lock
17 | package-lock.json
18 |
19 | # Created by https://www.gitignore.io/api/node,windows,osx,linux,vim
20 |
21 | ### Linux ###
22 | *~
23 |
24 | # temporary files which can be created if a process still has a handle open of a deleted file
25 | .fuse_hidden*
26 |
27 | # KDE directory preferences
28 | .directory
29 |
30 | # Linux trash folder which might appear on any partition or disk
31 | .Trash-*
32 |
33 | # .nfs files are created when an open file is removed but is still being accessed
34 | .nfs*
35 |
36 | ### Node ###
37 | # Logs
38 | logs
39 | *.log
40 | npm-debug.log*
41 | yarn-debug.log*
42 | yarn-error.log*
43 |
44 | # Runtime data
45 | pids
46 | *.pid
47 | *.seed
48 | *.pid.lock
49 |
50 | # Directory for instrumented libs generated by jscoverage/JSCover
51 | lib-cov
52 |
53 | # Coverage directory used by tools like istanbul
54 | coverage
55 |
56 | # nyc test coverage
57 | .nyc_output
58 |
59 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
60 | .grunt
61 |
62 | # Bower dependency directory (https://bower.io/)
63 | bower_components
64 |
65 | # node-waf configuration
66 | .lock-wscript
67 |
68 | # Compiled binary addons (http://nodejs.org/api/addons.html)
69 | build/Release
70 |
71 | # Dependency directories
72 | node_modules/
73 | jspm_packages/
74 |
75 | # Typescript v1 declaration files
76 | typings/
77 |
78 | # Optional npm cache directory
79 | .npm
80 |
81 | # Optional eslint cache
82 | .eslintcache
83 |
84 | # Optional REPL history
85 | .node_repl_history
86 |
87 | # Output of 'npm pack'
88 | *.tgz
89 |
90 | # Yarn Integrity file
91 | .yarn-integrity
92 |
93 | # dotenv environment variables file
94 | .env
95 |
96 |
97 | ### OSX ###
98 | *.DS_Store
99 | .AppleDouble
100 | .LSOverride
101 |
102 | # Icon must end with two \r
103 | Icon
104 |
105 |
106 | # Thumbnails
107 | ._*
108 |
109 | # Files that might appear in the root of a volume
110 | .DocumentRevisions-V100
111 | .fseventsd
112 | .Spotlight-V100
113 | .TemporaryItems
114 | .Trashes
115 | .VolumeIcon.icns
116 | .com.apple.timemachine.donotpresent
117 |
118 | # Directories potentially created on remote AFP share
119 | .AppleDB
120 | .AppleDesktop
121 | Network Trash Folder
122 | Temporary Items
123 | .apdisk
124 |
125 | ### Vim ###
126 | # swap
127 | [._]*.s[a-v][a-z]
128 | [._]*.sw[a-p]
129 | [._]s[a-v][a-z]
130 | [._]sw[a-p]
131 | # session
132 | Session.vim
133 | # temporary
134 | .netrwhist
135 | # auto-generated tag files
136 | tags
137 |
138 | ### Windows ###
139 | # Windows thumbnail cache files
140 | Thumbs.db
141 | ehthumbs.db
142 | ehthumbs_vista.db
143 |
144 | # Folder config file
145 | Desktop.ini
146 |
147 | # Recycle Bin used on file shares
148 | $RECYCLE.BIN/
149 |
150 | # Windows Installer files
151 | *.cab
152 | *.msi
153 | *.msm
154 | *.msp
155 |
156 | # Windows shortcuts
157 | *.lnk
158 | # es-module.patch
159 | *.patch
160 |
161 | # End of https://www.gitignore.io/api/node,windows,osx,linux,vim
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/favicon.ico
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/icons/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/icons/android-chrome-192x192.png
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/icons/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/icons/android-chrome-512x512.png
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/icons/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/icons/apple-touch-icon.png
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/icons/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/icons/favicon-16x16.png
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/icons/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/icons/favicon-32x32.png
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/assets/icons/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/examples/preact-playground/src/files/assets/icons/mstile-150x150.png
--------------------------------------------------------------------------------
/examples/preact-playground/src/files/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Preact PWA",
3 | "short_name": "Preact PWA",
4 | "start_url": "/",
5 | "display": "standalone",
6 | "orientation": "portrait",
7 | "background_color": "#fff",
8 | "theme_color": "#673ab8",
9 | "icons": [{
10 | "src": "/assets/icons/android-chrome-192x192.png",
11 | "type": "image/png",
12 | "sizes": "192x192"
13 | },
14 | {
15 | "src": "/assets/icons/android-chrome-512x512.png",
16 | "type": "image/png",
17 | "sizes": "512x512"
18 | }]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/index.js:
--------------------------------------------------------------------------------
1 | import "./style";
2 | import { h, Component, render } from "preact";
3 | import { ProppyProvider } from "proppy-preact";
4 | import createHistory from "history/createBrowserHistory";
5 | import { Router, Route, Switch } from "react-router-dom";
6 |
7 | import { routesConfig } from "./config";
8 | import store from "./proppy-redux/store";
9 |
10 | const history = createHistory();
11 | const providers = {
12 | store,
13 | history
14 | };
15 |
16 | export function App() {
17 | return (
18 |
19 |
20 |
21 |
{
23 | history.push(e.target.value);
24 | }}
25 | >
26 | -- Choose an example --
27 | {routesConfig.map(config => (
28 |
29 | {config.routes.map(route => (
30 |
31 | {route.path.replace("/", "")}
32 |
33 | ))}
34 |
35 | ))}
36 |
37 |
38 |
39 | {routesConfig.map(config =>
40 | config.routes.map(route => )
41 | )}
42 |
43 |
44 |
ProppyJS Playground with Preact
45 |
46 |
47 |
48 |
49 |
50 |
51 | Visit proppyjs.com for more info
52 |
53 |
54 |
55 |
56 | );
57 | }
58 |
59 | if (typeof window !== "undefined") {
60 | render( , document.getElementById("root"));
61 | }
62 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppyjs-preact-playground-wip",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "index.js",
7 | "dependencies": {
8 | "history": "^4.7.0",
9 | "preact": "8.2.5",
10 | "preact-compat": "3.17.0",
11 | "proppy": "^1.2.1",
12 | "proppy-preact": "^1.0.0",
13 | "proppy-redux": "^1.0.0",
14 | "proppy-rx": "^1.0.0",
15 | "react-router": "^4.0.0",
16 | "react-router-dom": "^4.0.0",
17 | "redux": "^4.0.0",
18 | "rxjs": "^6.0.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-redux/actions/counter.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from "../constants";
2 |
3 | export function incrementCounter() {
4 | return {
5 | type: INCREMENT_COUNTER
6 | };
7 | }
8 |
9 | export function decrementCounter() {
10 | return {
11 | type: DECREMENT_COUNTER
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-redux/components/PlayWithStore.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withStore } from "proppy-redux";
3 | import { attach } from "proppy-preact";
4 |
5 | import { incrementCounter, decrementCounter } from "../actions/counter";
6 |
7 | function mapState(state) {
8 | return {
9 | counter: state.counter.value
10 | };
11 | }
12 |
13 | const mapDispatch = {
14 | increment: incrementCounter,
15 | decrement: decrementCounter
16 | };
17 |
18 | const P = withStore(mapState, mapDispatch);
19 |
20 | function MyComponent({ counter, increment, decrement }) {
21 | return (
22 |
23 |
ProppyJS: withStore
24 |
25 | Counter: {counter}
26 |
27 | Increment
28 | Decrement
29 |
30 | );
31 | }
32 |
33 | export default attach(P)(MyComponent);
34 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-redux/constants/index.js:
--------------------------------------------------------------------------------
1 | export const INCREMENT_COUNTER = "INCREMENT_COUNTER";
2 | export const DECREMENT_COUNTER = "DECREMENT_COUNTER";
3 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-redux/reducers/counter.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from "../constants";
2 |
3 | const INITIAL_STATE = {
4 | value: 0
5 | };
6 |
7 | export default function counterReducer(state = INITIAL_STATE, action) {
8 | switch (action.type) {
9 | case INCREMENT_COUNTER:
10 | return Object.assign(
11 | {},
12 | {
13 | value: state.value + 1
14 | }
15 | );
16 | case DECREMENT_COUNTER:
17 | return Object.assign(
18 | {},
19 | {
20 | value: state.value - 1
21 | }
22 | );
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 |
3 | import counter from "./counter";
4 |
5 | export default combineReducers({
6 | counter
7 | });
8 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-redux/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "redux";
2 |
3 | import rootReducer from "../reducers";
4 |
5 | export default createStore(rootReducer);
6 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy-rx/PlayWithStream.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withStream } from "proppy-rx";
3 | import { map } from "rxjs/operators";
4 | import { attach } from "proppy-preact";
5 |
6 | const P = withStream(props$ =>
7 | props$.pipe(
8 | map(props => ({
9 | url: props.match.url
10 | }))
11 | )
12 | );
13 |
14 | function MyComponent({ url }) {
15 | return (
16 |
17 |
ProppyJS: withStream
18 |
19 | URL: {url}
20 |
21 | );
22 | }
23 |
24 | export default attach(P)(MyComponent);
25 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayCompose.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withProps, withState } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withProps({ foo: "foo value" }),
7 | withProps((parentProps, providers) => ({
8 | bar: `bar with ${parentProps.foo}`
9 | })),
10 | withState("counter", "setCounter", 0)
11 | );
12 |
13 | function MyComponent({ foo, bar, counter, setCounter }) {
14 | return (
15 |
16 |
ProppyJS: compose
17 |
18 | Foo: {foo}
19 |
20 | Bar: {bar}
21 |
22 | Counter: {counter}
23 |
24 | setCounter(counter + 1)}>Increment
25 |
26 | );
27 | }
28 |
29 | export default attach(P)(MyComponent);
30 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayDidSubscribe.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withState, didSubscribe } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withState("stars", "setStars", "n/a"),
7 | withState("isLoading", "setLoading", false),
8 | didSubscribe((props, providers) => {
9 | props.setLoading(true);
10 | fetch("https://api.github.com/repos/fahad19/proppy")
11 | .then(res => res.json())
12 | .then(data => data.stargazers_count)
13 | .then(stars => {
14 | props.setLoading(false);
15 | props.setStars(stars);
16 | });
17 | })
18 | );
19 |
20 | function MyComponent({ stars, isLoading }) {
21 | return (
22 |
23 |
ProppyJS: didSubscribe
24 |
25 |
26 | Stargazers count of{" "}
27 | fahad19/proppy : {stars}
28 |
29 |
30 |
Request in progress: {isLoading ? "Yes" : "No"}
31 |
32 | );
33 | }
34 |
35 | export default attach(P)(MyComponent);
36 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayEmit.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withProps, emit } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withProps({ interval: 0 }),
7 | emit((cb, props) => {
8 | let n = props.interval;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ interval: n });
13 | }, 300);
14 |
15 | return () => clearInterval(i);
16 | })
17 | );
18 |
19 | function MyComponent({ interval }) {
20 | return (
21 |
22 |
ProppyJS: emit
23 |
24 | Interval: {interval}
25 |
26 | );
27 | }
28 |
29 | export default attach(P)(MyComponent);
30 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayMap.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withProps, emit, map } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withProps({ number: 0 }),
7 | emit((cb, props) => {
8 | let n = props.number;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ number: n });
13 | }, 200);
14 |
15 | return () => clearInterval(i);
16 | }),
17 | map(props => ({
18 | ...props,
19 | number: props.number * 100
20 | }))
21 | );
22 |
23 | function MyComponent({ number }) {
24 | return (
25 |
26 |
ProppyJS: map
27 |
28 | Number: {number}
29 |
30 | );
31 | }
32 |
33 | export default attach(P)(MyComponent);
34 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayOnChange.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withProps, emit, onChange } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withProps({ interval: 0, type: "even" }),
7 | emit((cb, props) => {
8 | let n = props.interval;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ interval: n });
13 | }, 1000);
14 |
15 | return () => clearInterval(i);
16 | }),
17 | onChange("interval", props => ({
18 | type: props.interval % 2 === 0 ? "even" : "odd"
19 | }))
20 | );
21 |
22 | function MyComponent({ interval, type }) {
23 | return (
24 |
25 |
ProppyJS: onChange
26 |
27 | Interval: {interval}
28 |
29 | Number is: {type}
30 |
31 | );
32 | }
33 |
34 | export default attach(P)(MyComponent);
35 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayShouldUpdate.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withProps, emit, shouldUpdate } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withProps({ number: 0 }),
7 | emit((cb, props) => {
8 | let n = props.number;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ number: n });
13 | }, 1000);
14 |
15 | return () => clearInterval(i);
16 | }),
17 | shouldUpdate((prevProps, nextProps) => {
18 | return nextProps.number % 2 === 0;
19 | })
20 | );
21 |
22 | function MyComponent({ number }) {
23 | return (
24 |
25 |
ProppyJS: shouldUpdate
26 |
27 |
Number: {number}
28 |
29 |
(Re-renders when number is even)
30 |
31 | );
32 | }
33 |
34 | export default attach(P)(MyComponent);
35 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithHandlers.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withState, withHandlers } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withState("counter", "setCounter", 0),
7 | withHandlers({
8 | incrementBy: (props, providers) => n => {
9 | props.setCounter(props.counter + n);
10 | }
11 | })
12 | );
13 |
14 | function MyComponent({ counter, incrementBy }) {
15 | return (
16 |
17 |
ProppyJS: withHandlers
18 |
19 | Counter: {counter}
20 |
21 | incrementBy(5)}>Increment by 5
22 |
23 | );
24 | }
25 |
26 | export default attach(P)(MyComponent);
27 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithObservable.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withObservable } from "proppy";
3 | import { interval } from "rxjs";
4 | import { map } from "rxjs/operators";
5 | import { attach } from "proppy-preact";
6 |
7 | const P = withObservable(() => {
8 | return interval(300).pipe(
9 | map(n => ({
10 | interval: n
11 | }))
12 | );
13 | });
14 |
15 | function MyComponent({ interval }) {
16 | return (
17 |
18 |
ProppyJS: withObservable
19 |
20 | Interval: {interval}
21 |
22 | );
23 | }
24 |
25 | export default attach(P)(MyComponent);
26 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithProps.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withProps } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = withProps({
6 | foo: "foo value"
7 | });
8 |
9 | function MyComponent({ foo }) {
10 | return (
11 |
12 |
ProppyJS: withProps
13 |
14 | Foo: {foo}
15 |
16 | );
17 | }
18 |
19 | export default attach(P)(MyComponent);
20 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithReducer.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withReducer } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | function reducer(state, action) {
6 | switch (action.type) {
7 | case "INCREMENT":
8 | return { value: state.value + 1 };
9 | case "DECREMENT":
10 | return { value: state.value - 1 };
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | const P = withReducer("counter", "dispatch", reducer, { value: 0 });
17 |
18 | function MyComponent({ counter, dispatch }) {
19 | return (
20 |
21 |
ProppyJS: withReducer
22 |
23 | Counter: {counter.value}
24 |
25 | dispatch({ type: "INCREMENT" })}>Increment
26 | dispatch({ type: "DECREMENT" })}>Decrement
27 |
28 | );
29 | }
30 |
31 | export default attach(P)(MyComponent);
32 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithState.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withState } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = withState("counter", "setCounter", 0);
6 |
7 | function MyComponent({ counter, setCounter }) {
8 | return (
9 |
10 |
ProppyJS: withState
11 |
12 | Counter: {counter}
13 |
14 | setCounter(counter + 1)}>Increment
15 |
16 | );
17 | }
18 |
19 | export default attach(P)(MyComponent);
20 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithStateHandlers.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { withStateHandlers } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = withStateHandlers(
6 | // initial state
7 | {
8 | counter: 0
9 | },
10 |
11 | // handlers
12 | {
13 | incrementBy: (props, providers) => n => {
14 | // return new state
15 | return {
16 | counter: props.counter + n
17 | };
18 | }
19 | }
20 | );
21 |
22 | function MyComponent({ counter, incrementBy }) {
23 | return (
24 |
25 |
ProppyJS: withStateHandlers
26 |
27 | Counter: {counter}
28 |
29 | incrementBy(5)}>Increment by 5
30 |
31 | );
32 | }
33 |
34 | export default attach(P)(MyComponent);
35 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/proppy/PlayWithTimer.js:
--------------------------------------------------------------------------------
1 | import { h } from "preact";
2 | import { compose, withProps, withTimer } from "proppy";
3 | import { attach } from "proppy-preact";
4 |
5 | const P = compose(
6 | withProps({ hasElapsed: false }),
7 | withTimer(2000, { hasElapsed: true })
8 | );
9 |
10 | function MyComponent({ hasElapsed }) {
11 | return (
12 |
13 |
ProppyJS: withElapsed
14 |
15 | Elapsed two seconds: {hasElapsed ? "Yes!" : "No"}
16 |
17 | );
18 | }
19 |
20 | export default attach(P)(MyComponent);
21 |
--------------------------------------------------------------------------------
/examples/preact-playground/src/style/index.scss:
--------------------------------------------------------------------------------
1 | #root {
2 | font-family: sans-serif;
3 | text-align: center;
4 | color: #000;
5 | }
6 |
--------------------------------------------------------------------------------
/examples/react-playground/README.md:
--------------------------------------------------------------------------------
1 | # ReactJS Playground
2 |
3 | Examples of ProppyJS using React.
4 |
5 | ## Demo
6 |
7 | You can access this example on [CodeSandbox](https://codesandbox.io/s/github/fahad19/proppy/tree/master/examples/react-playground).
8 |
9 | ## Local development
10 |
11 | ```
12 | $ npm install
13 | $ npm start
14 | ```
15 |
--------------------------------------------------------------------------------
/examples/react-playground/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppyjs-react-playground",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "history": "^4.7.0",
9 | "proppy": "^1.2.1",
10 | "proppy-react": "^1.0.0",
11 | "proppy-redux": "^1.0.0",
12 | "proppy-rx": "^1.0.0",
13 | "react": "16.3.2",
14 | "react-dom": "16.3.2",
15 | "react-router": "^4.0.0",
16 | "react-router-dom": "^4.0.0",
17 | "react-scripts": "1.1.4",
18 | "redux": "^4.0.0",
19 | "rxjs": "^6.0.0"
20 | },
21 | "devDependencies": {},
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test --env=jsdom",
26 | "eject": "react-scripts eject"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/react-playground/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | React App
24 |
25 |
26 |
27 |
28 | You need to enable JavaScript to run this app.
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/react-playground/src/config.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | // proppy
4 | import PlayWithState from "./proppy/PlayWithState";
5 | import PlayWithProps from "./proppy/PlayWithProps";
6 | import PlayCompose from "./proppy/PlayCompose";
7 | import PlayWithReducer from "./proppy/PlayWithReducer";
8 | import PlayWithHandlers from "./proppy/PlayWithHandlers";
9 | import PlayWithStateHandlers from "./proppy/PlayWithStateHandlers";
10 | import PlayWithObservable from "./proppy/PlayWithObservable";
11 | import PlayWithTimer from "./proppy/PlayWithTimer";
12 | import PlayEmit from "./proppy/PlayEmit";
13 | import PlayOnChange from "./proppy/PlayOnChange";
14 | import PlayMap from "./proppy/PlayMap";
15 | import PlayShouldUpdate from "./proppy/PlayShouldUpdate";
16 | import PlayDidSubscribe from "./proppy/PlayDidSubscribe";
17 |
18 | // proppy-redux
19 | import PlayWithStore from "./proppy-redux/components/PlayWithStore";
20 |
21 | // proppy-rx
22 | import PlayWithStream from "./proppy-rx/PlayWithStream";
23 |
24 | export const routesConfig = [
25 | {
26 | name: "proppy",
27 | routes: [
28 | { path: "/withProps", render: () => },
29 | { path: "/withState", render: () => },
30 | { path: "/compose", render: () => },
31 | { path: "/withReducer", render: () => },
32 | { path: "/withHandlers", render: () => },
33 | { path: "/withStateHandlers", render: () => },
34 | { path: "/withObservable", render: () => },
35 | { path: "/withTimer", render: () => },
36 | { path: "/emit", render: () => },
37 | { path: "/onChange", render: () => },
38 | { path: "/map", render: () => },
39 | { path: "/shouldUpdate", render: () => },
40 | { path: "/didSubscribe", render: () => }
41 | ]
42 | },
43 | {
44 | name: "proppy-redux",
45 | routes: [{ path: "/withStore", render: () => }]
46 | },
47 | {
48 | name: "proppy-rx",
49 | routes: [
50 | { path: "/withStream", render: props => }
51 | ]
52 | }
53 | ];
54 |
--------------------------------------------------------------------------------
/examples/react-playground/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render } from "react-dom";
3 | import createHistory from "history/createBrowserHistory";
4 | import { Router, Route, Switch, Link } from "react-router-dom";
5 | import { ProppyProvider } from "proppy-react";
6 |
7 | import { routesConfig } from "./config";
8 | import store from "./proppy-redux/store";
9 |
10 | const history = createHistory();
11 |
12 | const styles = {
13 | fontFamily: "sans-serif",
14 | textAlign: "center"
15 | };
16 |
17 | const providers = {
18 | store,
19 | history
20 | };
21 |
22 | const App = () => (
23 |
24 |
25 |
26 |
{
28 | history.push(e.target.value);
29 | }}
30 | >
31 | -- Choose an example --
32 | {routesConfig.map(config => (
33 |
34 | {config.routes.map(route => (
35 |
36 | {route.path.replace("/", "")}
37 |
38 | ))}
39 |
40 | ))}
41 |
42 |
43 |
44 | {routesConfig.map(config =>
45 | config.routes.map(route => )
46 | )}
47 |
48 |
49 |
ProppyJS Playground with React
50 |
51 |
52 |
53 |
54 |
55 |
56 | Visit proppyjs.com for more info
57 |
58 |
59 |
60 |
61 | );
62 |
63 | render( , document.getElementById("root"));
64 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-redux/actions/counter.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from "../constants";
2 |
3 | export function incrementCounter() {
4 | return {
5 | type: INCREMENT_COUNTER
6 | };
7 | }
8 |
9 | export function decrementCounter() {
10 | return {
11 | type: DECREMENT_COUNTER
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-redux/components/PlayWithStore.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withStore } from "proppy-redux";
3 | import { attach } from "proppy-react";
4 |
5 | import { incrementCounter, decrementCounter } from "../actions/counter";
6 |
7 | function mapState(state) {
8 | return {
9 | counter: state.counter.value
10 | };
11 | }
12 |
13 | const mapDispatch = {
14 | increment: incrementCounter,
15 | decrement: decrementCounter
16 | };
17 |
18 | const P = withStore(mapState, mapDispatch);
19 |
20 | function MyComponent({ counter, increment, decrement }) {
21 | return (
22 |
23 |
ProppyJS: withStore
24 |
25 | Counter: {counter}
26 |
27 | Increment
28 | Decrement
29 |
30 | );
31 | }
32 |
33 | export default attach(P)(MyComponent);
34 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-redux/constants/index.js:
--------------------------------------------------------------------------------
1 | export const INCREMENT_COUNTER = "INCREMENT_COUNTER";
2 | export const DECREMENT_COUNTER = "DECREMENT_COUNTER";
3 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-redux/reducers/counter.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from "../constants";
2 |
3 | const INITIAL_STATE = {
4 | value: 0
5 | };
6 |
7 | export default function counterReducer(state = INITIAL_STATE, action) {
8 | switch (action.type) {
9 | case INCREMENT_COUNTER:
10 | return Object.assign(
11 | {},
12 | {
13 | value: state.value + 1
14 | }
15 | );
16 | case DECREMENT_COUNTER:
17 | return Object.assign(
18 | {},
19 | {
20 | value: state.value - 1
21 | }
22 | );
23 | default:
24 | return state;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-redux/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from "redux";
2 |
3 | import counter from "./counter";
4 |
5 | export default combineReducers({
6 | counter
7 | });
8 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-redux/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "redux";
2 |
3 | import rootReducer from "../reducers";
4 |
5 | export default createStore(rootReducer);
6 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy-rx/PlayWithStream.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withStream } from "proppy-rx";
3 | import { map } from "rxjs/operators";
4 | import { attach } from "proppy-react";
5 |
6 | const P = withStream(props$ =>
7 | props$.pipe(
8 | map(props => ({
9 | url: props.match.url
10 | }))
11 | )
12 | );
13 |
14 | function MyComponent({ url }) {
15 | return (
16 |
17 |
ProppyJS: withStream
18 |
19 | URL: {url}
20 |
21 | );
22 | }
23 |
24 | export default attach(P)(MyComponent);
25 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayCompose.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withProps, withState } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withProps({ foo: "foo value" }),
7 | withProps((parentProps, providers) => ({
8 | bar: `bar with ${parentProps.foo}`
9 | })),
10 | withState("counter", "setCounter", 0)
11 | );
12 |
13 | function MyComponent({ foo, bar, counter, setCounter }) {
14 | return (
15 |
16 |
ProppyJS: compose
17 |
18 | Foo: {foo}
19 |
20 | Bar: {bar}
21 |
22 | Counter: {counter}
23 |
24 | setCounter(counter + 1)}>Increment
25 |
26 | );
27 | }
28 |
29 | export default attach(P)(MyComponent);
30 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayDidSubscribe.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withState, didSubscribe } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withState("stars", "setStars", "n/a"),
7 | withState("isLoading", "setLoading", false),
8 | didSubscribe((props, providers) => {
9 | props.setLoading(true);
10 | fetch("https://api.github.com/repos/fahad19/proppy")
11 | .then(res => res.json())
12 | .then(data => data.stargazers_count)
13 | .then(stars => {
14 | props.setLoading(false);
15 | props.setStars(stars);
16 | });
17 | })
18 | );
19 |
20 | function MyComponent({ stars, isLoading }) {
21 | return (
22 |
23 |
ProppyJS: didSubscribe
24 |
25 |
26 | Stargazers count of{" "}
27 | fahad19/proppy : {stars}
28 |
29 |
30 |
Request in progress: {isLoading ? "Yes" : "No"}
31 |
32 | );
33 | }
34 |
35 | export default attach(P)(MyComponent);
36 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayEmit.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withProps, emit } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withProps({ interval: 0 }),
7 | emit((cb, props) => {
8 | let n = props.interval;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ interval: n });
13 | }, 300);
14 |
15 | return () => clearInterval(i);
16 | })
17 | );
18 |
19 | function MyComponent({ interval }) {
20 | return (
21 |
22 |
ProppyJS: emit
23 |
24 | Interval: {interval}
25 |
26 | );
27 | }
28 |
29 | export default attach(P)(MyComponent);
30 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayMap.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withProps, emit, map } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withProps({ number: 0 }),
7 | emit((cb, props) => {
8 | let n = props.number;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ number: n });
13 | }, 200);
14 |
15 | return () => clearInterval(i);
16 | }),
17 | map(props => ({
18 | ...props,
19 | number: props.number * 100
20 | }))
21 | );
22 |
23 | function MyComponent({ number }) {
24 | return (
25 |
26 |
ProppyJS: map
27 |
28 | Number: {number}
29 |
30 | );
31 | }
32 |
33 | export default attach(P)(MyComponent);
34 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayOnChange.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withProps, emit, onChange } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withProps({ interval: 0, type: "even" }),
7 | emit((cb, props) => {
8 | let n = props.interval;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ interval: n });
13 | }, 1000);
14 |
15 | return () => clearInterval(i);
16 | }),
17 | onChange("interval", props => ({
18 | type: props.interval % 2 === 0 ? "even" : "odd"
19 | }))
20 | );
21 |
22 | function MyComponent({ interval, type }) {
23 | return (
24 |
25 |
ProppyJS: onChange
26 |
27 | Interval: {interval}
28 |
29 | Number is: {type}
30 |
31 | );
32 | }
33 |
34 | export default attach(P)(MyComponent);
35 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayShouldUpdate.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withProps, emit, shouldUpdate } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withProps({ number: 0 }),
7 | emit((cb, props) => {
8 | let n = props.number;
9 |
10 | const i = setInterval(() => {
11 | n++;
12 | cb({ number: n });
13 | }, 1000);
14 |
15 | return () => clearInterval(i);
16 | }),
17 | shouldUpdate((prevProps, nextProps) => {
18 | return nextProps.number % 2 === 0;
19 | })
20 | );
21 |
22 | function MyComponent({ number }) {
23 | return (
24 |
25 |
ProppyJS: shouldUpdate
26 |
27 |
Number: {number}
28 |
29 |
(Re-renders when number is even)
30 |
31 | );
32 | }
33 |
34 | export default attach(P)(MyComponent);
35 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithHandlers.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withState, withHandlers } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withState("counter", "setCounter", 0),
7 | withHandlers({
8 | incrementBy: (props, providers) => n => {
9 | props.setCounter(props.counter + n);
10 | }
11 | })
12 | );
13 |
14 | function MyComponent({ counter, incrementBy }) {
15 | return (
16 |
17 |
ProppyJS: withHandlers
18 |
19 | Counter: {counter}
20 |
21 | incrementBy(5)}>Increment by 5
22 |
23 | );
24 | }
25 |
26 | export default attach(P)(MyComponent);
27 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithObservable.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withObservable } from "proppy";
3 | import { interval } from "rxjs";
4 | import { map } from "rxjs/operators";
5 | import { attach } from "proppy-react";
6 |
7 | const P = withObservable(() => {
8 | return interval(300).pipe(
9 | map(n => ({
10 | interval: n
11 | }))
12 | );
13 | });
14 |
15 | function MyComponent({ interval }) {
16 | return (
17 |
18 |
ProppyJS: withObservable
19 |
20 | Interval: {interval}
21 |
22 | );
23 | }
24 |
25 | export default attach(P)(MyComponent);
26 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithProps.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withProps } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = withProps({
6 | foo: "foo value"
7 | });
8 |
9 | function MyComponent({ foo }) {
10 | return (
11 |
12 |
ProppyJS: withProps
13 |
14 | Foo: {foo}
15 |
16 | );
17 | }
18 |
19 | export default attach(P)(MyComponent);
20 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithReducer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withReducer } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | function reducer(state, action) {
6 | switch (action.type) {
7 | case "INCREMENT":
8 | return { value: state.value + 1 };
9 | case "DECREMENT":
10 | return { value: state.value - 1 };
11 | default:
12 | return state;
13 | }
14 | }
15 |
16 | const P = withReducer("counter", "dispatch", reducer, { value: 0 });
17 |
18 | function MyComponent({ counter, dispatch }) {
19 | return (
20 |
21 |
ProppyJS: withReducer
22 |
23 | Counter: {counter.value}
24 |
25 | dispatch({ type: "INCREMENT" })}>Increment
26 | dispatch({ type: "DECREMENT" })}>Decrement
27 |
28 | );
29 | }
30 |
31 | export default attach(P)(MyComponent);
32 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithState.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withState } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = withState("counter", "setCounter", 0);
6 |
7 | function MyComponent({ counter, setCounter }) {
8 | return (
9 |
10 |
ProppyJS: withState
11 |
12 | Counter: {counter}
13 |
14 | setCounter(counter + 1)}>Increment
15 |
16 | );
17 | }
18 |
19 | export default attach(P)(MyComponent);
20 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithStateHandlers.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withStateHandlers } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = withStateHandlers(
6 | // initial state
7 | {
8 | counter: 0
9 | },
10 |
11 | // handlers
12 | {
13 | incrementBy: (props, providers) => n => {
14 | // return new state
15 | return {
16 | counter: props.counter + n
17 | };
18 | }
19 | }
20 | );
21 |
22 | function MyComponent({ counter, incrementBy }) {
23 | return (
24 |
25 |
ProppyJS: withStateHandlers
26 |
27 | Counter: {counter}
28 |
29 | incrementBy(5)}>Increment by 5
30 |
31 | );
32 | }
33 |
34 | export default attach(P)(MyComponent);
35 |
--------------------------------------------------------------------------------
/examples/react-playground/src/proppy/PlayWithTimer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { compose, withProps, withTimer } from "proppy";
3 | import { attach } from "proppy-react";
4 |
5 | const P = compose(
6 | withProps({ hasElapsed: false }),
7 | withTimer(2000, { hasElapsed: true })
8 | );
9 |
10 | function MyComponent({ hasElapsed }) {
11 | return (
12 |
13 |
ProppyJS: withElapsed
14 |
15 | Elapsed two seconds: {hasElapsed ? "Yes!" : "No"}
16 |
17 | );
18 | }
19 |
20 | export default attach(P)(MyComponent);
21 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "2.0.0-beta.38",
3 | "packages": [
4 | "packages/*"
5 | ],
6 | "version": "1.3.1",
7 | "changelog": {
8 | "repo": "fahad19/proppy",
9 | "labels": {
10 | "bug": "Bug fix",
11 | "PR": "Pull Requests"
12 | },
13 | "cacheDir": ".changelog"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy",
3 | "version": "0.0.1",
4 | "description": "",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "bootstrap": "lerna bootstrap",
8 | "test": "lerna run test",
9 | "cover": "lerna run cover",
10 | "lint": "lerna run lint",
11 | "transpile": "lerna run transpile",
12 | "clean": "lerna run clean",
13 | "dist": "lerna run dist",
14 | "dist:lib": "lerna run dist:lib",
15 | "dist:min": "lerna run dist:lib",
16 | "build": "lerna run build"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "git+https://github.com/fahad19/proppy.git"
21 | },
22 | "author": {
23 | "name": "Fahad Ibnay Heylaal",
24 | "url": "http://fahad19.com"
25 | },
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/fahad19/proppy/issues"
29 | },
30 | "homepage": "https://github.com/fahad19/proppy#readme",
31 | "dependencies": {},
32 | "devDependencies": {
33 | "@types/jest": "^22.2.3",
34 | "bulma": "^0.7.1",
35 | "compression-webpack-plugin": "^1.1.11",
36 | "jest": "^22.4.3",
37 | "js-yaml": "^3.11.0",
38 | "jsdom": "11.11.0",
39 | "lerna": "^2.11.0",
40 | "lerna-changelog": "^0.7.0",
41 | "live-server": "^1.2.0",
42 | "lodash": "^4.17.10",
43 | "marked": "^0.3.19",
44 | "metalsmith": "^2.3.0",
45 | "node-sass": "^4.9.0",
46 | "rimraf": "^2.6.2",
47 | "ts-jest": "^22.4.4",
48 | "ts-loader": "^4.2.0",
49 | "tslint": "^5.10.0",
50 | "typescript": "^3.1.2",
51 | "webpack": "^4.20.2",
52 | "webpack-cli": "^3.1.1"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/README.md:
--------------------------------------------------------------------------------
1 | # proppy-frint-react
2 |
3 | [](https://www.npmjs.com/package/proppy-frint-react)
4 |
5 | > FrintJS + React integration for ProppyJS
6 |
7 |
8 |
9 | - [Guide](#guide)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [API](#api)
13 | - [ProppyProvider](#proppyprovider)
14 |
15 |
16 |
17 | ---
18 |
19 | # Guide
20 |
21 | ## Installation
22 |
23 | With [npm](https://www.npmjs.com/):
24 |
25 | ```
26 | $ npm install --save proppy react proppy-react frint-react proppy-frint-react
27 | ```
28 |
29 | Via [unpkg](https://unpkg.com) CDN:
30 |
31 | ```html
32 |
33 |
34 |
35 |
38 | ```
39 |
40 | ## Usage
41 |
42 | In your Root component, wrap it with `ProppyProvider` component, and it will take care of getting all the providers from your FrintJS app automatically:
43 |
44 | ```js
45 | // components/Root.js
46 | import React from 'react';
47 | import { ProppyProvider } from 'proppy-frint-react';
48 |
49 | import MyComponent from './MyComponent';
50 |
51 | export default function Root() {
52 | return (
53 |
54 |
55 |
56 | );
57 | }
58 | ```
59 |
60 | Now anywhere from your components tree, you can use the `attach` higher-order component:
61 |
62 | ```js
63 | // components/MyComponent.js
64 | import React from 'react';
65 | import { compose, withProps } from 'proppy';
66 | import { attach } from 'proppy-react';
67 |
68 | const P = compose(
69 | withProps((props, providers) => {
70 | const { app, foo } = providers;
71 |
72 | return { foo: foo };
73 | }),
74 | withProps({ bar: 'bar value' }),
75 | );
76 |
77 | function MyComponent(props) {
78 | const { foo, bar } = props;
79 |
80 | return
;
81 | }
82 |
83 | export default attach(P)(MyComponent);
84 | ```
85 |
86 | # API
87 |
88 | ## ProppyProvider
89 |
90 | For setting providers at React's context-level.
91 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: ['/src'],
3 | transform: {
4 | '^.+\\.(ts|tsx)$': 'ts-jest',
5 | },
6 | testRegex: '\\.spec\\.(ts|tsx)',
7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'node'],
8 | collectCoverage: false,
9 | bail: true,
10 | };
11 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy-frint-react",
3 | "version": "1.3.1",
4 | "description": "FrintJS + React integration with ProppyJS",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy-frint-react",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "@types/react": "^16.4.6",
32 | "frint": "^5.6.1",
33 | "frint-config": "^5.4.3",
34 | "frint-react": "^5.4.3",
35 | "proppy": "^1.3.1",
36 | "proppy-react": "^1.3.1",
37 | "react": "^15.3.0"
38 | },
39 | "bugs": {
40 | "url": "https://github.com/fahad19/proppy/issues"
41 | },
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/src/ProppyProvider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { observe, streamProps, ProviderProps } from 'frint-react';
3 | import { ProppyProvider as ProppyReactProvider } from 'proppy-react';
4 |
5 | export class Base extends React.Component {
6 | public render() {
7 | const { providers, children } = this.props;
8 | return {children} ;
9 | }
10 | }
11 |
12 | export const ProppyProvider = observe((app: any) => {
13 | return streamProps({ providers: app.container.registry })
14 | .get$();
15 | })(Base);
16 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/src/index.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import * as lib from './index';
3 |
4 | describe('proppy-frint-preact :: index', () => {
5 | test('is an object', () => {
6 | expect(typeof lib).toEqual('object');
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ProppyProvider } from './ProppyProvider';
2 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": [
6 | "es2015",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "declaration": true,
11 | "sourceMap": true,
12 | "jsx": "react"
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ],
17 | "exclude": []
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy-frint-react/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 | const externals = require('frint-config').externals;
6 |
7 | const NODE_ENV = process.env.NODE_ENV === 'production' ? 'production' : 'development';
8 |
9 | const filename = NODE_ENV === 'production' ? '[name].min.js' : '[name].js';
10 |
11 | const plugins = [
12 | new webpack.DefinePlugin({
13 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
14 | }),
15 | ];
16 |
17 | if (NODE_ENV === 'production') {
18 | plugins.push(
19 | new CompressionPlugin({
20 | asset: '[path].gz[query]',
21 | algorithm: 'gzip',
22 | test: /\.js/,
23 | }),
24 | );
25 | }
26 |
27 | module.exports = {
28 | entry: {
29 | 'proppy-frint-react': path.join(__dirname, '/src/index.ts'),
30 | },
31 | output: {
32 | path: path.join(__dirname, 'dist'),
33 | filename: filename,
34 | libraryTarget: 'umd',
35 | library: 'ProppyFrintReact',
36 | },
37 | externals: externals.concat([
38 | {
39 | frint: 'Frint',
40 | lodash: '_',
41 | proppy: 'Proppy',
42 | react: 'React',
43 | rxjs: 'Rx',
44 | },
45 | ]),
46 | mode: process.env.NODE_ENV || 'development',
47 | devtool: 'source-map',
48 | plugins: plugins,
49 | performance: {
50 | hints: false,
51 | },
52 | resolve: {
53 | extensions: ['.ts', '.tsx', '.js'],
54 | },
55 | module: {
56 | rules: [
57 | {
58 | test: /\.(ts|tsx)$/,
59 | exclude: /(node_modules)/,
60 | loader: 'ts-loader',
61 | },
62 | ],
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/packages/proppy-preact/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy-preact/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy-preact/ProppyProvider.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/ProppyProvider');
2 |
--------------------------------------------------------------------------------
/packages/proppy-preact/README.md:
--------------------------------------------------------------------------------
1 | # proppy-preact
2 |
3 | [](https://www.npmjs.com/package/proppy-preact)
4 |
5 | > Preact integration package for ProppyJS
6 |
7 |
8 |
9 | - [Guide](#guide)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [API](#api)
13 | - [ProppyProvider](#proppyprovider)
14 | - [attach](#attach)
15 |
16 |
17 |
18 | ---
19 |
20 | # Guide
21 |
22 | ## Installation
23 |
24 | With [npm](https://www.npmjs.com/):
25 |
26 | ```
27 | $ npm install --save proppy preact proppy-preact
28 | ```
29 |
30 | Via [unpkg](https://unpkg.com) CDN:
31 |
32 | ```html
33 |
34 |
35 |
36 |
39 | ```
40 |
41 | ## Usage
42 |
43 | First, setup your root component with providers data:
44 |
45 | ```js
46 | // components/Root.js
47 | import { h } from 'preact';
48 | import { ProppyProvider } from 'proppy-preact';
49 |
50 | import MyComponent from './MyComponent';
51 |
52 | const providers = {
53 | foo: 'foo value',
54 | };
55 |
56 | export default function Root() {
57 | return (
58 |
59 |
60 |
61 | );
62 | }
63 | ```
64 |
65 | Now anywhere from your components tree, you can use the `attach` higher-order component:
66 |
67 | ```js
68 | // components/MyComponent.js
69 | import { h } from 'preact';
70 | import { compose, withProps } from 'proppy';
71 | import { attach } from 'proppy-preact';
72 |
73 | const P = compose(
74 | withProps((props, providers) => ({
75 | foo: providers.foo,
76 | })),
77 | withProps({ bar: 'bar value' }),
78 | );
79 |
80 | function MyComponent(props) {
81 | const { foo, bar } = props;
82 |
83 | return
;
84 | }
85 |
86 | export default attach(P)(MyComponent);
87 | ```
88 |
89 | # API
90 |
91 | ## ProppyProvider
92 |
93 | For setting providers at Preact's context-level.
94 |
95 | **Example:**
96 |
97 | ```js
98 | import { h } from 'preact';
99 | import { ProppyProvider } from 'proppy-preact';
100 |
101 | export default function Root() {
102 | return (
103 |
104 |
105 |
106 | );
107 | }
108 | ```
109 |
110 | ## attach
111 |
112 | > attach(P)(Component)
113 |
114 | Higher-order component for attaching your Proppy factory to your Component.
115 |
--------------------------------------------------------------------------------
/packages/proppy-preact/attach.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/attach');
2 |
--------------------------------------------------------------------------------
/packages/proppy-preact/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: ['/src'],
3 | transform: {
4 | '^.+\\.(ts|tsx)$': 'ts-jest',
5 | },
6 | testRegex: '\\.spec\\.(ts|tsx)',
7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'node'],
8 | collectCoverage: false,
9 | bail: true,
10 | };
11 |
--------------------------------------------------------------------------------
/packages/proppy-preact/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy-preact",
3 | "version": "1.3.1",
4 | "description": "Preact integration with ProppyJS",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy-preact",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "frint-test-utils": "^5.7.2",
32 | "preact": "^8.2.9",
33 | "preact-render-spy": "^1.3.0",
34 | "proppy": "^1.3.1"
35 | },
36 | "bugs": {
37 | "url": "https://github.com/fahad19/proppy/issues"
38 | },
39 | "license": "MIT",
40 | "dependencies": {
41 | "preact-context": "^1.0.2"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/proppy-preact/src/ProppyContext.ts:
--------------------------------------------------------------------------------
1 | import { createContext } from 'preact-context';
2 |
3 | export const ProppyContext = createContext({
4 | providers: {},
5 | });
6 |
--------------------------------------------------------------------------------
/packages/proppy-preact/src/ProppyProvider.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'preact';
2 |
3 | /**
4 | * Source: https://gist.github.com/developit/5d879edb820228224dc9
5 | */
6 | export class ProppyProvider extends Component {
7 | public getChildContext() {
8 | const { children, ...context } = this.props;
9 |
10 | return context;
11 | }
12 |
13 | public render({ children }) {
14 | return (children && children[0]) || null;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/proppy-preact/src/ProppySubscription.tsx:
--------------------------------------------------------------------------------
1 | import { Proppy, ProppyFactory, Providers, create } from 'proppy';
2 | import { Component, RenderableProps } from 'preact';
3 |
4 | export interface ComponentProps {
5 | parentProps: any;
6 | providers: Providers;
7 | proppyFactory: ProppyFactory;
8 | }
9 |
10 | export interface ComponentState {
11 | proppyProps: any;
12 | }
13 |
14 | export class ProppySubscription extends Component, ComponentState> {
15 | private _p: Proppy;
16 | private _parent: Proppy;
17 |
18 | constructor(props, context) {
19 | super(props, context);
20 |
21 | const { providers } = context;
22 |
23 | this._parent = create({
24 | initialize() {
25 | this.set(props);
26 | },
27 | })(providers);
28 | this._p = props.proppyFactory(providers, this._parent);
29 |
30 | this.state = {
31 | proppyProps: this._p.props,
32 | };
33 | }
34 |
35 | // @TODO: this needs attention
36 | public componentWillReceiveProps(nextProps) {
37 | this._parent.set(nextProps.parentProps);
38 | }
39 |
40 | public componentDidMount() {
41 | this._p.subscribe(proppyProps =>
42 | this.setState({
43 | proppyProps,
44 | }),
45 | );
46 | }
47 |
48 | public componentWillUnmount() {
49 | this._p.destroy();
50 | }
51 |
52 | public render(props) {
53 | return props.children[0](Object.assign({}, this.state.proppyProps, props.parentProps));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/packages/proppy-preact/src/attach.tsx:
--------------------------------------------------------------------------------
1 | import { h } from 'preact';
2 | import { ProppyFactory } from 'proppy';
3 |
4 | import { ProppyContext } from './ProppyContext';
5 | import { ProppySubscription } from './ProppySubscription';
6 |
7 | export function attach(P: ProppyFactory) {
8 | return function(Component) {
9 | return function(props) {
10 | return (
11 |
12 | {providers => (
13 |
14 | {proppyProps => }
15 |
16 | )}
17 |
18 | );
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/packages/proppy-preact/src/index.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import * as lib from './index';
3 |
4 | describe('proppy-preact :: index', () => {
5 | test('is an object', () => {
6 | expect(typeof lib).toEqual('object');
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/packages/proppy-preact/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ProppyProvider } from './ProppyProvider';
2 | export { attach } from './attach';
3 |
--------------------------------------------------------------------------------
/packages/proppy-preact/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": ["es2015", "dom"],
6 | "module": "commonjs",
7 | "declaration": true,
8 | "sourceMap": true,
9 | "jsx": "react",
10 | "jsxFactory": "h",
11 | "skipLibCheck": true
12 | },
13 | "include": ["./src/**/*.ts"],
14 | "exclude": []
15 | }
16 |
--------------------------------------------------------------------------------
/packages/proppy-preact/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 |
6 | const NODE_ENV = process.env.NODE_ENV === 'production' ? 'production' : 'development';
7 |
8 | const filename = NODE_ENV === 'production' ? '[name].min.js' : '[name].js';
9 |
10 | const plugins = [
11 | new webpack.DefinePlugin({
12 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
13 | }),
14 | ];
15 |
16 | if (NODE_ENV === 'production') {
17 | plugins.push(
18 | new CompressionPlugin({
19 | asset: '[path].gz[query]',
20 | algorithm: 'gzip',
21 | test: /\.js/,
22 | }),
23 | );
24 | }
25 |
26 | module.exports = {
27 | entry: {
28 | 'proppy-preact': path.join(__dirname, '/src/index.ts'),
29 | },
30 | output: {
31 | path: path.join(__dirname, 'dist'),
32 | filename: filename,
33 | libraryTarget: 'umd',
34 | library: 'ProppyPreact',
35 | },
36 | externals: {
37 | preact: 'Preact',
38 | proppy: 'Proppy',
39 | },
40 | mode: process.env.NODE_ENV || 'development',
41 | devtool: 'source-map',
42 | plugins: plugins,
43 | performance: {
44 | hints: false,
45 | },
46 | resolve: {
47 | extensions: ['.ts', '.tsx', '.js'],
48 | },
49 | module: {
50 | rules: [
51 | {
52 | test: /\.(ts|tsx)$/,
53 | exclude: /(node_modules)/,
54 | loader: 'ts-loader',
55 | },
56 | ],
57 | },
58 | };
59 |
--------------------------------------------------------------------------------
/packages/proppy-react/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy-react/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy-react/ProppyProvider.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/ProppyProvider');
2 |
--------------------------------------------------------------------------------
/packages/proppy-react/README.md:
--------------------------------------------------------------------------------
1 | # proppy-react
2 |
3 | [](https://www.npmjs.com/package/proppy-react)
4 |
5 | > React integration package for ProppyJS
6 |
7 |
8 |
9 | - [Guide](#guide)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [API](#api)
13 | - [ProppyProvider](#proppyprovider)
14 | - [attach](#attach)
15 |
16 |
17 |
18 | ---
19 |
20 | # Guide
21 |
22 | ## Installation
23 |
24 | With [npm](https://www.npmjs.com/):
25 |
26 | ```
27 | $ npm install --save proppy react proppy-react
28 | ```
29 |
30 | Via [unpkg](https://unpkg.com) CDN:
31 |
32 | ```html
33 |
34 |
35 |
36 |
39 | ```
40 |
41 | ## Usage
42 |
43 | First, setup your root component with providers data:
44 |
45 | ```js
46 | // components/Root.js
47 | import React from 'react';
48 | import { ProppyProvider } from 'proppy-react';
49 |
50 | import MyComponent from './MyComponent';
51 |
52 | const providers = {
53 | foo: 'foo value',
54 | };
55 |
56 | export default function Root() {
57 | return (
58 |
59 |
60 |
61 | );
62 | }
63 | ```
64 |
65 | Now anywhere from your components tree, you can use the `attach` higher-order component:
66 |
67 | ```js
68 | // components/MyComponent.js
69 | import React from 'react';
70 | import { compose, withProps } from 'proppy';
71 | import { attach } from 'proppy-react';
72 |
73 | const P = compose(
74 | withProps((props, providers) => ({
75 | foo: providers.foo,
76 | })),
77 | withProps({ bar: 'bar value' }),
78 | );
79 |
80 | function MyComponent(props) {
81 | const { foo, bar } = props;
82 |
83 | return
;
84 | }
85 |
86 | export default attach(P)(MyComponent);
87 | ```
88 |
89 | # API
90 |
91 | ## ProppyProvider
92 |
93 | For setting providers at React's context-level.
94 |
95 | **Example:**
96 |
97 | ```js
98 | import React from 'react';
99 | import { ProppyProvider } from 'proppy-react';
100 |
101 | export default function Root() {
102 | return (
103 |
104 |
105 |
106 | );
107 | }
108 | ```
109 |
110 | ## attach
111 |
112 | > attach(P)(Component)
113 |
114 | Higher-order component for attaching your Proppy factory to your Component.
115 |
--------------------------------------------------------------------------------
/packages/proppy-react/attach.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/attach');
2 |
--------------------------------------------------------------------------------
/packages/proppy-react/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: [
3 | '/src',
4 | ],
5 | transform: {
6 | '^.+\\.(ts|tsx)$': 'ts-jest',
7 | },
8 | testRegex: '\\.spec\\.(ts|tsx)',
9 | moduleFileExtensions: [
10 | 'ts',
11 | 'tsx',
12 | 'js',
13 | 'json',
14 | 'node',
15 | ],
16 | collectCoverage: false,
17 | bail: true,
18 | };
19 |
--------------------------------------------------------------------------------
/packages/proppy-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy-react",
3 | "version": "1.3.1",
4 | "description": "React integration with ProppyJS",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy-react",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "@types/react": "^16.3.13",
32 | "enzyme": "^3.3.0",
33 | "enzyme-adapter-react-16": "^1.1.1",
34 | "frint-test-utils": "^5.4.3",
35 | "jsdom": "^11.10.0",
36 | "proppy": "^1.3.1",
37 | "react": "^16.3.2",
38 | "react-dom": "^16.3.2",
39 | "react-test-renderer": "^16.3.2"
40 | },
41 | "bugs": {
42 | "url": "https://github.com/fahad19/proppy/issues"
43 | },
44 | "license": "MIT",
45 | "types": "./lib/index.d.ts"
46 | }
47 |
--------------------------------------------------------------------------------
/packages/proppy-react/src/ProppyContext.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export const ProppyContext = React.createContext('proppy');
4 |
--------------------------------------------------------------------------------
/packages/proppy-react/src/ProppyProvider.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Providers } from 'proppy';
3 |
4 | import { ProppyContext } from './ProppyContext';
5 |
6 | export class ProppyProvider extends React.Component {
7 | public props: {
8 | providers: Providers;
9 | children: any;
10 | };
11 |
12 | public render() {
13 | return (
14 |
15 | {this.props.children}
16 |
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/proppy-react/src/ProppySubscription.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Proppy, ProppyFactory, Providers, create } from 'proppy';
3 |
4 | export interface ComponentProps {
5 | children: any;
6 | parentProps: any;
7 | providers: Providers;
8 | proppyFactory: ProppyFactory;
9 | }
10 |
11 | export interface ComponentState {
12 | proppyProps: any;
13 | }
14 |
15 | export class ProppySubscription extends React.Component {
16 | private _p: Proppy;
17 | private _parent: Proppy;
18 |
19 | constructor(props, ...args) {
20 | super(props, ...args);
21 |
22 | const {
23 | providers,
24 | proppyFactory,
25 | parentProps,
26 | } = props;
27 |
28 | this._parent = create({
29 | initialize() {
30 | this.set(parentProps);
31 | },
32 | })(providers);
33 | this._p = props.proppyFactory(providers, this._parent);
34 |
35 | this.state = {
36 | proppyProps: this._p.props,
37 | };
38 | }
39 |
40 | // @TODO: this needs attention
41 | public UNSAFE_componentWillReceiveProps(nextProps) {
42 | this._parent.set(nextProps.parentProps);
43 | }
44 |
45 | public componentDidMount() {
46 | this._p.subscribe(
47 | proppyProps => this.setState({
48 | proppyProps,
49 | })
50 | );
51 | }
52 |
53 | public componentWillUnmount() {
54 | this._p.destroy();
55 | }
56 |
57 | public render() {
58 | return this.props.children(this.state.proppyProps);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/packages/proppy-react/src/attach.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { ProppyFactory } from 'proppy';
3 |
4 | import { ProppyContext } from './ProppyContext';
5 | import { ProppySubscription } from './ProppySubscription';
6 |
7 | export function attach(P: ProppyFactory) {
8 | return function (Component) {
9 | return function (props) {
10 | return (
11 |
12 | {providers => (
13 |
18 | {proppyProps => }
19 |
20 | )}
21 |
22 | );
23 | };
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/packages/proppy-react/src/index.ts:
--------------------------------------------------------------------------------
1 | export { ProppyContext } from './ProppyContext';
2 | export { ProppyProvider } from './ProppyProvider';
3 | export { ProppySubscription } from './ProppySubscription';
4 | export { attach } from './attach';
5 |
--------------------------------------------------------------------------------
/packages/proppy-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": [
6 | "es2015",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "declaration": true,
11 | "sourceMap": true,
12 | "jsx": "react"
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ],
17 | "exclude": []
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy-react/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 |
6 | const NODE_ENV = process.env.NODE_ENV === 'production'
7 | ? 'production'
8 | : 'development';
9 |
10 | const filename = NODE_ENV === 'production'
11 | ? '[name].min.js'
12 | : '[name].js';
13 |
14 | const plugins = [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
17 | }),
18 | ];
19 |
20 | if (NODE_ENV === 'production') {
21 | plugins.push(new CompressionPlugin({
22 | asset: '[path].gz[query]',
23 | algorithm: 'gzip',
24 | test: /\.js/,
25 | }));
26 | }
27 |
28 | module.exports = {
29 | entry: {
30 | 'proppy-react': path.join(__dirname, '/src/index.ts'),
31 | },
32 | output: {
33 | path: path.join(__dirname, 'dist'),
34 | filename: filename,
35 | libraryTarget: 'umd',
36 | library: 'ProppyReact',
37 | },
38 | externals: {
39 | proppy: 'Proppy',
40 | react: 'React',
41 | },
42 | mode: process.env.NODE_ENV || 'development',
43 | devtool: 'source-map',
44 | plugins: plugins,
45 | performance: {
46 | hints: false,
47 | },
48 | resolve: {
49 | extensions: [
50 | '.ts',
51 | '.tsx',
52 | '.js',
53 | ],
54 | },
55 | module: {
56 | rules: [
57 | {
58 | test: /\.(ts|tsx)$/,
59 | exclude: /(node_modules)/,
60 | loader: 'ts-loader',
61 | },
62 | ],
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/packages/proppy-redux/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy-redux/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy-redux/README.md:
--------------------------------------------------------------------------------
1 | # proppy-redux
2 |
3 | [](https://www.npmjs.com/package/proppy-redux)
4 |
5 | > Redux integration package for ProppyJS
6 |
7 |
8 |
9 | - [Guide](#guide)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [API](#api)
13 | - [withStore](#withstore)
14 |
15 |
16 |
17 | ---
18 |
19 | # Guide
20 |
21 | ## Installation
22 |
23 | With [npm](https://www.npmjs.com/):
24 |
25 | ```
26 | $ npm install --save proppy redux proppy-redux
27 | ```
28 |
29 | Via [unpkg](https://unpkg.com) CDN:
30 |
31 | ```html
32 |
33 |
34 |
35 |
38 | ```
39 |
40 | ## Usage
41 |
42 | With the `withStore` function, you can map state and dispatchable actions from Redux:
43 |
44 | ```js
45 | import { compose } from 'proppy';
46 | import { withStore } from 'proppy-redux';
47 | import { createStore };
48 |
49 | // Redux reducer
50 | import reducer from '../reducers';
51 |
52 | // Redux action creators
53 | import { incrementCounter } from '../actions/counter';
54 |
55 | function mapState(state) {
56 | return {
57 | counter: state.counter.value,
58 | };
59 | }
60 |
61 | const P = compose(
62 | withStore(
63 | mapState,
64 | {
65 | increment: incrementCounter,
66 | }
67 | )
68 | );
69 |
70 | // we should have the Redux store set in our providers
71 | const providers = {
72 | store: createStore(myReducer),
73 | };
74 |
75 | // now we can create our instance
76 | const p = P(providers);
77 |
78 | console.log(p.props);
79 | // {
80 | // counter: 0,
81 | // increment: Function
82 | // }
83 | ```
84 |
85 | # API
86 |
87 | ## withStore
88 |
89 | > withStore(mapState, mapDispatch, options)
90 |
91 | **Arguments:**
92 |
93 | * `mapState` (`Function`): Accepting Store's state, and returning mapped state
94 | * `mapDispatch` (`Object`): Action creators keyed by their names
95 | * `options` (`Object` [optional])
96 | * `options.providerName` (`String`): The provider name, defaults to `store`
97 |
--------------------------------------------------------------------------------
/packages/proppy-redux/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: [
3 | '/src',
4 | ],
5 | transform: {
6 | '^.+\\.(ts|tsx)$': 'ts-jest',
7 | },
8 | testRegex: '\\.spec\\.(ts|tsx)',
9 | moduleFileExtensions: [
10 | 'ts',
11 | 'tsx',
12 | 'js',
13 | 'json',
14 | 'node',
15 | ],
16 | collectCoverage: false,
17 | bail: true,
18 | };
19 |
--------------------------------------------------------------------------------
/packages/proppy-redux/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy-redux",
3 | "version": "1.3.1",
4 | "description": "Redux integration with ProppyJS",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy-redux",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "proppy": "^1.3.1",
32 | "redux": "^4.0.0"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/fahad19/proppy/issues"
36 | },
37 | "license": "MIT",
38 | "types": "./lib/index.d.ts"
39 | }
40 |
--------------------------------------------------------------------------------
/packages/proppy-redux/src/index.ts:
--------------------------------------------------------------------------------
1 | export { withStore } from './withStore';
2 |
--------------------------------------------------------------------------------
/packages/proppy-redux/src/withStore.ts:
--------------------------------------------------------------------------------
1 | import { create, ProppyFactory } from 'proppy';
2 |
3 | const defaultOptions = {
4 | providerName: 'store',
5 | };
6 |
7 | export function withStore(
8 | mapState,
9 | mapDispatch,
10 | options = defaultOptions
11 | ): ProppyFactory {
12 | return create({
13 | initialize() {
14 | this._store = this.providers[options.providerName];
15 |
16 | if (!this._store) {
17 | throw new Error('Store not found');
18 | }
19 |
20 | if (mapState) {
21 | this.set(mapState.apply(this, [
22 | this._store.getState(),
23 | this.providers,
24 | this.props,
25 | ]));
26 | }
27 |
28 | if (mapDispatch) {
29 | Object.keys(mapDispatch).forEach(actionName => {
30 | const actionCreator = mapDispatch[actionName];
31 |
32 | this.set({
33 | [actionName]: (...args) => this._store.dispatch(
34 | actionCreator(...args),
35 | ),
36 | });
37 | });
38 | }
39 | },
40 |
41 | didSubscribe() {
42 | if (mapState) {
43 | this._storeSubscription = this._store.subscribe(() => {
44 | this.set(mapState.apply(this, [
45 | this._store.getState(),
46 | this.providers,
47 | this.props,
48 | ]));
49 | });
50 | }
51 | },
52 |
53 | handleReceivedProps(parentProps) {
54 | if (mapState) {
55 | this.set(parentProps);
56 | this.set(mapState.apply(this, [
57 | this._store.getState(),
58 | this.providers,
59 | this.props,
60 | ]));
61 | }
62 | },
63 |
64 | willDestroy() {
65 | if (this._storeSubscription) {
66 | this._storeSubscription();
67 | }
68 | },
69 | });
70 | }
71 |
--------------------------------------------------------------------------------
/packages/proppy-redux/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": [
6 | "es2015",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "declaration": true,
11 | "sourceMap": true,
12 | "jsx": "react"
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ],
17 | "exclude": []
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy-redux/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 |
6 | const NODE_ENV = process.env.NODE_ENV === 'production'
7 | ? 'production'
8 | : 'development';
9 |
10 | const filename = NODE_ENV === 'production'
11 | ? '[name].min.js'
12 | : '[name].js';
13 |
14 | const plugins = [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
17 | }),
18 | ];
19 |
20 | if (NODE_ENV === 'production') {
21 | plugins.push(new CompressionPlugin({
22 | asset: '[path].gz[query]',
23 | algorithm: 'gzip',
24 | test: /\.js/,
25 | }));
26 | }
27 |
28 | module.exports = {
29 | entry: {
30 | 'proppy-redux': path.join(__dirname, '/src/index.ts'),
31 | },
32 | output: {
33 | path: path.join(__dirname, 'dist'),
34 | filename: filename,
35 | libraryTarget: 'umd',
36 | library: 'ProppyRedux',
37 | },
38 | externals: {
39 | proppy: 'Proppy',
40 | redux: 'Redux',
41 | },
42 | mode: process.env.NODE_ENV || 'development',
43 | devtool: 'source-map',
44 | plugins: plugins,
45 | performance: {
46 | hints: false,
47 | },
48 | resolve: {
49 | extensions: [
50 | '.ts',
51 | '.tsx',
52 | '.js',
53 | ],
54 | },
55 | module: {
56 | rules: [
57 | {
58 | test: /\.(ts|tsx)$/,
59 | exclude: /(node_modules)/,
60 | loader: 'ts-loader',
61 | },
62 | ],
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/packages/proppy-redux/withStore.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withStore');
2 |
--------------------------------------------------------------------------------
/packages/proppy-rx/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy-rx/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy-rx/README.md:
--------------------------------------------------------------------------------
1 | # proppy-rx
2 |
3 | [](https://www.npmjs.com/package/proppy-rx)
4 |
5 | > RxJS integration package for ProppyJS
6 |
7 |
8 |
9 | - [Guide](#guide)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [Interop with RxJS](#interop-with-rxjs)
13 | - [API](#api)
14 | - [withStream](#withstream)
15 | - [from](#from)
16 |
17 |
18 |
19 | ---
20 |
21 | # Guide
22 |
23 | ## Installation
24 |
25 | With [npm](https://www.npmjs.com/):
26 |
27 | ```
28 | $ npm install --save proppy rxjs proppy-rx
29 | ```
30 |
31 | Via [unpkg](https://unpkg.com) CDN:
32 |
33 | ```html
34 |
35 |
36 |
37 |
40 | ```
41 |
42 | ## Usage
43 |
44 | With the `withStream` function, you can map an incoming stream of props and return a new stream:
45 |
46 | ```js
47 | import { compose, withProps } from 'proppy';
48 | import { withStream } from 'proppy-rx';
49 | import { map } from 'rxjs/operators';
50 |
51 | const P = compose(
52 | withProps({ foo: 'foo value' }),
53 | withStream((props$, providers) => {
54 | // `props$` is an Observable here
55 |
56 | // let's now return a stream of props here
57 | return props$.pipe(
58 | map(props => props),
59 | );
60 | }),
61 | );
62 | ```
63 |
64 | ## Interop with RxJS
65 |
66 | You can also convert your instance to a props stream using RxJS:
67 |
68 | ```js
69 | import { from } from 'proppy-rx';
70 |
71 | const props$ = from(p);
72 | ```
73 |
74 |
75 | # API
76 |
77 | ## withStream
78 |
79 | > withStream((props$, providers) => props$)
80 |
81 | Used for taking an incoming stream of props, and returning a new stream.
82 |
83 | ## from
84 |
85 | > from(p)
86 |
87 | Returns an Observable of props.
88 |
--------------------------------------------------------------------------------
/packages/proppy-rx/from.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/from');
2 |
--------------------------------------------------------------------------------
/packages/proppy-rx/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: [
3 | '/src',
4 | ],
5 | transform: {
6 | '^.+\\.(ts|tsx)$': 'ts-jest',
7 | },
8 | testRegex: '\\.spec\\.(ts|tsx)',
9 | moduleFileExtensions: [
10 | 'ts',
11 | 'tsx',
12 | 'js',
13 | 'json',
14 | 'node',
15 | ],
16 | collectCoverage: false,
17 | bail: true,
18 | };
19 |
--------------------------------------------------------------------------------
/packages/proppy-rx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy-rx",
3 | "version": "1.3.1",
4 | "description": "RxJS integration with ProppyJS",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy-rx",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "proppy": "^1.3.1",
32 | "rxjs": "^6.0.0"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/fahad19/proppy/issues"
36 | },
37 | "license": "MIT",
38 | "types": "./lib/index.d.ts"
39 | }
40 |
--------------------------------------------------------------------------------
/packages/proppy-rx/src/from.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { map } from 'rxjs/operators';
3 | import { withState } from 'proppy';
4 |
5 | import { from } from './from';
6 |
7 | describe('proppy-rx :: from', () => {
8 | test('is a function', () => {
9 | expect(typeof from).toEqual('function');
10 | });
11 |
12 | test('subscription triggers with Observable', () => {
13 | const P = withState('counter', 'setCounter', 0);
14 | const p = P();
15 | const props$ = from(p);
16 |
17 | let triggerCount = 0;
18 |
19 | props$.subscribe(() => {
20 | triggerCount++;
21 | });
22 |
23 | p.props.setCounter(1);
24 | p.props.setCounter(2);
25 | p.props.setCounter(3);
26 |
27 | expect(triggerCount).toEqual(4);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/proppy-rx/src/from.ts:
--------------------------------------------------------------------------------
1 | import { Proppy } from 'proppy';
2 | import { Observable } from 'rxjs';
3 |
4 | export function from(p: Proppy) {
5 | return new Observable((observer) => {
6 | return p.subscribe(props => observer.next(props));
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/packages/proppy-rx/src/index.ts:
--------------------------------------------------------------------------------
1 | export { from } from './from';
2 | export { withStream } from './withStream';
3 |
--------------------------------------------------------------------------------
/packages/proppy-rx/src/withStream.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { map } from 'rxjs/operators';
3 | import { compose, withState } from 'proppy';
4 |
5 | import { withStream } from './withStream';
6 |
7 | describe('proppy-rx :: withStream', () => {
8 | test('is a function', () => {
9 | expect(typeof withStream).toEqual('function');
10 | });
11 |
12 | test('updates', () => {
13 | const P = compose(
14 | withState('counter', 'setCounter', 0),
15 | withStream(props$ => props$.pipe(
16 | map((props: any) => ({
17 | ...props,
18 | counter: props.counter * 10,
19 | })),
20 | )),
21 | );
22 |
23 | const p = P();
24 |
25 | p.subscribe(() => {});
26 |
27 | expect(p.props.counter).toEqual(0);
28 |
29 | p.props.setCounter(5);
30 |
31 | expect(p.props.counter).toEqual(50);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/packages/proppy-rx/src/withStream.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory, create } from 'proppy';
2 | import { of } from 'rxjs';
3 |
4 | import { from } from './from';
5 |
6 | const defaultOptions = {
7 | replace: false,
8 | };
9 |
10 | export function withStream(fn, givenOptions = {}): ProppyFactory {
11 | const options = {
12 | ...defaultOptions,
13 | ...givenOptions,
14 | };
15 |
16 | return create({
17 | initialize() {
18 | const parent$ = this.parent
19 | ? from(this.parent)
20 | : of({});
21 |
22 | this._$ = fn(parent$);
23 | },
24 |
25 | didSubscribe() {
26 | this._s = this._$.subscribe(props => {
27 | this.set(props, options.replace);
28 | });
29 | },
30 |
31 | willDestroy() {
32 | this._s.unsubscribe();
33 | },
34 | });
35 | }
36 |
--------------------------------------------------------------------------------
/packages/proppy-rx/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": [
6 | "es2015",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "declaration": true,
11 | "sourceMap": true,
12 | "jsx": "react"
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ],
17 | "exclude": []
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy-rx/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 |
6 | const NODE_ENV = process.env.NODE_ENV === 'production'
7 | ? 'production'
8 | : 'development';
9 |
10 | const filename = NODE_ENV === 'production'
11 | ? '[name].min.js'
12 | : '[name].js';
13 |
14 | const plugins = [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
17 | }),
18 | ];
19 |
20 | if (NODE_ENV === 'production') {
21 | plugins.push(new CompressionPlugin({
22 | asset: '[path].gz[query]',
23 | algorithm: 'gzip',
24 | test: /\.js/,
25 | }));
26 | }
27 |
28 | module.exports = {
29 | entry: {
30 | 'proppy-rx': path.join(__dirname, '/src/index.ts'),
31 | },
32 | output: {
33 | path: path.join(__dirname, 'dist'),
34 | filename: filename,
35 | libraryTarget: 'umd',
36 | library: 'ProppyRx',
37 | },
38 | externals: {
39 | proppy: 'Proppy',
40 | rxjs: 'Rx',
41 | },
42 | mode: process.env.NODE_ENV || 'development',
43 | devtool: 'source-map',
44 | plugins: plugins,
45 | performance: {
46 | hints: false,
47 | },
48 | resolve: {
49 | extensions: [
50 | '.ts',
51 | '.tsx',
52 | '.js',
53 | ],
54 | },
55 | module: {
56 | rules: [
57 | {
58 | test: /\.(ts|tsx)$/,
59 | exclude: /(node_modules)/,
60 | loader: 'ts-loader',
61 | },
62 | ],
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/packages/proppy-rx/withStream.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withStream');
2 |
--------------------------------------------------------------------------------
/packages/proppy-vue/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy-vue/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy-vue/README.md:
--------------------------------------------------------------------------------
1 | # proppy-vue
2 |
3 | [](https://www.npmjs.com/package/proppy-vue)
4 |
5 | > React integration package for ProppyJS
6 |
7 |
8 |
9 | - [Guide](#guide)
10 | - [Installation](#installation)
11 | - [Usage](#usage)
12 | - [API](#api)
13 | - [attach](#attach)
14 |
15 |
16 |
17 | ---
18 |
19 | # Guide
20 |
21 | ## Installation
22 |
23 | With [npm](https://www.npmjs.com/):
24 |
25 | ```
26 | $ npm install --save proppy vue proppy-vue
27 | ```
28 |
29 | Via [unpkg](https://unpkg.com) CDN:
30 |
31 | ```html
32 |
33 |
34 |
35 |
38 | ```
39 |
40 | ## Usage
41 |
42 | Render your Vue.js app with providers data:
43 |
44 | ```js
45 | // index.js
46 | import Vue from 'vue';
47 |
48 | import MyComponent from './components/MyComponent';
49 |
50 | const providers = {
51 | foo: 'foo value',
52 | };
53 |
54 | const app = new Vue({
55 | provide: {
56 | proppy: providers,
57 | },
58 |
59 | render(h) {
60 | return ;
61 | },
62 | });
63 | ```
64 |
65 | Now anywhere from your components tree, you can use the `attach` higher-order component:
66 |
67 | ```js
68 | // components/MyComponent.js
69 | import { compose, withProps } from 'proppy';
70 | import { attach } from 'proppy-vue';
71 |
72 | const P = compose(
73 | withProps((props, providers) => ({
74 | foo: providers.foo,
75 | })),
76 | withProps({ bar: 'bar value' }),
77 | );
78 |
79 | const MyComponent = {
80 | name: 'MyComponent',
81 |
82 | props: ['foo', 'bar'],
83 |
84 | render(h) {
85 | const { foo, bar } = this;
86 |
87 | return
;
88 | },
89 | };
90 |
91 | export default attach(P)(MyComponent);
92 | ```
93 |
94 | # API
95 |
96 | ## attach
97 |
98 | > attach(P)(Component)
99 |
100 | Higher-order component for attaching your Proppy factory to your Component.
101 |
--------------------------------------------------------------------------------
/packages/proppy-vue/attach.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/attach');
2 |
--------------------------------------------------------------------------------
/packages/proppy-vue/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: [
3 | '/src',
4 | ],
5 | transform: {
6 | '^.+\\.(ts|tsx)$': 'ts-jest',
7 | },
8 | testRegex: '\\.spec\\.(ts|tsx)',
9 | moduleFileExtensions: [
10 | 'ts',
11 | 'tsx',
12 | 'js',
13 | 'json',
14 | 'node',
15 | ],
16 | collectCoverage: false,
17 | bail: true,
18 | };
19 |
--------------------------------------------------------------------------------
/packages/proppy-vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy-vue",
3 | "version": "1.3.1",
4 | "description": "Vue.js integration with ProppyJS",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy-vue",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "@vue/test-utils": "^1.0.0-beta.15",
32 | "ajv": "^6.4.0",
33 | "proppy": "^1.3.1",
34 | "vue": "^2.5.16",
35 | "vue-template-compiler": "^2.5.16"
36 | },
37 | "bugs": {
38 | "url": "https://github.com/fahad19/proppy/issues"
39 | },
40 | "license": "MIT",
41 | "types": "./lib/index.d.ts"
42 | }
43 |
--------------------------------------------------------------------------------
/packages/proppy-vue/src/attach.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'proppy';
2 |
3 | export function attach(ProppyFactory) {
4 | return function(Component) {
5 | const componentPropNames = Component.props || [];
6 |
7 | const watch = componentPropNames.reduce((acc, propName) => {
8 | acc[propName] = function(newVal, oldVal) {
9 | if (typeof newVal !== 'undefined') {
10 | this._parent.set({
11 | [propName]: newVal
12 | });
13 | }
14 | };
15 |
16 | return acc;
17 | }, {});
18 |
19 | return {
20 | name: `Attached${Component.name}`,
21 |
22 | inject: ['proppy'],
23 |
24 | props: componentPropNames,
25 |
26 | watch,
27 |
28 | data() {
29 | return {
30 | proppyProps: {}
31 | };
32 | },
33 |
34 | methods: {
35 | setProppyProps(props) {
36 | this.proppyProps = Object.assign({}, props);
37 | }
38 | },
39 |
40 | beforeMount() {
41 | const parentProps = componentPropNames.reduce(
42 | (acc, componentPropName) => {
43 | if (typeof this[componentPropName] === 'undefined') {
44 | return acc;
45 | }
46 |
47 | return {
48 | ...acc,
49 | [componentPropName]: this[componentPropName]
50 | };
51 | },
52 | {}
53 | );
54 |
55 | this._parent = create({
56 | initialize() {
57 | this.set(parentProps);
58 | }
59 | })(this.proppy || {});
60 |
61 | this._p = ProppyFactory(this.proppy, this._parent);
62 |
63 | this.setProppyProps(this._p.props);
64 |
65 | this._p.subscribe(props => {
66 | this.setProppyProps(props);
67 | });
68 | },
69 |
70 | beforeDestroy() {
71 | if (this._p) {
72 | this._p.destroy();
73 | }
74 | },
75 |
76 | render(h) {
77 | const data = {
78 | props: this.proppyProps
79 | };
80 |
81 | return h(Component, data);
82 | },
83 | };
84 | };
85 | }
86 |
--------------------------------------------------------------------------------
/packages/proppy-vue/src/index.ts:
--------------------------------------------------------------------------------
1 | export { attach } from './attach';
2 |
--------------------------------------------------------------------------------
/packages/proppy-vue/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": [
6 | "es2015",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "declaration": true,
11 | "sourceMap": true,
12 | "jsx": "react"
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ],
17 | "exclude": []
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy-vue/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 |
6 | const NODE_ENV = process.env.NODE_ENV === 'production'
7 | ? 'production'
8 | : 'development';
9 |
10 | const filename = NODE_ENV === 'production'
11 | ? '[name].min.js'
12 | : '[name].js';
13 |
14 | const plugins = [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
17 | }),
18 | ];
19 |
20 | if (NODE_ENV === 'production') {
21 | plugins.push(new CompressionPlugin({
22 | asset: '[path].gz[query]',
23 | algorithm: 'gzip',
24 | test: /\.js/,
25 | }));
26 | }
27 |
28 | module.exports = {
29 | entry: {
30 | 'proppy-vue': path.join(__dirname, '/src/index.ts'),
31 | },
32 | output: {
33 | path: path.join(__dirname, 'dist'),
34 | filename: filename,
35 | libraryTarget: 'umd',
36 | library: 'ProppyVue',
37 | },
38 | externals: {
39 | proppy: 'Proppy',
40 | vue: 'Vue',
41 | },
42 | mode: process.env.NODE_ENV || 'development',
43 | devtool: 'source-map',
44 | plugins: plugins,
45 | performance: {
46 | hints: false,
47 | },
48 | resolve: {
49 | extensions: [
50 | '.ts',
51 | '.tsx',
52 | '.js',
53 | ],
54 | },
55 | module: {
56 | rules: [
57 | {
58 | test: /\.(ts|tsx)$/,
59 | exclude: /(node_modules)/,
60 | loader: 'ts-loader',
61 | },
62 | ],
63 | },
64 | };
65 |
--------------------------------------------------------------------------------
/packages/proppy/.gitignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | lib
4 | dist
5 |
--------------------------------------------------------------------------------
/packages/proppy/.npmignore:
--------------------------------------------------------------------------------
1 | package-lock.json
2 | coverage
3 | dist
4 | !lib
5 |
--------------------------------------------------------------------------------
/packages/proppy/compose.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/compose');
2 |
--------------------------------------------------------------------------------
/packages/proppy/create.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/create');
2 |
--------------------------------------------------------------------------------
/packages/proppy/didSubscribe.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/didSubscribe');
2 |
--------------------------------------------------------------------------------
/packages/proppy/emit.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/emit');
2 |
--------------------------------------------------------------------------------
/packages/proppy/handleReceivedProps.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/handleReceivedProps');
2 |
--------------------------------------------------------------------------------
/packages/proppy/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | roots: [
3 | '/src',
4 | ],
5 | transform: {
6 | '^.+\\.(ts|tsx)$': 'ts-jest',
7 | },
8 | testRegex: '\\.spec\\.(ts|tsx)',
9 | moduleFileExtensions: [
10 | 'ts',
11 | 'tsx',
12 | 'js',
13 | 'json',
14 | 'node',
15 | ],
16 | collectCoverage: false,
17 | bail: true,
18 | };
19 |
--------------------------------------------------------------------------------
/packages/proppy/map.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/map');
2 |
--------------------------------------------------------------------------------
/packages/proppy/onChange.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/onChange');
2 |
--------------------------------------------------------------------------------
/packages/proppy/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proppy",
3 | "version": "1.3.1",
4 | "description": "ProppyJS - Functional composition of props for components",
5 | "main": "lib/index.js",
6 | "homepage": "https://github.com/fahad19/proppy/tree/master/packages/proppy",
7 | "scripts": {
8 | "clean": "../../node_modules/.bin/rimraf dist lib",
9 | "lint": "../../node_modules/.bin/tslint -c ../../tslint.json -p tsconfig.json -t stylish",
10 | "transpile": "../../node_modules/.bin/tsc",
11 | "test": "../../node_modules/.bin/jest --",
12 | "cover": "../../node_modules/.bin/jest --coverage",
13 | "dist:lib": "../../node_modules/.bin/webpack --config ./webpack.config.js",
14 | "dist:min": "NODE_ENV=production ../../node_modules/.bin/webpack --config ./webpack.config.js",
15 | "dist": "npm run dist:lib && npm run dist:min",
16 | "prepublish": "npm run transpile",
17 | "build": "npm run transpile && npm run dist"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/fahad19/proppy.git"
22 | },
23 | "author": {
24 | "name": "Fahad Ibnay Heylaal",
25 | "url": "http://fahad19.com"
26 | },
27 | "keywords": [
28 | "proppy"
29 | ],
30 | "devDependencies": {
31 | "rxjs": "^6.1.0"
32 | },
33 | "bugs": {
34 | "url": "https://github.com/fahad19/proppy/issues"
35 | },
36 | "license": "MIT",
37 | "types": "./lib/index.d.ts"
38 | }
39 |
--------------------------------------------------------------------------------
/packages/proppy/shouldUpdate.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/shouldUpdate');
2 |
--------------------------------------------------------------------------------
/packages/proppy/src/compose.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { compose } from './compose';
3 | import { withProps } from './withProps';
4 | import { withState } from './withState';
5 | import { map } from './map';
6 | import { shouldUpdate } from './shouldUpdate';
7 |
8 | describe('proppy :: compose', () => {
9 | test('is a function', () => {
10 | expect(typeof compose).toEqual('function');
11 | });
12 |
13 | test('with multiple defaults', () => {
14 | const P = compose(
15 | withProps({ foo: 'foo value' }),
16 | withProps({ bar: 'bar value' }),
17 | withProps((props, { baz }) => ({ baz })),
18 | );
19 |
20 | const p = P({
21 | baz: 'baz value',
22 | });
23 |
24 | expect(p.props).toEqual({
25 | foo: 'foo value',
26 | bar: 'bar value',
27 | baz: 'baz value',
28 | });
29 | });
30 |
31 | test('with multiple states', () => {
32 | const P = compose(
33 | withState('counter', 'setCounter', 0),
34 | withState('age', 'setAge', 50),
35 | compose(
36 | withState('score', 'setScore', 0),
37 | shouldUpdate(
38 | (prevProps, nextProps) => nextProps.score % 2 === 0,
39 | ),
40 | ),
41 | map(props => ({
42 | ...props,
43 | score: props.score * 10,
44 | })),
45 | );
46 |
47 | const p = P();
48 |
49 | expect(p.props.counter).toEqual(0);
50 | expect(typeof p.props.setCounter).toEqual('function');
51 |
52 | expect(p.props.age).toEqual(50);
53 | expect(typeof p.props.setAge).toEqual('function');
54 |
55 | expect(p.props.score).toEqual(0);
56 | expect(typeof p.props.setScore).toEqual('function');
57 |
58 | let triggerCount = 0;
59 | p.subscribe(() => {
60 | triggerCount++;
61 | });
62 |
63 | p.props.setCounter(1);
64 | expect(p.props.counter).toEqual(1);
65 | p.props.setCounter(2);
66 | expect(p.props.counter).toEqual(2);
67 |
68 | p.props.setAge(55);
69 | expect(p.props.age).toEqual(55);
70 |
71 | expect(triggerCount).toEqual(4);
72 |
73 | expect(p.props.counter).toEqual(2);
74 | expect(typeof p.props.setCounter).toEqual('function');
75 | expect(p.props.age).toEqual(55);
76 | expect(typeof p.props.setAge).toEqual('function');
77 |
78 | p.props.setScore(1);
79 | expect(p.props.score).toEqual(0);
80 |
81 | p.props.setScore(2);
82 | expect(p.props.score).toEqual(20);
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/packages/proppy/src/compose.ts:
--------------------------------------------------------------------------------
1 | import { Proppy, ProppyFactory, Providers } from './types';
2 |
3 | export function compose(...funcs): ProppyFactory {
4 | if (funcs.length === 0) {
5 | throw new Error('Cannot compose without any ProppyJS factories');
6 | }
7 |
8 | if (funcs.length === 1) {
9 | return funcs[0];
10 | }
11 |
12 | return function (providers: Providers = {}, parent: Proppy|null = null) {
13 | let f = funcs[0](providers, parent);
14 |
15 | for (let i = 1; i < funcs.length; i++) {
16 | f = funcs[i](providers, f);
17 | }
18 |
19 | return f;
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/packages/proppy/src/create.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Proppy,
3 | ProppyFactory,
4 | ProppyFactoryOptions,
5 | } from './types';
6 |
7 | function notify(callbacks, props) {
8 | callbacks.forEach(cb => cb(props));
9 | }
10 |
11 | function defaultParentHandler() {
12 | this.set.apply(this, arguments);
13 | }
14 |
15 | function getParentHandler(parent: null|Proppy, options: ProppyFactoryOptions) {
16 | if (
17 | !parent ||
18 | options.handleReceivedProps === false
19 | ) {
20 | return;
21 | }
22 |
23 | if (
24 | options.handleReceivedProps === true ||
25 | options.handleReceivedProps === undefined
26 | ) {
27 | return defaultParentHandler;
28 | }
29 |
30 | return options.handleReceivedProps;
31 | }
32 |
33 | export function create(options: ProppyFactoryOptions): ProppyFactory {
34 | return function (providers = {}, parent): Proppy {
35 | let callbacks = [];
36 | let hasSubscribed = false;
37 | const parentHandler = getParentHandler(parent, options);
38 | let parentSubscription;
39 |
40 | const p: Proppy = {
41 | props: parent
42 | ? Object.assign({}, parent.props)
43 | : {},
44 | parent,
45 | providers,
46 | };
47 |
48 | p.set = function (props, replace = false) {
49 | p.props = replace
50 | ? Object.assign({}, props)
51 | : Object.assign({}, p.props, props);
52 |
53 | return notify(callbacks, p.props);
54 | };
55 |
56 | p.subscribe = function (cb) {
57 | if (!hasSubscribed) {
58 | hasSubscribed = true;
59 |
60 | if (options.didSubscribe) {
61 | options.didSubscribe.apply(p);
62 | }
63 | }
64 |
65 | callbacks.push(cb);
66 | cb(p.props);
67 |
68 | return function () {
69 | callbacks = callbacks.filter(callback => cb !== callback);
70 | };
71 | };
72 |
73 | p.destroy = function () {
74 | if (options.willDestroy) {
75 | options.willDestroy.apply(p);
76 | }
77 |
78 | if (parentSubscription) {
79 | parentSubscription();
80 | parent.destroy();
81 | }
82 |
83 | callbacks = [];
84 | };
85 |
86 | if (options.initialize) {
87 | options.initialize.apply(p);
88 | }
89 |
90 | if (parentHandler) {
91 | parentSubscription = parent.subscribe(
92 | props => parentHandler.apply(p, [props]),
93 | );
94 | }
95 |
96 | return p;
97 | };
98 | }
99 |
--------------------------------------------------------------------------------
/packages/proppy/src/didSubscribe.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { from } from 'rxjs';
3 |
4 | import { compose } from './compose';
5 | import { withState } from './withState';
6 | import { didSubscribe } from './didSubscribe';
7 |
8 | describe('proppy :: didSubscribe', () => {
9 | test('is a function', () => {
10 | expect(typeof didSubscribe).toEqual('function');
11 | });
12 |
13 | test('updates', () => {
14 | let called = false;
15 |
16 | const P = compose(
17 | withState('counter', 'setCounter', 0),
18 | didSubscribe((props) => {
19 | props.setCounter(1);
20 |
21 | return () => {
22 | called = true;
23 | };
24 | }),
25 | );
26 | const p = P();
27 |
28 | p.subscribe(() => {});
29 |
30 | expect(p.props.counter).toEqual(1);
31 | expect(typeof p.props.setCounter).toEqual('function');
32 | expect(called).toEqual(false);
33 |
34 | p.destroy();
35 | expect(called).toEqual(true);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/packages/proppy/src/didSubscribe.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function didSubscribe(fn): ProppyFactory {
5 | return create({
6 | didSubscribe: function () {
7 | this._u = fn(this.props, this.providers);
8 | },
9 |
10 | willDestroy() {
11 | if (typeof this._u === 'function') {
12 | this._u(this.props, this.providers);
13 | }
14 | }
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/packages/proppy/src/emit.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { from } from 'rxjs';
3 |
4 | import { emit } from './emit';
5 |
6 | describe('proppy :: emit', () => {
7 | test('is a function', () => {
8 | expect(typeof emit).toEqual('function');
9 | });
10 |
11 | test('initial state', () => {
12 | let cancelled = false;
13 |
14 | const P = emit(function (cb) {
15 | cb({ counter: 1 });
16 | cb({ counter: 2 });
17 |
18 | return (props) => {
19 | cancelled = props;
20 | };
21 | });
22 | const p = P();
23 |
24 | expect(p.props).toEqual({});
25 |
26 | p.subscribe(() => {});
27 |
28 | expect(p.props).toEqual({ counter: 2 });
29 | expect(cancelled).toEqual(false);
30 |
31 | p.destroy();
32 | expect(cancelled).toEqual({ counter: 2 });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/proppy/src/emit.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function emit(fn): ProppyFactory {
5 | return create({
6 | didSubscribe() {
7 | const cb = (...args) => this.set(...args);
8 |
9 | this._cancel = fn(cb, this.props, this.providers);
10 | },
11 |
12 | willDestroy() {
13 | if (typeof this._cancel === 'function') {
14 | this._cancel(this.props);
15 | }
16 | }
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy/src/handleReceivedProps.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { from } from 'rxjs';
3 |
4 | import { handleReceivedProps } from './handleReceivedProps';
5 | import { compose } from './compose';
6 | import { emit } from './emit';
7 |
8 | describe('proppy :: handleReceivedProps', () => {
9 | test('is a function', () => {
10 | expect(typeof handleReceivedProps).toEqual('function');
11 | });
12 |
13 | test('updates', () => {
14 | let cancelled = false;
15 |
16 | const P = compose(
17 | emit(function (cb) {
18 | cb({ counter: 1 });
19 | cb({ counter: 2 });
20 |
21 | return (props) => {
22 | cancelled = props;
23 | };
24 | }),
25 | handleReceivedProps(false),
26 | );
27 | const p = P();
28 |
29 | expect(p.props).toEqual({});
30 |
31 | p.subscribe(() => {});
32 |
33 | expect(p.props).toEqual({});
34 |
35 | p.destroy();
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/packages/proppy/src/handleReceivedProps.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function handleReceivedProps(fn): ProppyFactory {
5 | return create({
6 | handleReceivedProps: fn,
7 | });
8 | }
9 |
--------------------------------------------------------------------------------
/packages/proppy/src/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | Proppy,
3 | ProppyFactory,
4 | ProppyFactoryOptions,
5 | Props,
6 | Providers,
7 | } from './types';
8 |
9 | export { compose } from './compose';
10 |
11 | export { create } from './create';
12 |
13 | export { map } from './map';
14 | export { shouldUpdate } from './shouldUpdate';
15 | export { withHandlers } from './withHandlers';
16 | export { withObservable } from './withObservable';
17 | export { withProps } from './withProps';
18 | export { withReducer } from './withReducer';
19 | export { withState } from './withState';
20 | export { withStateHandlers } from './withStateHandlers';
21 | export { withTimer } from './withTimer';
22 | export { onChange } from './onChange';
23 | export { didSubscribe } from './didSubscribe';
24 | export { willDestroy } from './willDestroy';
25 | export { emit } from './emit';
26 | export { handleReceivedProps } from './handleReceivedProps';
27 |
--------------------------------------------------------------------------------
/packages/proppy/src/map.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { compose } from './compose';
3 | import { withProps } from './withProps';
4 | import { withState } from './withState';
5 | import { map } from './map';
6 |
7 | describe('proppy :: map', () => {
8 | test('is a function', () => {
9 | expect(typeof map).toEqual('function');
10 | });
11 |
12 | test('with plain object', () => {
13 | const P = compose(
14 | withProps({ counter: 10 }),
15 | map(props => ({
16 | counter: props.counter * 10,
17 | })),
18 | );
19 | const p = P();
20 |
21 | expect(p.props).toEqual({
22 | counter: 100,
23 | });
24 | });
25 |
26 | test('with state', () => {
27 | const P = compose(
28 | withProps({ staticCounter: 10 }),
29 | withState('counter', 'setCounter', 0),
30 | map(props => ({
31 | ...props,
32 | staticCounter: props.staticCounter * 10,
33 | counter: props.counter * 10,
34 | })),
35 | );
36 | const p = P();
37 |
38 | expect(p.props.counter).toEqual(0);
39 | expect(p.props.staticCounter).toEqual(100);
40 |
41 | p.props.setCounter(5);
42 |
43 | expect(p.props.counter).toEqual(50);
44 | expect(p.props.staticCounter).toEqual(100);
45 | });
46 |
47 | test('subscription', () => {
48 | expect.assertions(2);
49 |
50 | const P = compose(
51 | withProps({ counter: 10 }),
52 | map(props => ({
53 | counter: props.counter * 10,
54 | })),
55 | );
56 | const p = P();
57 |
58 | expect(p.props).toEqual({
59 | counter: 100,
60 | });
61 |
62 | p.subscribe(props => {
63 | expect(props).toEqual({
64 | counter: 100,
65 | });
66 | });
67 | });
68 | });
69 |
--------------------------------------------------------------------------------
/packages/proppy/src/map.ts:
--------------------------------------------------------------------------------
1 | import { Proppy, ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function map(mapperFn): ProppyFactory {
5 | return create({
6 | initialize() {
7 | this.set(mapperFn(this.props, this.providers), true);
8 | },
9 |
10 | handleReceivedProps(parentProps) {
11 | this.set(mapperFn(parentProps, this.providers), true);
12 | },
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/packages/proppy/src/onChange.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { compose } from './compose';
3 | import { withProps } from './withProps';
4 | import { withState } from './withState';
5 | import { onChange } from './onChange';
6 |
7 | describe('proppy :: onChange', () => {
8 | test('is a function', () => {
9 | expect(typeof onChange).toEqual('function');
10 | });
11 |
12 | test('changes by string', () => {
13 | const P = compose(
14 | withProps({ foo: 'initial foo' }),
15 | withState('counter', 'setCounter', 0),
16 | onChange('counter', (props) => ({
17 | foo: `changed foo with counter ${props.counter}`
18 | })),
19 | );
20 | const p = P();
21 |
22 | expect(p.props.foo).toEqual('initial foo');
23 | expect(p.props.counter).toEqual(0);
24 |
25 | p.props.setCounter(5);
26 | expect(p.props.foo).toEqual('changed foo with counter 5');
27 | });
28 |
29 | test('changes by comparison', () => {
30 | const P = compose(
31 | withProps({ foo: 'initial foo' }),
32 | withState('counter', 'setCounter', 0),
33 | onChange(
34 | (prevProps, nextProps) => prevProps.counter !== nextProps.counter,
35 | (props) => ({
36 | foo: `changed foo with counter ${props.counter}`
37 | })
38 | ),
39 | );
40 | const p = P();
41 |
42 | expect(p.props.foo).toEqual('initial foo');
43 | expect(p.props.counter).toEqual(0);
44 |
45 | p.props.setCounter(5);
46 | expect(p.props.counter).toEqual(5);
47 | expect(p.props.foo).toEqual('changed foo with counter 5');
48 |
49 | // should still be same since no change
50 | p.props.setCounter(5);
51 | expect(p.props.counter).toEqual(5);
52 | expect(p.props.foo).toEqual('changed foo with counter 5');
53 |
54 | p.props.setCounter(10);
55 | expect(p.props.counter).toEqual(10);
56 | expect(p.props.foo).toEqual('changed foo with counter 10');
57 | });
58 |
59 | test('changes by string, returning async props', () => {
60 | const P = compose(
61 | withState('foo', 'setFoo', 'initial foo'),
62 | withState('counter', 'setCounter', 0),
63 | onChange('counter', (props, providers, cb) => {
64 | cb({
65 | foo: `changed foo with counter ${props.counter}`
66 | });
67 | }),
68 | );
69 | const p = P();
70 |
71 | expect(p.props.foo).toEqual('initial foo');
72 | expect(p.props.counter).toEqual(0);
73 |
74 | p.props.setCounter(5);
75 | expect(p.props.foo).toEqual('changed foo with counter 5');
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/packages/proppy/src/onChange.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function onChange(propName, fn): ProppyFactory {
5 | return create({
6 | initialize() {
7 | this._prevProps = this.props;
8 | this._own = {};
9 |
10 | this._i = false;
11 | },
12 |
13 | handleReceivedProps(parentProps) {
14 | if (!this._i) {
15 | this._i = true;
16 |
17 | return;
18 | }
19 |
20 | const detector = typeof propName === 'string'
21 | ? (p, n) => p[propName] !== n[propName]
22 | : propName;
23 |
24 | this.set(Object.assign({}, parentProps, this._own));
25 |
26 | const cb = newProps => {
27 | this._own = newProps;
28 | this.set(newProps);
29 | };
30 |
31 | if (detector(this._prevProps, parentProps)) {
32 | const result = fn(parentProps, this.providers, cb);
33 |
34 | if (result) {
35 | cb(result);
36 | }
37 | }
38 |
39 | this._prevProps = this.props;
40 | },
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/packages/proppy/src/shouldUpdate.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { compose } from './compose';
3 | import { withState } from './withState';
4 | import { shouldUpdate } from './shouldUpdate';
5 |
6 | describe('proppy :: shouldUpdate', () => {
7 | test('is a function', () => {
8 | expect(typeof shouldUpdate).toEqual('function');
9 | });
10 |
11 | test('with state', () => {
12 | const P = compose(
13 | withState('counter', 'setCounter', 0),
14 | shouldUpdate(
15 | (prevProps, nextProps) => nextProps.counter % 2 === 0,
16 | ),
17 | );
18 | const p = P();
19 |
20 | expect(p.props.counter).toEqual(0);
21 |
22 | // shouldn NOT update
23 | p.props.setCounter(1);
24 | expect(p.props.counter).toEqual(0);
25 |
26 | // should update
27 | p.props.setCounter(2);
28 | expect(p.props.counter).toEqual(2);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/packages/proppy/src/shouldUpdate.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function shouldUpdate(fn): ProppyFactory {
5 | return create({
6 | initialize() {
7 | this._prevProps = this.props;
8 | },
9 |
10 | handleReceivedProps(parentProps) {
11 | if (fn(this._prevProps, parentProps, this.providers)) {
12 | this.set(parentProps, true);
13 | }
14 | },
15 | });
16 | }
17 |
--------------------------------------------------------------------------------
/packages/proppy/src/types.ts:
--------------------------------------------------------------------------------
1 | export type Props = any;
2 | export type Providers = any;
3 | export type SetFunction = (props: Props, replace?: boolean) => any;
4 | export type UnsubscribeFunction = () => void;
5 | export type SubscribeFunction = (cb: any) => UnsubscribeFunction;
6 | export type DestroyFunction = () => any;
7 |
8 | export interface Proppy {
9 | parent?: Proppy|null;
10 | providers?: Providers;
11 | props?: Props;
12 | set?: SetFunction;
13 | subscribe?: SubscribeFunction;
14 | destroy?: DestroyFunction;
15 | }
16 |
17 | export type ProppyFactory = (providers?: Providers, parent?: Proppy|null) => Proppy;
18 |
19 | export type InitializeFunction = () => void;
20 | export type DidSubscribeFunction = () => void;
21 | export type WillDestroyFunction = () => void;
22 | export type HandleReceivedPropsFunction = (props) => void;
23 |
24 | export interface ProppyFactoryOptions {
25 | initialize?: InitializeFunction;
26 | didSubscribe?: DidSubscribeFunction;
27 | willDestroy?: WillDestroyFunction;
28 | handleReceivedProps?: boolean|HandleReceivedPropsFunction;
29 | }
30 |
--------------------------------------------------------------------------------
/packages/proppy/src/willDestroy.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { from } from 'rxjs';
3 |
4 | import { compose } from './compose';
5 | import { withState } from './withState';
6 | import { willDestroy } from './willDestroy';
7 |
8 | describe('proppy :: willDestroy', () => {
9 | test('is a function', () => {
10 | expect(typeof willDestroy).toEqual('function');
11 | });
12 |
13 | test('updates', () => {
14 | let called = false;
15 |
16 | const P = compose(
17 | withState('counter', 'setCounter', 0),
18 | willDestroy(() => {
19 | called = true;
20 | }),
21 | );
22 | const p = P();
23 |
24 | p.subscribe(() => {});
25 |
26 | expect(p.props.counter).toEqual(0);
27 | expect(typeof p.props.setCounter).toEqual('function');
28 | expect(called).toBe(false);
29 |
30 | p.destroy();
31 | expect(called).toBe(true);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/packages/proppy/src/willDestroy.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function willDestroy(fn): ProppyFactory {
5 | return create({
6 | willDestroy: function () {
7 | fn(this.props, this.providers);
8 | },
9 | });
10 | }
11 |
--------------------------------------------------------------------------------
/packages/proppy/src/withHandlers.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { compose } from './compose';
3 | import { withState } from './withState';
4 | import { withHandlers } from './withHandlers';
5 |
6 | describe('proppy :: withHandlers', () => {
7 | test('is a function', () => {
8 | expect(typeof withHandlers).toEqual('function');
9 | });
10 |
11 | test('updates', () => {
12 | const P = compose(
13 | withState('counter', 'setCounter', 0),
14 | withHandlers({
15 | increment: props => () => props.setCounter(props.counter + 1),
16 | decrement: props => () => props.setCounter(props.counter - 1),
17 | }),
18 | );
19 | const p = P();
20 |
21 | expect(p.props.counter).toEqual(0);
22 | expect(typeof p.props.increment).toEqual('function');
23 | expect(typeof p.props.decrement).toEqual('function');
24 |
25 | p.props.increment();
26 | p.props.increment();
27 |
28 | expect(p.props.counter).toEqual(2);
29 |
30 | p.props.decrement();
31 |
32 | expect(p.props.counter).toEqual(1);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/proppy/src/withHandlers.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withHandlers(handlers): ProppyFactory {
5 | return create({
6 | initialize() {
7 | Object.keys(handlers).forEach((k) => {
8 | const v = handlers[k];
9 |
10 | this.set({
11 | [k]: (...args) => v(
12 | this.props,
13 | this.providers,
14 | )(...args),
15 | });
16 | });
17 | },
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/packages/proppy/src/withObservable.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { withObservable } from './withObservable';
3 | import { Observable } from 'rxjs';
4 |
5 | describe('proppy :: withObservable', () => {
6 | test('is a function', () => {
7 | expect(typeof withObservable).toEqual('function');
8 | });
9 |
10 | test('subscription', () => {
11 | expect.assertions(2);
12 |
13 | const P = withObservable(() => new Observable(observer => {
14 | observer.next({
15 | foo: 'foo value',
16 | });
17 | observer.complete();
18 | }));
19 | const p = P();
20 |
21 | expect(p.props).toEqual({});
22 |
23 | p.subscribe(props => {
24 | expect(props).toEqual({
25 | foo: 'foo value',
26 | });
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/packages/proppy/src/withObservable.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withObservable(stream$): ProppyFactory {
5 | return create({
6 | didSubscribe() {
7 | const observable = typeof stream$ === 'function'
8 | ? stream$(this.props, this.providers)
9 | : stream$;
10 |
11 | this._observableSubscription = observable.subscribe(
12 | props => this.set(props),
13 | );
14 | },
15 |
16 | willDestroy() {
17 | this._observableSubscription.unsubscribe();
18 | },
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/packages/proppy/src/withProps.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { withProps } from './withProps';
3 |
4 | describe('proppy :: withProps', () => {
5 | test('is a function', () => {
6 | expect(typeof withProps).toEqual('function');
7 | });
8 |
9 | test('with plain object', () => {
10 | const P = withProps({
11 | foo: 'foo value',
12 | });
13 | const p = P();
14 |
15 | expect(p.props).toEqual({
16 | foo: 'foo value',
17 | });
18 | });
19 |
20 | test('with function returning plain object', () => {
21 | const P = withProps(() => ({
22 | foo: 'foo value',
23 | }));
24 | const p = P();
25 |
26 | expect(p.props).toEqual({
27 | foo: 'foo value',
28 | });
29 | });
30 |
31 | test('with function accessing args returning plain object', () => {
32 | const P = withProps((props, { bar }) => ({
33 | foo: 'foo value',
34 | bar,
35 | }));
36 | const p = P({
37 | bar: 'bar value',
38 | });
39 |
40 | expect(p.props).toEqual({
41 | foo: 'foo value',
42 | bar: 'bar value',
43 | });
44 | });
45 |
46 | test('subscription', () => {
47 | expect.assertions(2);
48 |
49 | const P = withProps(() => ({
50 | foo: 'foo value',
51 | }));
52 | const p = P();
53 |
54 | expect(p.props).toEqual({
55 | foo: 'foo value',
56 | });
57 |
58 | p.subscribe(props => {
59 | expect(props).toEqual({
60 | foo: 'foo value',
61 | });
62 | });
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/packages/proppy/src/withProps.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withProps(defaults: any = {}): ProppyFactory {
5 | return create({
6 | initialize() {
7 | const props = typeof defaults === 'function'
8 | ? defaults(this.props, this.providers)
9 | : defaults;
10 |
11 | this.set(props);
12 | },
13 | });
14 | }
15 |
--------------------------------------------------------------------------------
/packages/proppy/src/withReducer.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { from } from 'rxjs';
3 |
4 | import { withReducer } from './withReducer';
5 |
6 | describe('proppy :: withReducer', () => {
7 | test('is a function', () => {
8 | expect(typeof withReducer).toEqual('function');
9 | });
10 |
11 | function testReducer(state, action) {
12 | switch (action.type) {
13 | case 'INCREMENT':
14 | return { value: state.value + 1 };
15 | case 'DECREMENT':
16 | return { value: state.value - 1 };
17 | default:
18 | return state;
19 | }
20 | }
21 |
22 | test('initial state', () => {
23 | const P = withReducer('counter', 'dispatch', testReducer, { value: 0 });
24 | const p = P();
25 |
26 | expect(p.props.counter.value).toEqual(0);
27 | expect(typeof p.props.dispatch).toEqual('function');
28 | });
29 |
30 | test('initial subscription', () => {
31 | expect.assertions(3);
32 |
33 | const P = withReducer('counter', 'dispatch', testReducer, { value: 0 });
34 | const p = P();
35 |
36 | expect(p.props.counter.value).toEqual(0);
37 | expect(typeof p.props.dispatch).toEqual('function');
38 |
39 | p.subscribe(props => {
40 | expect(props.counter.value).toEqual(0);
41 | });
42 | });
43 |
44 | test('updates', () => {
45 | const P = withReducer('counter', 'dispatch', testReducer, { value: 0 });
46 | const p = P({ foo: 'foo value' });
47 |
48 | expect(p.props.counter.value).toEqual(0);
49 | expect(typeof p.props.dispatch).toEqual('function');
50 |
51 | p.props.dispatch({ type: 'INCREMENT' });
52 | expect(p.props.counter.value).toEqual(1);
53 |
54 | p.props.dispatch({ type: 'INCREMENT' });
55 | expect(p.props.counter.value).toEqual(2);
56 |
57 | p.props.dispatch({ type: 'DECREMENT' });
58 | expect(p.props.counter.value).toEqual(1);
59 |
60 | p.props.dispatch({ type: 'I_DONT_EXIST' });
61 | expect(p.props.counter.value).toEqual(1);
62 |
63 | p.props.dispatch((props, providers) => {
64 | expect(p.props.counter.value).toEqual(1);
65 | expect(providers).toEqual({ foo: 'foo value' });
66 |
67 | return { type: 'INCREMENT' };
68 | });
69 | expect(p.props.counter.value).toEqual(2);
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/packages/proppy/src/withReducer.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withReducer(stateName, dispatcherName, reducer, initialState): ProppyFactory {
5 | return create({
6 | initialize() {
7 | this.props[stateName] = initialState;
8 | this.props[dispatcherName] = (action) => {
9 | const payload = typeof action === 'function'
10 | ? action(this.props, this.providers)
11 | : action;
12 |
13 | this.set({
14 | [stateName]: reducer(this.props[stateName], payload),
15 | });
16 | };
17 | },
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/packages/proppy/src/withState.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { from } from 'rxjs';
3 |
4 | import { withState } from './withState';
5 |
6 | describe('proppy :: withState', () => {
7 | test('is a function', () => {
8 | expect(typeof withState).toEqual('function');
9 | });
10 |
11 | test('initial state', () => {
12 | const P = withState('counter', 'setCounter', 0);
13 | const p = P();
14 |
15 | expect(p.props.counter).toEqual(0);
16 | expect(typeof p.props.setCounter).toEqual('function');
17 | });
18 |
19 | test('initial subscription', () => {
20 | expect.assertions(3);
21 |
22 | const P = withState('counter', 'setCounter', 0);
23 | const p = P();
24 |
25 | expect(p.props.counter).toEqual(0);
26 | expect(typeof p.props.setCounter).toEqual('function');
27 |
28 | p.subscribe(props => {
29 | expect(props.counter).toEqual(0);
30 | });
31 | });
32 |
33 | test('updates', () => {
34 | const P = withState('counter', 'setCounter', 0);
35 | const p = P();
36 |
37 | expect(p.props.counter).toEqual(0);
38 | expect(typeof p.props.setCounter).toEqual('function');
39 |
40 | p.props.setCounter(1);
41 | expect(p.props.counter).toEqual(1);
42 |
43 | p.props.setCounter(2);
44 | expect(p.props.counter).toEqual(2);
45 |
46 | p.props.setCounter(3);
47 | expect(p.props.counter).toEqual(3);
48 | });
49 |
50 | test('subscription triggers', () => {
51 | const P = withState('counter', 'setCounter', 0);
52 | const p = P();
53 |
54 | let triggerCount = 0;
55 | p.subscribe(() => {
56 | triggerCount++;
57 | });
58 |
59 | p.props.setCounter(1);
60 | p.props.setCounter(2);
61 | p.props.setCounter(3);
62 |
63 | expect(triggerCount).toEqual(4);
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/packages/proppy/src/withState.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withState(stateName, setterName, initialState): ProppyFactory {
5 | return create({
6 | initialize() {
7 | this.props[stateName] = initialState;
8 | this.props[setterName] = (value) => {
9 | this.set({
10 | [stateName]: value,
11 | });
12 | };
13 | },
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/packages/proppy/src/withStateHandlers.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { withStateHandlers } from './withStateHandlers';
3 |
4 | describe('proppy :: withStateHandlers', () => {
5 | test('is a function', () => {
6 | expect(typeof withStateHandlers).toEqual('function');
7 | });
8 |
9 | test('updates', () => {
10 | const P = withStateHandlers(
11 | { counter: 0 },
12 | {
13 | increment: ({ counter }) => () => ({ counter: counter + 1 }),
14 | decrement: ({ counter }) => () => ({ counter: counter - 1 }),
15 | },
16 | );
17 | const p = P();
18 |
19 | expect(p.props.counter).toEqual(0);
20 | expect(typeof p.props.increment).toEqual('function');
21 | expect(typeof p.props.decrement).toEqual('function');
22 |
23 | p.props.increment();
24 | p.props.increment();
25 |
26 | expect(p.props.counter).toEqual(2);
27 |
28 | p.props.decrement();
29 |
30 | expect(p.props.counter).toEqual(1);
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/packages/proppy/src/withStateHandlers.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withStateHandlers(
5 | initialState,
6 | handlers,
7 | acceptReceivedProps = true
8 | ): ProppyFactory {
9 | return create({
10 | initialize() {
11 | this.set(initialState, !acceptReceivedProps);
12 |
13 | Object.keys(handlers).forEach((k) => {
14 | const v = handlers[k];
15 |
16 | this.set({
17 | [k]: (...args) => this.set(
18 | v(
19 | this.props,
20 | this.providers,
21 | )(...args),
22 | ),
23 | });
24 | });
25 | },
26 |
27 | handleReceivedProps: acceptReceivedProps,
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/packages/proppy/src/withTimer.spec.ts:
--------------------------------------------------------------------------------
1 | /* global describe, test, expect */
2 | import { compose } from './compose';
3 | import { withTimer } from './withTimer';
4 |
5 | describe('proppy :: withTimer', () => {
6 | test('is a function', () => {
7 | expect(typeof withTimer).toEqual('function');
8 | });
9 |
10 | test('updates', (done) => {
11 | const P = compose(
12 | withTimer(100, { foo: 'foo value' }),
13 | withTimer(100, (props, providers) => ({
14 | foo: props.foo,
15 | bar: providers.bar,
16 | })),
17 | );
18 |
19 | const p = P({
20 | bar: 'bar value',
21 | });
22 |
23 | p.subscribe(() => {});
24 |
25 | expect(p.props).toEqual({});
26 |
27 | setTimeout(() => {
28 | expect(p.props).toEqual({
29 | foo: 'foo value',
30 | bar: 'bar value',
31 | });
32 |
33 | p.destroy();
34 | done();
35 | }, 150);
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/packages/proppy/src/withTimer.ts:
--------------------------------------------------------------------------------
1 | import { ProppyFactory } from './types';
2 | import { create } from './create';
3 |
4 | export function withTimer(timer: number, defaults: any = {}): ProppyFactory {
5 | return create({
6 | didSubscribe() {
7 | this._t = setTimeout(() => {
8 | const props = typeof defaults === 'function'
9 | ? defaults(this.props, this.providers)
10 | : defaults;
11 |
12 | this.set(props);
13 | }, timer);
14 | },
15 |
16 | willDestroy() {
17 | clearTimeout(this._t);
18 | },
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/packages/proppy/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./lib",
4 | "target": "es5",
5 | "lib": [
6 | "es2015",
7 | "dom"
8 | ],
9 | "module": "commonjs",
10 | "declaration": true,
11 | "sourceMap": true,
12 | "jsx": "react"
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ],
17 | "exclude": []
18 | }
19 |
--------------------------------------------------------------------------------
/packages/proppy/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | const webpack = require('webpack');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 |
6 | const NODE_ENV = process.env.NODE_ENV === 'production'
7 | ? 'production'
8 | : 'development';
9 |
10 | const filename = NODE_ENV === 'production'
11 | ? '[name].min.js'
12 | : '[name].js';
13 |
14 | const plugins = [
15 | new webpack.DefinePlugin({
16 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
17 | }),
18 | ];
19 |
20 | if (NODE_ENV === 'production') {
21 | plugins.push(new CompressionPlugin({
22 | asset: '[path].gz[query]',
23 | algorithm: 'gzip',
24 | test: /\.js/,
25 | }));
26 | }
27 |
28 | module.exports = {
29 | entry: {
30 | 'proppy': path.join(__dirname, '/src/index.ts'),
31 | },
32 | output: {
33 | path: path.join(__dirname, 'dist'),
34 | filename: filename,
35 | libraryTarget: 'window',
36 | library: 'Proppy',
37 | },
38 | externals: {},
39 | mode: process.env.NODE_ENV || 'development',
40 | devtool: 'source-map',
41 | plugins: plugins,
42 | performance: {
43 | hints: false,
44 | },
45 | resolve: {
46 | extensions: [
47 | '.ts',
48 | '.tsx',
49 | '.js',
50 | ],
51 | },
52 | module: {
53 | rules: [
54 | {
55 | test: /\.(ts|tsx)$/,
56 | exclude: /(node_modules)/,
57 | loader: 'ts-loader',
58 | },
59 | ],
60 | },
61 | };
62 |
--------------------------------------------------------------------------------
/packages/proppy/willDestroy.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/willDestroy');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withHandlers.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withHandlers');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withObservable.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withObservable');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withProps.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withProps');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withReducer.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withReducer');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withState.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withState');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withStateHandlers.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withStateHandlers');
2 |
--------------------------------------------------------------------------------
/packages/proppy/withTimer.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/withTimer');
2 |
--------------------------------------------------------------------------------
/site/assets/css/content.scss:
--------------------------------------------------------------------------------
1 | .content.main {
2 | font-size: 16px;
3 |
4 | h1,
5 | h2,
6 | h3,
7 | h4,
8 | h5,
9 | h6 {
10 | position: relative;
11 | }
12 |
13 | a.permalink {
14 | color: inherit;
15 | }
16 |
17 | a {
18 | border-bottom: 1px solid rgba(0,0,0,0.2);
19 |
20 | &,
21 | & code {
22 | background-color: rgba(187,239,253,0.3);
23 | color: #1a1a1a;
24 | }
25 |
26 | &:hover {
27 | background-color: #bbeffd;
28 | border-bottom-color: #1a1a1a;
29 | }
30 |
31 | &.has-image {
32 | background: none;
33 | color: transparent;
34 | border: none;
35 | }
36 | }
37 |
38 | &.column {
39 | p a,
40 | li a {
41 | // border-bottom: 1px solid #ccc;
42 | font-weight: 600;
43 | }
44 | }
45 |
46 | h1 {
47 | border-bottom: 1px solid #eee;
48 | padding-bottom: 18px;
49 | font-size: 36px !important;
50 | line-height: 50px !important;
51 | }
52 |
53 | h2 {
54 | font-size: 32px !important;
55 | border-bottom: 1px solid #eee;
56 | padding-bottom: 18px;
57 | }
58 |
59 | p > code {
60 | color: inherit;
61 | }
62 | }
63 |
64 | // resetting bulma for prism themes
65 | pre,
66 | pre[class*="language-"] {
67 | padding: 0;
68 | border: 1px solid #eee;
69 | border-radius: 2px;
70 | margin-bottom: 15px;
71 | font-size: 13px;
72 | line-height: 18px;
73 |
74 | .tag {
75 | align-items: center;
76 | background-color: transparent;
77 | border-radius: 0;
78 | color: auto;
79 | display: inline-block;
80 | font-size: 100%;
81 | height: auto;
82 | justify-content: center;
83 | line-height: 18px;
84 | padding-left: 0;
85 | padding-right: 0;
86 | vertical-align: top;
87 | white-space: nowrap;
88 | }
89 |
90 | .number {
91 | align-items: center;
92 | background-color: transparent;
93 | border-radius: 0;
94 | display: inline-block;
95 | font-size: 100%;
96 | height: auto;
97 | justify-content: center;
98 | margin-right: 0;
99 | min-width: 0;
100 | padding: 0;
101 | text-align: center;
102 | vertical-align: top;
103 | }
104 | }
105 |
106 | pre[class*="language-"] {
107 | border: 1px solid #f6e4cc;
108 | }
109 |
110 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
111 | code[class*="language-"]::selection, code[class*="language-"] ::selection {
112 | background: #afd5fe;
113 | }
114 |
115 | .line-highlight {
116 | margin-top: 18px;
117 | min-height: 24px;
118 |
119 | &:before,
120 | &:after {
121 | display: none;
122 | }
123 | }
124 |
125 | pre[class*="language-"] {
126 | &,
127 | & code,
128 | .line-highlight {
129 | line-height: 18px;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/site/assets/css/flow.scss:
--------------------------------------------------------------------------------
1 | @keyframes bounceInDown {
2 | from,
3 | 60%,
4 | 75%,
5 | 90%,
6 | to {
7 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
8 | }
9 |
10 | 0% {
11 | opacity: 0;
12 | transform: translate3d(0, -280px, 0);
13 | }
14 |
15 | 60% {
16 | opacity: 1;
17 | transform: translate3d(0, 25px, 0);
18 | }
19 |
20 | 75% {
21 | transform: translate3d(0, -10px, 0);
22 | }
23 |
24 | 90% {
25 | transform: translate3d(0, 5px, 0);
26 | }
27 |
28 | to {
29 | transform: translate3d(0, 0, 0);
30 | }
31 | }
32 |
33 | .bounceInDown {
34 | animation-name: bounceInDown;
35 | }
36 |
37 | @keyframes bounceInUp {
38 | from,
39 | 60%,
40 | 75%,
41 | 90%,
42 | to {
43 | animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
44 | }
45 |
46 | from {
47 | opacity: 0;
48 | transform: translate3d(0, 280px, 0);
49 | }
50 |
51 | 60% {
52 | opacity: 1;
53 | transform: translate3d(0, -20px, 0);
54 | }
55 |
56 | 75% {
57 | transform: translate3d(0, 10px, 0);
58 | }
59 |
60 | 90% {
61 | transform: translate3d(0, -5px, 0);
62 | }
63 |
64 | to {
65 | transform: translate3d(0, 0, 0);
66 | }
67 | }
68 |
69 | .bounceInUp {
70 | animation-name: bounceInUp;
71 | }
72 |
73 | .animated {
74 | animation-duration: 0.3s;
75 | animation-fill-mode: both;
76 | }
77 |
78 | .flow,
79 | .examples,
80 | .benefits {
81 | margin-top: 40px;
82 | margin-bottom: 40px;
83 |
84 | h2 {
85 | text-align: center;
86 | font-weight: 800;
87 | font-size: 54px;
88 | border-bottom: 1px solid #eee;
89 | padding-bottom: 10px;
90 | margin-bottom: 30px;
91 | }
92 |
93 | pre {
94 | -webkit-overflow-scrolling: touch;
95 | overflow-x: auto;
96 | padding: 1.25em 1.5em;
97 | white-space: pre;
98 | word-wrap: normal;
99 | margin-top: 0;
100 | }
101 |
102 | .menu-list a {
103 | font-size: 24px;
104 | position: relative;
105 |
106 | img {
107 | width: 24px;
108 | position: relative;
109 | top: 3px;
110 | margin-right: 6px;
111 | }
112 | }
113 |
114 | .toggler {
115 | position: absolute;
116 | z-index: 5;
117 | right: 11px;
118 | /* border: 1px solid red; */
119 | padding: 10px;
120 | border-bottom-left-radius: 4px;
121 | font-weight: 600;
122 | background: #f7e5cc;
123 | }
124 | }
125 |
126 | .flow {
127 | .box {
128 | position: relative;
129 | text-align: center;
130 | box-shadow: none;
131 |
132 | h3 {
133 | margin-bottom: 30px;
134 | font-weight: 600;
135 | text-transform: uppercase;
136 | }
137 |
138 | ul,
139 | li {
140 | list-style: none;
141 | padding: 0;
142 | margin: 0;
143 | }
144 |
145 | p {
146 | margin-top: 15px;
147 | text-transform: uppercase;
148 | }
149 |
150 | img {
151 | height: 140px;
152 | }
153 |
154 | i {
155 | position: absolute;
156 | font-size: 36px;
157 | top: 125px;
158 | }
159 |
160 | &.first {
161 | i {
162 | right: -24px;
163 | }
164 | }
165 |
166 | &.second {
167 | z-index: -1;
168 | }
169 |
170 | &.third {
171 | i {
172 | left: -24px;
173 | }
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/site/assets/css/footer.scss:
--------------------------------------------------------------------------------
1 | footer.footer {
2 | border-top: 1px solid #eee;
3 | padding: 3rem 1.5rem 3rem;
4 | position: relative;
5 |
6 | .social-icons {
7 | font-size: 24px;
8 |
9 | .icon {
10 | padding: 10px 20px;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/site/assets/css/header.scss:
--------------------------------------------------------------------------------
1 | .hero {
2 | .subtitle {
3 | font-size: 24px;
4 | }
5 |
6 | .title {
7 | font-size: 36px;
8 | line-height: 52px;
9 | font-weight: 100;
10 | }
11 |
12 | .subtitle,
13 | .title {
14 | color: #777 !important;
15 | font-family: 'Open Sans', sans-serif;
16 | margin-bottom: 30px;
17 |
18 | span {
19 | color: #fff;
20 | }
21 | }
22 |
23 | .brand {
24 |
25 | }
26 |
27 | .hero-body {
28 | .button {
29 | border-radius: 50px;
30 | padding: 0px 24px;
31 | margin: 0 3px;
32 | text-transform: uppercase;
33 | font-weight: 600;
34 | font-size: 16px;
35 | letter-spacing: 0px;
36 | height: 48px;
37 | }
38 |
39 | .button.is-primary {
40 | background: #f6566c;
41 | box-shadow: 5px 5px 50px #e4498e;
42 | }
43 |
44 | .button.is-transparent {
45 | color: #fff;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/site/assets/css/main.scss:
--------------------------------------------------------------------------------
1 | @import "content";
2 | @import "footer";
3 | @import "global";
4 | @import "header";
5 | @import "flow";
6 |
--------------------------------------------------------------------------------
/site/assets/img/banner-trimmed-900.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/banner-trimmed-900.png
--------------------------------------------------------------------------------
/site/assets/img/banner-trimmed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/banner-trimmed.png
--------------------------------------------------------------------------------
/site/assets/img/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/banner.png
--------------------------------------------------------------------------------
/site/assets/img/js-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/js-logo.png
--------------------------------------------------------------------------------
/site/assets/img/logo-text-80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/logo-text-80.png
--------------------------------------------------------------------------------
/site/assets/img/logo-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/logo-text.png
--------------------------------------------------------------------------------
/site/assets/img/logo-transparent-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/logo-transparent-128.png
--------------------------------------------------------------------------------
/site/assets/img/logo-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/logo-transparent.png
--------------------------------------------------------------------------------
/site/assets/img/preact-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/preact-logo.png
--------------------------------------------------------------------------------
/site/assets/img/proppy-flow.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/proppy-flow.gif
--------------------------------------------------------------------------------
/site/assets/img/proppy-og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/proppy-og.png
--------------------------------------------------------------------------------
/site/assets/img/proppy-square-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/proppy-square-128.png
--------------------------------------------------------------------------------
/site/assets/img/proppy-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/proppy-square.png
--------------------------------------------------------------------------------
/site/assets/img/react-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
11 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/site/assets/img/redux-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/redux-logo.png
--------------------------------------------------------------------------------
/site/assets/img/rxjs-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/rxjs-logo.png
--------------------------------------------------------------------------------
/site/assets/img/vue-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fahad19/proppy/4623c751cce1a2bd926ba2b25a0b41c54797e403/site/assets/img/vue-logo.png
--------------------------------------------------------------------------------
/site/content/docs/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Documentation
3 | sidebarPartial: docsSidebar
4 | importContentFromRoot: README.md
5 | ---
6 |
7 | # Documentation
8 |
9 |
--------------------------------------------------------------------------------
/site/content/docs/bundling.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Bundling
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Bundling
7 |
8 | Even though most packages are pretty small, you can optimize your production bundles even further by only including the functions that your application uses.
9 |
10 | ## Tree shaking
11 |
12 | If you are bundling your application with Webpack, you can consider applying tree shaking: [https://webpack.js.org/guides/tree-shaking/](https://webpack.js.org/guides/tree-shaking/).
13 |
14 | ## Import individually
15 |
16 | If you are unable to benefit from tree shaking in your environment, you can also import the functions from Proppy packages individually:
17 |
18 | ```js
19 | import { compose } from 'proppy/compose';
20 | import { withProps } from 'proppy/withProps';
21 |
22 | import { attach } from 'proppy-react/attach';
23 | ```
24 |
--------------------------------------------------------------------------------
/site/content/docs/changelog.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Change Log
3 | sidebarPartial: docsSidebar
4 | importContentFromRoot: CHANGELOG.md
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/contributing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Contribution Guide
3 | sidebarPartial: docsSidebar
4 | importContentFromRoot: CONTRIBUTING.md
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/examples/react-counter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Example: React Counter with and without Proppy"
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Example: React Counter with and without Proppy
7 |
8 |
9 |
10 | - [Without Proppy](#without-proppy)
11 | - [With Proppy](#with-proppy)
12 |
13 |
14 |
15 | A basic example of a React component:
16 |
17 | * Showing the `counter` value, and
18 | * An `Increment` button incrementing the counter's value by 1
19 | * Component re-renders only if `counter` is an even number
20 |
21 | ## Without Proppy
22 |
23 | ```js
24 | import React from 'react';
25 |
26 | class MyComponent extends React.Component {
27 | constructor(props) {
28 | super(props);
29 |
30 | this.state = {
31 | counter: 0
32 | };
33 |
34 | this.handleIncrement = this.handleIncrement.bind(this);
35 | }
36 |
37 | handleIncrement() {
38 | const { counter } = this.state;
39 |
40 | this.setState({
41 | counter: counter + 1
42 | });
43 | }
44 |
45 | // re-render only if `counter` is an even number
46 | shouldComponentUpdate(nextProps, nextState) {
47 | if (nextState.counter % 2 !== 0) {
48 | return false;
49 | }
50 |
51 | return true;
52 | }
53 |
54 | render() {
55 | const { counter } = this.state;
56 |
57 | return (
58 |
59 |
Counter: {counter}
60 |
61 |
62 | Increment
63 |
64 |
65 | );
66 | }
67 | }
68 |
69 | export default MyComponent;
70 | ```
71 |
72 | ## With Proppy
73 |
74 | ```js
75 | import React from 'react';
76 | import { compose, withStateHandlers, shouldUpdate } from 'proppy';
77 | import { attach } from 'proppy-react';
78 |
79 | const P = compose(
80 | withStateHandlers(
81 | { counter: 0 },
82 | { handleIncrement: props => () => ({ counter: props.counter + 1 }) }
83 | ),
84 | shouldUpdate((prevProps, nextProps) => nextProps.counter % 2 === 0)
85 | );
86 |
87 | function MyComponent({ counter, handleIncrement }) {
88 | return (
89 |
90 |
Counter: {counter}
91 |
92 |
93 | Increment
94 |
95 |
96 | );
97 | }
98 |
99 | export default attach(P)(MyComponent);
100 | ```
101 |
--------------------------------------------------------------------------------
/site/content/docs/examples/react-fetch-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Example: React Fetch Request with and without Proppy"
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Example: React Fetch Request with and without Proppy
7 |
8 |
9 |
10 | - [Without Proppy](#without-proppy)
11 | - [With Proppy](#with-proppy)
12 |
13 |
14 |
15 | A basic example of a React component:
16 |
17 | * Receiving `productId` prop
18 | * Handler for fetching a Product by that ID from server
19 | * Fetch it when component is mounted
20 | * Render the Product's title
21 | * Refresh button to fetch the Product again
22 | * A loading indicator when request is in progress
23 |
24 | ## Without Proppy
25 |
26 | ```js
27 | import React from 'react';
28 |
29 | class MyComponent extends React.Component {
30 | constructor(props) {
31 | super(props);
32 |
33 | this.state = {
34 | product: null,
35 | isLoading: false
36 | };
37 |
38 | this.handleFetch = this.handleFetch.bind(this);
39 | }
40 |
41 | handleFetch(productId) {
42 | this.setState({
43 | isLoading: true
44 | });
45 |
46 | fetch(`/api/products/${productId}.json`)
47 | .then(res => res.json())
48 | .then(res => this.setState({
49 | product: res.data,
50 | isLoading: false
51 | }));
52 | }
53 |
54 | componentDidMount() {
55 | const { productId } = this.props;
56 |
57 | this.handleFetch(productId);
58 | }
59 |
60 | render() {
61 | const { productId } = this.props;
62 | const { product, isLoading } = this.state;
63 |
64 | return (
65 |
66 | {loading &&
Loading...
}
67 |
68 |
Title: {product && product.title}
69 |
70 |
this.handleFetch(productId)}>
71 | Refresh
72 |
73 |
74 | );
75 | }
76 | }
77 |
78 | export default MyComponent;
79 | ```
80 |
81 | ## With Proppy
82 |
83 | ```js
84 | import React from 'react';
85 | import { compose, withState, withHandlers, didSubscribe } from 'proppy';
86 | import { attach } from 'proppy-react';
87 |
88 | const P = compose(
89 | withState('product', 'setProduct', null),
90 | withState('isLoading', 'setLoading', false),
91 | withHandlers({
92 | handleFetch: props => productId => {
93 | props.setLoading(true);
94 |
95 | return fetch(`/api/products/${productId}.json`)
96 | .then(res => {
97 | props.setProduct(res.data);
98 | props.setLoading(false);
99 | });
100 | }
101 | }),
102 | didSubscribe(props => props.handleFetch(props.productId))
103 | );
104 |
105 | function MyComponent({ productId, product, isLoading, handleFetch }) {
106 | return (
107 |
108 | {isLoading &&
Loading...
}
109 |
110 |
Title: {product && product.title}
111 |
112 |
handleFetch(productId)}>
113 | Refresh
114 |
115 |
116 | );
117 | }
118 |
119 | export default attach(P)(MyComponent);
120 | ```
121 |
--------------------------------------------------------------------------------
/site/content/docs/examples/react-rxjs.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Example: React + RxJS with and without Proppy"
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Example: React + RxJS with and without Proppy
7 |
8 |
9 |
10 | - [Without Proppy](#without-proppy)
11 | - [With Proppy](#with-proppy)
12 |
13 |
14 |
15 | A basic example of a React component:
16 |
17 | * Showing `interval` coming from RxJS Observable
18 | * Interval starts as soon as component mounts
19 | * Component renders only when `interval` is an even number
20 | * Observable is unsubscribed when component is unmounted
21 |
22 | ## Without Proppy
23 |
24 | ```js
25 | import React from 'react';
26 | import { interval } from 'rxjs';
27 | import { map } from 'rxjs/operators';
28 |
29 | class MyComponent extends React.Component {
30 | constructor(props) {
31 | super(props);
32 |
33 | this.state = {
34 | interval: 0
35 | };
36 | }
37 |
38 | componentDidMount() {
39 | // emits an integer every 1 second
40 | const interval$ = interval(1000).pipe(
41 | map(n => ({
42 | inverval: n
43 | }))
44 | );
45 |
46 | this._subscription = interval$.subscribe(
47 | result => this.setState(result)
48 | )
49 | }
50 |
51 | shouldComponentUpdate(nextProps, nextState) {
52 | if (nextState.interval % 2 !== 0) {
53 | return false;
54 | }
55 |
56 | return true;
57 | }
58 |
59 | componentWillUnmount() {
60 | this._subscription.unsubscribe();
61 | }
62 |
63 | render() {
64 | const { interval } = this.state;
65 |
66 | return Interval: {interval}
;
67 | }
68 | }
69 |
70 | export default MyComponent;
71 | ```
72 |
73 | ## With Proppy
74 |
75 | ```js
76 | import React from 'react';
77 | import { interval } from 'rxjs';
78 | import { map } from 'rxjs/operators';
79 | import { compose, withObservable, shouldUpdate } from 'proppy';
80 |
81 | const P = compose(
82 | withObservable(props => interval(1000).pipe(
83 | map(n => ({ interval: n }))
84 | )),
85 | shouldUpdate((prevProps, nextProps) => nextProps.counter % 2 === 0)
86 | );
87 |
88 | function MyComponent({ interval }) {
89 | return Interval: {interval}
;
90 | }
91 |
92 | export default attach(P)(MyComponent);
93 | ```
94 |
95 | For advanced usage, look into `proppy-rx` package, when you can also access incoming props as an Observable.
96 |
--------------------------------------------------------------------------------
/site/content/docs/examples/vue-counter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Example: Vue.js Counter with Proppy"
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Comparison: Vue.js Counter with Proppy
7 |
8 |
9 |
10 | - [Without Proppy](#without-proppy)
11 | - [With Proppy](#with-proppy)
12 |
13 |
14 |
15 | A basic example of a Vue.js component:
16 |
17 | * Showing the `counter` value, and
18 | * An `Increment` button incrementing the counter's value by 1
19 | * Component re-renders upon changes
20 |
21 | ## Without Proppy
22 |
23 | ```js
24 | const MyComponent = {
25 | data: function () {
26 | return {
27 | counter: 0
28 | };
29 | },
30 |
31 | methods: {
32 | handleIncrement: function () {
33 | const currentCounter = this.counter;
34 |
35 | this.counter = currentCounter + 1;
36 | }
37 | },
38 |
39 | render(h) {
40 | return (
41 |
42 |
Counter: {this.counter}
43 |
44 |
45 | Increment
46 |
47 |
48 | );
49 | }
50 | };
51 |
52 | export default MyComponent;
53 | ```
54 |
55 | ## With Proppy
56 |
57 | ```js
58 | import { withStateHandlers } from 'proppy';
59 | import { attach } from 'proppy-vue';
60 |
61 | const P = withStateHandlers(
62 | { counter: 0 },
63 | { handleIncrement: props => () => ({ counter: props.counter + 1 }) }
64 | );
65 |
66 | const MyComponent = {
67 | props: ['counter', 'handleIncrement'],
68 |
69 | render(h) {
70 | return (
71 |
72 |
Counter: {this.counter}
73 |
74 |
75 | Increment
76 |
77 |
78 | );
79 | }
80 | };
81 |
82 | export default attach(P)(MyComponent);
83 | ```
84 |
--------------------------------------------------------------------------------
/site/content/docs/factory-instance.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Factory & Instance
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Factory & Instance
7 |
8 |
9 |
10 | - [Factory](#factory)
11 | - [Instance](#instance)
12 |
13 |
14 |
15 | ## Factory
16 |
17 | Proppy factories are functions that return you the Proppy instance.
18 |
19 | The factories optionally accept a common `providers` object which can be used by the instances as they see fit.
20 |
21 | In the example below, `P` is a factory:
22 |
23 | ```js
24 | const P = function (providers = {}) {
25 | return instance;
26 | }
27 | ```
28 |
29 | We will learn about the API of the instance below:
30 |
31 | ## Instance
32 |
33 | Proppy instances allow you to access the props both synchronously, as well as asynchronously via subscription:
34 |
35 | ```js
36 | const p = P();
37 |
38 | // current props (synchronous)
39 | console.log(p.props);
40 |
41 | // subscription (asynchronous)
42 | // will emit as many times the props get updated
43 | const unsubscribe = p.subscribe(props => console.log(props));
44 | unsubscribe();
45 |
46 | // destroy
47 | // all listeners are unsubscribed and internal state is cleared
48 | p.destroy();
49 | ```
50 |
51 | The library itself is unopinionated how you implement your factories and instances yourself. But it comes with a handy [`create`](../packages/proppy#create) function that helps you to create your own instances quickly.
52 |
53 | It's best if you read the [quick start](../quickstart) guide next.
54 |
--------------------------------------------------------------------------------
/site/content/docs/faq.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Frequently Asked Questions
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # FAQ
7 |
8 |
9 |
10 | - [What is the primary feature of Proppy?](#what-is-the-primary-feature-of-proppy)
11 | - [Can I use Proppy with React, Vue.js, and Preact?](#can-i-use-proppy-with-react-vuejs-and-preact)
12 | - [Is Proppy a Redux alternative?](#is-proppy-a-redux-alternative)
13 | - [How is it different than Redux?](#how-is-it-different-than-redux)
14 | - [What is the difference between Proppy and Recompose?](#what-is-the-difference-between-proppy-and-recompose)
15 | - [Can I use RxJS with Proppy?](#can-i-use-rxjs-with-proppy)
16 | - [Is there any cost in performance?](#is-there-any-cost-in-performance)
17 |
18 |
19 |
20 | ## What is the primary feature of Proppy?
21 |
22 | It just helps you compose your props, that you can use anywhere later. Nothing more.
23 |
24 | ## Can I use Proppy with React, Vue.js, and Preact?
25 |
26 | Yes! We have integrations for all of them:
27 |
28 | * [`proppy-react`](../packages/proppy-react)
29 | * [`proppy-vue`](../packages/proppy-vue)
30 | * [`proppy-preact`](../packages/proppy-preact)
31 |
32 | ## Is Proppy a Redux alternative?
33 |
34 | Not at all. We have an integration package for ease of Redux usage too: [`proppy-redux`](../packages/proppy-redux).
35 |
36 | ## How is it different than Redux?
37 |
38 | [Redux](https://redux.js.org/) is meant for state management for your whole application, based on actions and reducers.
39 |
40 | Proppy is aimed at lifting the logic one level above your Components, and only deal with the behaviour of their props.
41 |
42 | While [react-redux](https://github.com/reduxjs/react-redux) allows you to access your single Store from anywhere in the components tree, Proppy allows you to access [providers](../providers).
43 |
44 | And the Redux store, for example, can be one of the providers here.
45 |
46 | Proppy is unopinionated about what you use for your application's state management, and doesn't lock you in to any specific state management or rendering library.
47 |
48 | ## What is the difference between Proppy and Recompose?
49 |
50 | [Recompose](https://github.com/acdlite/recompose) has been the original inspiration for building ProppyJS.
51 |
52 | A few key differences:
53 |
54 | * Not tied to any rendering libraries (like React or Vue.js)
55 | * Allows access to application-wide dependencies (like Redux store, config, etc)
56 | * Deals with only props generation
57 | * Doesn’t create a new component in tree per function
58 |
59 | ## Can I use RxJS with Proppy?
60 |
61 | Absolutely! At the end of the day Proppy only composes props, and allows you to subscribe to them too.
62 |
63 | RxJS fits in very nicely here. With [`proppy-rx`](../packages/proppy-rx) package, you have access to incoming props stream as an Observable, and you are in full control of what props get output via returning Obsevable.
64 |
65 | ## Is there any cost in performance?
66 |
67 | When you use Proppy with any rendering library, you end up wrapping your base components with the `attach` higher-order component.
68 |
69 | This means every time you use the `attach` higher-order component, you are creating one more stateful component which takes care of creating the Proppy instance internally and keep passing the generated props to the base component.
70 |
71 | This layer of abstraction has its costs, because it is adding one more component to your tree. But at the same time, Proppy arms you with functions like `shouldUpdate` which helps you control when new props are passed down to your base component trigerring a re-render.
72 |
73 | In the end, it can be considered that the benefits outweigh any performance cost that is introduced because of this additional component in your tree.
74 |
--------------------------------------------------------------------------------
/site/content/docs/packages/proppy-preact.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: proppy-preact
3 | importContentFromPackage: proppy-preact
4 | sidebarPartial: docsSidebar
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/packages/proppy-react.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: proppy-react
3 | importContentFromPackage: proppy-react
4 | sidebarPartial: docsSidebar
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/packages/proppy-redux.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: proppy-redux
3 | importContentFromPackage: proppy-redux
4 | sidebarPartial: docsSidebar
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/packages/proppy-rx.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: proppy-rx
3 | importContentFromPackage: proppy-rx
4 | sidebarPartial: docsSidebar
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/packages/proppy-vue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: proppy-vue
3 | importContentFromPackage: proppy-vue
4 | sidebarPartial: docsSidebar
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/packages/proppy.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: proppy
3 | importContentFromPackage: proppy
4 | sidebarPartial: docsSidebar
5 | ---
6 |
--------------------------------------------------------------------------------
/site/content/docs/playground.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Playgound
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Playgound
7 |
8 | Live examples of ProppyJS:
9 |
10 | * React:
11 | * [Source](https://github.com/fahad19/proppy/tree/master/examples/react-playground)
12 | * [CodeSandbox](https://codesandbox.io/s/github/fahad19/proppy/tree/master/examples/react-playground)
13 | * Preact:
14 | * [Source](https://github.com/fahad19/proppy/tree/master/examples/preact-playground)
15 | * [CodeSandbox](https://codesandbox.io/s/github/fahad19/proppy/tree/master/examples/preact-playground)
16 |
--------------------------------------------------------------------------------
/site/content/docs/props.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Props
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Props
7 |
8 | Props are read-only objects, and the concept is popularized by rendering libraries like [React](https://reactjs.org/docs/components-and-props.html) and [Vue.js](https://vuejs.org/v2/guide/components-props.html).
9 |
10 | The data available in props are used to render components.
11 |
12 | For example, if you happen to have a `Counter` component that renders the counter's value, it may receive a props object in this shape:
13 |
14 | ```js
15 | const props = { counter: 1 };
16 | ```
17 |
18 | In a React example, the props are used like this:
19 |
20 | ```js
21 | // components/Counter.js
22 | import React from 'react';
23 |
24 | function Counter(props) {
25 | return Counter value: {props.counter}
;
26 | }
27 | ```
28 |
29 | ## Handlers
30 |
31 | Props may also contain handlers (functions) which may trigger an update of the props, resulting in your Component being re-rendered.
32 |
33 | Keeping the previous example in mind, we can say that the `Counter` component could receive props in this shape too:
34 |
35 | ```js
36 | {
37 | counter: 1,
38 | setCounter: Function
39 | }
40 | ```
41 |
42 | The `setCounter` handler could be triggered via an `onClick` event handler for example:
43 |
44 | ```js
45 | // components/Counter.js
46 | import React from 'react';
47 |
48 | function Counter(props) {
49 | const { counter, setCounter } = props;
50 |
51 | return (
52 |
53 |
Counter value: {counter}
54 |
55 |
setCounter(counter + 1)}>
56 | Increment
57 |
58 |
59 | );
60 | }
61 | ```
62 |
--------------------------------------------------------------------------------
/site/content/docs/providers.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Providers
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Providers
7 |
8 | Providers are common stateful dependencies for your whole application.
9 |
10 | A few examples of such providers include:
11 |
12 | * Configuration
13 | * [Redux](https://redux.js.org/) store
14 | * Current theme
15 | * ...more
16 |
17 | The providers can be expressed in a single object here:
18 |
19 | ```js
20 | const providers = {
21 | store: reduxStore,
22 | config: {},
23 | theme: 'light'
24 | };
25 | ```
26 |
27 | The idea is to share this single `providers` object globally in your application.
28 |
--------------------------------------------------------------------------------
/site/content/docs/testing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Testing
3 | sidebarPartial: docsSidebar
4 | ---
5 |
6 | # Testing
7 |
8 | Unit testing Proppy is pretty straight forward because of it's functional nature.
9 |
10 | Assuming you are using Proppy and React together, your Component file may look like this:
11 |
12 | ```js
13 | // MyComponent.js
14 | import React from 'react';
15 | import { compose, withProps } from 'proppy';
16 | import { attach } from 'proppy-react';
17 |
18 | const P = compose(
19 | withProps({ foo: 'foo value' })
20 | );
21 |
22 | function MyComponent(props) {
23 | return
;
24 | }
25 |
26 | export default attach(P)(MyComponent);
27 | ```
28 |
29 | To make unit testing easier, we can change our original module to contain only named exports, so that we can import Proppy factory and the base stateless component separately:
30 |
31 | ```js
32 | // MyComponent.js
33 | import React from 'react';
34 | import { compose, withProps } from 'proppy';
35 | import { attach } from 'proppy-react';
36 |
37 | export const P = compose(
38 | withProps({ foo: 'foo value' })
39 | );
40 |
41 | export function Base(props) {
42 | return
;
43 | }
44 |
45 | export const MyComponent = attach(P)(Base);
46 | ```
47 |
48 | Now in our test file, we can import both the Proppy factory and the base component separately and unit test them:
49 |
50 | ```js
51 | // MyComponent.spec.js
52 | import { P, Base } from './MyComponent';
53 |
54 | describe('MyComponent', () => {
55 | test('Proppy', () => {
56 | const mockedProviders = {};
57 | const p = P(mockedProviders);
58 |
59 | expect(p.props).toEqual({
60 | foo: 'foo value'
61 | });
62 |
63 | p.destroy();
64 | });
65 |
66 | test('Component', () => {
67 | // test `Base` React component
68 | });
69 | })
70 | ```
71 |
--------------------------------------------------------------------------------
/site/data/defaults.json:
--------------------------------------------------------------------------------
1 | {
2 | "meta": {
3 | "description": "ProppyJS: JavaScript library for functional composition of props for UI components",
4 | "keywords": "javascript, library, react.js, vue.js, preact, rxjs, composition, props, framework"
5 | },
6 | "og": {
7 | "title": "ProppyJS: Functional props composition for UI components",
8 | "description": "ProppyJS is a tiny 1.5kB JavaScript library for functionally composing props for your UI components. Supporting integrations with React.js, Vue.js, Preact, Redux, and RxJS.",
9 | "image": "/img/proppy-og.png",
10 | "image:width": "1600",
11 | "image:height": "630",
12 | "type": "website"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/site/data/sidebarLinks.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Intro",
4 | "links": [
5 | { "title": "Introduction", "url": "/docs/introduction" }
6 | ]
7 | },
8 | {
9 | "title": "Concepts",
10 | "links": [
11 | { "title": "Props", "url": "/docs/props" },
12 | { "title": "Providers", "url": "/docs/providers" },
13 | { "title": "Factory & Instance", "url": "/docs/factory-instance" }
14 | ]
15 | },
16 | {
17 | "title": "Getting started",
18 | "links": [
19 | { "title": "Quick start", "url": "/docs/quickstart" }
20 | ]
21 | },
22 | {
23 | "title": "Examples",
24 | "links": [
25 | { "title": "React: Counter", "url": "/docs/examples/react-counter" },
26 | { "title": "React: Fetch Request", "url": "/docs/examples/react-fetch-request" },
27 | { "title": "React: Redux", "url": "/docs/examples/react-redux" },
28 | { "title": "React: RxJS", "url": "/docs/examples/react-rxjs" },
29 | { "title": "Vue.js: Counter", "url": "/docs/examples/vue-counter" }
30 | ]
31 | },
32 | {
33 | "title": "Advanced",
34 | "links": [
35 | { "title": "Lifecycle", "url": "/docs/lifecycle" },
36 | { "title": "Testing", "url": "/docs/testing" },
37 | { "title": "Bundling", "url": "/docs/bundling" }
38 | ]
39 | },
40 | {
41 | "title": "Packages",
42 | "links": [
43 | { "title": "proppy", "url": "/docs/packages/proppy" },
44 | { "title": "proppy-react", "url": "/docs/packages/proppy-react" },
45 | { "title": "proppy-vue", "url": "/docs/packages/proppy-vue" },
46 | { "title": "proppy-preact", "url": "/docs/packages/proppy-preact" },
47 | { "title": "proppy-redux", "url": "/docs/packages/proppy-redux" },
48 | { "title": "proppy-rx", "url": "/docs/packages/proppy-rx" }
49 | ]
50 | },
51 | {
52 | "title": "Project",
53 | "links": [
54 | { "title": "API", "url": "/docs/api" },
55 | { "title": "Playground", "url": "/docs/playground" },
56 | { "title": "Change Log", "url": "/docs/changelog" },
57 | { "title": "Contribution Guide", "url": "/docs/contributing" },
58 | { "title": "FAQ", "url": "/docs/faq" }
59 | ]
60 | }
61 | ]
62 |
--------------------------------------------------------------------------------
/site/layouts/default.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= title %> | ProppyJS
5 | <%= renderPartial('assets_head') %>
6 |
7 |
8 |
9 |
10 |
11 | <%= renderPartial('navLinks') %>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | <%= renderPartial(sidebarPartial) %>
20 |
21 |
22 |
23 | <%= contents %>
24 |
25 |
52 |
53 |
54 |
55 |
56 |
57 | <%= renderPartial('footer') %>
58 |
59 | <%= renderPartial('assets_body') %>
60 |
61 |
62 |
--------------------------------------------------------------------------------
/site/layouts/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= title %> | Functional props composition for UI components
5 | <%= renderPartial('assets_head') %>
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | <%= renderPartial('navLinks') %>
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | Tiny 1.5kB utility library enabling
26 |
27 |
28 |
29 | Functional props composition for components
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
Flow of props
43 |
44 |
45 |
46 |
47 |
Various sources
48 |
49 |
50 |
51 |
52 | Redux
53 |
54 |
55 |
56 |
57 | JavaScript
58 |
59 |
60 |
61 |
62 | RxJS
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
Compose as props
73 |
74 |
75 |
76 |
Proppy
77 |
78 |
79 |
80 |
81 |
82 |
Pass to any Component
83 |
84 |
85 |
86 |
87 |
88 |
89 | React
90 |
91 |
92 |
93 |
94 | Vue.js
95 |
96 |
97 |
98 |
99 | Preact
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | <%= contents %>
109 |
110 | <%= renderPartial('footer') %>
111 | <%= renderPartial('assets_body') %>
112 |
113 |
114 |
--------------------------------------------------------------------------------
/site/partials/assets_body.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
19 |
--------------------------------------------------------------------------------
/site/partials/assets_head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <% if (meta && meta.description) { %>
6 |
7 | <% } %>
8 |
9 | <% if (meta && meta.keywords) { %>
10 |
11 | <% } %>
12 |
13 |
14 |
15 | <% if (og && og.title) { %>
16 |
17 | <% } %>
18 |
19 | <% if (og && og.description) { %>
20 |
21 | <% } %>
22 |
23 | <% if (og && og.image) { %>
24 |
25 | <% } %>
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/site/partials/docsSidebar.html:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/site/partials/footer.html:
--------------------------------------------------------------------------------
1 |
24 |
--------------------------------------------------------------------------------
/site/partials/navLinks.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
25 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": [
4 | "tslint:recommended"
5 | ],
6 | "rules": {
7 | "arrow-parens": false,
8 | "interface-name": false,
9 | "no-empty": false,
10 | "object-literal-sort-keys": false,
11 | "object-literal-shorthand": false,
12 | "only-arrow-functions": false,
13 | "ordered-imports": false,
14 | "quotemark": false,
15 | "space-before-function-paren": false,
16 | "trailing-comma": false,
17 | "variable-name": false
18 | }
19 | }
20 |
--------------------------------------------------------------------------------