├── .github └── FUNDING.yml ├── .gitignore ├── .storybook ├── addons.js ├── config.js └── webpack.config.js ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build ├── build-docs.js └── publish-docs.sh ├── docs ├── LANGS.md ├── assets │ └── libreact.png ├── book.json └── en │ ├── ActiveSensor.md │ ├── AfterDraf.md │ ├── AfterTimeout.md │ ├── Alert.md │ ├── Animation.md │ ├── Audio.md │ ├── BatterySensor.md │ ├── Boundaries.md │ ├── BrowserOnly.md │ ├── ClassNames.md │ ├── Context.md │ ├── Counter.md │ ├── Dimmable.md │ ├── Dimmer.md │ ├── DropArea.md │ ├── Dummies.md │ ├── ElectronOnly.md │ ├── ErrorBoundary.md │ ├── ExitSensor.md │ ├── Flipflop.md │ ├── FocusSensor.md │ ├── FullScreen.md │ ├── GeoLocationSensor.md │ ├── GoogleAuth.md │ ├── Group.md │ ├── HoverSensor.md │ ├── IdleSensor.md │ ├── Img.md │ ├── InfiniteScroll.md │ ├── InlineWidthQuery.md │ ├── Interpolation.md │ ├── Introduction.md │ ├── Inversion.md │ ├── LICENSE.md │ ├── Lifecycles.md │ ├── LightSensor.md │ ├── List.md │ ├── ListTable.md │ ├── LocalStorage.md │ ├── LocationSensor.md │ ├── Mailto.md │ ├── Map.md │ ├── MediaDeviceSensor.md │ ├── MediaSensor.md │ ├── Modal.md │ ├── MotionSensor.md │ ├── MouseSensor.md │ ├── NetworkSensor.md │ ├── OrientationSensor.md │ ├── Other.md │ ├── OutsideClick.md │ ├── Overlay.md │ ├── Parallax.md │ ├── Portal.md │ ├── Prompt.md │ ├── Provider.md │ ├── README.md │ ├── Render.md │ ├── RenderInterval.md │ ├── Resolve.md │ ├── Ripple.md │ ├── SUMMARY.md │ ├── ScratchSensor.md │ ├── ScrollSensor.md │ ├── Sensors.md │ ├── ServerOnly.md │ ├── ShouldUpdate.md │ ├── Side-effects.md │ ├── SizeSensor.md │ ├── Slider.md │ ├── Sms.md │ ├── Speak.md │ ├── State.md │ ├── Toggle.md │ ├── TouchSupportSensor.md │ ├── Tween.md │ ├── UI.md │ ├── Value.md │ ├── Vibrate.md │ ├── Video.md │ ├── View.md │ ├── ViewportSensor.md │ ├── WhenIdle.md │ ├── WidthQuery.md │ ├── WidthSensor.md │ ├── WindowScrollSensor.md │ ├── WindowSizeSensor.md │ ├── WindowWidthQuery.md │ ├── WindowWidthSensor.md │ ├── css │ ├── StyleSheet.md │ ├── css.md │ ├── jsxstyle.md │ ├── rule.md │ └── styled.md │ ├── cssvars.md │ ├── delayed.md │ ├── getDisplayName.md │ ├── invert.md │ ├── lazy.md │ ├── loadable.md │ ├── mock.md │ ├── next.md │ ├── next │ ├── createLifecycleEvents.md │ ├── createRef.md │ └── createState.md │ ├── pure.md │ ├── reset │ ├── CssResetEricMeyer.md │ ├── CssResetEricMeyerCondensed.md │ ├── CssResetMinimalistic.md │ ├── CssResetMinimalistic2.md │ ├── CssResetMinimalistic3.md │ ├── CssResetPoorMan.md │ ├── CssResetShaunInman.md │ ├── CssResetSiolon.md │ ├── CssResetTantek.md │ ├── CssResetUniversal.md │ └── CssResetYahoo.md │ ├── routing.md │ ├── theme.md │ ├── translate.md │ └── viewport.md ├── package.json ├── src ├── ActiveSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── AfterDraf │ ├── RAF.ts │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── index.server.test.tsx │ │ └── index.test.tsx │ ├── createSingleRunDraf.ts │ └── index.ts ├── AfterTimeout │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Alert │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Audio │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── BatterySensor │ ├── __story__ │ │ └── story.ts │ ├── __tests__ │ │ └── index.test-server.ts │ └── index.ts ├── BrowserOnly │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ClassNames │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── index.test-server.tsx │ │ └── index.test.tsx │ └── index.tsx ├── Counter │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── CssVars │ └── index.ts ├── Dimmable │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Dimmer │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── index.test-server.tsx │ │ └── index.test.tsx │ └── index.tsx ├── DropArea │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ElectronOnly │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ErrorBoundary │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ExitSensor │ ├── __story__ │ │ ├── StoryExitSensorExample.tsx │ │ └── story.tsx │ ├── __tests__ │ │ └── index.test-server.ts │ └── index.ts ├── Flipflop │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── FocusSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── FullScreen │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── GeoLocation │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── GoogleAuth │ ├── __story__ │ │ └── story.tsx │ ├── gapi.ts │ └── index.ts ├── Group │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ └── index.test.tsx │ └── index.ts ├── HoverSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── IdleSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Img │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── InfiniteScroll │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── InlineWidthQuery │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Interpolation │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Lifecycles │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── LightSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Link │ └── index.ts ├── List │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ListTable │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── LocalStorage │ ├── __story__ │ │ ├── StoryLocalStorageForm.tsx │ │ └── story.tsx │ ├── __tests__ │ │ ├── index.test-server.ts │ │ └── index.test.tsx │ ├── index.ts │ └── local-storage.ts ├── LocationSensor │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ ├── index.test-server.tsx │ │ └── index.test.tsx │ └── index.ts ├── Mailto │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── index.test-server.ts │ │ └── index.test.ts │ └── index.ts ├── Map │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Media │ ├── __tests__ │ │ └── index.test.ts │ ├── index.ts │ └── parseTimeRanges.ts ├── MediaDeviceSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── MediaSensor │ ├── __story__ │ │ └── story.ts │ ├── __tests__ │ │ ├── index.test-server.ts │ │ └── index.test.tsx │ └── index.ts ├── Modal │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── MotionSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── MouseSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── NetworkSensor │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ └── index.test-server.ts │ └── index.ts ├── OrientationSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── OutsideClick │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Overlay │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Parallax │ ├── __story__ │ │ ├── StoryParallax1.tsx │ │ ├── StoryParallax2.tsx │ │ ├── StoryParallax3.tsx │ │ ├── StoryParallax4.tsx │ │ ├── StoryParallax5.tsx │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.server.test.tsx.snap │ │ ├── index.server.test.tsx │ │ └── index.test.tsx │ └── index.ts ├── Pluggable │ └── index.js ├── Portal │ └── index.ts ├── Prompt │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Render │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── RenderInterval │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Resolve │ ├── index.ts │ └── story.tsx ├── Ripple │ ├── __story__ │ │ ├── Button.tsx │ │ └── story.tsx │ └── index.ts ├── ScratchSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ScrollSensor │ ├── index.ts │ └── story.ts ├── ServerOnly │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ └── index.test-server.tsx │ └── index.ts ├── ShouldUpdate │ ├── __story__ │ │ ├── Example1.tsx │ │ ├── Example2.tsx │ │ ├── Example3.tsx │ │ ├── Example4.tsx │ │ └── story.tsx │ └── index.ts ├── ShowDocs.ts ├── SizeSensor │ ├── __story__ │ │ └── story.ts │ └── index.ts ├── Slider │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ └── index.test.tsx │ └── index.ts ├── Sms │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── index.test-server.ts │ │ └── index.test.ts │ └── index.ts ├── Speak │ ├── __story__ │ │ └── story.ts │ ├── __tests__ │ │ └── index.test-server.ts │ └── index.ts ├── State │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── index.test-server.tsx │ │ └── index.test.tsx │ └── index.ts ├── SyncSensor │ └── index.ts ├── Toggle │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── TouchSupportSensor │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ └── index.test.tsx │ └── index.ts ├── Tween │ ├── __story__ │ │ └── story.tsx │ ├── createBezierEasing.ts │ ├── easing.ts │ └── index.ts ├── Value │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── Vibrate │ ├── __tests__ │ │ └── index.test-server.ts │ ├── index.ts │ └── story.tsx ├── Video │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── View │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ └── index.test.tsx │ └── index.ts ├── ViewportObserverSensor │ ├── __story__ │ │ ├── StoryViewportSensorBasic.tsx │ │ ├── StoryViewportSensorConf.tsx │ │ ├── StoryViewportSensorHorizontal.tsx │ │ └── story.tsx │ └── index.ts ├── ViewportScrollSensor │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── ViewportSensor │ ├── __story__ │ │ ├── StoryViewportSensorBasicJsx.tsx │ │ └── story.tsx │ └── index.ts ├── WhenIdle │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── WidthQuery │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.test.tsx.snap │ │ └── index.test.tsx │ └── index.ts ├── WidthSensor │ ├── __story__ │ │ └── story.ts │ └── index.ts ├── WindowScrollSensor │ ├── __story__ │ │ └── story.ts │ ├── __tests__ │ │ └── index.test-server.ts │ └── index.ts ├── WindowSizeSensor │ ├── __story__ │ │ └── story.ts │ ├── __tests__ │ │ └── index.test-server.ts │ └── index.ts ├── WindowWidthQuery │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── WindowWidthSensor │ ├── __story__ │ │ └── story.js │ └── index.ts ├── __story__ │ ├── lazy.story.tsx │ └── loadable.story.tsx ├── __tests__ │ ├── delayed.test.ts │ ├── lazy.test.ts │ ├── loadable.test.ts │ ├── mock.test.ts │ └── setup.js ├── context │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ ├── index.server.test.tsx │ │ └── observable.test.ts │ ├── index.ts │ └── observable.ts ├── cssvars │ ├── __story__ │ │ ├── Example1.tsx │ │ ├── Example2.tsx │ │ └── story.tsx │ └── __tests__ │ │ └── index.test-server-disable.tsx ├── delayed.ts ├── index.ts ├── invert.story.tsx ├── invert.ts ├── lazy.ts ├── loadable.ts ├── mock.ts ├── nano.ts ├── pixel.ts ├── pure.ts ├── route │ ├── Link.ts │ ├── Match.ts │ ├── Route.ts │ ├── Router.ts │ ├── Switch.ts │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ └── index.server.test.tsx │ ├── createMatcher.ts │ ├── createRouter.ts │ ├── go.ts │ └── index.ts ├── shim │ ├── __story__ │ │ ├── createLifecycleEvents.story.tsx │ │ ├── createRef.story.tsx │ │ └── createState.story.tsx │ ├── createLifecycleEvents.ts │ ├── createRef.ts │ ├── createState.ts │ └── index.ts ├── theme │ ├── __story__ │ │ └── story.tsx │ ├── __tests__ │ │ └── index.server.test.ts │ └── index.ts ├── translate │ ├── __story__ │ │ └── story.tsx │ └── index.ts ├── typing.ts ├── util.ts ├── util │ ├── __tests__ │ │ └── getDisplayName.test.tsx │ ├── addClassDecoratorSupport.ts │ ├── compose.ts │ ├── faccToHoc.ts │ ├── getDisplayName.ts │ ├── isStatelessComponent.ts │ ├── mapProps.ts │ ├── renderProp.ts │ └── wrapInStatefulComponent.ts ├── viewport.story.tsx └── viewport.ts ├── tsconfig.es6.json ├── tsconfig.json └── yarn.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: streamich 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules/ 3 | /.idea/ 4 | /.nyc_output/ 5 | /coverage/ 6 | package-lock.json 7 | /lib/ 8 | /modules/ 9 | /.vscode/ 10 | .DS_Store 11 | /dist_docs/ 12 | _book/ 13 | /storybook-static/ 14 | yarn-error.log 15 | -------------------------------------------------------------------------------- /.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import '@storybook/addon-actions/register'; 2 | import '@storybook/addon-links/register'; 3 | -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import {configure} from '@storybook/react'; 2 | 3 | const req = require.context('../src/', true, /.*(stories|story)\.(js|jsx|ts|tsx)?$/); 4 | 5 | const loadStories = () => { 6 | req.keys().forEach((filename) => req(filename)); 7 | }; 8 | 9 | configure(loadStories, module); 10 | -------------------------------------------------------------------------------- /.storybook/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const {compilerOptions} = require('../tsconfig.json'); 3 | 4 | const SRC_PATH = path.join(__dirname, '../src'); 5 | 6 | module.exports = { 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.tsx?$/, 11 | loader: 'ts-loader', 12 | include: [ 13 | SRC_PATH, 14 | ], 15 | options: { 16 | transpileOnly: true, // use transpileOnly mode to speed-up compilation 17 | compilerOptions: { 18 | ...compilerOptions, 19 | declaration: false, 20 | }, 21 | }, 22 | } 23 | ] 24 | }, 25 | 26 | resolve: { 27 | extensions: ['.ts', '.tsx', '.js', '.jsx'], 28 | enforceExtension: false 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | os: 3 | - linux 4 | cache: 5 | yarn: true 6 | directories: 7 | - ~/.npm 8 | notifications: 9 | email: false 10 | node_js: 11 | - '8' 12 | script: 13 | - yarn test 14 | - yarn build 15 | - yarn build:modules 16 | matrix: 17 | allow_failures: [] 18 | fast_finish: true 19 | after_success: 20 | - npx ci-scripts github-post 21 | - npx ci-scripts slack 22 | - yarn semantic-release 23 | branches: 24 | except: 25 | - /^v\d+\.\d+\.\d+$/ 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /build/build-docs.js: -------------------------------------------------------------------------------- 1 | const glob = require('glob'); 2 | const {join, sep, relative} = require('path'); 3 | const {copyFileSync} = require('fs'); 4 | const mkdirp = require('mkdirp'); 5 | 6 | const ROOT = join(__dirname, '..'); 7 | const SRC = join(ROOT, 'src'); 8 | const DOCS = join(ROOT, 'docs'); 9 | const DOCS_BUILD = join(ROOT, 'dist_docs'); 10 | 11 | const copy = (src, dest) => { 12 | const targetDir = join(dest, '..'); 13 | 14 | mkdirp.sync(targetDir); 15 | copyFileSync(src, dest); 16 | }; 17 | 18 | const copyInlineDocsFile = (srcFile) => { 19 | const steps = srcFile.split(sep); 20 | const indexDocs = steps.lastIndexOf('__docs__'); 21 | const name = steps[indexDocs - 1]; 22 | const lang = steps[indexDocs + 1]; 23 | const relSteps = steps.slice(indexDocs + 2); 24 | const targetFile = join(DOCS_BUILD, lang, ...relSteps); 25 | 26 | copy(srcFile, targetFile); 27 | }; 28 | 29 | const copyToBuildFolder = (srcFile) => { 30 | const relPath = relative(DOCS, srcFile); 31 | const targetFile = join(DOCS_BUILD, relPath); 32 | 33 | copy(srcFile, targetFile); 34 | }; 35 | 36 | const inlineDocFiles = glob.sync(`${SRC}/**/__docs__/**/*.md`); 37 | const docFiles = glob.sync(`${DOCS}/**/**/*`); 38 | 39 | inlineDocFiles.forEach(copyInlineDocsFile); 40 | docFiles.forEach(copyToBuildFolder); 41 | -------------------------------------------------------------------------------- /build/publish-docs.sh: -------------------------------------------------------------------------------- 1 | mv storybook-static/ docs/_book/demos/ 2 | cd docs/_book 3 | git init 4 | git add -A 5 | git commit -m 'update docs' 6 | git push -f git@github.com:streamich/libreact.git master:gh-pages 7 | -------------------------------------------------------------------------------- /docs/LANGS.md: -------------------------------------------------------------------------------- 1 | # Languages 2 | 3 | * [English](en/) 4 | -------------------------------------------------------------------------------- /docs/assets/libreact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamich/libreact/384ea447f19441f7b606a9dbd457c2729e59aa51/docs/assets/libreact.png -------------------------------------------------------------------------------- /docs/book.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "libreact", 3 | "gitbook": ">3.0.0", 4 | "plugins": ["edit-link", "theme-libreact", "-fontsettings", "github"], 5 | "pluginsConfig": { 6 | "edit-link": { 7 | "base": "https://github.com/MailOnline/libreact/edit/master/docs", 8 | "label": "Edit This Page" 9 | }, 10 | "github": { 11 | "url": "https://github.com/MailOnline/libreact/" 12 | } 13 | }, 14 | "links": { 15 | "sharing": { 16 | "facebook": false, 17 | "twitter": false 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/en/AfterDraf.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Renders its children waiting twice for [`requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). 4 | Useful to improving perceived performance of rendering. 5 | 6 | 7 | ## Usage 8 | 9 | ```jsx 10 | import {AfterDraf} from 'libreact/lib/AfterDraf'; 11 | 12 | 13 | Hello world! 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/en/AfterTimeout.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Renders its children only after a specified timeout. Useful to 4 | improving perceived performance by not blocking the main event loop. 5 | 6 | 7 | ## Usage 8 | 9 | ```jsx 10 | import {AfterTimeout} from 'libreact/lib/AfterTimeout'; 11 | 12 | 13 | Hello world! 14 | 15 | ``` 16 | 17 | 18 | ## Props 19 | 20 | - `ms` — optional, number, time in milliseconds after which to render children, defaults to `200`. 21 | -------------------------------------------------------------------------------- /docs/en/Alert.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Uses [`Window.alert()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert) to display message to a user. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {Alert} from 'libreact/lib/Alert'; 9 | 10 | 11 | ``` 12 | 13 | ## Props 14 | 15 | - `show` - boolean, optional, whether to show the alert. 16 | - `text` - string, require, string message to display to the user. 17 | -------------------------------------------------------------------------------- /docs/en/Animation.md: -------------------------------------------------------------------------------- 1 | # Animation 2 | 3 | Below primitives are available to do basic animation in JavaScript. 4 | 5 | - [``](./AfterTimeout.md) — renders children after a timeout. 6 | - [``](./AfterDraf.md) — renders children after double `requestAnimationFrame`. 7 | - [``](./WhenIdle.md) — renders children on browser idle time. 8 | - [``](./Render.md) — re-renders children on every `requestAnimationFrame`. 9 | - [``](./RenderInterval.md) — re-renders children at a specified frame rate per second. 10 | - [``](./Tween.md) — applies an easing function to animation duration value from ``. 11 | - [``](./Interpolation.md) — interpolates a map of values using a ``. 12 | -------------------------------------------------------------------------------- /docs/en/Boundaries.md: -------------------------------------------------------------------------------- 1 | # Boundaries 2 | 3 | Boundaries are components that split the element tree — its ancestors and children — 4 | according to some semantic meaning. 5 | 6 | - [``](./BrowserOnly.md) — renders children only in browser. 7 | - [``](./ServerOnly.md) — renders children only on server. 8 | - [``](./ElectronOnly.md) — renders children only in an Electron app. 9 | - [``](./ErrorBoundary.md) — reports errors in its children. -------------------------------------------------------------------------------- /docs/en/BrowserOnly.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Component that renders its children only if used in browser environment. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {BrowserOnly} from 'libreact/lib/BrowserOnly'; 9 | 10 | 11 |
12 | You can see this only in browser. 13 |
14 |
15 | ``` 16 | -------------------------------------------------------------------------------- /docs/en/ClassNames.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Side-effect component that when mounted sets class names on a given DOM element 4 | and removes them, when it is being unmounted. 5 | 6 | 7 | ## Usage 8 | 9 | ```jsx 10 | import {ClassNames} from 'libreact/lib/ClassNames'; 11 | 12 | 13 | ``` 14 | 15 | 16 | ## Props 17 | 18 | - `list` — required, array of string, list of class names to set. 19 | - `el` — optional, DOM element on which to set the class names, defaults to `document.body`. 20 | - `persist` — optional, boolean, whether to keep the class names when component un-mounts, defaults to `false`. 21 | -------------------------------------------------------------------------------- /docs/en/Context.md: -------------------------------------------------------------------------------- 1 | # Context 2 | 3 | `libreact` provides a range of component's that use React's context API and components that allow you 4 | to easily use React's context. 5 | 6 | - [``](./Provider.md) and [``](./Provider.md#consumer) — utilities to work with React's context. 7 | - [``](./theme.md#theme) and [``](./theme.md#themed) — theme provider and consumer. 8 | - [``](./cssvars.md) — use CSS variables today. 9 | - [``](./docs/route.md#router), [``](./docs/route.md#route), and `` — best router for React. 10 | - [``](./docs/translate.md#translations) and [``](./docs/translate.md#translate-or-t) — translation provider and consumer. 11 | -------------------------------------------------------------------------------- /docs/en/Dimmable.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Dims element similar to [``](./Dimmer.md), with additional features: 4 | 5 | - Works on server side. 6 | - Sets `aria-hidden="true"`, if element is dimmed. 7 | - Sets `pointer-events: none`, if element is dimmed. 8 | - Blurs dimmed element. 9 | 10 | 11 | ## Usage 12 | 13 | ```jsx 14 | import {Dimmable} from 'libreact/lib/Dimmable'; 15 | 16 | 'Overlay...'}> 17 |
18 | Inline... 19 |
20 |
21 | ``` 22 | 23 | 24 | ## Props 25 | 26 | Accepts all [`` props](./Dimmer.md#props) (except `hidden`, use `dim` instead) in addition to its own: 27 | 28 | - `dim` — optional, boolean, whether to show dim overlay, defaults to `false`. 29 | - `blur` — optional, number, blur intensity in `px` when dimmed, defaults to `5`. 30 | - `renderOverlay` — optional, function, returns contents to render in overlay when element is dimmed. 31 | Receives a single argument — boolean, whether element is dimmed. 32 | -------------------------------------------------------------------------------- /docs/en/Dimmer.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Dims or create an overlay over its parent element. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {Dimmer} from 'libreact/lib/Dimmer'; 10 | 11 |
12 | Inline... 13 | 14 | Children... 15 | 16 |
17 | ``` 18 | 19 | 20 | ## Props 21 | 22 | - `color` — optional, string, overlay color, defaults to `rgba(0,0,0,0.5)`. 23 | - `ms` — optional, number, background color appearance animation time in milliseconds, defaults to `300`. 24 | - `hidden` — optional, boolean, whther to hide dim overlay. 25 | -------------------------------------------------------------------------------- /docs/en/DropArea.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Catches file and text drop and paste events. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {DropArea} from 'libreact/lib/DropArea'; 10 | 11 | console.log(files)} 13 | onUri={(uri, event) => console.log(uri)} 14 | onText={(text, event) => console.log(text)} 15 | > 16 |
17 | Drop it like a bomb! 18 |
19 |
20 | ``` 21 | 22 | 23 | ## Props 24 | 25 | - `onFiles` — called when files are dropped or pasted into the area. 26 | - `onUri` — called an URI from another tab is dropped in the area. 27 | - `onText` — called when text is paste in the area. 28 | -------------------------------------------------------------------------------- /docs/en/Dummies.md: -------------------------------------------------------------------------------- 1 | # Dummies 2 | 3 | Dummies are empty *"shell"* components that don't contain the actual implementation. 4 | However, those dummies can be used as real React components and they will re-render 5 | automatically once they get implemented. 6 | 7 | Essentially you can create dummies, which will not add any size to your bundle, use them, 8 | but implement them with real components only later when necessary. 9 | 10 | - [`mock()`](./mock.md) - dummy that can be implemented using `.implement()` method. 11 | - [`loadable()`](./loadable.md) - dummy that can be loaded using `.load()` method. 12 | - [`lazy()`](./lazy.md) - like `loadable()`, but also loads automatically when rendered for the first time. 13 | - [`delayed()`](./delayed.md) - like `lazy()`, but its loading can be delayed. 14 | -------------------------------------------------------------------------------- /docs/en/ElectronOnly.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Component that renders its children only if used in an Electron app. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {ElectronOnly} from 'libreact/lib/ElectronOnly'; 9 | 10 | 11 |
12 | You can see this only if Electron app. 13 |
14 |
15 | ``` 16 | -------------------------------------------------------------------------------- /docs/en/ExitSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Allows you to show an "exit" animation when un-mounting your component. 4 | 5 | An `exiting` prop with value `true` will be injected in your component when it is being unmounted. 6 | 7 | `ExitSensor` expects a single child component with `key` prop specified. The `key` 8 | prop is used to identify components. 9 | 10 | 11 | ## Usage 12 | 13 | ```jsx 14 | import {ExitSensor} from 'libreact/lib/ExitSensor'; 15 | 16 | 17 | {isRectangle ? : } 18 | 19 | ``` 20 | 21 | , where 22 | 23 | ```jsx 24 | const Rectangle = ({exiting}) => { 25 | return ( 26 |
33 | ); 34 | }; 35 | ``` 36 | 37 | 38 | ## Props 39 | 40 | - `time` — number, optional, time in milliseconds how long to still show the old 41 | component after it has been replaced by a new one. Defaults to `200`. 42 | -------------------------------------------------------------------------------- /docs/en/FullScreen.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Displays child elements full-screen. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {FullScreen} from 'libreact/lib/FullScreen'; 9 | 10 | 11 | Hello world! 12 | 13 | ``` 14 | 15 | ## Props 16 | 17 | Props have the following signature 18 | 19 | ```ts 20 | interface IFullScreenProps { 21 | on: boolean; 22 | video?: HTMLVideoElement; 23 | innerRef?: (el) => void; 24 | onClose?: () => void; 25 | tag?: keyof React.ReactHTML; 26 | } 27 | ``` 28 | 29 | , where 30 | 31 | - `on` - required, boolean, whether to display element in full screen or inline. 32 | - `video` - optional, DOM video element to try to display that video in full screen using alternative `.webkitEnterFullscreen()` 33 | available only on `HTMLVideoElement`. That way video element will get displayed full screen. Only used when regular full screen 34 | API is not available. 35 | - `innerRef` - optional, callback that receives the root element reference. 36 | - `onClose` - optional, callback, called when full screen is exited. 37 | - `tag` - optional, string, specifying tag name to use for root element, defaults to `div`. 38 | -------------------------------------------------------------------------------- /docs/en/GeoLocationSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Render-prop/FaCC that re-renders on device location change, uses [Geolocation](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation) API. 4 | 5 | 6 | ### Props 7 | 8 | None. 9 | 10 | 11 | ### Example 12 | 13 | ```jsx 14 | import {GeoLocationSensor} from 'libreact/lib/GeoLocationSensor'; 15 | 16 | {(state) => 17 | JSON.strinfigy(state, null 4) 18 | } 19 | ``` 20 | 21 | Result 22 | 23 | ```json 24 | { 25 | "accuracy": 86, 26 | "altitude": null, 27 | "altitudeAccuracy": null, 28 | "heading": null, 29 | "latitude": 54.4, 30 | "longitude": -0.3, 31 | "speed": null, 32 | "timestamp": 1517137742896 33 | } 34 | ``` 35 | 36 | ## `withGeoLocation()` HOC 37 | 38 | HOC that merges `geoLocation` prop into enhanced component's props. 39 | 40 | ```jsx 41 | import {withGeoLocation} from 'libreact/lib/GeoLocationSensor'; 42 | ``` 43 | 44 | 45 | ## `@withGeoLocation` decorator 46 | 47 | React stateful component decorator that adds `geoLocation` prop. 48 | 49 | ```js 50 | import {withGeoLocation} from 'libreact/lib/GeoLocationSensor'; 51 | 52 | @withGeoLocation 53 | class MyComp extends Component { 54 | 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/en/Group.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Inserts separators between its children. 4 | 5 | 6 | ## Usage 7 | 8 | Import: 9 | 10 | ```jsx 11 | import {Group} from 'libreact/lib/Group'; 12 | ``` 13 | 14 | Default separator is a space `" "`. 15 | 16 | ```jsx 17 | 18 | Hello 19 | world 20 | 21 | ``` 22 | 23 | Result: 24 | 25 | ```html 26 |
27 | Hello world 28 |
29 | ``` 30 | 31 | Use custom separator. 32 | 33 | ```jsx 34 | }> 35 | Hello 36 | world 37 | 38 | ``` 39 | 40 | Use `as` prop to specify wrapper element tag name. 41 | 42 | ```jsx 43 | 44 | Hello 45 | world 46 | 47 | ``` 48 | 49 | Pass through any props to the wrapper element. 50 | 51 | ```jsx 52 | 53 | Hello 54 | world 55 | 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/en/Img.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | A wrapper around `` element that accepts all `` props and adds these additional ones: 4 | 5 | - `renderLoad(img, props)` — render prop used to render content while image is being loaded 6 | - `renderError(img, props)` — render prop used to render if image fails to load 7 | - `$ref` — passed as `ref` to the underlying `` 8 | 9 | 10 | ## Usage 11 | 12 | ```jsx 13 | import {Img} from 'libreact/lib/Img'; 14 | 15 |
Loading...{img}
} 18 | renderError={() =>
Error happened
} 19 | /> 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/en/InfiniteScroll.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Fires `loadMore` prop when end of content becomes visible. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {InfiniteScroll} from 'libreact/lib/InfiniteScroll'; 10 | 11 | {/* ... */}} 15 | > 16 | {items} 17 | 18 | ``` 19 | 20 | 21 | ## Props 22 | 23 | - `loadMore` — required, function that is called when user scrolls to the bottom of the component. 24 | - `cursor` — required, unique identifier of current page, `loadMore` is called only once for each adjacent unique value of `cursor`. 25 | - `hasMore` — optional, boolean, whether there are more items to load, if set to `false`, `loadMore` will not be called. 26 | - `sentinel` — optional, React element to render at the bottom of the component, when this element becomes visible it triggers `loadMore` function, defaults to empty `
` pixel. 27 | - `margin` — optional, number, invisible margin before `sentinel` when to already call `loadMore` before `sentinel` is visible, defaults to `100`. 28 | -------------------------------------------------------------------------------- /docs/en/InlineWidthQuery.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Use it for responsive design, renders only the first child that matches query against 4 | the current available width of this element. 5 | 6 | It is similar to [``](./WindowWidthQuery.md), but instead of using 7 | the browser window width, it detects the available inline width of the current element. 8 | 9 | 10 | ## Usage 11 | 12 | ```jsx 13 | import {InlineWidthQuery} from 'libreact/lib/InlineWidthQuery'; 14 | import {View} from 'libreact/lib/View'; 15 | 16 | 17 | 18 | Up to 300px 19 | 20 | 21 | More than 300px 22 | 23 | 24 | ``` 25 | 26 | `` is simply a shortcut for 27 | 28 | ```jsx 29 | {({width}) => 30 |
31 | 32 | {/* Your queries here... */} 33 | 34 |
35 | }
36 | ``` 37 | -------------------------------------------------------------------------------- /docs/en/Inversion.md: -------------------------------------------------------------------------------- 1 | # Inversion 2 | 3 | > __*inversion*__ 4 | > 5 | > the action of inverting something or the state of being inverted. 6 | 7 | Inversion is a technique where one injects state and/or life-cycle methods into a render prop. 8 | 9 | This effectively allows you to have 10 | 11 | 1. State 12 | 2. Life-cycle methods 13 | 3. Refs to DOM elements 14 | 4. Other objects 15 | 16 | in JSX tree without having to write stateful React components. 17 | 18 | - [``](./State.md) — inverts `.state` and `.setState()` method. 19 | - [``](./Toggle.md), [``](./Flipflop.md), [``](./Value.md), [``](./Counter.md), [``](./List.md), and [``](./Map.md) 20 | - [``](./ShouldUpdate.md) — inverts `.shouldComponentUpdate()` life-cycle method. 21 | - [`invert()`](./invert.md) and [``](./invert.md#inverted) — inverts DOM element `ref` reference. 22 | 23 | 24 | ## Example 25 | 26 | A basic example, `cnt` is incremented by 1 on each click 27 | 28 | ```jsx 29 | 30 | {({cnt}, set) => 31 |
set({cnt: cnt + 1})}> 32 | {cnt} 33 |
34 | } 35 | 36 | ``` 37 | 38 | or the same using `` component 39 | 40 | ```jsx 41 | {({value, inc}) => 42 |
43 | {value} 44 |
45 | }
46 | ``` 47 | -------------------------------------------------------------------------------- /docs/en/LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /docs/en/Lifecycles.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Inverts life-cycle methods. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {Lifecycles} from 'libreact/lib/Lifecycles'; 10 | 11 | console.log('Component did mount!')}> 12 | Hello world! 13 | 14 | ``` 15 | 16 | 17 | ## Props 18 | 19 | Signature. 20 | 21 | ```ts 22 | interface ILifecyclesProps { 23 | willMount?: (props) => void; 24 | didMount?: (props) => void; 25 | willReceiveProps?: (nextProps, props) => void; 26 | shouldUpdate?: (nextProps, props) => boolean; 27 | willUpdate?: (nextProps, props) => void; 28 | didUpdate?: (props, prevProps) => void; 29 | willUnmount?: (props) => void; 30 | didCatch?: (error, info, props) => void; 31 | [key: string]: any; 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/en/LightSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Ues [`AmbientLightSensor`](https://developer.mozilla.org/en-US/docs/Web/API/AmbientLightSensor) API 4 | to privide lightning data. 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {LightSensor} from 'libreact/lib/LightSensor'; 10 | 11 | {(light) => 12 | JSON.stringify(light, null, 4) 13 | } 14 | ``` 15 | 16 | 17 | ## `withLight()` HOC 18 | 19 | Higher order component that injects `illumination` prop into your component. 20 | 21 | ```js 22 | import {withLight} from 'libreact/lib/LightSensor'; 23 | 24 | const MyCompWithLight = withLight(MyComp); 25 | ``` 26 | 27 | 28 | ## `@withLight` HOC 29 | 30 | Class decorator that injects `illumination` prop into your component. 31 | 32 | ```js 33 | import {withLight} from 'libreact/lib/LightSensor'; 34 | 35 | @withLight 36 | class MyComp extends Component { 37 | 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/en/ListTable.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Wraps `children` into a table with specified column number. 4 | 5 | ## Usage 6 | 7 | Below renders 2x3 table. 8 | 9 | ```jsx 10 | import {ListTable} from 'libreact/lib/ListTable'; 11 | 12 | 13 |
1
14 |
2
15 |
3
16 |
4
17 |
5
18 |
6
19 |
20 | ``` 21 | 22 | 23 | ## Props 24 | 25 | - `cols` — optional, number or columns table should have, defaults to `3`. 26 | - `renderRow` — optional, function that receives an array of `` cells as React 27 | elements and should return a `` React element, defaults to `cells => h('tr', null, ...cells)`. 28 | -------------------------------------------------------------------------------- /docs/en/Mailto.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Creates `` element with `href` attribute formatted to send an e-mail. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {Mailto} from 'libreact/lib/Mailto'; 9 | 10 | 11 | Click me! 12 | 13 | ``` 14 | 15 | ### Props 16 | 17 | Signature 18 | 19 | ```ts 20 | interface IMailtoProps { 21 | email: string; 22 | subject?: string; 23 | cc?: string[]; 24 | bcc?: string[]; 25 | body?: string; 26 | } 27 | ``` 28 | 29 | , where 30 | 31 | - `email` - required, string, e-mail address. 32 | - `subject` - optional, string. 33 | - `cc` - optional, array of strings, representing e-mail addresses to CC. 34 | - `bcc` - optional, array of strings, representing e-mail addresses to BCC. 35 | - `body` - optional, string, e-mail body message. 36 | -------------------------------------------------------------------------------- /docs/en/MediaDeviceSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Uses [`MediaDevices`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices) to track 4 | user's connected media devices. 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {MediaDeviceSensor} from 'libreact/lib/MediaDeviceSensor'; 10 | 11 | {(state) => 12 | JSON.stringify(state, null, 4) 13 | } 14 | ``` 15 | 16 | 17 | ## `withMediaDevices()` 18 | 19 | Higher order component that injects `devices` prop into your component. 20 | 21 | ```js 22 | import {withMediaDevices} from 'libreact/lib/MediaDeviceSensor'; 23 | 24 | const MyCompDevices = withMediaDevices(MyComp); 25 | ``` 26 | 27 | 28 | ## `@withMediaDevices` 29 | 30 | Stateful component class decorator that injects `devices` prop into your component. 31 | 32 | ```js 33 | import {withMediaDevices} from 'libreact/lib/MediaDeviceSensor'; 34 | 35 | @withMediaDevices 36 | class MyComp extends Component { 37 | 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/en/MediaSensor.md: -------------------------------------------------------------------------------- 1 | # `MediaSensor` 2 | 3 | FaCC that re-renders on [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries) changes. 4 | 5 | ## Example 6 | 7 | ```jsx 8 | import {MediaSensor} from 'libreact/lib/MediaSensor'; 9 | 10 | {({matches}) => 11 | `WIDTH IS GREATED THAN 480PX: ${matches}` 12 | } 13 | ``` 14 | 15 | 16 | ## `withMedia()` 17 | 18 | Higher order component that injects a `media` prop into your component that specifies if media query matches. 19 | 20 | ```js 21 | import {withMedia} from 'libreact/lib/MediaSensor'; 22 | 23 | const MyCompWithSize = withMedia(MyComp, 'isBigScreen', '(min-width: 480px)'); 24 | ``` 25 | 26 | 27 | ## `@withMedia` 28 | 29 | Stateful component class decorator that injects a boolean prop into your component that specifies if media query matches. 30 | 31 | ```js 32 | import {withMedia} from 'libreact/lib/MediaSensor'; 33 | 34 | @withMedia('isBigScreen', '(min-width: 480px)') 35 | class MyComp extends Component { 36 | 37 | } 38 | ``` -------------------------------------------------------------------------------- /docs/en/MotionSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Render-prop/FaCC that re-renders on device motion. 4 | 5 | 6 | ### Props 7 | 8 | None. 9 | 10 | 11 | ### Example 12 | 13 | ```jsx 14 | import {MotionSensor} from 'libreact/lib/MotionSensor'; 15 | 16 | {(state) => 17 | JSON.strinfigy(state, null 4) 18 | } 19 | ``` 20 | 21 | Result 22 | 23 | ```json 24 | { 25 | "acceleration": { 26 | "x": null, 27 | "y": null, 28 | "z": null 29 | }, 30 | "accelerationIncludingGravity": { 31 | "x": null, 32 | "y": null, 33 | "z": null 34 | }, 35 | "rotationRate": { 36 | "alpha": null, 37 | "beta": null, 38 | "gamma": null 39 | }, 40 | "interval": 16 41 | } 42 | ``` 43 | 44 | ## `withMotion()` HOC 45 | 46 | HOC that merges `motion` prop into enhanced component's props. 47 | 48 | ```jsx 49 | import {withMotion} from 'libreact/lib/MotionSensor'; 50 | ``` 51 | 52 | 53 | ## `@withMotion` decorator 54 | 55 | React stateful component decorator that adds `motion` prop. 56 | 57 | ```js 58 | import {withMotion} from 'libreact/lib/MotionSensor'; 59 | 60 | @withMotion 61 | class MyComp extends Component { 62 | 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/en/OrientationSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Tracks screen orientation using [`orientationchange` event](https://developer.mozilla.org/en-US/docs/Web/Events/orientationchange). 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {OrientationSensor} from 'libreact/lib/OrientationSensor'; 9 | 10 | (state) => 11 |
{JSON.stringify(state, null, 4)}
12 |
13 | ``` 14 | 15 | ## Props 16 | 17 | None. 18 | 19 | ## State 20 | 21 | Has signature 22 | 23 | ```ts 24 | interface IOrientationSensorState { 25 | angle: number; 26 | type: string; 27 | } 28 | ``` 29 | 30 | , where 31 | 32 | - `angle` - screen rotation angle in degrees. 33 | - `type` - is one of `portrait-primary`, `portrait-secondary`, `landscape-primary`, or `landscape-secondary`. 34 | 35 | 36 | ## `withOrientation()` HOC 37 | 38 | HOC that merges `orientation` prop into enhanced component's props. 39 | 40 | ```jsx 41 | import {withOrientation} from 'libreact/lib/OrientationSensor'; 42 | ``` 43 | 44 | 45 | ## `@withOrientation` decorator 46 | 47 | React stateful component decorator that adds `orientation` prop. 48 | 49 | ```js 50 | import {withOrientation} from 'libreact/lib/OrientationSensor'; 51 | 52 | @withOrientation 53 | class MyComp extends Component { 54 | 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/en/Other.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamich/libreact/384ea447f19441f7b606a9dbd457c2729e59aa51/docs/en/Other.md -------------------------------------------------------------------------------- /docs/en/OutsideClick.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Tracks clicks outside its `children` or `clickRoots()`. 4 | 5 | 6 | ## Usage 7 | 8 | ### Default 9 | 10 | ```jsx 11 | import {OutsideClick} from 'libreact/lib/OutsideClick'; 12 | 13 | 14 |
Don't click here.
15 |
16 | ``` 17 | 18 | ### Using a Portal 19 | 20 | ```jsx 21 | import {OutsideClick} from 'libreact/lib/OutsideClick'; 22 | 23 | ([ 24 | document.getElementById('my-portal-root') 25 | ])}> 26 |
Click anywhere outside `#my-portal-root` and my `onClick` will fire.
27 |
28 | ``` 29 | 30 | 31 | ## Props 32 | 33 | - `onClick` — event called when user click outside of its children. 34 | - `event` — optional, string, event name subscribe to, defaults to `mousedown`. 35 | - `clickRoots` — optional, function that should return an array of DOM nodes. These should be considered where the user clicks inside, i.e. clicking outside of these roots will fire `onClick`. 36 | -------------------------------------------------------------------------------- /docs/en/Overlay.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Creates overlay over whole page. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {Overlay} from 'libreact/lib/Overlay'; 10 | 11 | 12 | This is rendered over the whole page. 13 | 14 | ``` 15 | 16 | 17 | ## Props 18 | 19 | - `color` — optional, string, overlay color, defaults to `rgba(0, 0, 0, 0.5)`. 20 | - `time` — optional, number, entrance opacity animation length in milliseconds, defaults to `300`. 21 | - `onElement` — optional, callback that receives the DOM element overlay created. 22 | - `onClick` — optional, callback, which is called when user click on overlay but not on its children. 23 | -------------------------------------------------------------------------------- /docs/en/Portal.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Uses [React Portals API](https://reactjs.org/docs/portals.html) to render its children in `document.body`. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {Portal} from 'libreact/lib/Portal'; 9 | 10 | 11 | This will appear in a new node in document.body. 12 | 13 | ``` 14 | 15 | ## Props 16 | 17 | - `el` — optional, DOM element, where to render children. If not provided, a new `
` element is created and 18 | appended to `document.body`. 19 | -------------------------------------------------------------------------------- /docs/en/Prompt.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Uses [`Window.prompt()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) to get user input. 4 | 5 | ## Usage 6 | 7 | Use it as a standalone compnent that returns result in `onResult` handler 8 | 9 | ```jsx 10 | import {Prompt} from 'libreact/lib/Prompt'; 11 | 12 | 18 | ``` 19 | 20 | Or use it as a FaCC 21 | 22 | ```jsx 23 | {(result) =>
{result}
}
29 | ``` 30 | 31 | ## Props 32 | 33 | - `show` - boolean, optional, whether to show the prompt modal. 34 | - `message` - string, optional, string message to display to the user. 35 | - `default` - string, optional, default text to pre-fill the user's response input. 36 | - `onResult` - function, optional, function that receives prompt result string as a single argument. 37 | -------------------------------------------------------------------------------- /docs/en/Render.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Re-renders its children on every `requestAnimationFrame` for a specified 4 | interval of time. Keeps track of a normalized duration value `[0...1]`, which 5 | is passed to children and can be used for animation. 6 | 7 | 8 | ## Usage 9 | 10 | Below example re-renders its children on every `requestAnimationFrame` for 1 second. 11 | 12 | ```jsx 13 | import {Render} from 'libreact/lib/Render'; 14 | 15 | {({value}) => 16 |
Value: {value}
17 | }
18 | ``` 19 | 20 | 21 | ## Props 22 | 23 | - `ms` — optional, number, time in milliseconds how long to re-render its children, defaults to `300`. 24 | 25 | 26 | ## `withRender` HOC 27 | 28 | Enhancer that injects `render` prop into your component. 29 | 30 | 31 | ## `@withRender` decorator 32 | 33 | Stateful component decorator that injects `render` prop into your component. 34 | -------------------------------------------------------------------------------- /docs/en/RenderInterval.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Same as [``](./Render.md), but uses `setTimeout` to re-render its children 4 | at a specified frame rate. 5 | 6 | 7 | ## Usage 8 | 9 | ```jsx 10 | import {RenderInterval} from 'libreact/lib/RenderInterval'; 11 | 12 | {({value}) => 13 |
Value: {value}
14 | }
15 | ``` 16 | 17 | 18 | ## Props 19 | 20 | - `ms` — optional, number, time in milliseconds how long to re-render its children, defaults to `300`. 21 | - `fps` — optional, number, target frames per second, defaults to `30`. 22 | 23 | 24 | ## `withRenderInterval` HOC 25 | 26 | Enhancer that injects `renderInterval` prop into your component. 27 | 28 | 29 | ## `@withRenderInterval` decorator 30 | 31 | Stateful component decorator that injects `renderInterval` prop into your component. 32 | -------------------------------------------------------------------------------- /docs/en/Resolve.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | FaCC that resolves a promise. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {Resolve} from 'libreact/lib/Resolve'; 9 | 10 | {({pending, value, error}) => 11 | pending ? null : JSON.stringify(value) 12 | } 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/en/Ripple.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Creates Material Design ripple effect on click inside your element. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {Ripple} from 'libreact/lib/Ripple'; 10 | 11 | 12 |
16 | foobar 17 |
18 |
19 | ``` 20 | 21 | 22 | ## Props 23 | 24 | - `color` — optional, string, ripple color. 25 | - `ms` — optional, number, animation time in milliseconds. 26 | 27 | 28 | ## `withRipple()` 29 | 30 | A higher order component that transforms plain DOM type elements into ones with ripple effects. 31 | 32 | ```js 33 | import {withRipple} from 'libreact/lib/Ripple'; 34 | 35 | const DivWithRipple = withRipple('div'); 36 | const ButtonWithRipple = withRipple('button', {ms: 1000, color: red}); 37 | ``` 38 | 39 | As an optional second arguments accepts props to provide to `` component. 40 | -------------------------------------------------------------------------------- /docs/en/ScrollSensor.md: -------------------------------------------------------------------------------- 1 | # `ScrollSensor` 2 | 3 | FaCC that re-renders on when scroll position in a DOM element changes. 4 | 5 | ## Props 6 | 7 | - `el` - HTMLElement whose `scrollTop` and `scrollLeft` to track. 8 | 9 | ## Example 10 | 11 | ```jsx 12 | import {ScrollSensor} from 'libreact/lib/ScrollSensor'; 13 | 14 | {({x, y}) => 15 | `x: ${x}, y: ${y}` 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/en/ServerOnly.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Component that renders its children only if used on a Node.js server. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {ServerOnly} from 'libreact/lib/ServerOnly'; 9 | 10 | 11 |
12 | You can see this only on server. 13 |
14 |
15 | ``` 16 | -------------------------------------------------------------------------------- /docs/en/ShouldUpdate.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Inverts `.shouldComponentUpdate()` life-cycle method. Allows you to use specify whether its children should update based 4 | on passed in props. 5 | 6 | 7 | ## Usage 8 | 9 | The below example will always re-render on new `data`, because `when` method always returns `true`. 10 | 11 | ```jsx 12 | import {ShouldUpdate} from 'libreact/lib/ShouldUpdate'; 13 | 14 | true} props={data}>{(props) => 15 |
Hello world!
16 | }
17 | ``` 18 | 19 | 20 | ## Props 21 | 22 | - `when` — required, function, return boolean whether its children should re-render. Receives new and old props as two arguments. 23 | - `props` — required, object, props to pass to children. 24 | 25 | 26 | ## `shouldUpdate()` HOC 27 | 28 | Higher order component that re-renders only when a condition is met. 29 | 30 | ```jsx 31 | import {shouldUpdate} from 'libreact/lib/ShouldUpdate'; 32 | 33 | const PrintOver3 = shouldUpdate((props) => props.cnt > 3)(Print); 34 | ``` 35 | 36 | The above example creates a `PrintOver3` component that will re-render only when 37 | `cnt` prop is greater than 3. 38 | 39 | 40 | See also [`pure()`](./pure.md). 41 | -------------------------------------------------------------------------------- /docs/en/Side-effects.md: -------------------------------------------------------------------------------- 1 | # Side Effects 2 | 3 | Components with side effects are ones that use global environment APIs to modify environment state. 4 | 5 | - [``](./LocalStorage.md) — saves and retrieves objects from `localStorage`. 6 | 7 | -------------------------------------------------------------------------------- /docs/en/Sms.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Creates `
` element with `href` attribute formatted according to SMS protocol. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {Sms} from 'libreact/lib/Sms'; 9 | 10 | Click me! 11 | ``` 12 | 13 | ### Props 14 | 15 | ```ts 16 | interface ISmsProps { 17 | phone: string; 18 | body?: string; 19 | } 20 | ``` 21 | 22 | , where 23 | 24 | - `phone` - required, string, phone number. 25 | - `body` - optional, string, SMS text body. 26 | -------------------------------------------------------------------------------- /docs/en/Speak.md: -------------------------------------------------------------------------------- 1 | # Speak 2 | 3 | Read out loud provided text. 4 | 5 | # Props 6 | 7 | - `text` - string of text to read. 8 | 9 | # Example 10 | 11 | ```jsx 12 | import {Speak} from 'libreact/lib/Speak'; 13 | 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/en/State.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Injects state into a stateless component. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {State} from 'libreact/lib/State'; 10 | 11 | {(state, set) => 12 | 13 | } 14 | ``` 15 | 16 | Render prop receives two arguments: (1) state of the component; and (2) the `setState` function. 17 | 18 | 19 | ## Props 20 | 21 | - `init` - optional, object, initial state. 22 | 23 | 24 | ## Example 25 | 26 | Create a counter 27 | 28 | ```jsx 29 | {({cnt}, set) => 30 |
set({cnt: cnt + 1})}> 31 | {cnt} 32 |
33 | }
34 | ``` 35 | 36 | 37 | ## `withState()` HOC 38 | 39 | HOC that merges `state` prop into enhanced component's props. Your component will receive 40 | the state object with merged in `set()` method that you can use to update your state. 41 | 42 | ```jsx 43 | import {withState} from 'libreact/lib/State'; 44 | 45 | const MyCompWithState = withState(MyComp); 46 | ``` 47 | 48 | You can overwrite the injected prop name 49 | 50 | ```js 51 | const MyCompWithState = withState(MyComp, 'foobar'); 52 | ``` 53 | 54 | Or simply merge the whole object into your props 55 | 56 | ```js 57 | const MyCompWithState = withState(MyComp, ''); 58 | ``` 59 | 60 | Specify default state 61 | 62 | ```js 63 | const MyCompWithState = withState(MyComp, '', {counter: 0}); 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/en/TouchSupportSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | [![React Universal Interface](https://img.shields.io/badge/React-Universal%20Interface-green.svg)](https://github.com/streamich/react-universal-interface) 4 | 5 | Render prop that detects if touch interactions are available or not. 6 | It's important to remember that touch detection can be [fallible](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). 7 | 8 | ## Example 9 | 10 | Use it as FaCC, attach to root element 11 | 12 | ```jsx 13 | import {TouchSupportSensor} from 'libreact/lib/TouchSupportSensor'; 14 | 15 | {({touchSupported}) => 16 |
{touchSupported ? 'touch interactions available' : 'touch interactions not available'}
17 | }
18 | ``` 19 | 20 | Use it as a plain function 21 | 22 | ```js 23 | import { touchSupported } from 'libreact/lib/TouchSupportSensor'; 24 | 25 | console.log(touchSupported()) 26 | ``` 27 | 28 | 29 | ## Props 30 | 31 | Prop signature 32 | 33 | ```ts 34 | interface ITouchSupportSensorProps { 35 | onlyMouse?: boolean; 36 | onlyTouch?: boolean; 37 | } 38 | ``` 39 | 40 | , where 41 | 42 | - `onlyMouse` - optional, boolean, will only render children if touch support is not detected. 43 | - `onlyTouch` - optional, boolean, will only render children if touch support is detected. 44 | -------------------------------------------------------------------------------- /docs/en/Vibrate.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Use [`navigator.vibrate()`](https://developer.mozilla.org/en-US/docs/Web/API/Vibration_API) to generate 4 | device vibrations. 5 | 6 | ## Props 7 | 8 | - `ms` - time in milliseconds for how long to vibrate. Can be a single number, or an array of numbers. 9 | If array of numbers is specified, every second value is used a vibration lenght and values in between 10 | are considered pauses between the vibrations. 11 | 12 | ## Example 13 | 14 | ```jsx 15 | import {Vibrate} from 'libreact/lib/Vibrate'; 16 | 17 | 18 | 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/en/View.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | A shell components that simply renders its children using [`react-universal-interface`](https://github.com/streamich/react-universal-interface). 4 | 5 | Useful to temporary store some data in JSX props before the actual render. 6 | 7 | 8 | ## Usage 9 | 10 | ```jsx 11 | import {View} from 'libreact/lib/View'; 12 | 13 | 14 | All children are simply rendered. 15 | 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/en/WhenIdle.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Renders its children on [`requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback). 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {WhenIdle} from 'libreact/lib/WhenIdle'; 10 | 11 | 12 | Hello world! 13 | 14 | ``` 15 | 16 | 17 | ## Props 18 | 19 | - `timeout` — optional, number, `timeout` parameter to provide to `requestIdleCallback`. 20 | -------------------------------------------------------------------------------- /docs/en/WidthQuery.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Renders only one of its children that matches width query. 4 | 5 | ## Usage 6 | 7 | ```jsx 8 | import {WidthQuery} from 'libreact/lib/WidthQuery'; 9 | import {View} from 'libreact/lib/View'; 10 | 11 | 12 | 13 | This will not render. 14 | 15 | 16 | This will render! 17 | 18 | 19 | ``` 20 | 21 | 22 | ## Props 23 | 24 | - `width` — required, number, width value to match queries against. 25 | 26 | 27 | ## Children 28 | 29 | Children of `` must be an array of at least two React elements. 30 | Use `minWidth` and `maxWidth` properties to match against the width, first 31 | matched child will be rendered. You can use any react component as children 32 | or the [``](./View.md) wrapper component that is simply a shell that just 33 | renders its children. 34 | -------------------------------------------------------------------------------- /docs/en/WidthSensor.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | FaCC that works similar to [``](./SizeSensor.md) but re-renders only on width change. 4 | 5 | ## Example 6 | 7 | Use it as FaCC 8 | 9 | ```jsx 10 | import {WidthSensor} from 'libreact/lib/WidthSensor'; 11 | 12 | {({width, height}) => 13 | `WIDTH: ${width}, HEIGHT: ${height}` 14 | } 15 | ``` 16 | 17 | Or use `onWidth` prop 18 | 19 | ```jsx 20 | import {WidthSensor} from 'libreact/lib/WidthSensor'; 21 | 22 | console.log(width, height)}> 23 | Resize me! 24 | 25 | ``` 26 | 27 | 28 | ## `withWidth()` HOC and `@withWidth` decorator 29 | 30 | Works same as [`withSize()`](./SizeSensor.md#withsize-hoc) and [`@withSize`](./SizeSensor.md#withsize-decorator). 31 | 32 | Incldue `withWidth` as follows 33 | 34 | ```js 35 | import {withWidth} from 'libreact/lib/WidthSensor'; 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/en/WindowScrollSensor.md: -------------------------------------------------------------------------------- 1 | # `WindowScrollSensor` 2 | 3 | FaCC that re-renders on window scroll. 4 | 5 | ## Example 6 | 7 | ```jsx 8 | import {WindowScrollSensor} from 'libreact/lib/WindowScrollSensor'; 9 | 10 | {({x, y}) => 11 | `x: ${x}, y: ${y}` 12 | } 13 | ``` 14 | 15 | You can also use it without children. 16 | 17 | ```jsx 18 | console.log(x, y)} /> 19 | ``` 20 | 21 | 22 | ## `withWindowScroll()` HOC 23 | 24 | HOC that merges `windowScroll` prop into enhanced component's props. 25 | 26 | ```jsx 27 | import {withWindowScroll} from 'libreact/lib/WindowScrollSensor'; 28 | ``` 29 | 30 | 31 | ## `@withWindowScroll` decorator 32 | 33 | React stateful component decorator that adds `windowScroll` prop. 34 | 35 | ```js 36 | import {withWindowScroll} from 'libreact/lib/WindowScrollSensor'; 37 | 38 | @withWindowScroll 39 | class MyComp extends Component { 40 | 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/en/WindowSizeSensor.md: -------------------------------------------------------------------------------- 1 | # `WindowSizeSensor` 2 | 3 | FaCC that re-renders on window size change. 4 | 5 | ## Example 6 | 7 | ```jsx 8 | import {WindowSizeSensor} from 'libreact/lib/WindowSizeSensor'; 9 | 10 | {({width, height}) => 11 | `width: ${width}, height: ${height}` 12 | } 13 | ``` 14 | 15 | You can use it without children. 16 | 17 | ```jsx 18 | console.log(width, height)} /> 19 | ``` 20 | 21 | ## `withWindowSize()` HOC 22 | 23 | HOC that merges `windowSize` prop into enhanced component's props. 24 | 25 | ```jsx 26 | import {withWindowSize} from 'libreact/lib/WindowSizeSensor'; 27 | ``` 28 | 29 | 30 | ## `@withWindowSize` decorator 31 | 32 | React stateful component decorator that adds `windowSize` prop. 33 | 34 | ```js 35 | import {withWindowSize} from 'libreact/lib/WindowSizeSensor'; 36 | 37 | @withWindowSize 38 | class MyComp extends Component { 39 | 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/en/WindowWidthQuery.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Use it for responsive design, renders only the first child that matches query against 4 | the current window width. 5 | 6 | 7 | ## Usage 8 | 9 | ```jsx 10 | import {WindowWidthQuery} from 'libreact/lib/WindowWidthQuery'; 11 | import {View} from 'libreact/lib/View'; 12 | 13 | 14 | 15 | Up to 300px 16 | 17 | 18 | More than 300px 19 | 20 | 21 | ``` 22 | 23 | `` is simply a shortcut for 24 | 25 | ```jsx 26 | {({width}) => 27 | 28 | {/* Your queries here... */} 29 | 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/en/WindowWidthSensor.md: -------------------------------------------------------------------------------- 1 | # `WindowWidthSensor` 2 | 3 | FaCC that re-renders on window width change. 4 | 5 | ## Example 6 | 7 | ```jsx 8 | import {WindowWidthSensor} from 'libreact/lib/WindowWidthSensor'; 9 | 10 | {({width}) => 11 | `width: ${width}` 12 | } 13 | ``` 14 | 15 | You can use it without children. 16 | 17 | ```jsx 18 | console.log(width)} /> 19 | ``` 20 | 21 | ## `withWindowWidth()` HOC 22 | 23 | HOC that merges `windowWidth` prop into enhanced component's props. 24 | 25 | ```jsx 26 | import {withWindowWidth} from 'libreact/lib/WindowWidthSensor'; 27 | ``` 28 | 29 | 30 | ## `@withWindowWidth` decorator 31 | 32 | React stateful component decorator that adds `windowWidth` prop. 33 | 34 | ```js 35 | import {withWindowWidth} from 'libreact/lib/WindowWidthSensor'; 36 | 37 | @withWindowWidth 38 | class MyComp extends Component { 39 | 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/en/css/StyleSheet.md: -------------------------------------------------------------------------------- 1 | # `StyleSheet.create()` Interface 2 | 3 | `StyleSheet.create()` interface is similar to to [`rule()`](./rule.md) interface, but instead of creating 4 | a single "rule" you create a collection of rules for any logical unit you need in your component. 5 | 6 | __Example__ 7 | 8 | ```jsx 9 | import {StyleSheet} from 'libreact/lib/css'; 10 | 11 | const styles = StyleSheet.create({ 12 | container: { 13 | border: '1px solid tomato', 14 | } 15 | button: { 16 | background: 'red', 17 | borderRadius: '5px', 18 | color: '#fff', 19 | } 20 | }); 21 | 22 | class App extends Component { 23 | render () { 24 | return ( 25 |
26 |
28 | ); 29 | } 30 | } 31 | ``` 32 | 33 | This approach has a couple of advantages over the [`rule`](./rule.md) interface. Firstly, it returns 34 | an object `styles` with `button` and `link` keys you specify that will each hold a string of class 35 | names. Secondly, `StyleSheet` styles evaluate lazily — the CSS will not be inject into the page 36 | when you create the `style` object. The actual CSS will be inserted into the page when you reference 37 | a "style", like `styles.button` for the first time. 38 | -------------------------------------------------------------------------------- /docs/en/css/css.md: -------------------------------------------------------------------------------- 1 | # `@css()` Decorator Interface 2 | 3 | Allows you to style your stateful React components using a `@css()` class and `.render()` method decorators. 4 | `@css` accepts a CSS-like object or a function that returns a CSS-like object as a single argument. 5 | 6 | If function is provided as a parameters, it will receive a single argument: instance object of the component. 7 | 8 | Class decorator styles will not update when your component re-renders, use `.render()` method decorator 9 | if you want your CSS to update every time your component re-renders. 10 | 11 | ## Usage 12 | 13 | Import `@css` decorator. 14 | 15 | ```js 16 | import {css} from 'libreact/lib/css'; 17 | ``` 18 | 19 | Add styling to your component using CSS-like object. 20 | 21 | ```jsx 22 | @css({ 23 | border: '1px solid tomato' 24 | }) 25 | class App extends Component { 26 | 27 | @css(({props}) => ({ 28 | color: props.color 29 | })) 30 | render () { 31 | return
Hello world!
; 32 | } 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/en/css/jsxstyle.md: -------------------------------------------------------------------------------- 1 | # `jsxstyle()` Interfaces 2 | 3 | `jsxstyle` interface allows you to define CSS right inside your JSX nodes. 4 | 5 | ## Usage 6 | 7 | Import the `jsxstyle()` function. 8 | 9 | ```js 10 | import {jsxstyle} from 'libreact/lib/css'; 11 | ``` 12 | 13 | Now create your "building blocks". 14 | 15 | ```js 16 | const Button = jsxstyle('button', { 17 | bg: '#07f', 18 | col: '#fff', 19 | pad: '20px', 20 | mar: '5px', 21 | bd: 0 22 | }); 23 | ``` 24 | 25 | You can add extra styles to your building blocks. 26 | 27 | ```jsx 28 | 29 | 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/en/css/rule.md: -------------------------------------------------------------------------------- 1 | # `rule()` Interface 2 | 3 | Given a CSS-like object returns a string of class names. 4 | 5 | __Example__ 6 | 7 | ```js 8 | import {rule} from 'libreact/lib/css'; 9 | 10 | const classNames = rule({ 11 | color: 'blue', 12 | background: 'yellow', 13 | border: '1px solid tomato', 14 | }); 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/en/css/styled.md: -------------------------------------------------------------------------------- 1 | # `styled()()` Component Interface 2 | 3 | `styled` component syntax allows you to "attach" styles to some HTML element or React component. It is a [HOC](../Introduction.md#hoc) generator, 4 | that creates a HOC that receives a CSS-like object and returns a component that that will be styled accordingly. 5 | 6 | # Usage 7 | 8 | Import `styled()()` function. 9 | 10 | ```js 11 | import {styled} from 'libreact/lib/css'; 12 | ``` 13 | 14 | Now create a styled `` component. 15 | 16 | ```jsx 17 | const Border = styled('div')({ 18 | border: '1px solid tomato', 19 | bdrad: '3px' 20 | }); 21 | 22 | Hello world! 23 | ``` 24 | 25 | "Styled" component's styles can be a function that returns a CSS-like object. This function receives components props as an argument. 26 | 27 | ```jsx 28 | const Border = styled.div(({color = 'tomato'}) => ({ 29 | border: '1px solid ' + color, 30 | bdrad: '3px' 31 | })); 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/en/getDisplayName.md: -------------------------------------------------------------------------------- 1 | # `getDisplayName()` 2 | 3 | Returns display name of a React component. 4 | 5 | ```jsx 6 | import getDisplayName from 'libreact/lib/util/getDisplayName'; 7 | 8 | const name = getDisplayName(MyComponent); 9 | ``` 10 | 11 | Accepts a single argument, which can be a stateful or stateless React component, or element. Returns a string. 12 | -------------------------------------------------------------------------------- /docs/en/mock.md: -------------------------------------------------------------------------------- 1 | # `mock()` 2 | 3 | Create a mock React component whose implementation can be postponed. 4 | 5 | 6 | ## Example 7 | 8 | Create a mock and implement it 9 | 10 | ```js 11 | import {mock} from 'libreact/lib/mock'; 12 | 13 | const Player = mock(); 14 | 15 | // Now you can already use . 16 | 17 | 18 | // But implement it only later. 19 | Player.implement(RealPlayer); 20 | ``` 21 | 22 | Specify placeholder for the mock 23 | 24 | ```jsx 25 | const MySvg = mock({ 26 | loading: SVG is loading... 27 | }); 28 | ``` 29 | 30 | 31 | ## Reference 32 | 33 | Receives configuration object, with the following keys: 34 | 35 | - `loading` - React element or component to show while the mock is not implemented. 36 | - `.implement()` - use this method to set the implementation of your mock component. 37 | -------------------------------------------------------------------------------- /docs/en/next.md: -------------------------------------------------------------------------------- 1 | # Next 2 | 3 | Allows you to use proposed future API of React already today. 4 | 5 | - [`createRef()`](./next/createRef.md) — use [new `ref` API](https://github.com/TrySound/rfcs/blob/634a8569d875eb58cede396e4409916f03355e87/text/0017-new-create-ref.md). 6 | - [`createState()`](./next/createState.md) — use new [proposed local state management](https://gist.github.com/trueadm/35f083d32e5af93dd8fd706dae378123). 7 | - [`createLifecycleEvents()`](./next/createLifecycleEvents.md) — use new [proposed life-cycle method API](https://gist.github.com/trueadm/35f083d32e5af93dd8fd706dae378123). 8 | -------------------------------------------------------------------------------- /docs/en/next/createLifecycleEvents.md: -------------------------------------------------------------------------------- 1 | # `createLifecycleEvents()` 2 | 3 | Create life-cycles for component tree. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {createLifecycleEvents} from 'libreact/lib/shim'; 10 | 11 | const Lifecycles = createLifecycleEvents({ 12 | didMount: ({foo}) => { 13 | console.log('Tree did mount!'); 14 | console.log(`"foo" is "${foo}.`); 15 | }, 16 | willUnmount: () => { 17 | console.log('Tree will un-mount!'); 18 | }, 19 | }); 20 | 21 | 22 |
23 | Hello world! 24 |
25 |
26 | ``` 27 | -------------------------------------------------------------------------------- /docs/en/next/createRef.md: -------------------------------------------------------------------------------- 1 | # `createRef()` 2 | 3 | Get a reference to DOM element. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {createRef} from 'libreact/lib/shim'; 10 | 11 | const div = createRef(); 12 | 13 |
14 | Hello world! 15 |
16 | ``` 17 | -------------------------------------------------------------------------------- /docs/en/next/createState.md: -------------------------------------------------------------------------------- 1 | # `createState()` 2 | 3 | Create a state container. 4 | 5 | 6 | ## Usage 7 | 8 | ```jsx 9 | import {createState} from 'libreact/lib/shim'; 10 | 11 | const State = createState({ 12 | cnt: 1, 13 | }); 14 | 15 | {(state, setState) => 16 | 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/en/pure.md: -------------------------------------------------------------------------------- 1 | # `pure()` 2 | 3 | An enhancer that makes component re-render only when its props change. Uses [`fast-shallow-equal`](https://github.com/streamich/fast-shallow-equal) 4 | to do shallow prop comparison. 5 | 6 | 7 | ## Usage 8 | 9 | ```js 10 | import {pure} from 'libreact/lib/pure'; 11 | 12 | const PureComp = pure(Comp); 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetEricMeyer.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | Eric Meyer's CSS reset. 4 | 5 | ## Usage 6 | 7 | Simply render it in your React app 8 | 9 | ```jsx 10 | import CssResetEricMeyer from 'libreact/lib/reset/CssResetEricMeyer'; 11 | 12 | 13 | ``` 14 | 15 | ## Contents 16 | 17 | ```js 18 | { 19 | 'html,body,div,span,applet,object,iframe,table,caption,tbody,tfoot,thead,tr,th,td,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,dl,dt,dd,ol,ul,li,fieldset,form,label,legend': { 20 | 'vertical-align': 'baseline', 21 | ff: 'inherit', 22 | fw: 'inherit', 23 | fs: 'inherit', 24 | fz: '100%', 25 | out: 0, 26 | pad: 0, 27 | mar: 0, 28 | bd: 0, 29 | }, 30 | ':focus': { 31 | out: 0, 32 | }, 33 | body: { 34 | bg: 'white', 35 | lh: 1, 36 | col: 'black', 37 | }, 38 | 'ol, ul': { 39 | 'list-style': 'none', 40 | }, 41 | table: { 42 | 'border-collapse': 'separate', 43 | 'border-spacing': 0, 44 | }, 45 | 'caption, th, td': { 46 | fw: 'normal', 47 | ta: 'left', 48 | }, 49 | 'blockquote:before, blockquote:after, q:before, q:after': { 50 | content: '""', 51 | }, 52 | 'blockquote, q': { 53 | quotes: '"" ""', 54 | }, 55 | } 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetEricMeyerCondensed.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetEricMeyerCondensed from 'libreact/lib/reset/CssResetEricMeyerCondensed'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | 'body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td': { 18 | pad: 0, 19 | mar: 0, 20 | }, 21 | 'fieldset, img': { 22 | bd: 0, 23 | }, 24 | table: { 25 | 'border-collapse': 'collapse', 26 | 'border-spacing': 0, 27 | }, 28 | 'ol, ul': { 29 | 'list-style': 'none', 30 | }, 31 | 'address, caption, cite, code, dfn, em, strong, th, var': { 32 | fw: 'normal', 33 | fs: 'normal', 34 | }, 35 | 'caption, th': { 36 | ta: 'left', 37 | }, 38 | 'h1, h2, h3, h4, h5, h6': { 39 | fw: 'normal', 40 | fs: '100%', 41 | }, 42 | 'q:before, q:after': { 43 | con: "''", 44 | }, 45 | 'abbr, acronym': { 46 | bd: 0, 47 | }, 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetMinimalistic.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetMinimalistic from 'libreact/lib/reset/CssResetMinimalistic'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | '*': { 18 | pad: 0, 19 | mar: 0, 20 | }, 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetMinimalistic2.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetMinimalistic2 from 'libreact/lib/reset/CssResetMinimalistic2'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | '*': { 18 | pad: 0, 19 | mar: 0, 20 | bd: 0, 21 | }, 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetMinimalistic3.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetMinimalistic3 from 'libreact/lib/reset/CssResetMinimalistic3'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | '*': { 18 | pad: 0, 19 | mar: 0, 20 | bd: 0, 21 | out: 0, 22 | }, 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetPoorMan.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetPoorMan from 'libreact/lib/reset/CssResetPoorMan'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | 'html,body': { 18 | pad: 0, 19 | mar: 0, 20 | }, 21 | html: { 22 | fz: '1em', 23 | }, 24 | body: { 25 | fz: '100%', 26 | }, 27 | 'a img,a:link img,a:visited img': { 28 | bd: 0, 29 | }, 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetShaunInman.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetShaunInman from 'libreact/lib/reset/CssResetShaunInman'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | 'body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,table,th,td,embed,object': { 18 | pad: 0, 19 | mar: 0, 20 | }, 21 | table: { 22 | 'border-collapse': 'collapse', 23 | 'border-spacing': 0, 24 | }, 25 | 'fieldset,img,abbr': { 26 | bd: 0, 27 | }, 28 | 'address,caption,cite,code,dfn,em,h1,h2,h3,h4,h5,h6,strong,th,var': { 29 | fw: 'normal', 30 | fs: 'normal', 31 | }, 32 | ul: { 33 | 'list-style': 'none', 34 | }, 35 | 'caption,th': { 36 | ta: 'left', 37 | }, 38 | 'h1,h2,h3,h4,h5,h6': { 39 | fz: '1.0em', 40 | }, 41 | 'q:before,q:after': { 42 | con: '""', 43 | }, 44 | 'a,ins': { 45 | td: 'none', 46 | }, 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetSiolon.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetSiolon from 'libreact/lib/reset/CssResetSiolon'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | '*': { 18 | 'vertical-align': 'baseline', 19 | ff: 'inherit', 20 | fs: 'inherit', 21 | fz: '100%', 22 | bd: 'none', 23 | pad: 0, 24 | mar: 0, 25 | }, 26 | body: { 27 | pad: '5px', 28 | }, 29 | 'h1, h2, h3, h4, h5, h6, p, pre, blockquote, form, ul, ol, dl': { 30 | mar: '20px 0', 31 | }, 32 | 'li, dd, blockquote': { 33 | marl: '40px', 34 | }, 35 | table: { 36 | 'border-collapse': 'collapse', 37 | 'border-spacing': 0, 38 | }, 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetTantek.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetTantek from 'libreact/lib/reset/CssResetTantek'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | ':link,:visited': { 18 | td: 'none', 19 | }, 20 | 'ul,ol': { 21 | 'list-style': 'none', 22 | }, 23 | 'h1,h2,h3,h4,h5,h6,pre,code,p': { 24 | fz: '1em', 25 | }, 26 | 'ul,ol,dl,li,dt,dd,h1,h2,h3,h4,h5,h6,pre,form,body,html,p,blockquote,fieldset,input': { 27 | pad: 0, 28 | mar: 0, 29 | }, 30 | 'a img,:link img,:visited img': { 31 | bd: 'none', 32 | }, 33 | address: { 34 | fs: 'normal', 35 | }, 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetUniversal.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetUniversal from 'libreact/lib/reset/CssResetUniversal'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | '*': { 18 | 'vertical-align': 'baseline', 19 | fw: 'inherit', 20 | ff: 'inherit', 21 | fs: 'inherit', 22 | fz: '100%', 23 | bd: '0 none', 24 | out: 0, 25 | pad: 0, 26 | mar: 0, 27 | }, 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/en/reset/CssResetYahoo.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | ## Usage 4 | 5 | Simply render it in your React app 6 | 7 | ```jsx 8 | import CssResetYahoo from 'libreact/lib/reset/CssResetYahoo'; 9 | 10 | 11 | ``` 12 | 13 | ## Contents 14 | 15 | ```js 16 | { 17 | 'body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td': { 18 | pad: 0, 19 | mar: 0, 20 | }, 21 | table: { 22 | 'border-collapse': 'collapse', 23 | 'border-spacing': 0, 24 | }, 25 | 'fieldset,img': { 26 | bd: 0, 27 | }, 28 | 'address,caption,cite,code,dfn,em,strong,th,var': { 29 | fw: 'normal', 30 | fs: 'normal', 31 | }, 32 | 'ol,ul': { 33 | 'list-style': 'none', 34 | }, 35 | 'caption,th': { 36 | ta: 'left', 37 | }, 38 | 'h1,h2,h3,h4,h5,h6': { 39 | fw: 'normal', 40 | fz: '100%', 41 | }, 42 | 'q:before,q:after': { 43 | con: '""', 44 | }, 45 | 'abbr,acronym': { 46 | bd: 0, 47 | }, 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/en/viewport.md: -------------------------------------------------------------------------------- 1 | # `viewport()` 2 | 3 | Displays your component only after it appears in the viewport. 4 | 5 | Before your component has appeared in viewport at least once it displays 6 | a placeholder, which defaults to a transparent 1x1 pixel. 7 | 8 | `viewport()` is a component enhancer with the following signature. 9 | 10 | ```ts 11 | viewport: (Comp, params: IViewportParams) => React.ComponentClass; 12 | 13 | interface IViewportParams { 14 | placeholder: React.ReactElement; 15 | } 16 | ``` 17 | 18 | , where 19 | 20 | - `placeholder` - React element to render while this component is out of view. 21 | 22 | 23 | ## Usage 24 | 25 | ```jsx 26 | import {viewport} from 'libreact/lib/viewport'; 27 | 28 | const MyCompEnhanced = viewport(MyComp); 29 | ``` 30 | 31 | ## Example 32 | 33 | Here we create a lazy loaded component, which is loaded only when it appears in the viewport. 34 | 35 | ```jsx 36 | import {delayed} from 'libreact/lib/delayed'; 37 | import {viewport} from 'libreact/lib/viewport'; 38 | 39 | 40 | const loader = () => import('./MyComp'); 41 | const MyCompDelayed = delayed({loader}); 42 | const MyCompOnViewport = viewport(MyCompDelayed); 43 | 44 | 45 | ``` 46 | -------------------------------------------------------------------------------- /src/AfterDraf/RAF.ts: -------------------------------------------------------------------------------- 1 | const RAF = typeof window === 'object' ? requestAnimationFrame : () => {}; 2 | 3 | export default RAF as (callback: (...args) => any) => any; 4 | -------------------------------------------------------------------------------- /src/AfterDraf/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {AfterDraf} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Animation/AfterDraf', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/AfterDraf.md')})) 8 | .add('Defaults', () => 9 | 10 | Hello world! 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /src/AfterDraf/__tests__/index.server.test.tsx: -------------------------------------------------------------------------------- 1 | /** @jest-environment node */ 2 | import {createElement as h} from 'react'; 3 | import ReactDOMServer from 'react-dom/server'; 4 | import {AfterDraf} from '..'; 5 | 6 | const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time)); 7 | 8 | describe('', () => { 9 | it('Renders children', async () => { 10 | const str = ReactDOMServer.renderToStaticMarkup( 11 |
foobar
12 |
); 13 | 14 | expect(str).toBe('
foobar
'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/AfterDraf/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {render} from 'react-dom'; 3 | import {AfterDraf} from '..'; 4 | import RAF from '../RAF'; 5 | 6 | jest.mock('../RAF'); 7 | 8 | const RAFMock = RAF as any as jest.SpyInstance; 9 | const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time)); 10 | 11 | beforeEach(() => { 12 | RAFMock.mockClear(); 13 | RAFMock.mockImplementation(cb => setImmediate(cb, 17)); 14 | }); 15 | 16 | describe('', () => { 17 | it('default exists', () => { 18 | expect(typeof AfterDraf).toBe('function'); 19 | }); 20 | 21 | // TODO: enble this test later 22 | /* 23 | it('waits for DRAF on client before rendering', async () => { 24 | const div = document.createElement('div'); 25 | 26 | document.body.appendChild(div); 27 | render( 28 |
foobar
29 |
, 30 | div); 31 | 32 | expect(div.innerHTML).toBe(''); 33 | 34 | await sleep(100); 35 | 36 | expect(div.innerHTML).toBe('
foobar
'); 37 | 38 | document.body.removeChild(div); 39 | }); 40 | */ 41 | }); 42 | -------------------------------------------------------------------------------- /src/AfterDraf/index.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {isClient} from '../util'; 3 | 4 | export interface IAfterDrafState { 5 | ready: boolean; 6 | } 7 | 8 | export const AfterDraf = isClient 9 | ? class AfterDraf extends React.Component<{}, IAfterDrafState> { 10 | frame; 11 | state: IAfterDrafState; 12 | 13 | constructor (props, context) { 14 | super(props, context); 15 | 16 | if (isClient) { 17 | this.state = { 18 | ready: false 19 | }; 20 | } 21 | } 22 | 23 | componentDidMount () { 24 | const RAF = requestAnimationFrame; 25 | 26 | this.frame = RAF(() => { 27 | this.frame = RAF(() => { 28 | this.setState({ready: true}); 29 | }); 30 | }); 31 | } 32 | 33 | componentWillUnmount () { 34 | cancelAnimationFrame(this.frame); 35 | } 36 | 37 | render () { 38 | return this.state.ready ? this.props.children : null; 39 | }; 40 | } 41 | : (props) => props.children; 42 | -------------------------------------------------------------------------------- /src/AfterTimeout/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {AfterTimeout} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Animation/AfterTimeout', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/AfterTimeout.md')})) 8 | .add('Defaults', () => 9 | 10 | Hello world! 11 | 12 | ) 13 | .add('Two second delay', () => 14 |
15 | 16 |
Hello world!
17 |
18 |
19 | ) 20 | -------------------------------------------------------------------------------- /src/AfterTimeout/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | 3 | export interface IAfterTimeoutProps { 4 | ms?: number; 5 | } 6 | 7 | export interface IAfterTimeoutState { 8 | ready: boolean; 9 | } 10 | 11 | export class AfterTimeout extends Component { 12 | static defaultProps = { 13 | ms: 200 14 | }; 15 | 16 | state: IAfterTimeoutState = { 17 | ready: false 18 | }; 19 | 20 | timer; 21 | 22 | componentDidMount () { 23 | this.timer = setTimeout(() => { 24 | this.setState({ 25 | ready: true 26 | }); 27 | }, this.props.ms); 28 | } 29 | 30 | componentWillUnmount () { 31 | clearTimeout(this.timer); 32 | } 33 | 34 | render () { 35 | return this.state.ready ? this.props.children : null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Alert/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Alert} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Generators/Alert', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Alert.md')})) 8 | .add('Basic example', () => ) 9 | .add('Don\'t show', () => ); 10 | -------------------------------------------------------------------------------- /src/Alert/index.ts: -------------------------------------------------------------------------------- 1 | import {PureComponent} from 'react'; 2 | 3 | export interface IAlertProps { 4 | show?: boolean; 5 | text?: string; 6 | } 7 | 8 | export class Alert extends PureComponent { 9 | componentDidMount () { 10 | this.alert(); 11 | } 12 | 13 | componentDidUpdate () { 14 | this.alert(); 15 | } 16 | 17 | alert () { 18 | const {show, text} = this.props; 19 | 20 | if (show) { 21 | alert(text); 22 | } 23 | } 24 | 25 | render () { 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/BatterySensor/__story__/story.ts: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {BatterySensor, withBattery} from '..'; 4 | import ShowDocs from '../../ShowDocs'; 5 | 6 | const Print = (props) => h('pre', { 7 | style: { 8 | fontFamily: 'monospace' 9 | } 10 | }, 11 | JSON.stringify(props, null, 4) 12 | ); 13 | 14 | const PrintBattery = withBattery(Print, 'overwritten'); 15 | 16 | @withBattery 17 | class Printer extends Component { 18 | render () { 19 | return h('pre', { 20 | style: { 21 | fontFamily: 'monospace' 22 | } 23 | }, 24 | JSON.stringify(this.props, null, 4) 25 | ); 26 | } 27 | } 28 | 29 | storiesOf('Sensors/BatterySensor', module) 30 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/BatterySensor.md')})) 31 | .add('FaCC', () => 32 | h(BatterySensor, {}, (state) => 33 | h('pre', {style: { 34 | fontFamily: 'monospace' 35 | }}, 36 | JSON.stringify(state, null, 4) 37 | ) 38 | ) 39 | ) 40 | .add('Render prop', () => 41 | h(BatterySensor, { 42 | render: (state) => 43 | h('pre', {style: { 44 | fontFamily: 'monospace' 45 | }}, 46 | JSON.stringify(state, null, 4) 47 | ) 48 | }) 49 | ) 50 | .add('HOC', () => h(PrintBattery)) 51 | .add('Decorator', () => h(Printer)); 52 | -------------------------------------------------------------------------------- /src/BatterySensor/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {BatterySensor} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing with no value', () => { 8 | const html = renderToStaticMarkup(h(BatterySensor, {}, 9 | (battery) => h('div', {}, String(battery)) 10 | )); 11 | 12 | expect(html).to.equal('
null
'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/BrowserOnly/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {BrowserOnly} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Boundaries/BrowserOnly', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/BrowserOnly.md')})) 8 | .add('Basic example', () => 9 | 10 |
11 | This should be visible only in a browser. 12 |
13 |
14 | ); 15 | -------------------------------------------------------------------------------- /src/BrowserOnly/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | 3 | export interface IBrowserOnlyProps { 4 | children?; 5 | } 6 | 7 | export interface IBrowserOnlyState { 8 | isClient: boolean; 9 | } 10 | 11 | export class BrowserOnly extends Component { 12 | state: IBrowserOnlyState = { 13 | isClient: false 14 | }; 15 | 16 | componentDidMount () { 17 | this.setState({ 18 | isClient: true 19 | }); 20 | } 21 | 22 | render () { 23 | return this.state.isClient ? this.props.children : null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ClassNames/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {ClassNames} from '..'; 4 | import {Toggle} from '../../Toggle'; 5 | 6 | storiesOf('Side Effects/ClassNames', module) 7 | .add('Basic example', () => 8 | 9 | ) 10 | .add('Toggle', () => 11 | {({on, toggle}) => 12 |
13 |
Toggle: {on ? 'on' : 'off'}
14 | {on && } 15 |
16 | }
17 | ) 18 | -------------------------------------------------------------------------------- /src/ClassNames/__tests__/index.test-server.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {ClassNames} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing with no value', () => { 8 | const html = renderToStaticMarkup(h(ClassNames, {list: ['foobar']})); 9 | 10 | expect(html).to.equal(''); 11 | }); 12 | 13 | it('renders its children', () => { 14 | const html = renderToStaticMarkup(h(ClassNames, {list: ['foobar']}, h('div', null, 'foo'))); 15 | 16 | expect(html).to.equal('
foo
'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/Counter/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Counter, withCounter} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Val = ({value, inc}) => 7 |
inc(3)} onDoubleClick={inc}>{value}
; 8 | 9 | const Hoc1 = withCounter(({counter}) => h(Val, counter)); 10 | const Hoc2 = withCounter(Val, '', -5); 11 | 12 | @withCounter('foo', -4) 13 | class Decorator extends Component { 14 | render () { 15 | return 16 | } 17 | } 18 | 19 | storiesOf('Inversion/Counter', module) 20 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Counter.md')})) 21 | .add('FaCC', () => 22 | {(props) => } 23 | ) 24 | .add('HOC 1', () => ) 25 | .add('HOC 2', () => ) 26 | .add('Decorator', () => ); 27 | -------------------------------------------------------------------------------- /src/Counter/index.ts: -------------------------------------------------------------------------------- 1 | import {Value, faccToHocInit} from '../Value'; 2 | import renderProp from '../util/renderProp'; 3 | 4 | export interface ICounterProps { 5 | init?: number; 6 | } 7 | 8 | export const Counter: React.StatelessComponent = (props) => { 9 | return Value({ 10 | init: props.init || 0, 11 | render: (state) => renderProp(props, Object.assign(state, { 12 | inc: (by: number = 1) => state.set(state.value + (+by || 0)) 13 | })) 14 | }); 15 | }; 16 | 17 | export const withCounter = faccToHocInit(Counter, 'counter'); 18 | -------------------------------------------------------------------------------- /src/Dimmer/__tests__/index.test-server.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {Dimmer} from '..'; 4 | 5 | describe(' SSR', () => { 6 | it('renders without crashing with no value', () => { 7 | renderToStaticMarkup(h(Dimmer)); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/Dimmer/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {render, unmountComponentAtNode} from 'react-dom'; 3 | import {Dimmer} from '..'; 4 | 5 | describe('', () => { 6 | it('is a component', () => { 7 | expect(Dimmer).toBeDefined(); 8 | }); 9 | 10 | it('sets position="relative" to parent node', () => { 11 | const div = document.createElement('div'); 12 | 13 | document.body.appendChild(div); 14 | 15 | const element =
16 | foobar 17 | overlay 18 |
; 19 | 20 | render(element, div); 21 | 22 | const root = div.children[0] as HTMLDivElement; 23 | 24 | expect(root.style.position).toBe('relative'); 25 | 26 | unmountComponentAtNode(div); 27 | }); 28 | 29 | it('inserts overlay node', () => { 30 | const div = document.createElement('div'); 31 | 32 | document.body.appendChild(div); 33 | 34 | const element =
35 | foobar 36 | overlay 37 |
; 38 | 39 | render(element, div); 40 | 41 | const root = div.children[0] as HTMLDivElement; 42 | 43 | expect(root.querySelector('div').innerHTML).toBe('overlay'); 44 | 45 | unmountComponentAtNode(div); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /src/DropArea/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {DropArea} from '..'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | storiesOf('UI/DropArea', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/DropArea.md')})) 9 | .add('Example', () => 10 | 15 |
19 | Drop here! 20 |
21 |
22 | ) 23 | -------------------------------------------------------------------------------- /src/ElectronOnly/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {ElectronOnly} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Boundaries/ElectronOnly', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/ElectronOnly.md')})) 8 | .add('Basic example', () => 9 | 10 |
11 | This should be visible only in Electron app. 12 |
13 |
14 | ); 15 | -------------------------------------------------------------------------------- /src/ElectronOnly/index.ts: -------------------------------------------------------------------------------- 1 | import {idx} from '../util'; 2 | 3 | const isElectron = (typeof process === 'object') && idx(process, (_) => _.versions.electron); 4 | 5 | export const ElectronOnly = (props) => isElectron ? props.children : null; 6 | -------------------------------------------------------------------------------- /src/ErrorBoundary/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | import {h, noop} from '../util'; 3 | 4 | export interface IErrorBoundaryProps { 5 | children: any; 6 | renderError?: (state: IErrorBoundaryState) => React.ReactElement; 7 | onError?: (error?: Error, info?) => void; 8 | } 9 | 10 | export interface IErrorBoundaryState { 11 | error?: Error; 12 | info?: any; 13 | } 14 | 15 | export class ErrorBoundary extends Component { 16 | state: IErrorBoundaryState = { 17 | }; 18 | 19 | componentDidCatch (error, info) { 20 | this.setState({ 21 | error, 22 | info 23 | }); 24 | 25 | (this.props.onError || noop)(error, info); 26 | } 27 | 28 | render () { 29 | const {props, state} = this; 30 | const {renderError, children} = props; 31 | 32 | return state.error ? 33 | (typeof renderError === 'function' ? renderError(state) : null) : 34 | children; 35 | } 36 | } 37 | 38 | export const withErrorBoundary = (Comp, boundaryProps) => (props) => { 39 | return h(ErrorBoundary, boundaryProps, 40 | h(Comp, props) 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/ExitSensor/__story__/StoryExitSensorExample.tsx: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | import {h} from '../../util'; 3 | import {ExitSensor} from '..'; 4 | 5 | const Rectangle = ({exiting}: any) => { 6 | return ( 7 |
14 | ); 15 | }; 16 | 17 | const Circle = ({exiting}: any) => { 18 | return ( 19 |
27 | ); 28 | }; 29 | 30 | export class StoryExitSensorExample extends Component { 31 | state = { 32 | shape: 'rectangle' 33 | }; 34 | 35 | render () { 36 | const shape = this.state.shape === 'rectangle' ? 37 | : ; 38 | 39 | return ( 40 |
41 | 42 | {shape} 43 | 44 | 45 | 46 |
47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/ExitSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {ExitSensor} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | import {StoryExitSensorExample} from './StoryExitSensorExample'; 6 | 7 | storiesOf('Sensors/ExitSensor', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/ExitSensor.md')})) 9 | .add('Example', () => ) 10 | .add('Can display nothing', () => 11 |
12 | 13 |
14 | ) 15 | .add('Shows error on invalid child', () => 16 |
17 | {'Hello' as any} 18 |
19 | ); 20 | -------------------------------------------------------------------------------- /src/ExitSensor/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {ExitSensor} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing inner element', () => { 8 | const html = renderToStaticMarkup(h(ExitSensor, {}, 9 | h('div', {}, 'foobar') 10 | )); 11 | 12 | expect(html).to.equal('
foobar
'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/Flipflop/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Flipflop} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Inversion/Flipflop', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Flipflop.md')})) 8 | .add('Basic example', () => 9 | {({on, flip, flop}) => 10 |
11 |
22 | {on ? 'ON' : 'OFF'} 23 |
24 | 25 | {' '} 26 | 27 |
28 | }
29 | ); 30 | -------------------------------------------------------------------------------- /src/Flipflop/index.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {State} from '../State'; 3 | import renderProp from '../util/renderProp'; 4 | import faccToHoc from '../util/faccToHoc'; 5 | 6 | export interface IFlipflopProps { 7 | init?: boolean; 8 | } 9 | 10 | export const Flipflop: React.StatelessComponent = (props) => { 11 | const initialState = props.init || false; 12 | 13 | return h(State, { 14 | init: {on: initialState}, 15 | render: ({on}, set) => renderProp(props, { 16 | on, 17 | flip: () => { 18 | if (on === initialState) { 19 | set({on: !initialState}); 20 | } 21 | }, 22 | flop: () => { 23 | if (on !== initialState) { 24 | set({on: initialState}); 25 | } 26 | } 27 | }) 28 | }); 29 | }; 30 | 31 | export const withFlipflop = faccToHoc(Flipflop, 'flipflop'); 32 | -------------------------------------------------------------------------------- /src/FocusSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {FocusSensor} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Sensors/FocusSensor', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/FocusSensor.md')})) 8 | .add('With bond', () => 9 | {({isFocused, bond}) => 10 |
14 | {isFocused ? 'Focused' : '...'} 15 | 16 |
17 | }
18 | ) 19 | -------------------------------------------------------------------------------- /src/FocusSensor/index.ts: -------------------------------------------------------------------------------- 1 | import {cloneElement} from 'react'; 2 | import {h} from '../util'; 3 | import {Value} from '../Value'; 4 | import renderProp from '../util/renderProp'; 5 | import faccToHoc from '../util/faccToHoc'; 6 | 7 | export interface IFocusSensorProps { 8 | bond?: boolean | string; 9 | } 10 | 11 | export const FocusSensor: React.StatelessComponent = (props: IFocusSensorProps) => { 12 | let {bond} = props; 13 | 14 | return Value({ 15 | render: ({value, set}) => { 16 | if (bond) { 17 | if (typeof bond === 'boolean') { 18 | bond = 'bond'; 19 | } 20 | 21 | return renderProp(props, { 22 | isFocused: value, 23 | [bond]: { 24 | onFocus: () => set(true), 25 | onBlur: () => set(false), 26 | } 27 | }); 28 | } else { 29 | const element = renderProp(props, { 30 | isFocused: value 31 | }); 32 | 33 | return cloneElement(element, { 34 | onFocus: () => set(true), 35 | onBlur: () => set(false), 36 | }); 37 | } 38 | } 39 | }); 40 | }; 41 | 42 | const FocusSensorWithBond = (props) => h(FocusSensor, { 43 | bond: true, 44 | ...props 45 | }); 46 | 47 | export const withFocus = faccToHoc(FocusSensorWithBond, 'focus'); 48 | -------------------------------------------------------------------------------- /src/GeoLocation/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {GeoLocationSensor, withGeoLocation} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Print = ({geoLocation}) => 7 |
 8 |     {JSON.stringify(geoLocation, null, 4)}
 9 |   
; 10 | 11 | const Hoc = withGeoLocation(Print); 12 | 13 | storiesOf('Sensors/GeoLocationSensor', module) 14 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/GeoLocationSensor.md')})) 15 | .add('FaCC', () => 16 | {(state) => 17 |
{JSON.stringify(state, null, 4)}
18 | }
19 | ) 20 | .add('HOC', () => ); 21 | -------------------------------------------------------------------------------- /src/GoogleAuth/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {createGoogleAuthContext} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const clientId = '305188012168-htfit0k0u4vegn0f6hn10rcqoj1m77ca.apps.googleusercontent.com'; 7 | const options = { 8 | client_id: clientId, 9 | }; 10 | const ctx1 = createGoogleAuthContext(options); 11 | 12 | storiesOf('Context/GoogleAuthButton', module) 13 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/GoogleAuth.md')})) 14 | .add('Default', () => 15 | 16 | {({loading, signIn, signOut, isSignedIn, user}) => { 17 | if (loading) { 18 | return 'Loading...'; 19 | } 20 | console.log('user', user); 21 | return ( 22 |
23 | 26 |
Is signed in: {isSignedIn ? 'true' : 'false'}
27 | {user && 28 |
29 |
Name: {user.getBasicProfile().getName()}
30 |
JWT: {user.getAuthResponse().id_token}
31 |
32 | } 33 |
34 | ); 35 | }}
36 |
37 | ); 38 | -------------------------------------------------------------------------------- /src/Group/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Group} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Custom = () =>
; 7 | 8 | storiesOf('UI/Group', module) 9 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Group.md')})) 10 | .add('No group', () => 11 |
12 | Hello 13 | world 14 |
15 | ) 16 | .add('Using default separator', () => 17 | 18 | Hello 19 | world 20 | 21 | ) 22 | .add('Using
separator', () => 23 | }> 24 | Hello 25 | world 26 | 27 | ) 28 | .add('Using
separator', () => 29 | }> 30 | Hello 31 | world 32 | 33 | ) 34 | .add('Using custom separator', () => 35 | }> 36 | Hello 37 | world 38 | 39 | ) 40 | .add('No children', () => 41 | }> 42 | ) -------------------------------------------------------------------------------- /src/Group/__tests__/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` can change element type 1`] = ` 4 | 8 | 9 | 10 | `; 11 | 12 | exports[` can set custom separator 1`] = ` 13 | } 16 | > 17 |
18 | 21 | Hello 22 | 23 |
24 | 27 | world 28 | 29 |
30 |
31 | `; 32 | 33 | exports[` default separator is space 1`] = ` 34 | 38 |
39 | 42 | Hello 43 | 44 | 45 | 48 | world 49 | 50 |
51 |
52 | `; 53 | 54 | exports[` passes through props 1`] = ` 55 | 60 |
63 | 64 | `; 65 | 66 | exports[` works with no children 1`] = ` 67 | 71 |
72 | 73 | `; 74 | -------------------------------------------------------------------------------- /src/Group/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {mount} from 'enzyme'; 3 | import toJson from 'enzyme-to-json'; 4 | import {Group} from '..'; 5 | 6 | describe('', () => { 7 | it('exists', () => { 8 | expect(typeof Group).toBe('function'); 9 | }); 10 | 11 | it('works with no children', () => { 12 | const wrapper = mount(); 13 | 14 | expect(toJson(wrapper)).toMatchSnapshot(); 15 | }); 16 | 17 | it('can change element type', () => { 18 | const wrapper = mount(); 19 | 20 | expect(toJson(wrapper)).toMatchSnapshot(); 21 | }); 22 | 23 | it('passes through props', () => { 24 | const wrapper = mount(); 25 | 26 | expect(toJson(wrapper)).toMatchSnapshot(); 27 | }); 28 | 29 | it('default separator is space', () => { 30 | const wrapper = mount( 31 | 32 | Hello 33 | world 34 | 35 | ); 36 | 37 | expect(toJson(wrapper)).toMatchSnapshot(); 38 | }); 39 | 40 | it('can set custom separator', () => { 41 | const wrapper = mount( 42 | }> 43 | Hello 44 | world 45 | 46 | ); 47 | 48 | expect(toJson(wrapper)).toMatchSnapshot(); 49 | }); 50 | }); -------------------------------------------------------------------------------- /src/Group/index.ts: -------------------------------------------------------------------------------- 1 | import {Children} from 'react'; 2 | import {h} from '../util'; 3 | 4 | export interface IGroupProps { 5 | [key: string]: any; 6 | as?: string; 7 | children?: any; 8 | separator?: React.ReactChild; 9 | } 10 | 11 | export const Group: React.SFC = ({as, children, separator, ...rest}) => { 12 | children = Children.toArray(children); 13 | 14 | const newChildren = []; 15 | 16 | if (children.length) { 17 | newChildren.push(children[0]); 18 | 19 | for (let i = 1; i < children.length; i++) { 20 | newChildren.push(separator); 21 | newChildren.push(children[i]); 22 | } 23 | } 24 | 25 | return h(as, rest, ...newChildren); 26 | }; 27 | 28 | Group.defaultProps = { 29 | as: 'div', 30 | separator: ' ', 31 | }; 32 | -------------------------------------------------------------------------------- /src/HoverSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {HoverSensor, withHover} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Demo = ({bond, isHover}) => 7 |
11 | {isHover ? 'HOVERED' : '...'} 12 |
; 13 | 14 | const Hoc = withHover(Demo, ''); 15 | 16 | storiesOf('Sensors/HoverSensor', module) 17 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/HoverSensor.md')})) 18 | .add('FaCC', () => 19 | {({isHover}) => 20 |
24 | {isHover ? 'HOVERED' : '...'} 25 |
26 | }
27 | ) 28 | .add('FaCC with bond', () => 29 | {({bond, isHover}) => 30 |
31 |
36 | {isHover ? 'HOVERED' : '...'} 37 |
38 |
39 | }
40 | ) 41 | .add('HOC', () => ); 42 | -------------------------------------------------------------------------------- /src/IdleSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {IdleSensor, withIdle} from '..'; 5 | import ShowDocs from '../../ShowDocs'; 6 | 7 | const Print = ({idle}) => 8 |
9 | Is idle: {idle ? 'TRUE' : 'FALSE'} 10 |
; 11 | 12 | const Hoc1 = withIdle(Print, '', {ms: 3000}); 13 | 14 | storiesOf('Sensors/IdleSensor', module) 15 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/IdleSensor.md')})) 16 | .add('FaCC - 2 sec', () => 17 | {({idle}) => 18 |
19 | Is idle: {idle ? 'TRUE' : 'FALSE'} 20 |
21 | }
22 | ) 23 | .add('HOC - 3 sec', () => ) 24 | .add('Only .onChange() - 2 sec', () => ) 25 | -------------------------------------------------------------------------------- /src/Img/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Img} from '..'; 4 | import ShowDocs from '../../ShowDocs'; 5 | 6 | storiesOf('UI/Img', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Img.md')})) 8 | .add('', () => ) 9 | .add('Defaults', () => ) 10 | .add('renderError', () =>
Error...
} />) 11 | .add('renderLoad', () =>
Loading...{img}
} />) 12 | -------------------------------------------------------------------------------- /src/InlineWidthQuery/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {InlineWidthQuery} from '..'; 4 | import {View} from '../../View'; 5 | import ShowDocs from '../../ShowDocs'; 6 | 7 | storiesOf('UI/InlineWidthQuery', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/InlineWidthQuery.md')})) 9 | .add('Example', () => 10 | 11 | 12 | Up to 300px 13 | 14 | 15 | More than 300px 16 | 17 | 18 | ) 19 | .add('100px increments', () => 20 | 21 | 22 | Up to 100px 23 | 24 | 25 | 100px - 200px 26 | 27 | 28 | 200px - 300px 29 | 30 | 31 | 300px - 400px 32 | 33 | 34 | 400px - 500px 35 | 36 | 37 | 500px - 600px 38 | 39 | 40 | 600px - 700px 41 | 42 | 43 | 700px+ 44 | 45 | 46 | ) 47 | -------------------------------------------------------------------------------- /src/InlineWidthQuery/index.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {WidthQuery} from '../WidthQuery'; 3 | import {SizeSensor} from '../SizeSensor'; 4 | 5 | export {View} from '../View'; 6 | 7 | export const InlineWidthQuery = (props) => 8 | h(SizeSensor, null, (state) => 9 | h('div', null, 10 | h(WidthQuery, state, props.children) 11 | ) 12 | ); 13 | -------------------------------------------------------------------------------- /src/Interpolation/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Interpolation} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Animation/Interpolation', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Interpolation.md')})) 8 | .add('Defaults', () => 9 |
10 | {({foo}) => 11 |
{foo}
12 | }
13 |
14 | ) 15 | .add('Basic map', () => 16 |
17 | {({left, top, opacity}) => 22 |
31 | } 32 |
33 | ) -------------------------------------------------------------------------------- /src/Interpolation/index.ts: -------------------------------------------------------------------------------- 1 | import {render, createEnhancer} from 'react-universal-interface'; 2 | import {Tween, ITweenProps} from '../Tween'; 3 | import {h} from '../util'; 4 | 5 | export interface IInterpolationProps extends ITweenProps { 6 | map: {[key: string]: [number, number]}; 7 | } 8 | 9 | export const Interpolation: React.SFC = (props) => { 10 | return h(Tween, props, 11 | ({value}) => { 12 | const {map} = props; 13 | const keys = Object.keys(map); 14 | const interpolated = {}; 15 | 16 | for (let i = 0; i < keys.length; i++) { 17 | const key = keys[i]; 18 | const [start, end] = map[key]; 19 | const diff = end - start; 20 | 21 | interpolated[key] = start + (diff * value); 22 | } 23 | 24 | return render(props, interpolated); 25 | } 26 | ); 27 | }; 28 | 29 | export const withInterpolation = createEnhancer(Interpolation, 'interpolation'); 30 | -------------------------------------------------------------------------------- /src/Lifecycles/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {Lifecycles} from '..'; 5 | import ShowDocs from '../../ShowDocs'; 6 | 7 | storiesOf('Inversion/Lifecycles', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Lifecycles.md')})) 9 | .add('Example', () => 10 | 14 |
Hello world!
15 |
16 | ) 17 | -------------------------------------------------------------------------------- /src/LightSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {LightSensor, withLight} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Print = (props) => 7 |
 8 |     {JSON.stringify(props, null, 4)}
 9 |   
; 10 | 11 | const PrintWithLight = withLight(Print); 12 | 13 | @withLight 14 | class LightPrinter extends Component { 15 | render () { 16 | return Print(this.props); 17 | } 18 | } 19 | 20 | storiesOf('Sensors/LightSensor', module) 21 | .add('Documentation', () => ) 22 | .add('FaCC', () => 23 | {(light) => 24 |
25 |         {JSON.stringify(light, null, 4)}
26 |       
27 | }
28 | ) 29 | .add('Render prop', () => 30 | 31 |
32 |         {JSON.stringify(light, null, 4)}
33 |       
34 | } /> 35 | ) 36 | .add('HOC', () => ) 37 | .add('Decorator', () => ); 38 | -------------------------------------------------------------------------------- /src/Link/index.ts: -------------------------------------------------------------------------------- 1 | import Link from '../route/Link'; 2 | 3 | export {Link}; 4 | -------------------------------------------------------------------------------- /src/List/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {List, withList} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Demo = ({value, push, filter, sort}) => 7 |
8 |
{JSON.stringify(value)}
9 | 10 | 11 | 12 |
; 13 | 14 | const Hoc1 = withList(({list}) => h(Demo, list)); 15 | const Hoc2 = withList(Demo, '', [1, 2, 3]); 16 | 17 | @withList('foo', [1, 2, 3]) 18 | class Decorator1 extends Component { 19 | render () { 20 | return 21 | } 22 | } 23 | 24 | @withList 25 | class Decorator2 extends Component { 26 | render () { 27 | return 28 | } 29 | } 30 | 31 | storiesOf('Inversion/List', module) 32 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/List.md')})) 33 | .add('FaCC', () => 34 | {Demo} 35 | ) 36 | .add('HOC 1', () => ) 37 | .add('HOC 2', () => ) 38 | .add('Decorator 1', () => ) 39 | .add('Decorator 2', () => ); 40 | -------------------------------------------------------------------------------- /src/List/index.ts: -------------------------------------------------------------------------------- 1 | import {Value, faccToHocInit} from '../Value'; 2 | import renderProp from '../util/renderProp'; 3 | 4 | export interface IListProps { 5 | init?: any[]; 6 | } 7 | 8 | export const List: React.StatelessComponent = (props) => { 9 | return Value({ 10 | init: Array.isArray(props.init) ? props.init : [], 11 | render: (state) => { 12 | const {value, set} = state; 13 | 14 | return renderProp(props, Object.assign(state, { 15 | push: (entry) => set([...value, entry]), 16 | filter: (fn) => set(value.filter(fn)), 17 | sort: (fn?) => set([...value].sort(fn)) 18 | })); 19 | } 20 | }); 21 | }; 22 | 23 | export const withList = faccToHocInit(List, 'list'); 24 | -------------------------------------------------------------------------------- /src/ListTable/index.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const h = React.createElement; 4 | 5 | const defaultRenderRow = cells => h('tr', null, ...cells); 6 | 7 | export interface Props extends React.AllHTMLAttributes { 8 | cols?: number; 9 | renderRow?: (cells: React.ReactElement[]) => React.ReactElement; 10 | } 11 | 12 | export const ListTable: React.SFC = ({cols, renderRow, children, ...rest}) => { 13 | const list = React.Children.toArray(children); 14 | const rows: React.ReactElement[] = []; 15 | const length = list.length; 16 | const rowLength = Math.ceil(length / cols); 17 | 18 | let i = 0; 19 | for (let m = 0; m < rowLength; m++) { 20 | const cells: React.ReactElement[] = []; 21 | for (let n = 0; n < cols; n++) { 22 | cells.push(h('td', null, i < length ? list[i] : null)); 23 | i++; 24 | } 25 | rows.push(renderRow(cells)); 26 | } 27 | 28 | return h('table', rest, h('tbody', null, 29 | ...rows, 30 | )); 31 | }; 32 | 33 | ListTable.defaultProps = { 34 | cols: 3, 35 | renderRow: defaultRenderRow, 36 | }; 37 | -------------------------------------------------------------------------------- /src/LocalStorage/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {LocalStorage} from '..'; 4 | import StoryLocalStorageForm from './StoryLocalStorageForm'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | storiesOf('Side Effects/LocalStorage', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/LocalStorage.md')})) 9 | .add('Basic example', () => 10 |
11 | 12 |
13 |         {``}
14 |       
15 |
16 | ) 17 | .add('Using persist', () => 18 |
19 | 20 |
21 |         {``}
22 |       
23 |
24 | ) 25 | .add('Form example', () => ); 26 | -------------------------------------------------------------------------------- /src/LocalStorage/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {LocalStorage} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing with no value', () => { 8 | const html = renderToStaticMarkup(h(LocalStorage, { 9 | name: 'foo', 10 | data: 'bar' 11 | })); 12 | 13 | expect(html).to.equal(''); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/LocalStorage/local-storage.ts: -------------------------------------------------------------------------------- 1 | export const get = (key: string) => localStorage[key]; 2 | export const set = (key: string, value: string) => localStorage[key] = value; 3 | export const del = (key: string) => { 4 | delete localStorage[key]; 5 | }; 6 | -------------------------------------------------------------------------------- /src/LocationSensor/__tests__/index.test-server.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {LocationSensor} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing', () => { 8 | const html = renderToStaticMarkup( 9 | {() => 10 |
foo
11 | }
12 | ); 13 | 14 | expect(html).to.equal('
foo
'); 15 | }); 16 | 17 | it('returns expected state', () => { 18 | renderToStaticMarkup( 19 | {(location) => { 20 | expect(location).to.eql({ 21 | trigger: 'load', 22 | length: 1 23 | }); 24 | 25 | return null; 26 | }} 27 | ); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/Mailto/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Mailto} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Other/Mailto', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Mailto.md')})) 8 | .add('Example', () => Click me!); 9 | -------------------------------------------------------------------------------- /src/Mailto/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders as expected 1`] = `"
Click me"`; 4 | 5 | exports[` renders as expected with just e-mail 1`] = `""`; 6 | -------------------------------------------------------------------------------- /src/Mailto/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {Mailto} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing as expected', () => { 8 | const html = renderToStaticMarkup(h(Mailto, { 9 | email: 'foo@bar.baz', 10 | subject: 'Hello' 11 | })); 12 | 13 | expect(html).to.equal(''); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/Mailto/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import {mount} from 'enzyme'; 2 | import {h} from '../../util'; 3 | import {Mailto} from '..'; 4 | 5 | describe('', () => { 6 | it('is a component', () => { 7 | expect(Mailto).toBeInstanceOf(Function); 8 | }); 9 | 10 | it('renders as expected with just e-mail', () => { 11 | const wrapper = mount(h(Mailto, { 12 | email: 'foo@bar.baz' 13 | })); 14 | 15 | expect(wrapper.html()).toMatchSnapshot(); 16 | }); 17 | 18 | it('renders as expected ', () => { 19 | const wrapper = mount(h(Mailto, { 20 | email: 'foo@bar.baz', 21 | subject: 'Hello world', 22 | cc: ['foo.cc@bar.baz'], 23 | bcc: ['foo.bcc@bar.baz'], 24 | body: 'Hello world!' 25 | }, 'Click me')); 26 | 27 | expect(wrapper.html()).toMatchSnapshot(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/Mailto/index.ts: -------------------------------------------------------------------------------- 1 | // Original: https://github.com/jaredpalmer/react-fns/blob/master/src/Mailto.tsx 2 | import {h} from '../util'; 3 | import {stringify} from 'qs'; 4 | 5 | export interface IMailtoProps extends React.HTMLAttributes { 6 | email: string; 7 | subject?: string; 8 | cc?: string[]; 9 | bcc?: string[]; 10 | body?: string; 11 | } 12 | 13 | export const Mailto: React.StatelessComponent = ({ 14 | email, 15 | subject = '', 16 | cc, 17 | bcc, 18 | body = '', 19 | ...props 20 | }) => { 21 | const query = stringify({ 22 | subject, 23 | cc: cc && cc.join(', '), 24 | bcc: bcc && bcc.join(', '), 25 | body 26 | }); 27 | 28 | return h('a', { 29 | href: `mailto:${email}?${query}`, 30 | ...props 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /src/Map/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Map, withMap} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Demo = ({get, set, remove}) => 7 |
8 |
a: {get('a')}
9 |
{JSON.stringify(get())}
10 | 11 | 12 |
; 13 | 14 | const Hoc1 = withMap(({map}) => h(Demo, map)); 15 | const Hoc2 = withMap(Demo, '', {c: 'd'}); 16 | 17 | @withMap('foo', {c: 'd'}) 18 | class Decorator1 extends Component { 19 | render () { 20 | return 21 | } 22 | } 23 | 24 | @withMap 25 | class Decorator2 extends Component { 26 | render () { 27 | return 28 | } 29 | } 30 | 31 | storiesOf('Inversion/Map', module) 32 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Map.md')})) 33 | .add('FaCC', () => 34 | {Demo} 35 | ) 36 | .add('HOC 1', () => ) 37 | .add('HOC 2', () => ) 38 | .add('Decorator 1', () => ) 39 | .add('Decorator 2', () => ); 40 | -------------------------------------------------------------------------------- /src/Map/index.ts: -------------------------------------------------------------------------------- 1 | import renderProp from '../util/renderProp'; 2 | import {Value, faccToHocInit} from '../Value'; 3 | 4 | export interface IMapProps { 5 | init?: {[key: string]: any}; 6 | } 7 | 8 | export const Map: React.StatelessComponent = (props) => { 9 | return Value({ 10 | init: props.init && (typeof props.init === 'object') ? props.init : {}, 11 | render: ({value, set}) => renderProp(props, { 12 | get: (key?) => key ? value[key] : value, 13 | set: (key, entry) => set({ 14 | ...value, 15 | [key]: entry 16 | }), 17 | remove: (key) => { 18 | const {[key]: omit, ...rest} = value; 19 | set(rest); 20 | } 21 | }) 22 | }); 23 | }; 24 | 25 | export const withMap = faccToHocInit(Map, 'map'); 26 | -------------------------------------------------------------------------------- /src/Media/parseTimeRanges.ts: -------------------------------------------------------------------------------- 1 | const parseTimeRanges = (ranges) => { 2 | const result = []; 3 | 4 | for (let i = 0; i < ranges.length; i++) { 5 | result.push({ 6 | start: ranges.start(i), 7 | end: ranges.end(i) 8 | }); 9 | } 10 | 11 | return result; 12 | }; 13 | 14 | export default parseTimeRanges; 15 | -------------------------------------------------------------------------------- /src/MotionSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {MotionSensor, withMotion} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Print = ({motion}) => 7 |
 8 |     {JSON.stringify(motion, null, 4)}
 9 |   
; 10 | 11 | const Hoc = withMotion(Print); 12 | 13 | storiesOf('Sensors/MotionSensor', module) 14 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/MotionSensor.md')})) 15 | .add('FaCC', () => 16 | {(state) => 17 |
{JSON.stringify(state, null, 4)}
18 | }
19 | ) 20 | .add('HOC', () => ); 21 | -------------------------------------------------------------------------------- /src/NetworkSensor/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {NetworkSensor} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing', () => { 8 | renderToStaticMarkup(h(NetworkSensor, {}, 9 | () => h('div', {}, 'foobar') 10 | )); 11 | }); 12 | 13 | it('has expected server-side values', () => { 14 | renderToStaticMarkup(h(NetworkSensor, {}, 15 | (net) => { 16 | const {online, since} = net; 17 | 18 | expect(online).to.equal(true); 19 | expect(since).to.equal(undefined); 20 | 21 | return null; 22 | } 23 | )); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/OrientationSensor/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {OrientationSensor, withOrientation} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Print = ({orientation}) => 7 |
 8 |     {JSON.stringify(orientation, null, 4)}
 9 |   
; 10 | 11 | const PrintOrientation = withOrientation(Print); 12 | 13 | storiesOf('Sensors/OrientationSensor', module) 14 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/OrientationSensor.md')})) 15 | .add('FaCC', () => 16 | {(state) => 17 |
{JSON.stringify(state, null, 4)}
18 | }
19 | ) 20 | .add('HOC', () => ); 21 | -------------------------------------------------------------------------------- /src/OutsideClick/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {OutsideClick} from '..'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | 8 | storiesOf('UI/OutsideClick', module) 9 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/OutsideClick.md')})) 10 | .add('FaCC', () => 11 | h(OutsideClick, {onClick: action('onClick')}, 12 | h('div', {style: { 13 | width: 300, 14 | height: 300, 15 | border: '1px solid red' 16 | }}) 17 | ) 18 | ) 19 | -------------------------------------------------------------------------------- /src/Parallax/__story__/StoryParallax1.tsx: -------------------------------------------------------------------------------- 1 | import {Parallax} from '..'; 2 | import {h} from '../../util'; 3 | 4 | const StoryParallax1 = () => { 5 | return ( 6 |
7 |
12 | 13 | console.log(data)}>{(state) => 14 |
{JSON.stringify(state, null, 4)}
15 | }
16 | 17 |
22 |
23 | ); 24 | }; 25 | 26 | export default StoryParallax1; 27 | -------------------------------------------------------------------------------- /src/Parallax/__story__/StoryParallax3.tsx: -------------------------------------------------------------------------------- 1 | import {Parallax} from '..'; 2 | import {h} from '../../util'; 3 | 4 | const StoryParallax1 = () => { 5 | return ( 6 |
7 |
12 | 13 | console.log(data)}>{(state) => 14 |
{JSON.stringify(state, null, 4)}
15 | }
16 | 17 |
22 |
23 | ); 24 | }; 25 | 26 | export default StoryParallax1; 27 | -------------------------------------------------------------------------------- /src/Parallax/__story__/StoryParallax4.tsx: -------------------------------------------------------------------------------- 1 | import {Parallax} from '..'; 2 | import {h} from '../../util'; 3 | 4 | const StoryParallax1 = () => { 5 | return ( 6 |
7 |
12 | 13 | console.log(data)}>{(state) => 14 |
{JSON.stringify(state, null, 4)}
15 | }
16 | 17 |
22 |
23 | ); 24 | }; 25 | 26 | export default StoryParallax1; 27 | -------------------------------------------------------------------------------- /src/Parallax/__story__/StoryParallax5.tsx: -------------------------------------------------------------------------------- 1 | import {Parallax} from '..'; 2 | import {h} from '../../util'; 3 | 4 | const StoryParallax1 = () => { 5 | return ( 6 |
7 |
12 | 13 | console.log(data)}>{(state) => 14 |
{JSON.stringify(state, null, 4)}
15 | }
16 | 17 |
22 |
23 | ); 24 | }; 25 | 26 | export default StoryParallax1; 27 | -------------------------------------------------------------------------------- /src/Parallax/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import StoryParallax1 from './StoryParallax1'; 4 | import StoryParallax2 from './StoryParallax2'; 5 | import StoryParallax3 from './StoryParallax3'; 6 | import StoryParallax4 from './StoryParallax4'; 7 | import StoryParallax5 from './StoryParallax5'; 8 | 9 | storiesOf('UI/Parallax', module) 10 | .add('Basic example', () => ) 11 | .add('Card', () => ) 12 | .add('With margin', () => ) 13 | .add('Distance', () => ) 14 | .add('Scroll slowdown', () => ); 15 | -------------------------------------------------------------------------------- /src/Parallax/__tests__/__snapshots__/index.server.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` Server throws on invalid children in development mode 1`] = `" accepts a single child which must be a plain DOM element or a function that returns one."`; 4 | -------------------------------------------------------------------------------- /src/Pluggable/index.js: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | import {h} from '../util'; 3 | 4 | export class Pluggable extends Component { 5 | plugins = []; 6 | 7 | constructor (props, context) { 8 | super(props, context); 9 | } 10 | 11 | exec = (name) => { 12 | const {plugins} = this.props; 13 | let result; 14 | let method = this.props[name]; 15 | 16 | if (method) { 17 | 18 | } 19 | 20 | for (let i = 0; i < plugins.length; i++) { 21 | method = plugins[i][name]; 22 | 23 | if (!method) { 24 | continue; 25 | } 26 | 27 | result = method(this); 28 | 29 | if (result !== undefined) { 30 | return result; 31 | } 32 | } 33 | }; 34 | 35 | render () { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Portal/index.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {createPortal} from 'react-dom'; 3 | import {isClient, noop} from '../util'; 4 | 5 | export interface IPortalProps { 6 | children?: any; 7 | el?: HTMLElement; 8 | onElement?: (el: HTMLElement) => void; 9 | } 10 | 11 | export interface IPortalState { 12 | } 13 | 14 | export class Portal extends React.Component { 15 | el: HTMLElement = null; 16 | 17 | constructor (props, context) { 18 | super(props, context); 19 | 20 | if (isClient && !props.el) { 21 | this.createEl(); 22 | (props.onElement || noop)(this.el); 23 | } 24 | } 25 | 26 | componentWillUpdate (props) { 27 | if (!props.el && !this.el) { 28 | this.createEl(); 29 | } 30 | } 31 | 32 | componentWillUnmount () { 33 | if (this.el) { 34 | document.body.removeChild(this.el); 35 | } 36 | 37 | this.el = null; 38 | } 39 | 40 | createEl () { 41 | this.el = document.createElement('div'); 42 | document.body.appendChild(this.el); 43 | } 44 | 45 | render () { 46 | if (!isClient) { 47 | return null; 48 | } 49 | 50 | const {el, children} = this.props; 51 | 52 | return createPortal(children, el || this.el); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Prompt/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Prompt} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Generators/Prompt', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Prompt.md')})) 8 | .add('Basic example', () => 9 | 15 | ) 16 | .add('FaCC', () => 17 | {(result) =>
{result}
}
23 | ); 24 | -------------------------------------------------------------------------------- /src/Prompt/index.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {noop, isClient} from '../util'; 3 | 4 | export interface IPromptProps { 5 | children?: React.ReactElement | ((result: string) => React.ReactElement); 6 | show?: boolean; 7 | message?: string; 8 | default?: string; 9 | onResult?: (result: string) => void; 10 | } 11 | 12 | export class Prompt extends React.PureComponent { 13 | componentDidMount () { 14 | if (!(typeof this.props.children === 'function')) { 15 | this.prompt(); 16 | } 17 | } 18 | 19 | componentDidUpdate () { 20 | if (!(typeof this.props.children === 'function')) { 21 | this.prompt(); 22 | } 23 | } 24 | 25 | prompt (): string { 26 | const {show, message, default: def, onResult} = this.props; 27 | 28 | if (show) { 29 | const result = prompt(message, def); 30 | 31 | (onResult || noop)(result); 32 | 33 | return result; 34 | } 35 | 36 | return undefined; 37 | } 38 | 39 | render () { 40 | const {children, default: def} = this.props; 41 | 42 | if (typeof children === 'function') { 43 | if (isClient) { 44 | const result = this.prompt(); 45 | 46 | return children(result); 47 | } else { 48 | return children(def); 49 | } 50 | } else { 51 | return (children as React.ReactElement) || null; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Render/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Render} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Animation/Render', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Render.md')})) 8 | .add('Defaults', () => 9 |
10 | {({value}) => 11 |
{value}
12 | }
13 |
14 | ) 15 | .add('Entry animation', () => 16 |
17 | {({value}) => 18 |
{value}
27 | }
28 |
29 | ) 30 | .add('Entry with delay', () => 31 |
32 | {({value}) => 33 |
{value}
42 | }
43 |
44 | ) 45 | -------------------------------------------------------------------------------- /src/Resolve/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Resolve} from '.'; 4 | 5 | storiesOf('Other/Resolve', module) 6 | .add('Example', () => { 7 | const promise = new Promise((resolve) => { 8 | setTimeout(resolve, 2000); 9 | }); 10 | 11 | return h(Resolve, {promise}, ({pending}) => 12 |
{pending ? 'PENDING...' : 'RESOLVED'}
13 | ) 14 | }); 15 | -------------------------------------------------------------------------------- /src/Ripple/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Ripple} from '..'; 4 | import Button from './Button'; 5 | import ShowDocs from '../../ShowDocs'; 6 | 7 | storiesOf('UI/Ripple', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Ripple.md')})) 9 | .add('Plain example', () => 10 |
11 | 12 |
13 | foobar 14 |
15 |
16 |
17 | ) 18 | .add('Example', () => 19 |
20 | 21 |
29 | foobar 30 |
31 |
32 |
33 | ) 34 | .add('Button', () => ) 35 | -------------------------------------------------------------------------------- /src/ScrollSensor/story.ts: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {ScrollSensor} from '.'; 4 | import ShowDocs from '../ShowDocs' 5 | 6 | class StoryScrollSensorBasic extends Component { 7 | state = { 8 | el: null 9 | }; 10 | 11 | ref = (el) => this.setState({el}); 12 | 13 | render () { 14 | return h('div', { 15 | ref: this.ref, 16 | style: { 17 | border: '1px solid red', 18 | width: 300, 19 | height: 500, 20 | overflow: 'scroll' 21 | } 22 | }, 23 | h(ScrollSensor, {el: this.state.el}, ({x, y}) => 24 | h('div', { 25 | style: { 26 | position: 'absolute', 27 | top: 20, 28 | left: 20 29 | } 30 | }, 31 | `x: ${x}, y: ${y}` 32 | ) 33 | ), 34 | h('div', { style: { 35 | background: 'linear-gradient(to bottom, #fff 0%, #000 100%)', 36 | width: 2000, 37 | height: 2000 38 | }}) 39 | ); 40 | } 41 | } 42 | 43 | storiesOf('Sensors/ScrollSensor', module) 44 | .add('Documentation', () => h(ShowDocs, {md: require('../../docs/en/ScrollSensor.md')})) 45 | .add('Example', () => h(StoryScrollSensorBasic)); 46 | -------------------------------------------------------------------------------- /src/ServerOnly/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {ServerOnly} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Boundaries/ServerOnly', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/ServerOnly.md')})) 8 | .add('Basic example', () => 9 | 10 |
11 | This should be visible only in a browser. 12 |
13 |
14 | ); 15 | -------------------------------------------------------------------------------- /src/ServerOnly/__tests__/index.test-server.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {ServerOnly} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders its children as is', () => { 8 | const html = renderToStaticMarkup(h(ServerOnly, {}, 9 | h('div', {}, 'foobar') 10 | )); 11 | 12 | expect(html).to.equal('
foobar
'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/ServerOnly/index.ts: -------------------------------------------------------------------------------- 1 | const isServer = (typeof process === 'object') && (typeof process.exit === 'function'); 2 | 3 | export const ServerOnly = (props) => isServer ? props.children : null; 4 | -------------------------------------------------------------------------------- /src/ShouldUpdate/__story__/Example1.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {ShouldUpdate} from '..'; 3 | 4 | export class Example1 extends Component { 5 | state = { 6 | cnt: 0 7 | }; 8 | 9 | render () { 10 | return ( 11 | props.cnt > 3} props={this.state}> 12 |
this.setState({cnt: this.state.cnt + 1})}> 13 | Click me ({this.state.cnt}) 14 |
15 |
16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ShouldUpdate/__story__/Example2.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {shouldUpdate} from '..'; 3 | 4 | const Print = ({cnt}) => Click me ({cnt}); 5 | const LazyPrint = shouldUpdate((props) => props.cnt > 3)(Print); 6 | 7 | export class Example2 extends Component { 8 | state = { 9 | cnt: 0 10 | }; 11 | 12 | render () { 13 | return ( 14 |
this.setState({cnt: this.state.cnt + 1})}> 15 | 16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ShouldUpdate/__story__/Example3.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {shouldUpdate} from '..'; 3 | 4 | const Print = ({cnt}) => Click me ({cnt}); 5 | const LazyPrint = shouldUpdate((props) => !(props.cnt % 3))(Print); 6 | 7 | export class Example3 extends Component { 8 | state = { 9 | cnt: 0 10 | }; 11 | 12 | render () { 13 | return ( 14 |
this.setState({cnt: this.state.cnt + 1})}> 15 | 16 |
17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/ShouldUpdate/__story__/Example4.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {pure} from '../../pure'; 3 | 4 | const Print = ({cnt}) => { 5 | console.log('RENDERING'); 6 | 7 | return Click me ({cnt}); 8 | }; 9 | const LazyPrint = pure(Print); 10 | 11 | export class Example4 extends Component { 12 | state = { 13 | cnt: 0 14 | }; 15 | 16 | render () { 17 | return ( 18 |
this.setState({cnt: this.state.cnt + 1})}> 19 | 20 |
21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ShouldUpdate/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {ShouldUpdate} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | import {Example1} from './Example1'; 6 | import {Example2} from './Example2'; 7 | import {Example3} from './Example3'; 8 | import {Example4} from './Example4'; 9 | 10 | storiesOf('Inversion/ShouldUpdate', module) 11 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/ShouldUpdate.md')})) 12 | .add('Renders children', () => 13 | h(ShouldUpdate, {when: () => false, props: {}}, 14 | h('div', {}, 'Hello foobar!') 15 | ) 16 | ) 17 | .add('When greater than 3', () => ) 18 | .add('Passes props', () => ( 19 | true} props={{foo: 'bar'}}>{({foo}) => 20 |
21 | {foo} 22 |
23 | }
24 | )) 25 | .add('HOC - greater than 3', () => ) 26 | .add('HOC - increments of 3', () => ) 27 | .add('pure()', () => ) 28 | -------------------------------------------------------------------------------- /src/ShouldUpdate/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | import {h} from '../util'; 3 | import renderProp from '../util/renderProp'; 4 | 5 | export interface IShouldUpdateProps { 6 | props; 7 | when: (newProps, oldProps) => boolean; 8 | } 9 | 10 | export interface IShouldUpdateState { 11 | } 12 | 13 | export class ShouldUpdate extends Component { 14 | shouldComponentUpdate (props) { 15 | return this.props.when(props.props, this.props.props); 16 | } 17 | 18 | render () { 19 | return renderProp(this.props, this.props.props); 20 | } 21 | } 22 | 23 | export const shouldUpdate = (when) => (Comp) => (props) => 24 | h(ShouldUpdate, {when, props}, h(Comp, props)); 25 | -------------------------------------------------------------------------------- /src/Slider/__tests__/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders as expected 1`] = ` 4 |
8 | 0.2 9 |
10 | `; 11 | -------------------------------------------------------------------------------- /src/Sms/__tests__/__snapshots__/index.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders as expected 1`] = `""`; 4 | 5 | exports[` renders as expected without body 1`] = `""`; 6 | -------------------------------------------------------------------------------- /src/Sms/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {Sms} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing as expected', () => { 8 | const html = renderToStaticMarkup(h(Sms, { 9 | phone: '123', 10 | body: 'foobar' 11 | })); 12 | 13 | expect(html).to.equal(''); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/Sms/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import {mount} from 'enzyme'; 2 | import {h} from '../../util'; 3 | import {Sms} from '..'; 4 | 5 | describe('', () => { 6 | it('is a component', () => { 7 | expect(Sms).toBeInstanceOf(Function); 8 | }); 9 | 10 | it('renders as expected without body', () => { 11 | const wrapper = mount(h(Sms, { 12 | phone: '123' 13 | })); 14 | 15 | expect(wrapper.html()).toMatchSnapshot(); 16 | }); 17 | 18 | it('renders as expected ', () => { 19 | const wrapper = mount(h(Sms, { 20 | phone: '123', 21 | body: 'Hello world' 22 | })); 23 | 24 | expect(wrapper.html()).toMatchSnapshot(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/Sms/index.ts: -------------------------------------------------------------------------------- 1 | // Original: https://github.com/jaredpalmer/react-fns/blob/master/src/Sms.tsx 2 | import {h} from '../util'; 3 | import {stringify} from 'qs'; 4 | 5 | export interface ISmsProps extends React.HTMLAttributes { 6 | children?; 7 | phone: string; 8 | body?: string; 9 | } 10 | 11 | export const Sms: React.StatelessComponent = ({phone, body, ...props}) => { 12 | return h('a', { 13 | href: `sms:${phone}?${stringify({body})}`, 14 | ...props 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /src/Speak/__story__/story.ts: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Speak} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | class StorySpeakBasic extends Component { 7 | state = { 8 | text: 'Hello user, how are you today?', 9 | speakText: '', 10 | }; 11 | 12 | render () { 13 | return h('div', {}, 14 | h('input', {value: this.state.text, onChange: (e) => this.setState({text: e.target.value})}), 15 | h('button', {onClick: () => this.setState({speakText: this.state.text})}, 'Speak'), 16 | h('pre', {style: {fontFamily: 'monospace'}}, 17 | `` 18 | ), 19 | h(Speak, { 20 | text: this.state.speakText 21 | }) 22 | ); 23 | } 24 | } 25 | 26 | storiesOf('Generators/Speak', module) 27 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Speak.md')})) 28 | .add('Example', () => 29 | h(StorySpeakBasic) 30 | ); 31 | -------------------------------------------------------------------------------- /src/Speak/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {Speak} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders nothing without crashing', () => { 8 | const html = renderToStaticMarkup(h(Speak, {text: 'hello'})); 9 | 10 | expect(html).to.equal(''); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/Speak/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | 3 | export interface ISpeakProps { 4 | text: string; 5 | } 6 | 7 | export class Speak extends Component { 8 | componentDidMount () { 9 | this.speak(); 10 | } 11 | 12 | componentDidUpdate (props) { 13 | if (props.text !== this.props.text) { 14 | this.speak(); 15 | } 16 | } 17 | 18 | speak () { 19 | const {text} = this.props; 20 | 21 | if (text && window.speechSynthesis) { 22 | window.speechSynthesis.speak(new SpeechSynthesisUtterance(text)); 23 | } 24 | } 25 | 26 | render () { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/State/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {State, withState} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Cnt = ({cnt, set}) => 7 |
set({cnt: cnt + 1})}>{cnt}
; 8 | 9 | const Hoc1 = withState(Cnt, '', {cnt: 3}); 10 | const Hoc2 = withState(({foobar}) => h(Cnt, foobar), 'foobar', {cnt: 0}); 11 | 12 | storiesOf('Inversion/State', module) 13 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/State.md')})) 14 | .add('Basic example', () => 15 | {({cnt}, set) => 16 |
set({cnt: cnt + 1})} 26 | > 27 | {cnt} 28 |
29 | }
30 | ) 31 | .add('HOC 1', () => ) 32 | .add('HOC 2', () => ); 33 | -------------------------------------------------------------------------------- /src/State/__tests__/index.test-server.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {State} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing with the expected value', () => { 8 | const html = renderToStaticMarkup(h(State, { 9 | init: { 10 | foo: 'bar' 11 | }, 12 | render: ({foo}) =>
{foo}
13 | })); 14 | 15 | expect(html).to.equal('
bar
'); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/Toggle/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Toggle, withToggle} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Checkbox = ({on, toggle}) => 7 |
8 | 9 | Toggle me! 10 |
; 11 | 12 | const Checkbox2 = ({toggle}) => h(Checkbox, toggle); 13 | 14 | const Hoc1 = withToggle(Checkbox, '', {init: true}); 15 | const Hoc2 = withToggle(Checkbox2); 16 | 17 | @withToggle 18 | class Decorator extends Component { 19 | render () { 20 | const {on, toggle} = this.props.toggle; 21 | 22 | return ( 23 |
24 | 25 | Toggle me! 26 |
27 | ); 28 | } 29 | } 30 | 31 | storiesOf('Inversion/Toggle', module) 32 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Toggle.md')})) 33 | .add('Example', () => 34 | {({on, toggle}) => 35 |
36 | 37 | Toggle me! 38 |
39 | }
40 | ) 41 | .add('HOC 1', () => ) 42 | .add('HOC 2', () => ) 43 | .add('Decorator', () => ); 44 | -------------------------------------------------------------------------------- /src/Toggle/index.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {State} from '../State'; 3 | import renderProp from '../util/renderProp'; 4 | import faccToHoc from '../util/faccToHoc'; 5 | 6 | export interface IToggleProps { 7 | init?: boolean; 8 | } 9 | 10 | export const Toggle: React.StatelessComponent = (props) => 11 | h(State, { 12 | init: {on: props.init || false}, 13 | render: ({on}, set) => renderProp(props, { 14 | on, 15 | toggle: () => set({on: !on}), 16 | set: (on) => set({on}) 17 | }) 18 | }); 19 | 20 | export const withToggle = faccToHoc(Toggle, 'toggle'); 21 | -------------------------------------------------------------------------------- /src/TouchSupportSensor/__tests__/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` returns expected default state 1`] = ` 4 | Object { 5 | "touchSupported": false, 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /src/Tween/createBezierEasing.ts: -------------------------------------------------------------------------------- 1 | const precision = 1e-3; 2 | const maxIterations = 5; 3 | 4 | const createBezierEasing = (x1, y1, x2, y2) => { 5 | const Cx = 3 * x1; 6 | const Bx = 3 * (x2 - x1) - Cx; 7 | const Bx2 = Bx * 2; 8 | const Ax = 1 - Cx - Bx; 9 | const Ax3 = Ax * 3; 10 | const Cy = 3 * y1; 11 | const By = 3 * (y2 - y1) - Cy; 12 | const Ay = 1 - Cy - By; 13 | 14 | return (t) => { 15 | let x = t; 16 | let i = 0; 17 | let z; 18 | 19 | // Newton's root finding algorithm. 20 | for (; i < maxIterations; i++) { 21 | z = x * (Cx + x * (Bx + x * Ax)) - t; 22 | if (Math.abs(z) < precision) { 23 | break; 24 | } 25 | x = x - z / (Cx + x * (Bx2 + x * Ax3)); 26 | } 27 | 28 | return x * (Cy + x * (By + x * Ay)); 29 | }; 30 | }; 31 | 32 | export default createBezierEasing; 33 | -------------------------------------------------------------------------------- /src/Value/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Value, withValue} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | const Val = ({value, set}) => 7 | set(e.target.value)} />; 8 | 9 | const Hoc1 = withValue(({value}) => h(Val, value), 'value', 'default'); 10 | // const Hoc2 = withToggle(Checkbox2); 11 | 12 | @withValue 13 | class Decorator extends Component { 14 | render () { 15 | return 16 | } 17 | } 18 | 19 | storiesOf('Inversion/Value', module) 20 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/Value.md')})) 21 | .add('Example', () => 22 | {(props) => } 23 | ) 24 | .add('HOC 1', () => ) 25 | .add('Decorator', () => ); 26 | -------------------------------------------------------------------------------- /src/Value/index.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {State} from '../State'; 3 | import renderProp from '../util/renderProp'; 4 | import faccToHoc from '../util/faccToHoc'; 5 | 6 | export interface IValueProps { 7 | init?: any; 8 | children?: (state) => React.ReactElement; 9 | render?: (state) => React.ReactElement; 10 | } 11 | 12 | export const Value: React.StatelessComponent = (props) => { 13 | return h(State, { 14 | init: { 15 | value: props.init 16 | }, 17 | render: ({value}, set) => renderProp(props, { 18 | value, 19 | set: (value) => set({value}) 20 | }) 21 | }); 22 | }; 23 | 24 | export const faccToHocInit = (Facc, name) => (Comp, name2?, init?) => { 25 | const isClassDecoratorMethodCall = typeof Comp === 'string'; 26 | 27 | if (isClassDecoratorMethodCall) { 28 | return faccToHoc(Facc, name)(Comp, {init: name2}); 29 | } else { 30 | return faccToHoc(Facc, name)(Comp, name2, {init}); 31 | } 32 | }; 33 | 34 | export const withValue = faccToHocInit(Value, 'value'); 35 | -------------------------------------------------------------------------------- /src/Vibrate/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {Vibrate} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders nothing without crashing', () => { 8 | const html = renderToStaticMarkup(h(Vibrate, {ms: 300})); 9 | 10 | expect(html).to.equal(''); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/Vibrate/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | 3 | export interface IVibrateProps { 4 | ms: number | number[]; 5 | } 6 | 7 | export class Vibrate extends Component { 8 | componentDidMount () { 9 | this.vibrate(); 10 | } 11 | 12 | componentDidUpdate (props) { 13 | if (String(props.ms) !== String(this.props.ms)) { 14 | this.vibrate(); 15 | } 16 | } 17 | 18 | vibrate () { 19 | if (navigator.vibrate) { 20 | navigator.vibrate(this.props.ms); 21 | } 22 | } 23 | 24 | render () { 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Vibrate/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Vibrate} from '.'; 4 | import ShowDocs from '../ShowDocs' 5 | 6 | storiesOf('Generators/Vibrate', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../docs/en/Vibrate.md')})) 8 | .add('One beep', () => 9 |
10 | 11 |
12 |         {''}
13 |       
14 |
15 | ) 16 | .add('Sequence', () => 17 |
18 | 19 |
20 |         {''}
21 |       
22 |
23 | ); 24 | -------------------------------------------------------------------------------- /src/Video/index.ts: -------------------------------------------------------------------------------- 1 | import {Media, IMediaProps, IMediaState, IMedia} from '../Media'; 2 | import renderProp from '../util/renderProp'; 3 | 4 | export interface IVideoProps extends IMediaProps { 5 | 6 | } 7 | 8 | export interface IVideoState extends IMediaState { 9 | 10 | } 11 | 12 | export interface IVideo extends IMedia { 13 | video: React.ReactElement; 14 | } 15 | 16 | export class Video extends Media implements IVideo { 17 | el: HTMLVideoElement = null; 18 | video: React.ReactElement = null; 19 | 20 | render () { 21 | this.video = super.render(); 22 | 23 | return renderProp(this.props, this as IVideo, this.state) || this.video; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/View/__tests__/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` renders child function as children 1`] = ` 4 |
5 | children.... 6 |
7 | `; 8 | 9 | exports[` renders its children 1`] = ` 10 |
11 | children.... 12 |
13 | `; 14 | 15 | exports[` renders nothing by default 1`] = `null`; 16 | -------------------------------------------------------------------------------- /src/View/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {render} from 'react-dom'; 3 | import renderer from 'react-test-renderer'; 4 | import {View} from '..'; 5 | 6 | describe('', () => { 7 | it('renders without crashing', () => { 8 | render(, document.createElement('div')); 9 | }); 10 | 11 | it('renders nothing by default', () => { 12 | expect(renderer.create().toJSON()).toMatchSnapshot(); 13 | }); 14 | 15 | it('renders its children', () => { 16 | const jsx =
children....
; 17 | 18 | expect(renderer.create(jsx).toJSON()).toMatchSnapshot(); 19 | }); 20 | 21 | it('renders child function as children', () => { 22 | const jsx = {() =>
children....
}
; 23 | 24 | expect(renderer.create(jsx).toJSON()).toMatchSnapshot(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/View/index.ts: -------------------------------------------------------------------------------- 1 | import {render} from 'react-universal-interface'; 2 | 3 | export const View = (props) => render(props, null); 4 | -------------------------------------------------------------------------------- /src/ViewportObserverSensor/__story__/StoryViewportSensorBasic.tsx: -------------------------------------------------------------------------------- 1 | import {h} from '../../util'; 2 | 3 | const StoryViewportSensorBasic = ({sensor: Sensor, onChange}) => { 4 | return ( 5 |
6 |
11 | 12 | {(state) => 13 |
{JSON.stringify(state, null, 4)}
14 | }
15 | 16 |
21 |
22 | ); 23 | }; 24 | 25 | export default StoryViewportSensorBasic; 26 | -------------------------------------------------------------------------------- /src/ViewportObserverSensor/__story__/StoryViewportSensorConf.tsx: -------------------------------------------------------------------------------- 1 | import {h} from '../../util'; 2 | 3 | const StoryViewportSensorConf = ({sensor: Sensor, onChange, threshold, margin=[0, 0, 0, 0]}) => { 4 | return ( 5 |
6 |
11 | 12 | {(state) => 18 |
25 |
{JSON.stringify(state, null, 4)}
26 |
27 | }
28 | 29 |
34 |
35 | ); 36 | }; 37 | 38 | export default StoryViewportSensorConf; 39 | -------------------------------------------------------------------------------- /src/ViewportObserverSensor/__story__/StoryViewportSensorHorizontal.tsx: -------------------------------------------------------------------------------- 1 | import {h} from '../../util'; 2 | 3 | const StoryViewportSensorHorizontal = ({sensor: Sensor, onChange}) => { 4 | return ( 5 |
10 |
15 | {(state) => 16 |
{JSON.stringify(state, null, 4)}
17 | }
18 |
19 |
20 | ); 21 | }; 22 | 23 | export default StoryViewportSensorHorizontal; 24 | -------------------------------------------------------------------------------- /src/ViewportSensor/__story__/StoryViewportSensorBasicJsx.tsx: -------------------------------------------------------------------------------- 1 | import {h} from '../../util'; 2 | 3 | const StoryViewportSensorBasicJsx = ({children}) => { 4 | return ( 5 |
6 |
11 | 12 | {children} 13 | 14 |
19 |
20 | ); 21 | }; 22 | 23 | export default StoryViewportSensorBasicJsx; 24 | -------------------------------------------------------------------------------- /src/ViewportSensor/index.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {ViewportObserverSensor, IViewportObserverSensorProps} from '../ViewportObserverSensor'; 3 | import {loadable} from '../loadable'; 4 | import faccToHoc, {divWrapper} from '../util/faccToHoc'; 5 | 6 | let Sensor: any = ViewportObserverSensor; 7 | 8 | if (typeof window === 'object') { 9 | if (!(window as any).IntersectionObserver) { 10 | const loader = () => import('../ViewportScrollSensor').then((module) => module.ViewportScrollSensor); 11 | Sensor = loadable({loader}); 12 | Sensor.load(); 13 | } 14 | } 15 | 16 | export const ViewportSensor: React.StatelessComponent = (props) => { 17 | return h(Sensor, props); 18 | }; 19 | 20 | const wrapper = (Comp, propName, props, state) => 21 | divWrapper(Comp, propName || 'viewport', props, state.visible); 22 | 23 | export const withViewport = faccToHoc(ViewportSensor, 'viewport', wrapper); 24 | -------------------------------------------------------------------------------- /src/WhenIdle/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {WhenIdle} from '..'; 4 | import ShowDocs from '../../ShowDocs' 5 | 6 | storiesOf('Animation/WhenIdle', module) 7 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/WhenIdle.md')})) 8 | .add('Defaults', () => 9 | 10 | Hello world! 11 | 12 | ) 13 | .add('Timeout prop', () => 14 | 15 | Hello world! 16 | 17 | ) 18 | -------------------------------------------------------------------------------- /src/WhenIdle/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | 3 | const RIC = (window as any).requestIdleCallback || ((fn, {timeout = 35} = {}) => setTimeout(fn, timeout)); 4 | 5 | export interface IWhenIdleProps { 6 | timeout?: number; 7 | } 8 | 9 | export interface IWhenIdleState { 10 | ready: boolean; 11 | } 12 | 13 | export class WhenIdle extends Component { 14 | mounted = false; 15 | 16 | state: IWhenIdleState = { 17 | ready: false 18 | }; 19 | 20 | componentDidMount () { 21 | this.mounted = true; 22 | 23 | const {timeout} = this.props; 24 | 25 | RIC(() => { 26 | if (this.mounted) { 27 | this.setState({ready: true}); 28 | } 29 | }, {timeout}); 30 | } 31 | 32 | componentWillUnmount () { 33 | this.mounted = false; 34 | } 35 | 36 | render () { 37 | return this.state.ready ? this.props.children : null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/WidthQuery/__tests__/__snapshots__/index.test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` picks the correct by maxWidth 1 1`] = ` 4 | 7 | view 4 8 | 9 | `; 10 | 11 | exports[` picks the correct by maxWidth 2 1`] = ` 12 | 15 | view 4 16 | 17 | `; 18 | 19 | exports[` renders first matching view 1`] = ` 20 | 23 | view 1 24 | 25 | `; 26 | 27 | exports[` treats empty maxWidth as Infinity 1`] = ` 28 | 29 | view 5 30 | 31 | `; 32 | -------------------------------------------------------------------------------- /src/WidthQuery/index.ts: -------------------------------------------------------------------------------- 1 | export {View} from '../View'; 2 | 3 | export interface IWidthQueryProps { 4 | width: number, 5 | } 6 | 7 | export const WidthQuery: React.SFC = ({width, children}) => { 8 | if (process.env.NODE_ENV !== 'production') { 9 | if (!Array.isArray(children)) { 10 | throw new TypeError(' expects multiple children.'); 11 | } 12 | } 13 | 14 | for (const child of children as any) { 15 | const {maxWidth = Infinity, minWidth = 0} = child.props; 16 | 17 | if (maxWidth > width && minWidth <= width) { 18 | return child; 19 | } 20 | 21 | if (maxWidth === Infinity && width === Infinity) { 22 | return child; 23 | } 24 | } 25 | 26 | return null; 27 | }; 28 | -------------------------------------------------------------------------------- /src/WidthSensor/__story__/story.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {WidthSensor, withWidth} from '..'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | const Print = (props) => h('pre', { 8 | style: {fontFamily: 'monospace'} 9 | }, JSON.stringify(props, null, 4)); 10 | 11 | const Hoc1 = withWidth(Print); 12 | const Hoc2 = withWidth(Print, 'mySize'); 13 | const Hoc3 = withWidth(Print, ''); 14 | 15 | storiesOf('Sensors/WidthSensor', module) 16 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/WidthSensor.md')})) 17 | .add('Example', () => 18 | h('div', {}, 19 | h(WidthSensor, { 20 | onWidth: action('onWidth') 21 | }, ({width, height}) => 22 | h('div', { 23 | style: { 24 | border: '1px solid red' 25 | } 26 | }, 27 | `WIDTH: ${width}, HEIGHT: ${height}` 28 | ) 29 | ) 30 | ) 31 | ) 32 | .add('HOC 1', () => h(Hoc1)) 33 | .add('HOC 2', () => h(Hoc2)) 34 | .add('HOC 3', () => h(Hoc3)); 35 | -------------------------------------------------------------------------------- /src/WidthSensor/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | import {SizeSensor, ISizeSensorProps, ISizeSensorState} from '../SizeSensor'; 3 | import {noop, h} from '../util'; 4 | import faccToHoc, {divWrapper} from '../util/faccToHoc'; 5 | 6 | export interface IWidthSensorProps extends ISizeSensorProps { 7 | onWidth?: (size: ISizeSensorState) => void; 8 | } 9 | 10 | export class WidthSensor extends Component { 11 | static defaultProps = { 12 | onWidth: noop 13 | }; 14 | 15 | state = { 16 | width: Infinity, 17 | height: Infinity, 18 | }; 19 | 20 | onSize = (size) => { 21 | if (this.state.width !== size.width) { 22 | this.setState(size); 23 | this.props.onWidth(size); 24 | } 25 | }; 26 | 27 | render () { 28 | const {onWidth, ..._rest} = this.props; 29 | const rest: ISizeSensorProps = _rest; 30 | 31 | rest.onSize = this.onSize; 32 | 33 | return h(SizeSensor, rest); 34 | } 35 | } 36 | 37 | export const withWidth = faccToHoc(WidthSensor, 'width', divWrapper); 38 | -------------------------------------------------------------------------------- /src/WindowScrollSensor/__story__/story.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {WindowScrollSensor} from '..'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | storiesOf('Sensors/WindowScrollSensor', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/WindowScrollSensor.md')})) 9 | .add('Example', () => 10 | h('div', {style: { 11 | background: 'linear-gradient(to bottom, #fff 0%, #000 100%)', 12 | width: 5000, 13 | height: 5000 14 | }}, 15 | h(WindowScrollSensor, {}, ({x, y}) => 16 | h('div', { 17 | style: { 18 | border: '1px solid red', 19 | position: 'fixed', 20 | top: 0, 21 | left: 0, 22 | width: '100%' 23 | } 24 | }, 25 | `x: ${x}, y: ${y}` 26 | ) 27 | ) 28 | ) 29 | ) 30 | .add('No children', () => 31 | h('div', {style: { 32 | background: 'linear-gradient(to bottom, #fff 0%, #000 100%)', 33 | width: 5000, 34 | height: 5000 35 | }}, 36 | h(WindowScrollSensor, { 37 | onChange: action('onChange') 38 | }) 39 | ) 40 | ) 41 | -------------------------------------------------------------------------------- /src/WindowScrollSensor/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {WindowScrollSensor} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders as expected', () => { 8 | const html = renderToStaticMarkup(h(WindowScrollSensor, {}, 9 | ({x, y}) => h('div', {}, `${x}, ${y}`) 10 | )); 11 | 12 | expect(html).to.equal('
0, 0
'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/WindowSizeSensor/__story__/story.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {WindowSizeSensor} from '..'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | storiesOf('Sensors/WindowSizeSensor', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/WindowSizeSensor.md')})) 9 | .add('Example', () => 10 | h(WindowSizeSensor, {}, ({width, height}) => 11 | h('div', {}, 12 | `WIDTH: ${width}, HEIGHT: ${height}` 13 | ) 14 | ) 15 | ) 16 | .add('No children', () => 17 | h(WindowSizeSensor, {onChange: action('onChange')}) 18 | ) 19 | -------------------------------------------------------------------------------- /src/WindowSizeSensor/__tests__/index.test-server.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {WindowSizeSensor} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders without crashing', () => { 8 | const html = renderToStaticMarkup(h(WindowSizeSensor, {}, 9 | ({width, height}) => h('div', {}, `${width}, ${height}`) 10 | )); 11 | 12 | expect(html).to.equal('
1920, 1080
'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/WindowSizeSensor/index.ts: -------------------------------------------------------------------------------- 1 | import {Component} from 'react'; 2 | import {SyncSensor} from '../SyncSensor'; 3 | import {isClient, h} from '../util'; 4 | import faccToHoc from '../util/faccToHoc'; 5 | import {IUniversalInterfaceProps} from '../typing'; 6 | 7 | export interface IWindowSizeSensorValue { 8 | width: number; 9 | height: number; 10 | } 11 | 12 | export interface IWindowSizeSensorProps extends IUniversalInterfaceProps { 13 | onChange?: (state: IWindowSizeSensorValue) => void; 14 | } 15 | 16 | const addListener = (handler) => window.addEventListener('resize', handler); 17 | const removeListener = (handler) => window.removeEventListener('resize', handler); 18 | const onEvent = () => ({ 19 | width: window.innerWidth, 20 | height: window.innerHeight 21 | }); 22 | 23 | const getInitialState = () => { 24 | if (isClient) { 25 | return onEvent(); 26 | } else { 27 | return { 28 | width: 1920, 29 | height: 1080 30 | }; 31 | } 32 | }; 33 | 34 | export class WindowSizeSensor extends Component { 35 | initial = getInitialState(); 36 | 37 | render () { 38 | return h(SyncSensor, { 39 | ...this.props, 40 | initial: this.initial, 41 | addListener, 42 | removeListener, 43 | onEvent 44 | }); 45 | } 46 | } 47 | 48 | export const withWindowSize = faccToHoc(WindowSizeSensor, 'windowSize'); 49 | -------------------------------------------------------------------------------- /src/WindowWidthQuery/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {WindowWidthQuery} from '..'; 4 | import {View} from '../../View'; 5 | import ShowDocs from '../../ShowDocs'; 6 | 7 | storiesOf('UI/WindowWidthQuery', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/WindowWidthQuery.md')})) 9 | .add('Example', () => 10 | 11 | 12 | Up to 300px 13 | 14 | 15 | More than 300px 16 | 17 | 18 | ) 19 | .add('100px increments', () => 20 | 21 | 22 | Up to 100px 23 | 24 | 25 | 100px - 200px 26 | 27 | 28 | 200px - 300px 29 | 30 | 31 | 300px - 400px 32 | 33 | 34 | 400px - 500px 35 | 36 | 37 | 500px - 600px 38 | 39 | 40 | 600px - 700px 41 | 42 | 43 | 700px+ 44 | 45 | 46 | ) 47 | -------------------------------------------------------------------------------- /src/WindowWidthQuery/index.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {WidthQuery} from '../WidthQuery'; 3 | import {WindowWidthSensor} from '../WindowWidthSensor'; 4 | 5 | export {View} from '../View'; 6 | 7 | export const WindowWidthQuery = (props) => 8 | h(WindowWidthSensor, null, 9 | (state) => h(WidthQuery, state, props.children) 10 | ); 11 | -------------------------------------------------------------------------------- /src/WindowWidthSensor/__story__/story.js: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {WindowWidthSensor} from '..'; 5 | import ShowDocs from '../../ShowDocs' 6 | 7 | storiesOf('Sensors/WindowWidthSensor', module) 8 | .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/WindowWidthSensor.md')})) 9 | .add('Example', () => 10 | h(WindowWidthSensor, {onWidth: action('onWidth')}, ({width, height}) => 11 | h('div', {}, 12 | `WIDTH: ${width}, HEIGHT: ${height}` 13 | ) 14 | ) 15 | ) 16 | .add('No children', () => 17 | h(WindowWidthSensor, {onWidth: action('onWidth')}) 18 | ) 19 | -------------------------------------------------------------------------------- /src/__story__/lazy.story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {lazy} from '../lazy'; 4 | import ShowDocs from '../ShowDocs' 5 | 6 | const MyComponent = () =>
; 11 | 12 | const Spinner = () => Spinner :); 13 | 14 | const Lazy1 = lazy({ 15 | loader: () => new Promise((resolve) => { 16 | setTimeout(() => { 17 | resolve(MyComponent); 18 | }, 1000); 19 | }), 20 | loading: 'Loading...' 21 | }); 22 | 23 | const Lazy2 = lazy({ 24 | loader: () => new Promise((resolve) => { 25 | setTimeout(() => { 26 | resolve(MyComponent); 27 | }, 1000); 28 | }), 29 | loading: 30 | }); 31 | 32 | const Lazy3 = lazy({ 33 | // @ts-ignore 34 | loader: () => new Promise((resolve, reject) => { 35 | setTimeout(() => { 36 | reject(new Error('could not load')); 37 | }, 1000); 38 | }), 39 | loading: , 40 | error: (error) => { 41 | console.log(error); 42 | 43 | return () =>
Could not load
; 44 | } 45 | }); 46 | 47 | storiesOf('Dummies/lazy()', module) 48 | .add('Documentation', () => h(ShowDocs, {md: require('../../docs/en/lazy.md')})) 49 | .add('Loads in 1s', () => ) 50 | .add('Loads in 1s with ', () => ) 51 | .add('Error', () => ) 52 | -------------------------------------------------------------------------------- /src/__story__/loadable.story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import ShowDocs from '../ShowDocs' 4 | 5 | storiesOf('Dummies/loadable()', module) 6 | .add('Documentation', () => h(ShowDocs, {md: require('../../docs/en/loadable.md')})) 7 | -------------------------------------------------------------------------------- /src/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | const {configure} = require('enzyme'); 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | configure({ 5 | adapter: new Adapter() 6 | }); 7 | 8 | if (typeof window === 'object') { 9 | global.requestAnimationFrame = window.requestAnimationFrame = (callback) => setTimeout(callback, 17); 10 | global.matchMedia = window.matchMedia = (() => { return { matches: false, addListener: () => {}, removeListener: () => {}, }; }); 11 | } 12 | -------------------------------------------------------------------------------- /src/context/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h, Component} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Provider, Consumer, withContext} from '..'; 4 | 5 | const ColorIs = ({ctx}) =>
Color is: {ctx.color}
; 6 | const ColorIsConnected = withContext(ColorIs, 'ctx', {name: 'ctx'}); 7 | 8 | @withContext('', {name: 'theme'}) 9 | class Decorator1 extends Component { 10 | render () { 11 | return ; 12 | } 13 | } 14 | 15 | storiesOf('Context/context', module) 16 | .add('FaCC', () => 17 | 18 | {(theme) => { 19 | return
Color is: {theme.color}
; 20 | }}
21 |
22 | ) 23 | .add('HOC', () => 24 | 25 | 26 | 27 | ) 28 | .add('Decorator 1', () => 29 | 30 | 31 | 32 | ); 33 | -------------------------------------------------------------------------------- /src/context/observable.ts: -------------------------------------------------------------------------------- 1 | export type TObservalbeUnsub = (() => void); 2 | export type TObservableSet = (state: T) => void; 3 | export interface IObservable { 4 | get: () => T; 5 | set: TObservableSet; 6 | sub: (listener: TObservableSet) => TObservalbeUnsub; 7 | } 8 | 9 | export const observable: (state?: T) => IObservable = state => { 10 | let listeners = []; 11 | let currentState = state; 12 | 13 | const get = () => currentState; 14 | 15 | const set = state => { 16 | currentState = state; 17 | for (const listener of listeners) listener(currentState); 18 | }; 19 | 20 | const sub = listener => { 21 | listeners.push(listener); 22 | 23 | return () => (listeners = listeners.filter(item => item !== listener)); 24 | }; 25 | 26 | return { 27 | get, 28 | set, 29 | sub, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /src/cssvars/__story__/Example1.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {CssVarsProvider, CssVars} from '..'; 3 | 4 | const Print = (props) =>
{JSON.stringify(props, null, 4)}
; 5 | 6 | export class Example1 extends Component { 7 | el; 8 | vars; 9 | 10 | ref = (el) => { 11 | this.el = el; 12 | }; 13 | 14 | componentDidMount () { 15 | this.applyCssVars(); 16 | } 17 | 18 | componentDidUpdate () { 19 | this.applyCssVars(); 20 | } 21 | 22 | applyCssVars () { 23 | if (this.el && this.vars) { 24 | for (const name in this.vars) { 25 | console.log('name, this.vars[name]', name, this.vars[name]); 26 | this.el.style.setProperty(name, this.vars[name]); 27 | } 28 | } 29 | } 30 | 31 | render () { 32 | return ( 33 | 37 | {(vars) => { 38 | this.vars = vars; 39 | 40 | return ( 41 |
42 | 43 | 44 |
45 | ); 46 | }}
47 |
48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/cssvars/__tests__/index.test-server-disable.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {renderToStaticMarkup} from 'react-dom/server'; 3 | import {expect} from 'chai'; 4 | import {CssVarsProvider, CssVars} from '..'; 5 | 6 | describe(' SSR', () => { 7 | it('renders as expected without crashing', () => { 8 | const element = ( 9 | 13 | {(vars) => 14 |
15 | {JSON.stringify(vars)} 16 |
17 | }
18 |
19 | ); 20 | const html = renderToStaticMarkup(element); 21 | 22 | expect(html).to.equal('
{"color":"tomato","border":"1px solid tomato"}
'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './util'; 2 | 3 | // Dummies 4 | export * from './mock'; 5 | export * from './loadable'; 6 | export * from './lazy'; 7 | export * from './delayed'; 8 | export * from './viewport'; 9 | export * from './invert'; 10 | 11 | // Sensors 12 | export * from './SizeSensor'; 13 | export * from './WidthSensor'; 14 | export * from './MediaSensor'; 15 | export * from './ScrollSensor'; 16 | export * from './WindowSizeSensor'; 17 | export * from './WindowScrollSensor'; 18 | export * from './LightSensor'; 19 | export * from './LocationSensor'; 20 | 21 | // Generators 22 | export * from './Speak'; 23 | export * from './Vibrate'; 24 | export * from './LocalStorage'; 25 | export * from './Audio'; 26 | 27 | // Context 28 | export * from './context'; 29 | export * from './theme'; 30 | export * from './route'; 31 | export * from './CssVars'; 32 | 33 | // UI 34 | export * from './Portal'; 35 | export * from './Overlay'; 36 | export * from './Modal'; 37 | export * from './FullScreen'; 38 | export * from './Ripple'; 39 | export * from './Slider'; 40 | export * from './DropArea'; 41 | 42 | // Animation 43 | export * from './AfterTimeout'; 44 | export * from './AfterDraf'; 45 | export * from './WhenIdle'; 46 | export * from './Render'; 47 | export * from './RenderInterval'; 48 | export * from './Tween'; 49 | export * from './Interpolation'; 50 | 51 | // Other 52 | export * from './Resolve'; 53 | -------------------------------------------------------------------------------- /src/lazy.ts: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {loadable, ILoadableParams} from './loadable'; 3 | 4 | export interface ILazyParams extends ILoadableParams { 5 | 6 | } 7 | 8 | export interface ILazyComponent extends React.SFC { 9 | load(); 10 | } 11 | 12 | export type TLazy = (params: ILazyParams) => ILazyComponent; 13 | 14 | export const lazy: TLazy = (params) => { 15 | const Loadable = loadable(params); 16 | 17 | let needsLoading = true; 18 | const Lazy: ILazyComponent = ((props: TProps) => { 19 | if (needsLoading) { 20 | needsLoading = false; 21 | Loadable.load(); 22 | } 23 | 24 | return h(Loadable, props); 25 | }) as ILazyComponent; 26 | 27 | Lazy.load = Loadable.load; 28 | 29 | return Lazy; 30 | }; 31 | -------------------------------------------------------------------------------- /src/loadable.ts: -------------------------------------------------------------------------------- 1 | import {mock, IMockParams, IMockComponent} from './mock'; 2 | import {noop} from './util'; 3 | 4 | export type TLoaderPromise = () => Promise | React.StatelessComponent>; 5 | export type TLoader = TLoaderPromise; 6 | 7 | export interface ILoadableParams extends IMockParams { 8 | error?: (error) => (React.Component | React.SFC); 9 | loader: TLoader; 10 | } 11 | 12 | export interface ILoadableComponent extends IMockComponent { 13 | load(); 14 | } 15 | 16 | export type TLoadable = (params: ILoadableParams) => ILoadableComponent; 17 | 18 | export const loadable: TLoadable = (params: ILoadableParams) => { 19 | const {loader} = params; 20 | const Mock: ILoadableComponent = mock(params) as ILoadableComponent; 21 | 22 | Mock.load = () => { 23 | loader().then((Implementation) => { 24 | Mock.implement((Implementation as any).default || Implementation); 25 | }, (error) => { 26 | const element = params.error ? params.error(error) : null; 27 | 28 | Mock.implement(element || null); 29 | }); 30 | 31 | Mock.load = noop; 32 | }; 33 | 34 | return Mock; 35 | }; 36 | -------------------------------------------------------------------------------- /src/nano.ts: -------------------------------------------------------------------------------- 1 | import {create} from 'nano-css'; 2 | import {addon as addonRule} from 'nano-css/addon/rule'; 3 | import {addon as addonKeyframes} from 'nano-css/addon/keyframes'; 4 | 5 | const nano = create({ 6 | pfx: 'libreact-' 7 | }); 8 | 9 | addonRule(nano); 10 | addonKeyframes(nano); 11 | 12 | const {rule, keyframes} = nano; 13 | 14 | export { 15 | nano, 16 | rule, 17 | keyframes, 18 | }; 19 | -------------------------------------------------------------------------------- /src/pixel.ts: -------------------------------------------------------------------------------- 1 | import {h} from './util'; 2 | 3 | export const pixel = h('div', { 4 | style: { 5 | width: 1, 6 | height: 1, 7 | overflow: 'hidden', 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /src/pure.ts: -------------------------------------------------------------------------------- 1 | import {shallowEqual} from 'fast-af/shallowEqual'; 2 | import {shouldUpdate} from './ShouldUpdate'; 3 | 4 | export const pure = shouldUpdate((a, b) => !shallowEqual(a, b)); 5 | -------------------------------------------------------------------------------- /src/route/Route.ts: -------------------------------------------------------------------------------- 1 | import render from '../util/renderProp'; 2 | import Match, {IMatchProps} from './Match'; 3 | import {h} from '../util'; 4 | 5 | const Route: React.SFC = props => 6 | h(Match, props, data => 7 | data.matches 8 | ? render(props, data) 9 | : null 10 | ); 11 | 12 | export default Route; 13 | -------------------------------------------------------------------------------- /src/route/Router.ts: -------------------------------------------------------------------------------- 1 | import {Provider} from '../context'; 2 | import {h, ns} from '../util'; 3 | import {TRouteMatchResult} from './createMatcher'; 4 | 5 | export interface IRouteProviderProps { 6 | route: string; 7 | children: any; 8 | ns?: string; 9 | fullRoute?: string; 10 | parent?: TRouteMatchResult; 11 | } 12 | 13 | const Router: React.SFC = (props) => { 14 | const {ns: namespace, route, fullRoute, parent, children} = props; 15 | 16 | if (process.env.NODE_ENV !== 'production') { 17 | if (typeof route !== 'string') { 18 | throw new TypeError('Router route must be a string.'); 19 | } 20 | } 21 | 22 | return h(Provider as any, { 23 | name: ns(`route/${namespace}`), 24 | value: { 25 | fullRoute: fullRoute || route, 26 | route, 27 | parent, 28 | } 29 | }, children); 30 | } 31 | 32 | export default Router; 33 | -------------------------------------------------------------------------------- /src/route/Switch.ts: -------------------------------------------------------------------------------- 1 | import {Children} from 'react'; 2 | import {Consumer} from '../context'; 3 | import {h, ns} from '../util'; 4 | import Route from './Route'; 5 | import createMatcher from './createMatcher'; 6 | 7 | export interface ISwitchProps { 8 | ns?: string; 9 | } 10 | 11 | const Switch: React.SFC = (props) => { 12 | return h(Consumer, {name: ns(`route/${props.ns}`)}, ({route}) => { 13 | const routes = Children.toArray(props.children); 14 | 15 | for (let i = 0; i < routes.length; i++) { 16 | if (process.env.NODE_ENV !== 'production') { 17 | if ((typeof routes[i] !== 'object') || ((routes[i] as any).type !== Route)) { 18 | throw new TypeError('All children must be elements.'); 19 | } 20 | } 21 | 22 | const {match, exact} = (routes[i] as any).props; 23 | const matchResult = createMatcher(match, exact)(route); 24 | 25 | if (matchResult) { 26 | return routes[i]; 27 | } 28 | } 29 | 30 | return null; 31 | }); 32 | }; 33 | 34 | export default Switch; 35 | -------------------------------------------------------------------------------- /src/route/createMatcher.ts: -------------------------------------------------------------------------------- 1 | export type TRouteMatchResult = RegExpMatchArray | string[]; 2 | 3 | export type TRouteMatcher = (route: string) => TRouteMatchResult; 4 | 5 | export default function createMatcher (match: string | RegExp | TRouteMatcher, exact?: boolean): TRouteMatcher { 6 | if (typeof match === 'function') { 7 | return match; 8 | } 9 | 10 | let regex = typeof match === 'string' 11 | ? new RegExp(`^(${match}${exact ? '$' : ''})`) 12 | : match; 13 | 14 | return (route: string) => route.match(regex); 15 | } 16 | -------------------------------------------------------------------------------- /src/route/createRouter.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import Router from './Router'; 3 | import Match from './Match'; 4 | import Route from './Route'; 5 | import Switch from './Switch'; 6 | import defaultGo from './go'; 7 | import Link from './Link'; 8 | 9 | const createRouter = (ns: string, go = defaultGo) => { 10 | const proxy = (Comp) => (props) => h(Comp, {...props, ns}); 11 | 12 | return { 13 | Router: proxy(Router), 14 | Match: proxy(Match), 15 | Route: proxy(Route), 16 | Switch: proxy(Switch), 17 | go, 18 | Link: (props) => h(Link, {...props, go}), 19 | }; 20 | }; 21 | 22 | export default createRouter; 23 | -------------------------------------------------------------------------------- /src/route/go.ts: -------------------------------------------------------------------------------- 1 | import {isClient} from '../util'; 2 | 3 | export interface IGoParams { 4 | replace?: boolean; 5 | title?: string; 6 | state?: any; 7 | } 8 | 9 | export type TGo = (page: string, params?: IGoParams) => void; 10 | 11 | const go: TGo = (page: string, {replace, title, state}: IGoParams = {}) => { 12 | if (isClient) { 13 | history[replace ? 'replaceState' : 'pushState'](state, title || '', page); 14 | } 15 | }; 16 | 17 | export default go; 18 | -------------------------------------------------------------------------------- /src/route/index.ts: -------------------------------------------------------------------------------- 1 | import Router from './Router'; 2 | import Match from './Match'; 3 | import Route from './Route'; 4 | import Switch from './Switch'; 5 | import go from './go'; 6 | import Link from './Link'; 7 | import createRouter from './createRouter'; 8 | 9 | export { 10 | Router, 11 | Match, 12 | Route, 13 | Switch, 14 | go, 15 | Link, 16 | createRouter, 17 | }; 18 | -------------------------------------------------------------------------------- /src/shim/__story__/createLifecycleEvents.story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {action} from '@storybook/addon-actions'; 4 | import {createLifecycleEvents} from '../createLifecycleEvents'; 5 | 6 | const Lifecycles = createLifecycleEvents({ 7 | willMount: action('willMount'), 8 | didMount: action('didMount'), 9 | willUnmount: action('willUnmount') 10 | }); 11 | 12 | storiesOf('Shims/createLifecycleEvents()', module) 13 | .add('Example', () => 14 | 15 |
Hello world!
16 |
17 | ) 18 | -------------------------------------------------------------------------------- /src/shim/__story__/createRef.story.tsx: -------------------------------------------------------------------------------- 1 | import {Component, createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {createRef} from '../createRef'; 4 | 5 | class Example extends Component { 6 | divRef = createRef(); 7 | 8 | onClick = () => { 9 | console.log('ref', this.divRef.value); 10 | }; 11 | 12 | render () { 13 | return
{'See
ref in console.'}
; 14 | } 15 | } 16 | 17 | storiesOf('Shims/createRef()', module) 18 | .add('Basic example', () => ); 19 | -------------------------------------------------------------------------------- /src/shim/__story__/createState.story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {createState} from '../createState'; 4 | 5 | const State1 = createState({ 6 | cnt: 1, 7 | }); 8 | 9 | storiesOf('Shims/createState()', module) 10 | .add('Basic example', () => 11 | {(state, setState) => 12 |
setState({cnt: state.cnt + 1})}>Count: {state.cnt}
13 | }
14 | ); 15 | -------------------------------------------------------------------------------- /src/shim/createLifecycleEvents.ts: -------------------------------------------------------------------------------- 1 | import {extend} from 'fast-af/extend'; 2 | import {Lifecycles} from '../Lifecycles'; 3 | import {h} from '../util'; 4 | 5 | export const createLifecycleEvents = (lifecycles) => { 6 | return (props) => h(Lifecycles, extend({}, lifecycles, props)); 7 | }; 8 | -------------------------------------------------------------------------------- /src/shim/createRef.ts: -------------------------------------------------------------------------------- 1 | export const createRef = () => { 2 | const ref: any = (el) => { 3 | ref.value = el; 4 | }; 5 | 6 | return ref; 7 | }; 8 | -------------------------------------------------------------------------------- /src/shim/createState.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | import {State} from '../State'; 3 | 4 | export const createState = (init?) => { 5 | return (props) => h(State, {init}, props.children); 6 | }; 7 | -------------------------------------------------------------------------------- /src/shim/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createRef'; 2 | export * from './createState'; 3 | export * from './createLifecycleEvents'; 4 | -------------------------------------------------------------------------------- /src/theme/__story__/story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h, Component} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {Theme, Themed, withTheme} from '..'; 4 | 5 | const theme = { 6 | color: 'white', 7 | background: 'tomato' 8 | }; 9 | 10 | const Block = ({theme: {color, background}}) => 11 |
Color is: {color}
; 12 | 13 | const BlockThemed = withTheme(Block); 14 | 15 | @withTheme 16 | class Decorator1 extends Component { 17 | render () { 18 | const {color, background} = this.props.theme; 19 | 20 | return ( 21 |
22 | Background: {background}, color: {color} 23 |
24 | ); 25 | } 26 | } 27 | 28 | storiesOf('Context/theme', module) 29 | .add('FaCC', () => 30 | 31 | {({color, background}) => 32 |
Color is: {color}
33 | }
34 |
35 | ) 36 | .add('HOC', () => 37 | 38 | 39 | 40 | ) 41 | .add('Decorator 1', () => 42 | 43 | 44 | 45 | ) 46 | -------------------------------------------------------------------------------- /src/theme/__tests__/index.server.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment node 3 | */ 4 | 5 | import {createElement as h} from 'react'; 6 | import {renderToStaticMarkup} from 'react-dom/server'; 7 | import {Theme, Themed, withTheme} from '../index'; 8 | 9 | describe('theme', () => { 10 | describe('', () => { 11 | it(' passes theme to ', () => { 12 | const html = renderToStaticMarkup(h(Theme, {value: {color: 'red'}}, 13 | h('div', {}, 14 | h(Themed, {}, theme => { 15 | return h('span', {}, theme.color); 16 | }) 17 | ) 18 | )); 19 | 20 | expect(html).toMatch('red'); 21 | }); 22 | }); 23 | 24 | describe('withTheme()', () => { 25 | it('works', () => { 26 | const Box = ({theme}) => { 27 | return h('div', {}, theme.color); 28 | }; 29 | const BoxThemed = withTheme(Box); 30 | const html = renderToStaticMarkup(h(Theme, {value: {color: 'red'}}, h(BoxThemed))); 31 | 32 | expect(html).toBe('
red
'); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/theme/index.ts: -------------------------------------------------------------------------------- 1 | import {Provider, Consumer} from '../context'; 2 | import {h} from '../util'; 3 | import faccToHocc from '../util/faccToHoc'; 4 | 5 | export interface IThemeProps { 6 | name?: string; 7 | value: object; 8 | } 9 | 10 | export const Theme: React.StatelessComponent = (props) => { 11 | let {name = 'theme', value, children} = props; 12 | 13 | return h(Provider as any, {name, value}, children); 14 | }; 15 | 16 | export interface IThemedProps { 17 | name?: string; 18 | } 19 | 20 | export const Themed: React.StatelessComponent = (props) => { 21 | let {name = 'theme', children} = props; 22 | 23 | return h(Consumer, {name}, children); 24 | }; 25 | 26 | export const withTheme = faccToHocc(Themed, 'theme'); 27 | -------------------------------------------------------------------------------- /src/typing.ts: -------------------------------------------------------------------------------- 1 | export interface IUniversalInterfaceProps { 2 | children?: ((data: TData) => React.ReactNode) | React.ReactNode | any; 3 | render?: (data: TData) => React.ReactNode; 4 | comp?: React.ComponentClass | React.SFC; 5 | component?: React.ComponentClass | React.SFC; 6 | } 7 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export type TComponent = React.ComponentClass | React.StatelessComponent; 4 | export type THoc = (Comp: TComponent) => TComponent 5 | 6 | export const noop: (...args) => any = () => {}; 7 | 8 | export const idx = (obj, accessor) => { 9 | try { 10 | return accessor(obj); 11 | } catch (error) { 12 | return undefined; 13 | } 14 | }; 15 | 16 | export const isClient = typeof window === 'object'; 17 | 18 | export const on = (obj, ...args) => obj.addEventListener(...args); 19 | 20 | export const off = (obj, ...args) => obj.removeEventListener(...args); 21 | 22 | export const ns = (name) => `@@libreact/${name}`; 23 | 24 | const hasSymbols = typeof Symbol !== 'undefined'; 25 | 26 | export const sym = (name) => { 27 | return hasSymbols ? Symbol(name) : ns(name); 28 | }; 29 | 30 | export const isFn = (fn) => typeof fn === 'function'; 31 | 32 | export const h = React.createElement; 33 | -------------------------------------------------------------------------------- /src/util/addClassDecoratorSupport.ts: -------------------------------------------------------------------------------- 1 | import isStatelessComponent from './isStatelessComponent'; 2 | import wrapInStatefulComponent from './wrapInStatefulComponent'; 3 | 4 | const addClassDecoratorSupport = (Comp) => { 5 | return isStatelessComponent(Comp) ? 6 | Comp : 7 | wrapInStatefulComponent(Comp); 8 | }; 9 | 10 | export default addClassDecoratorSupport; 11 | -------------------------------------------------------------------------------- /src/util/compose.ts: -------------------------------------------------------------------------------- 1 | const compose = (...functions) => { 2 | if (!functions.length) { 3 | return arg => arg; 4 | } 5 | 6 | if (functions.length === 1) { 7 | return functions[0]; 8 | } 9 | 10 | return (...args2) => { 11 | let result = functions[functions.length - 1](...args2); 12 | 13 | for (let i = functions.length - 2; i >= 0; i--) { 14 | result = functions[i](result); 15 | } 16 | 17 | return result; 18 | }; 19 | }; 20 | 21 | export default compose; 22 | -------------------------------------------------------------------------------- /src/util/faccToHoc.ts: -------------------------------------------------------------------------------- 1 | import createEnhancer, {divWrapper} from 'react-universal-interface/lib/createEnhancer'; 2 | 3 | export { 4 | divWrapper 5 | }; 6 | 7 | export default createEnhancer; 8 | -------------------------------------------------------------------------------- /src/util/getDisplayName.ts: -------------------------------------------------------------------------------- 1 | const getDisplayName = (Comp: any) => { 2 | const tipof = typeof Comp; 3 | 4 | switch (tipof) { 5 | case 'string': 6 | return Comp; 7 | case 'function': 8 | return Comp.displayName || Comp.name || 'Unknown'; 9 | case 'object': 10 | const {type} = Comp; 11 | 12 | return typeof type === 'function' ? 13 | `<${type.displayName || type.name || String(Comp)}>` : 14 | ``; 15 | default: 16 | return 'Unknown'; 17 | } 18 | } 19 | 20 | export default getDisplayName; 21 | -------------------------------------------------------------------------------- /src/util/isStatelessComponent.ts: -------------------------------------------------------------------------------- 1 | const isStatelessComponent = (Comp) => { 2 | if (typeof Comp !== 'function') { 3 | return false; 4 | } 5 | 6 | return !Comp.prototype || (Comp instanceof Function); 7 | }; 8 | 9 | export default isStatelessComponent; 10 | -------------------------------------------------------------------------------- /src/util/mapProps.ts: -------------------------------------------------------------------------------- 1 | import {h} from '../util'; 2 | 3 | export const mapProps = (mapper) => (Comp) => (props) => h(Comp, mapper(props)); 4 | -------------------------------------------------------------------------------- /src/util/renderProp.ts: -------------------------------------------------------------------------------- 1 | import {render} from 'react-universal-interface'; 2 | 3 | export default render; 4 | -------------------------------------------------------------------------------- /src/util/wrapInStatefulComponent.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {h} from '../util'; 3 | 4 | const wrapInStatefulComponent = (Comp) => { 5 | const Decorated = class Decorated extends React.Component { 6 | render () { 7 | return h(Comp, this.props); 8 | } 9 | }; 10 | 11 | return Decorated; 12 | }; 13 | 14 | export default wrapInStatefulComponent; 15 | -------------------------------------------------------------------------------- /src/viewport.story.tsx: -------------------------------------------------------------------------------- 1 | import {createElement as h} from 'react'; 2 | import {storiesOf} from '@storybook/react'; 3 | import {delayed} from './delayed'; 4 | import {viewport} from './viewport'; 5 | import ShowDocs from './ShowDocs' 6 | 7 | 8 | const Loaded = () =>
LOADED
; 9 | const Loadable = delayed({ 10 | loading:
Loading....
, 11 | loader: () => Promise.resolve(Loaded), 12 | delay: 1000 13 | }); 14 | 15 | const MyComp = viewport(Loadable); 16 | 17 | const Demo1 = () => 18 |
19 |
20 | 21 |
; 22 | 23 | const Demo2 = () => 24 |
25 | 26 |
; 27 | 28 | storiesOf('Dummies/viewport()', module) 29 | .add('Documentation', () => h(ShowDocs, {name: 'viewport'})) 30 | .add('Example', () => h(Demo1)) 31 | .add('Shows when visible', () => h(Demo2)); 32 | -------------------------------------------------------------------------------- /src/viewport.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {h} from './util'; 3 | import {ViewportSensor} from './ViewportSensor'; 4 | import {pixel} from './pixel'; 5 | 6 | export interface IViewportParams { 7 | placeholder: React.ReactElement; 8 | } 9 | 10 | export type TViewport =

(Comp: React.StatelessComponent

| React.ComponentClass

, params: IViewportParams) => React.ComponentClass

; 11 | 12 | export const viewport = (Comp, {placeholder = pixel} = {}) => { 13 | let flipped = false; 14 | 15 | const Viewport = class Viewport extends React.Component { 16 | onChange = () => { 17 | flipped = true; 18 | this.forceUpdate(); 19 | }; 20 | 21 | render () { 22 | return flipped ? 23 | h(Comp, this.props) : 24 | h(ViewportSensor, {onChange: this.onChange}, placeholder) 25 | } 26 | } 27 | 28 | return Viewport; 29 | }; 30 | -------------------------------------------------------------------------------- /tsconfig.es6.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "removeComments": true, 7 | "noImplicitAny": false, 8 | "outDir": "./modules", 9 | "allowJs": false, 10 | "allowSyntheticDefaultImports": true, 11 | "jsx": "react", 12 | "jsxFactory": "h", 13 | "skipDefaultLibCheck": true, 14 | "skipLibCheck": true, 15 | "experimentalDecorators": true, 16 | "importHelpers": true, 17 | "pretty": true, 18 | "sourceMap": false, 19 | "esModuleInterop": true, 20 | "noEmitHelpers": true, 21 | "noErrorTruncation": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noImplicitReturns": true, 24 | "declaration": true, 25 | "lib": ["dom", "es5", "es6", "es7", "es2015", "es2017", "scripthost", "dom.iterable"] 26 | }, 27 | "include": ["typing.d.ts", "src"], 28 | "exclude": [ 29 | "node_modules", 30 | "lib", 31 | "**/__tests__/**/*", 32 | "**/__story__/**/*", 33 | "*.test.ts", 34 | "*.test.tsx" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "Node", 6 | "removeComments": true, 7 | "noImplicitAny": false, 8 | "outDir": "./lib", 9 | "allowJs": false, 10 | "allowSyntheticDefaultImports": true, 11 | "jsx": "react", 12 | "jsxFactory": "h", 13 | "skipDefaultLibCheck": true, 14 | "skipLibCheck": true, 15 | "experimentalDecorators": true, 16 | "importHelpers": true, 17 | "pretty": true, 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "noEmitHelpers": true, 21 | "noErrorTruncation": true, 22 | "noFallthroughCasesInSwitch": true, 23 | "noImplicitReturns": true, 24 | "declaration": true, 25 | "lib": ["dom", "es5", "es6", "es7", "es2015", "es2017", "scripthost", "dom.iterable"] 26 | }, 27 | "include": ["typing.d.ts", "src"], 28 | "exclude": [ 29 | "node_modules", 30 | "lib", 31 | "**/__tests__/**/*", 32 | "**/__story__/**/*", 33 | "*.test.ts", 34 | "*.test.tsx" 35 | ] 36 | } 37 | --------------------------------------------------------------------------------