├── .all-contributorsrc ├── .github ├── FUNDING.yml └── stale.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── api.md ├── how-it-works.md └── installation.md ├── package.json ├── rollup.config.js ├── src ├── DeviceMotion │ ├── DeviceMotion.tsx │ ├── index.tsx │ └── withDeviceMotion.tsx ├── DeviceOrientation │ ├── DeviceOrientation.tsx │ ├── index.tsx │ └── withDeviceOrientation.tsx ├── GeoPosition │ ├── GeoPosition.tsx │ ├── index.tsx │ └── withGeoPosition.tsx ├── Locales │ ├── Locales.tsx │ ├── index.tsx │ └── withLocales.tsx ├── Mailto.tsx ├── Media │ ├── Media.tsx │ └── index.tsx ├── Network │ ├── Network.tsx │ ├── __tests__ │ │ ├── Network.test.tsx │ │ └── withNetwork.test.tsx │ ├── index.tsx │ └── withNetwork.tsx ├── ReducerComponent.tsx ├── Scroll │ ├── Scroll.tsx │ ├── __tests__ │ │ ├── Scroll.test.tsx │ │ └── withScroll.test.tsx │ ├── index.tsx │ └── withScroll.tsx ├── Sms.tsx ├── WindowSize │ ├── WindowSize.tsx │ ├── __tests__ │ │ ├── WindowSize.test.tsx │ │ └── withWindowSize.test.tsx │ ├── index.tsx │ └── withWindowSize.tsx ├── hoistStatics.tsx ├── index.tsx ├── types.tsx └── utils │ ├── debounce.ts │ ├── featureDetection.ts │ ├── index.tsx │ ├── isEmptyChildren.tsx │ └── throttle.ts ├── tsconfig.json ├── website ├── core │ └── Footer.js ├── deploy.js ├── i18n │ └── en.json ├── languages.js ├── package.json ├── pages │ └── en │ │ ├── help-with-translations.js │ │ └── index.js ├── sidebars.json ├── siteConfig.js └── static │ ├── circle.yml │ ├── css │ ├── custom.css │ └── reason.css │ ├── img │ ├── reason-react-red.svg │ └── reason-react-white.svg │ └── js │ ├── pjax-api.js │ ├── redirect.js │ ├── redirectBlog.js │ └── redirectIndex.js └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "react-fns", 3 | "projectOwner": "jaredpalmer", 4 | "files": [ 5 | "README.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": false, 9 | "contributors": [ 10 | { 11 | "login": "mjackson", 12 | "name": "MICHAEL JACKSON", 13 | "avatar_url": "https://avatars0.githubusercontent.com/u/92839?v=4", 14 | "profile": "https://twitter.com/mjackson", 15 | "contributions": [ 16 | "ideas" 17 | ] 18 | }, 19 | { 20 | "login": "prichodko", 21 | "name": "Pavel Prichodko", 22 | "avatar_url": "https://avatars2.githubusercontent.com/u/14926950?v=4", 23 | "profile": "https://github.com/prichodko", 24 | "contributions": [ 25 | "code", 26 | "doc" 27 | ] 28 | }, 29 | { 30 | "login": "rpowell", 31 | "name": "Richard Powell", 32 | "avatar_url": "https://avatars3.githubusercontent.com/u/7615?v=4", 33 | "profile": "https://github.com/rpowell", 34 | "contributions": [ 35 | "code" 36 | ] 37 | }, 38 | { 39 | "login": "brimtown", 40 | "name": "Tim Brown", 41 | "avatar_url": "https://avatars2.githubusercontent.com/u/3269550?v=4", 42 | "profile": "https://github.com/brimtown", 43 | "contributions": [ 44 | "doc" 45 | ] 46 | }, 47 | { 48 | "login": "jtmthf", 49 | "name": "Jack Moore", 50 | "avatar_url": "https://avatars1.githubusercontent.com/u/8162598?v=4", 51 | "profile": "https://github.com/jtmthf", 52 | "contributions": [ 53 | "code" 54 | ] 55 | }, 56 | { 57 | "login": "daylerees", 58 | "name": "Dayle Rees", 59 | "avatar_url": "https://avatars0.githubusercontent.com/u/207870?v=4", 60 | "profile": "http://www.daylerees.com", 61 | "contributions": [ 62 | "doc" 63 | ] 64 | }, 65 | { 66 | "login": "thomasfl", 67 | "name": "Thomas Flemming", 68 | "avatar_url": "https://avatars3.githubusercontent.com/u/1520?v=4", 69 | "profile": "http://thomasflemming.no", 70 | "contributions": [ 71 | "doc" 72 | ] 73 | }, 74 | { 75 | "login": "skvale", 76 | "name": "Sam Kvale", 77 | "avatar_url": "https://avatars0.githubusercontent.com/u/5314713?v=4", 78 | "profile": "http://skvale.github.io", 79 | "contributions": [ 80 | "bug", 81 | "code" 82 | ] 83 | }, 84 | { 85 | "login": "rpowelll", 86 | "name": "Rhys Powell", 87 | "avatar_url": "https://avatars0.githubusercontent.com/u/320910?v=4", 88 | "profile": "http://rpowell.me", 89 | "contributions": [ 90 | "code" 91 | ] 92 | }, 93 | { 94 | "login": "jreinhold", 95 | "name": "Jeppe Reinhold", 96 | "avatar_url": "https://avatars3.githubusercontent.com/u/5678122?v=4", 97 | "profile": "https://reinhold.is", 98 | "contributions": [ 99 | "doc" 100 | ] 101 | }, 102 | { 103 | "login": "vhfmag", 104 | "name": "Victor Magalhães", 105 | "avatar_url": "https://avatars3.githubusercontent.com/u/357835?v=4", 106 | "profile": "https://github.com/vhfmag", 107 | "contributions": [ 108 | "bug", 109 | "code" 110 | ] 111 | },{ 112 | "login": "macklinu", 113 | "name": "Macklin Underdown", 114 | "avatar_url": "https://avatars1.githubusercontent.com/u/2344137?v=4", 115 | "profile": "http://macklin.underdown.me", 116 | "contributions": [ 117 | "doc" 118 | ] 119 | } 120 | ] 121 | } 122 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jaredpalmer] 4 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | Hola! So here's the deal, between open source and my day job and life and what not, I have a lot to manage, so I use a GitHub bot to automate a few things here and there. This particular GitHub bot is going to mark this as stale because it has not had 14 | recent activity for a while. It will be closed if no further activity occurs in a few days. Do not take this personally--seriously--this is a completely automated action. If this is a mistake, just make a comment, DM me, send a carrier pidgeon, or a smoke signal. 15 | # Comment to post when closing a stale issue. Set to `false` to disable 16 | closeComment: > 17 | Closed due to inactivity. Holler if this is a mistake, and we'll re-open it. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | dist 4 | compiled 5 | .DS_Store 6 | website/build 7 | website/node_modules 8 | website/.netlify -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: node_js 3 | node_js: 4 | - '8' 5 | script: 6 | - yarn test -- --runInBand --no-cache --coverage -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jared Palmer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ![repo-banner](https://user-images.githubusercontent.com/4060187/32896325-bff4a758-cab0-11e7-8cf5-62759e13fa6b.png) 5 | 6 | **react-fns** is a collection of imperative Browser API's turned into 7 | declarative [React](https://github.com/facebook/react) components and 8 | higher-order components for lots of common situations. 9 | 10 | ## Quick Start 11 | 12 | ``` 13 | npm i react-fns --save 14 | ``` 15 | 16 | ## Resources 17 | 18 | * [Website](https://react-fns.netlify.com) 19 | * [API Reference](https://react-fns.netlify.com/docs/en/api.html) 20 | * [Community Discord](https://discord.com/invite/RevdZTYMzr) 21 | 22 | ## Author 23 | 24 | * Jared Palmer [@jaredpalmer](https://twitter.com/jaredpalmer) 25 | 26 | ## Contributors 27 | 28 | Thanks goes to these wonderful people 29 | ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): 30 | 31 | 32 | | [
MICHAEL JACKSON](https://twitter.com/mjackson)
[🤔](#ideas-mjackson "Ideas, Planning, & Feedback") | [
Pavel Prichodko](https://github.com/prichodko)
[💻](https://github.com/jaredpalmer/react-fns/commits?author=prichodko "Code") [📖](https://github.com/jaredpalmer/react-fns/commits?author=prichodko "Documentation") | [
Richard Powell](https://github.com/rpowell)
[💻](https://github.com/jaredpalmer/react-fns/commits?author=rpowell "Code") | [
Tim Brown](https://github.com/brimtown)
[📖](https://github.com/jaredpalmer/react-fns/commits?author=brimtown "Documentation") | [
Jack Moore](https://github.com/jtmthf)
[💻](https://github.com/jaredpalmer/react-fns/commits?author=jtmthf "Code") | [
Dayle Rees](http://www.daylerees.com)
[📖](https://github.com/jaredpalmer/react-fns/commits?author=daylerees "Documentation") | [
Thomas Flemming](http://thomasflemming.no)
[📖](https://github.com/jaredpalmer/react-fns/commits?author=thomasfl "Documentation") | 33 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: | 34 | | [
Sam Kvale](http://skvale.github.io)
[🐛](https://github.com/jaredpalmer/react-fns/issues?q=author%3Askvale "Bug reports") [💻](https://github.com/jaredpalmer/react-fns/commits?author=skvale "Code") | [
Rhys Powell](http://rpowell.me)
[💻](https://github.com/jaredpalmer/react-fns/commits?author=rpowelll "Code") | [
Jeppe Reinhold](https://reinhold.is)
[📖](https://github.com/jaredpalmer/react-fns/commits?author=jreinhold "Documentation") | [
Victor Magalhães](https://github.com/vhfmag)
[🐛](https://github.com/jaredpalmer/react-fns/issues?q=author%3Avhfmag "Bug reports") [💻](https://github.com/jaredpalmer/react-fns/commits?author=vhfmag "Code") | [
Macklin Underdown](http://macklin.underdown.me)
[📖](https://github.com/jaredpalmer/react-fns/commits?author=macklinu "Documentation") | 35 | 36 | 37 | This project follows the 38 | [all-contributors](https://github.com/kentcdodds/all-contributors) 39 | specification. Contributions of any kind welcome! 40 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api 3 | title: API Reference 4 | --- 5 | 6 | * [DeviceMotion](#devicemotion) 7 | * [DeviceMotion props](#devicemotion-props) 8 | * [``](#devicemotion-render) 9 | * [`withDeviceMotion()`](#withdevicemotion) 10 | * [DeviceOrientation](#deviceorientation) 11 | * [DeviceOrientation props](#deviceorientation-props) 12 | * [``](#deviceorientation-render) 13 | * [`withDeviceOrientation()`](#withdeviceorientation) 14 | * [Network](#network) 15 | * [Network props](#network-props) 16 | * [``](#network-render) 17 | * [`withNetwork()`](#withnetwork) 18 | * [GeoPosition](#geoposition) 19 | * [GeoPosition props](#geoposition-props) 20 | * [``](#geoposition-render) 21 | * [`withGeoPosition()`](#withgeoposition) 22 | * [Media](#media) 23 | * [Media props](#media-props) 24 | * [Media render props](#media-render-props) 25 | * [``](#media-render) 26 | * [`withMedia()`](#withmedia) 27 | * [Scroll](#scroll) 28 | * [Scroll props](#scroll-props) 29 | * [``](#scroll-render) 30 | * [`withScroll()`](#withscroll) 31 | * [WindowSize](#windowsize) 32 | * [WindowSize props](#windowsize-props) 33 | * [``](#windowsize-render) 34 | * [`withWindowSize()`](#withwindowsize) 35 | * [Locales](#locales) 36 | * [Locales props](#locales-props) 37 | * [``](#locales-render) 38 | * [`withLocales()`](#withlocales) 39 | * [Utility Components](#utility-components) 40 | * [``](#mailto) 41 | * [Mailto props](#mailto-props) 42 | * [``](#sms) 43 | * [Sms props](#sms-props) 44 | 45 | ## DeviceMotion 46 | 47 | Detect and retrieve current device Motion. 48 | 49 | ### DeviceMotion props 50 | 51 | * `acceleration: DeviceMotionEvent.acceleration` 52 | * `accelerationIncludingGravity: DeviceMotionEvent.accelerationIncludingGravity` 53 | * `rotationRate: DeviceMotionEvent.rotationRate` 54 | * `interval: DeviceMotionEvent.interval` 55 | 56 | For more information about the DeviceMotion API, 57 | [check out the MDN reference](https://developer.mozilla.org/en-US/docs/Web/Events/devicemotion) 58 | 59 | ### `` 60 | 61 | ```javascript 62 | import { DeviceMotion } from 'react-fns'; 63 | 64 | const Example = () => ( 65 | ( 72 |
 73 |         {JSON.stringify(
 74 |           {
 75 |             acceleration,
 76 |             accelerationIncludingGravity,
 77 |             rotationRate,
 78 |             interval,
 79 |           },
 80 |           null,
 81 |           2
 82 |         )}
 83 |       
84 | )} 85 | /> 86 | ); 87 | 88 | export default Example; 89 | ``` 90 | 91 | ### `withDeviceMotion()` 92 | 93 | ```javascript 94 | import { withDeviceMotion } from 'react-fns'; 95 | 96 | const Inner = ({ 97 | acceleration, 98 | accelerationIncludingGravity, 99 | rotationRate, 100 | interval, 101 | }) => ( 102 |
103 |     {JSON.stringify(
104 |       { acceleration, accelerationIncludingGravity, rotationRate, interval },
105 |       null,
106 |       2
107 |     )}
108 |   
109 | ); 110 | 111 | export default withDeviceMotion(Inner); 112 | ``` 113 | 114 | ## DeviceOrientation 115 | 116 | Detect and retrieve current device orientation. 117 | 118 | ### DeviceOrientation props 119 | 120 | * `alpha: number`: value represents the motion of the device around the z axis, 121 | represented in degrees with values ranging from 0 to 360. 122 | * `beta: number`: value represents the motion of the device around the x axis, 123 | represented in degrees with values ranging from -180 to 180. This represents a 124 | front to back motion of the device. 125 | * `gamma: number`: value represents the motion of the device around the y axis, 126 | represented in degrees with values ranging from -90 to 90. This represents a 127 | left to right motion of the device. 128 | 129 | For more information about the DeviceOrientation API, 130 | [check out the MDN reference](https://developer.mozilla.org/en-US/docs/Web/API/Detecting_device_orientation) 131 | 132 | ### `` 133 | 134 | ```javascript 135 | import { DeviceOrientation } from 'react-fns'; 136 | 137 | const Example = () => ( 138 | ( 140 |
{JSON.stringify({ alpha, beta, gamma, absolute }, null, 2)}
141 | )} 142 | /> 143 | ); 144 | 145 | export default Example; 146 | ``` 147 | 148 | ### `withDeviceOrientation()` 149 | 150 | ```javascript 151 | import { withDeviceOrientation } from 'react-fns'; 152 | 153 | const Inner = ({ alpha, beta, gamma, absolute }) => ( 154 |
{JSON.stringify({ alpha, beta, gamma, absolute }, null, 2)}
155 | ); 156 | 157 | export default withDeviceOrientation(Inner); 158 | ``` 159 | 160 | ## Network 161 | 162 | Retrieve network access from the browser. 163 | 164 | ### Network props 165 | 166 | * `online: boolean`: `true` if the browser has network access. `false` 167 | otherwise. 168 | * `offlineAt?: Date`: Date when network connection was lost. 169 | 170 | ### `` 171 | 172 | ```javascript 173 | import { Network } from 'react-fns'; 174 | 175 | const Example = () => ( 176 | ( 178 |
179 | {online ? 'Online!' : 'Offline'} 180 | {offlineAt && `Last connected ${offlineAt.toISOString()}`} 181 |
182 | )} 183 | /> 184 | ); 185 | 186 | export default Example; 187 | ``` 188 | 189 | ### `withNetwork()` 190 | 191 | ```javascript 192 | import { withNetwork } from 'react-fns'; 193 | 194 | const Inner = ({ online, offlineAt }) => ( 195 |
196 | {online ? 'Online!' : 'Offline'} 197 | {offlineAt && `Last connected ${offlineAt.toISOString()}`} 198 |
199 | ); 200 | 201 | export default withNetwork(Inner); 202 | ``` 203 | 204 | ## GeoPosition 205 | 206 | Retrieve Geo position from the browser. 207 | 208 | ### GeoPosition props 209 | 210 | * `isLoading: boolean`: `true` request status 211 | * `coords?: Position`: Geoposition object. Has keys of `latitude` and 212 | `longitude` 213 | * `error?: PositionError`: GeoPosition error. See MDN for shape. 214 | 215 | ### `` 216 | 217 | ```javascript 218 | import { GeoPosition } from 'react-fns'; 219 | 220 | const Example = () => ( 221 | ( 223 |
{coords && `${coords.longitude},${coords.latitude}`}
224 | )} 225 | /> 226 | ); 227 | 228 | export default Example; 229 | ``` 230 | 231 | ### `withGeoPosition()` 232 | 233 | ```javascript 234 | import { withGeoPosition } from 'react-fns'; 235 | 236 | const Inner = ({ isLoading, coords, error }) => ( 237 |
{coords && `${cords.longitude}$,{coords.latitude}`}
238 | ); 239 | 240 | export default withGeoPosition(Inner); 241 | ``` 242 | 243 | ## Media 244 | 245 | Retrieve media query (i.e. `window.matchMedia().matches`) from the browser. Note 246 | this component is taken from @mjackson's awesome 247 | [react-media](https://github.com/reacttraining/react-media) 248 | 249 | ### Media props 250 | 251 | * `query: string`: A media query string 252 | 253 | ### Media render props 254 | 255 | * `matches: boolean`: `true` if browser matches the media query 256 | 257 | ### `` 258 | 259 | ```javascript 260 | import { Media } from 'react-fns'; 261 | 262 | const Example = () => ( 263 |
{match ? 'mobile' : 'desktop'}
} 266 | /> 267 | ); 268 | 269 | export default Example; 270 | ``` 271 | 272 | ### `withMedia()` 273 | 274 | Not implemented 275 | 276 | ## Scroll 277 | 278 | ### Scroll props 279 | 280 | * `x`: Horizontal scroll in pixels (`window.pageXOffset`) 281 | * `y`: Vertical scroll in pixels (`window.pageYOffset`) 282 | 283 | ### `` 284 | 285 | Returns `window.pageYOffset` and `window.pageXOffset`. 286 | 287 | ```javascript 288 | import { Scroll } from 'react-fns'; 289 | 290 | const Example = () => ( 291 | ( 293 |
294 | Scroll: {x}, {y} 295 |
296 | )} 297 | /> 298 | ); 299 | 300 | export default Example; 301 | ``` 302 | 303 | ### `withScroll()` 304 | 305 | Injects `window.pageYOffset` and `window.pageXOffset` as `x` and `y` props. 306 | 307 | ```javascript 308 | import { withScroll } from 'react-fns'; 309 | 310 | const Inner = ({ x, y }) => ( 311 |
312 | Scroll Position: {x}, {y} 313 |
314 | ); 315 | 316 | export default withScroll(Inner); 317 | ``` 318 | 319 | ## WindowSize 320 | 321 | ### WindowSize props 322 | 323 | * `width`: Width of browser viewport (`window.innerWidth`) 324 | * `height`: Height of browser viewport (`window.innerHeight`) 325 | 326 | ### `` 327 | 328 | Returns `window.innerWidth` and `window.innerHeight`. 329 | 330 | ```javascript 331 | import { WindowSize } from 'react-fns'; 332 | 333 | const Example = () => ( 334 | ( 336 |
337 | Window size: {width}, {height} 338 |
339 | )} 340 | /> 341 | ); 342 | 343 | export default Example; 344 | ``` 345 | 346 | ### `withWindowSize()` 347 | 348 | Injects `window.innerWidth` and `window.innerHeight` as `width` and `height` 349 | props. 350 | 351 | ```javascript 352 | import { withWindowSize } from 'react-fns'; 353 | 354 | const Inner = ({ width, height }) => ( 355 |
356 | Window size: {width}, {height} 357 |
358 | ); 359 | 360 | export default withWindowSize(Inner); 361 | ``` 362 | 363 | ## Locales 364 | 365 | ### Locales props 366 | 367 | * `locales`: The current browser locales (`navigator.languages` or 368 | `navigator.language`) 369 | 370 | ### `` 371 | 372 | Returns canonical `navigator.languages` or `navigator.language` as `locales`. 373 | 374 | ```javascript 375 | import { Locales } from 'react-fns'; 376 | 377 | const Example = () => ( 378 | ( 380 | 381 | Right now the time and date is{' '} 382 | {new Intl.DateTimeFormat(locales).format(new Date())} 383 | 384 | )} 385 | /> 386 | ); 387 | 388 | export default Example; 389 | ``` 390 | 391 | ### `withLocales()` 392 | 393 | Injects canonical `navigator.languages` or `navigator.language` as `locales` 394 | prop. 395 | 396 | ```javascript 397 | import { withLocales } from 'react-fns' 398 | 399 | const Inner = ({ locales }) => Right now the time and date is {new Intl.DateTimeFormat(locales).format(new 400 | 401 | export default withLocales(Inner) 402 | ``` 403 | 404 | ## Utility Components 405 | 406 | ### `` 407 | 408 | Renders `` 409 | 410 | #### Mailto props 411 | 412 | * `email: string`: Recipient's email address 413 | * `subject?: string`: Subject of the email 414 | * `cc?: string | string[]`: Email address or an array of email addresses to Cc 415 | * `bcc?: string | string[]`: Email address or an array of email addresses to Bcc 416 | (blind copy) 417 | * `body?: string`: Body copy of the email 418 | 419 | ### `` 420 | 421 | Renders `` 422 | 423 | #### Sms props 424 | 425 | * `phone: string`: Phone number 426 | * `body?: string`: Body copy of the text message 427 | -------------------------------------------------------------------------------- /docs/how-it-works.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: how-it-works 3 | title: How it works 4 | --- 5 | 6 | When possible, each component (e.g. ``) in react-fns also exports a 7 | higher-order component with identical functionality (e.g. `withThing()`. 8 | 9 | Every render prop'd component shares the same three rendering methods: 10 | 11 | * ` }>` 12 | * `` 13 | * `{props => }` 14 | 15 | All HoC's will pass through any and all additional props through to the inner 16 | component in addition to the props that they inject. 17 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: installation 3 | title: Installation 4 | --- 5 | 6 | You can install react-fns with [NPM](https://npmjs.com), 7 | [Yarn](https://yarnpkg.com), or good ol' ` 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | 63 | 64 | 65 | ``` 66 | 67 | All of the components will then be available on `window.ReactFns`. For example, 68 | to use ``, you would use `var DeviceMotion = 69 | window.ReactFns.DeviceMotion` and then ``. 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-fns", 3 | "version": "1.4.0", 4 | "description": "Modern React components, hoc's, and utilities functions.", 5 | "main": "dist/index.js", 6 | "module": "dist/index.es6.js", 7 | "types": "dist/index.d.ts", 8 | "sideEffects": false, 9 | "scripts": { 10 | "test": "jest --env=jsdom", 11 | "test:watch": "npm run test -- --watch", 12 | "start": "cross-env NODE_ENV=development tsc-watch --onSuccess \"rollup -c\"", 13 | "prebuild": "rimraf dist", 14 | "build": "tsc && cross-env NODE_ENV=production rollup -c && cross-env NODE_ENV=development rollup -c && rimraf compiled", 15 | "prepublish": "npm run build", 16 | "format": "prettier --trailing-comma es5 --single-quote --write 'src/**/*.{ts,tsx}' 'test/**/*.{ts,tsx}'", 17 | "doctoc": "doctoc .", 18 | "precommit": "lint-staged" 19 | }, 20 | "lint-staged": { 21 | "**/*.{ts,tsx}": [ 22 | "prettier --write", 23 | "git add" 24 | ] 25 | }, 26 | "prettier": { 27 | "trailingComma": "es5", 28 | "singleQuote": true 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/jaredpalmer/react-fns.git" 33 | }, 34 | "keywords": [ 35 | "react", 36 | "react-fns", 37 | "render-props", 38 | "hoc" 39 | ], 40 | "author": "Jared Palmer ", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/jaredpalmer/react-fns/issues" 44 | }, 45 | "homepage": "https://github.com/jaredpalmer/react-fns#readme", 46 | "devDependencies": { 47 | "@types/jest": "20.0.6", 48 | "@types/node": "^8.0.22", 49 | "@types/prop-types": "15.5.1", 50 | "@types/qs": "^6.5.0", 51 | "@types/react": "16.0.2", 52 | "@types/react-dom": "15.5.1", 53 | "@types/react-test-renderer": "15.5.2", 54 | "cross-env": "5.0.5", 55 | "doctoc": "^1.3.0", 56 | "husky": "^0.14.3", 57 | "jest": "^20.0.4", 58 | "lint-staged": "^5.0.0", 59 | "prettier": "^1.10.2", 60 | "react": "^15.6.1", 61 | "react-dom": "^15.6.1", 62 | "react-test-renderer": "^15.6.1", 63 | "rollup": "^0.47.6", 64 | "rollup-plugin-commonjs": "^8.1.0", 65 | "rollup-plugin-filesize": "^1.4.2", 66 | "rollup-plugin-node-resolve": "^3.0.0", 67 | "rollup-plugin-replace": "^1.1.1", 68 | "rollup-plugin-sourcemaps": "^0.4.2", 69 | "rollup-plugin-uglify": "^2.0.1", 70 | "ts-jest": "^20.0.10", 71 | "ts-lint": "^4.5.1", 72 | "tsc-watch": "1.0.8", 73 | "tslint": "^5.9.1", 74 | "tslint-react": "^3.2.0", 75 | "typescript": "^2.4.2" 76 | }, 77 | "jest": { 78 | "collectCoverageFrom": [ 79 | "src/**/*.{ts,tsx}" 80 | ], 81 | "transform": { 82 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 83 | }, 84 | "testMatch": [ 85 | "/src/**/?(*.)(spec|test).ts?(x)" 86 | ], 87 | "transformIgnorePatterns": [ 88 | "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$" 89 | ], 90 | "moduleFileExtensions": [ 91 | "ts", 92 | "tsx", 93 | "js", 94 | "json" 95 | ] 96 | }, 97 | "dependencies": { 98 | "qs": "^6.5.1", 99 | "react-media": "^1.6.1" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from 'rollup-plugin-commonjs'; 2 | import filesize from 'rollup-plugin-filesize'; 3 | import replace from 'rollup-plugin-replace'; 4 | import resolve from 'rollup-plugin-node-resolve'; 5 | import sourceMaps from 'rollup-plugin-sourcemaps'; 6 | import uglify from 'rollup-plugin-uglify'; 7 | 8 | const shared = { 9 | entry: `compiled/index.js`, 10 | sourceMap: true, 11 | external: ['react'], 12 | globals: { 13 | react: 'React', 14 | }, 15 | exports: 'named', 16 | }; 17 | 18 | export default [ 19 | Object.assign({}, shared, { 20 | moduleName: 'RenderProps', 21 | format: 'umd', 22 | dest: 23 | process.env.NODE_ENV === 'production' 24 | ? './dist/index.umd.min.js' 25 | : './dist/index.umd.js', 26 | plugins: [ 27 | resolve(), 28 | replace({ 29 | exclude: 'node_modules/**', 30 | 'process.env.NODE_ENV': JSON.stringify( 31 | process.env.NODE_ENV || 'development' 32 | ), 33 | }), 34 | commonjs(), 35 | sourceMaps(), 36 | process.env.NODE_ENV === 'production' && filesize(), 37 | process.env.NODE_ENV === 'production' && uglify(), 38 | ], 39 | }), 40 | Object.assign({}, shared, { 41 | targets: [ 42 | { dest: 'dist/index.es6.js', format: 'es' }, 43 | { dest: 'dist/index.js', format: 'cjs' }, 44 | ], 45 | plugins: [resolve(), sourceMaps()], 46 | }), 47 | ]; 48 | -------------------------------------------------------------------------------- /src/DeviceMotion/DeviceMotion.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | 12 | export interface DeviceMotionProps { 13 | acceleration: DeviceAcceleration | null; 14 | accelerationIncludingGravity: DeviceAcceleration | null; 15 | rotationRate: DeviceRotationRate | null; 16 | interval: number | null; 17 | } 18 | 19 | export class DeviceMotion extends React.Component< 20 | SharedRenderProps, 21 | DeviceMotionProps 22 | > { 23 | state: DeviceMotionProps = { 24 | acceleration: { 25 | x: null, 26 | y: null, 27 | z: null, 28 | }, 29 | accelerationIncludingGravity: { 30 | x: null, 31 | y: null, 32 | z: null, 33 | }, 34 | rotationRate: { 35 | alpha: null, 36 | beta: null, 37 | gamma: null, 38 | }, 39 | interval: 0, 40 | }; 41 | 42 | handleDeviceMotion = (e: DeviceMotionEvent) => { 43 | this.setState({ 44 | acceleration: e.acceleration, 45 | accelerationIncludingGravity: e.accelerationIncludingGravity, 46 | rotationRate: e.rotationRate, 47 | interval: e.interval, 48 | }); 49 | }; 50 | 51 | componentDidMount() { 52 | window.addEventListener('devicemotion', this.handleDeviceMotion, true); 53 | } 54 | 55 | componentWillUnmount() { 56 | window.removeEventListener('devicemotion', this.handleDeviceMotion); 57 | } 58 | 59 | render() { 60 | const { render, component, children } = this.props; 61 | return component 62 | ? React.createElement(component as any, this.state) 63 | : render 64 | ? (render as any)(this.state) 65 | : children // children come last, always called 66 | ? typeof children === 'function' 67 | ? children(this.state) 68 | : !isEmptyChildren(children) ? React.Children.only(children) : null 69 | : null; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/DeviceMotion/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './DeviceMotion'; 9 | export * from './withDeviceMotion'; 10 | -------------------------------------------------------------------------------- /src/DeviceMotion/withDeviceMotion.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { DeviceMotion, DeviceMotionProps } from './DeviceMotion'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withDeviceMotion( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } 19 | /> 20 | ); 21 | }; 22 | 23 | return hoistNonReactStatics( 24 | S as any, 25 | Component as React.ComponentClass 26 | ) as React.ComponentType; 27 | } 28 | -------------------------------------------------------------------------------- /src/DeviceOrientation/DeviceOrientation.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | 12 | export interface DeviceOrientationProps { 13 | alpha: number | null; 14 | beta: number | null; 15 | gamma: number | null; 16 | absolute: boolean; 17 | } 18 | 19 | export class DeviceOrientation extends React.Component< 20 | SharedRenderProps, 21 | DeviceOrientationProps 22 | > { 23 | state: DeviceOrientationProps = { 24 | alpha: null, 25 | beta: null, 26 | gamma: null, 27 | absolute: false, 28 | }; 29 | 30 | handleDeviceOrientation = (e: DeviceOrientationEvent) => { 31 | this.setState({ 32 | beta: e.beta, 33 | alpha: e.alpha, 34 | gamma: e.gamma, 35 | absolute: e.absolute, 36 | }); 37 | }; 38 | 39 | componentDidMount() { 40 | window.addEventListener( 41 | 'deviceorientation', 42 | this.handleDeviceOrientation, 43 | true 44 | ); 45 | } 46 | 47 | componentWillUnmount() { 48 | window.removeEventListener( 49 | 'deviceorientation', 50 | this.handleDeviceOrientation 51 | ); 52 | } 53 | 54 | render() { 55 | const { render, component, children } = this.props; 56 | return component 57 | ? React.createElement(component as any, this.state) 58 | : render 59 | ? (render as any)(this.state) 60 | : children // children come last, always called 61 | ? typeof children === 'function' 62 | ? children(this.state) 63 | : !isEmptyChildren(children) ? React.Children.only(children) : null 64 | : null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/DeviceOrientation/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './DeviceOrientation'; 9 | export * from './withDeviceOrientation'; 10 | -------------------------------------------------------------------------------- /src/DeviceOrientation/withDeviceOrientation.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { DeviceOrientation, DeviceOrientationProps } from './DeviceOrientation'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withDeviceOrientation( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } 19 | /> 20 | ); 21 | }; 22 | 23 | return hoistNonReactStatics( 24 | S as any, 25 | Component as React.ComponentClass 26 | ) as React.ComponentType; 27 | } 28 | -------------------------------------------------------------------------------- /src/GeoPosition/GeoPosition.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | 12 | export interface GeoPositionProps { 13 | isLoading: boolean; 14 | coords?: { 15 | longitude: number; 16 | latitude: number; 17 | }; 18 | error?: PositionError; 19 | } 20 | 21 | export class GeoPosition extends React.Component< 22 | SharedRenderProps, 23 | GeoPositionProps 24 | > { 25 | geoId: any; 26 | 27 | state = { 28 | isLoading: true, 29 | }; 30 | 31 | componentDidMount() { 32 | this.requestGeo(); 33 | } 34 | 35 | requestGeo = () => { 36 | this.setState({ isLoading: true }); 37 | this.geoId = navigator.geolocation.watchPosition( 38 | (position: Position) => 39 | this.setState({ 40 | isLoading: false, 41 | coords: { 42 | latitude: position.coords.latitude, 43 | longitude: position.coords.longitude, 44 | }, 45 | error: undefined, 46 | }), 47 | (error: PositionError) => this.setState({ error, isLoading: false }) 48 | ); 49 | }; 50 | 51 | componentWillUnmount() { 52 | navigator.geolocation.clearWatch(this.geoId); 53 | } 54 | 55 | render() { 56 | const { render, component, children } = this.props; 57 | return component 58 | ? React.createElement(component as any, this.state) 59 | : render 60 | ? (render as any)(this.state) 61 | : children // children come last, always called 62 | ? typeof children === 'function' 63 | ? children(this.state) 64 | : !isEmptyChildren(children) ? React.Children.only(children) : null 65 | : null; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/GeoPosition/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './GeoPosition'; 9 | export * from './withGeoPosition'; 10 | -------------------------------------------------------------------------------- /src/GeoPosition/withGeoPosition.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { GeoPosition, GeoPositionProps } from './GeoPosition'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withGeoPosition( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } 19 | /> 20 | ); 21 | }; 22 | 23 | return hoistNonReactStatics( 24 | S as any, 25 | Component as React.ComponentClass 26 | ) as React.ComponentType; 27 | } 28 | -------------------------------------------------------------------------------- /src/Locales/Locales.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | 12 | export interface LocalesProps { 13 | locale: string; 14 | } 15 | 16 | // TypeScript's definitions don't include this, though it has decent support in 17 | // modern browsers. 18 | // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales 19 | declare namespace Intl { 20 | function getCanonicalLocales(localse: string[]): string[]; 21 | } 22 | 23 | export class Locales extends React.Component< 24 | SharedRenderProps, 25 | LocalesProps 26 | > { 27 | state: LocalesProps = { locale: this.preferredLocales() }; 28 | 29 | preferredLocales(): string { 30 | if (navigator.languages && navigator.languages.length > 0) { 31 | return Intl.getCanonicalLocales(navigator.languages)[0]; 32 | } 33 | return Intl.getCanonicalLocales([navigator.language])[0]; 34 | } 35 | 36 | handleLanguageChange = () => { 37 | this.setState({ 38 | locale: this.preferredLocales(), 39 | }); 40 | }; 41 | 42 | componentDidMount() { 43 | window.addEventListener('languagechange', this.handleLanguageChange); 44 | } 45 | 46 | componentWillUnmount() { 47 | window.removeEventListener('languagechange', this.handleLanguageChange); 48 | } 49 | 50 | render() { 51 | const { render, component, children } = this.props; 52 | return component 53 | ? React.createElement(component as any, this.state) 54 | : render 55 | ? (render as any)(this.state) 56 | : children // children come last, always called 57 | ? typeof children === 'function' 58 | ? children(this.state) 59 | : !isEmptyChildren(children) ? React.Children.only(children) : null 60 | : null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Locales/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './Locales'; 9 | export * from './withLocales'; 10 | -------------------------------------------------------------------------------- /src/Locales/withLocales.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { Locales, LocalesProps } from './Locales'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withLocales( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } /> 18 | ); 19 | }; 20 | 21 | return hoistNonReactStatics( 22 | S as any, 23 | Component as React.ComponentClass 24 | ) as React.ComponentType; 25 | } 26 | -------------------------------------------------------------------------------- /src/Mailto.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import * as qs from 'qs'; 10 | 11 | export interface MailtoProps extends React.HTMLAttributes { 12 | /** Email address */ 13 | email: string; 14 | /** Subject */ 15 | subject?: string; 16 | /** List of email addresses to CC */ 17 | cc?: string[]; 18 | /** List of email addresses to Bcc */ 19 | bcc?: string[]; 20 | /** Email body text */ 21 | body?: string; 22 | } 23 | 24 | export const Mailto: React.SFC = ({ 25 | email, 26 | subject, 27 | cc, 28 | bcc, 29 | body, 30 | children, 31 | ...props 32 | }) => { 33 | return ( 34 |
43 | {children} 44 | 45 | ); 46 | }; 47 | 48 | Mailto.displayName = 'Mailto'; 49 | -------------------------------------------------------------------------------- /src/Media/Media.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | const M = require('react-media'); 11 | 12 | export const Media: React.SFC> = M; 13 | 14 | export interface MediaProps { 15 | matches: boolean; 16 | } 17 | -------------------------------------------------------------------------------- /src/Media/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './Media'; 9 | -------------------------------------------------------------------------------- /src/Network/Network.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | 12 | export interface NetworkProps { 13 | online: boolean; 14 | offlineAt?: Date; 15 | } 16 | 17 | export class Network extends React.Component< 18 | SharedRenderProps, 19 | NetworkProps 20 | > { 21 | state: NetworkProps = { online: navigator.onLine }; 22 | 23 | handleOnline = () => { 24 | this.setState({ online: true, offlineAt: undefined }); 25 | }; 26 | 27 | handleOffline = () => { 28 | this.setState({ online: false, offlineAt: new Date() }); 29 | }; 30 | 31 | componentDidMount() { 32 | if (typeof window !== 'undefined' && navigator) { 33 | this.setState({ online: navigator.onLine }); 34 | } 35 | 36 | window.addEventListener('online', this.handleOnline); 37 | window.addEventListener('offline', this.handleOffline); 38 | } 39 | 40 | componentWillUnmount() { 41 | window.removeEventListener('online', this.handleOnline); 42 | window.removeEventListener('offline', this.handleOffline); 43 | } 44 | 45 | render() { 46 | const { render, component, children } = this.props; 47 | return component 48 | ? React.createElement(component as any, this.state) 49 | : render 50 | ? (render as any)(this.state) 51 | : children // children come last, always called 52 | ? typeof children === 'function' 53 | ? children(this.state) 54 | : !isEmptyChildren(children) ? React.Children.only(children) : null 55 | : null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Network/__tests__/Network.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { Network } from '../'; 5 | 6 | describe('', () => { 7 | describe('', () => { 8 | const node = document.createElement('div'); 9 | 10 | afterEach(() => { 11 | ReactDOM.unmountComponentAtNode(node); 12 | }); 13 | 14 | it('receives { online } props', () => { 15 | ReactDOM.render( 16 | 18 | console.log(props) || expect(props.online).toEqual(true) || null 19 | } 20 | />, 21 | node 22 | ); 23 | }); 24 | 25 | it('renders elements', () => { 26 | ReactDOM.render(
online
} />, node); 27 | 28 | expect(node.innerHTML.includes('online')).toBe(true); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/Network/__tests__/withNetwork.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { withNetwork } from '../'; 5 | 6 | describe('withNetwork()', () => { 7 | const node = document.createElement('div'); 8 | 9 | afterEach(() => { 10 | ReactDOM.unmountComponentAtNode(node); 11 | }); 12 | 13 | it('receives { online } props', () => { 14 | const hello = 'hi'; 15 | 16 | const WrappedComponent = withNetwork<{ hello: string }>( 17 | props => expect(props.online).toEqual(true) || null 18 | ); 19 | 20 | ReactDOM.render(, node); 21 | }); 22 | 23 | // it('renders elements and passes thru props', () => { 24 | // const hello = 'hi'; 25 | 26 | // const WrappedComponent = withNetwork<{ hello: string }>(props => ( 27 | //
28 | // x: {props.x}, y: {props.y} {hello} 29 | //
30 | // )); 31 | 32 | // ReactDOM.render(, node); 33 | 34 | // expect(node.innerHTML.includes('0')).toBe(true); 35 | // expect(node.innerHTML.includes('hi')).toBe(true); 36 | // }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/Network/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './Network'; 9 | export * from './withNetwork'; 10 | -------------------------------------------------------------------------------- /src/Network/withNetwork.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { Network, NetworkProps } from './Network'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withNetwork( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } /> 18 | ); 19 | }; 20 | 21 | return hoistNonReactStatics( 22 | S as any, 23 | Component as React.ComponentClass 24 | ) as React.ComponentType; 25 | } 26 | -------------------------------------------------------------------------------- /src/ReducerComponent.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import React from 'react'; 9 | 10 | export class ReducerComponent extends React.Component { 11 | constructor(props: P) { 12 | super(props); 13 | } 14 | 15 | reducer = (state: S, action: A) => { 16 | console.log('No reducer implemented!'); 17 | console.log(action); 18 | return state; 19 | }; 20 | 21 | dispatch = (action: A) => 22 | this.setState((state: S) => this.reducer(state, action)); 23 | } 24 | -------------------------------------------------------------------------------- /src/Scroll/Scroll.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | import { throttle } from '../utils/throttle'; 12 | import { supportsPassiveListener } from '../utils/featureDetection'; 13 | 14 | export interface ScrollProps { 15 | x: number; 16 | y: number; 17 | } 18 | 19 | export interface ScrollConfig { 20 | throttle?: number; 21 | } 22 | 23 | export class Scroll extends React.Component< 24 | ScrollConfig & SharedRenderProps, 25 | ScrollProps 26 | > { 27 | static defaultProps: Partial = { 28 | throttle: 100, 29 | }; 30 | 31 | state: ScrollProps = { x: 0, y: 0 }; 32 | 33 | handleWindowScroll = throttle(() => { 34 | this.setState({ x: window.pageXOffset, y: window.pageYOffset }); 35 | }, this.props.throttle!); 36 | 37 | componentDidMount() { 38 | this.handleWindowScroll(); 39 | (window as EventTarget).addEventListener( 40 | 'scroll', 41 | this.handleWindowScroll, 42 | supportsPassiveListener ? { passive: true } : false 43 | ); 44 | } 45 | 46 | componentWillUnmount() { 47 | window.removeEventListener('scroll', this.handleWindowScroll); 48 | } 49 | 50 | render() { 51 | const { render, component, children } = this.props; 52 | return component 53 | ? React.createElement(component as any, this.state) 54 | : render 55 | ? (render as any)(this.state) 56 | : children // children come last, always called 57 | ? typeof children === 'function' 58 | ? children(this.state) 59 | : !isEmptyChildren(children) ? React.Children.only(children) : null 60 | : null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Scroll/__tests__/Scroll.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { Scroll } from '../Scroll'; 5 | 6 | describe('', () => { 7 | describe('', () => { 8 | const node = document.createElement('div'); 9 | 10 | afterEach(() => { 11 | ReactDOM.unmountComponentAtNode(node); 12 | }); 13 | 14 | it('receives { x, y } props', () => { 15 | ReactDOM.render( 16 | 18 | expect(scrollProps).toEqual({ x: 0, y: 0 }) || null 19 | } 20 | />, 21 | node 22 | ); 23 | }); 24 | 25 | it('renders elements', () => { 26 | ReactDOM.render( 27 | ( 29 |
30 | x: {scrollProps.x}, y: {scrollProps.y} 31 |
32 | )} 33 | />, 34 | node 35 | ); 36 | 37 | expect(node.innerHTML.includes('0')).toBe(true); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/Scroll/__tests__/withScroll.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { withScroll } from '../'; 5 | 6 | describe('withScroll()', () => { 7 | const node = document.createElement('div'); 8 | 9 | afterEach(() => { 10 | ReactDOM.unmountComponentAtNode(node); 11 | }); 12 | 13 | it('receives { x, y } props', () => { 14 | const hello = 'hi'; 15 | 16 | const WrappedComponent = withScroll<{ hello: string }>( 17 | props => expect(props).toEqual({ x: 0, y: 0, hello }) || null 18 | ); 19 | 20 | ReactDOM.render(, node); 21 | }); 22 | 23 | it('renders elements and passes thru props', () => { 24 | const hello = 'hi'; 25 | 26 | const WrappedComponent = withScroll<{ hello: string }>(props => ( 27 |
28 | x: {props.x}, y: {props.y} {hello} 29 |
30 | )); 31 | 32 | ReactDOM.render(, node); 33 | 34 | expect(node.innerHTML.includes('0')).toBe(true); 35 | expect(node.innerHTML.includes('hi')).toBe(true); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/Scroll/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './Scroll'; 9 | export * from './withScroll'; 10 | -------------------------------------------------------------------------------- /src/Scroll/withScroll.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { Scroll, ScrollProps } from './Scroll'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withScroll( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } /> 18 | ); 19 | }; 20 | 21 | return hoistNonReactStatics( 22 | S as any, 23 | Component as React.ComponentClass 24 | ) as React.ComponentType; 25 | } 26 | -------------------------------------------------------------------------------- /src/Sms.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import * as qs from 'qs'; 10 | 11 | export interface SmsProps extends React.HTMLAttributes { 12 | /** Phone number */ 13 | phone: string; 14 | /** Email body text */ 15 | body?: string; 16 | } 17 | 18 | export const Sms: React.SFC = ({ 19 | phone, 20 | body, 21 | children, 22 | ...props 23 | }) => { 24 | return ( 25 | 31 | {children} 32 | 33 | ); 34 | }; 35 | 36 | Sms.displayName = 'Sms'; 37 | -------------------------------------------------------------------------------- /src/WindowSize/WindowSize.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { SharedRenderProps } from '../types'; 10 | import { isEmptyChildren } from '../utils'; 11 | import { throttle } from '../utils/throttle'; 12 | 13 | export interface WindowSizeProps { 14 | width: number; 15 | height: number; 16 | } 17 | 18 | export interface WindowSizeConfig { 19 | throttle?: number; 20 | } 21 | 22 | export class WindowSize extends React.Component< 23 | WindowSizeConfig & SharedRenderProps, 24 | WindowSizeProps 25 | > { 26 | static defaultProps: Partial = { 27 | throttle: 100, 28 | }; 29 | 30 | unmounted: boolean; 31 | 32 | state: WindowSizeProps = { 33 | width: window.innerWidth, 34 | height: window.innerHeight 35 | }; 36 | 37 | handleWindowSize = throttle(() => { 38 | if (this.unmounted) return; 39 | this.setState({ width: window.innerWidth, height: window.innerHeight }); 40 | }, this.props.throttle!); 41 | 42 | componentDidMount() { 43 | this.handleWindowSize(); 44 | window.addEventListener('resize', this.handleWindowSize); 45 | } 46 | 47 | componentWillUnmount() { 48 | this.unmounted = true; 49 | window.removeEventListener('resize', this.handleWindowSize); 50 | } 51 | 52 | render() { 53 | const { render, component, children } = this.props; 54 | return component 55 | ? React.createElement(component as any, this.state) 56 | : render 57 | ? (render as any)(this.state) 58 | : children // children come last, always called 59 | ? typeof children === 'function' 60 | ? children(this.state) 61 | : !isEmptyChildren(children) ? React.Children.only(children) : null 62 | : null; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/WindowSize/__tests__/WindowSize.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { WindowSize } from '../WindowSize'; 5 | 6 | describe('', () => { 7 | describe('', () => { 8 | const node = document.createElement('div'); 9 | 10 | afterEach(() => { 11 | ReactDOM.unmountComponentAtNode(node); 12 | }); 13 | 14 | it('receives { width, height } props', () => { 15 | ReactDOM.render( 16 | 18 | expect(sizeProps).toEqual({ width: 1024, height: 768 }) || null 19 | } 20 | />, 21 | node 22 | ); 23 | }); 24 | 25 | it('renders elements', () => { 26 | ReactDOM.render( 27 | ( 29 |
30 | x: {sizeProps.width}, y: {sizeProps.width} 31 |
32 | )} 33 | />, 34 | node 35 | ); 36 | 37 | expect(node.innerHTML.includes('0')).toBe(true); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/WindowSize/__tests__/withWindowSize.test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import { withWindowSize } from '../withWindowSize'; 5 | 6 | describe('withScroll()', () => { 7 | const node = document.createElement('div'); 8 | 9 | afterEach(() => { 10 | ReactDOM.unmountComponentAtNode(node); 11 | }); 12 | 13 | it('receives { width, height } props', () => { 14 | const hello = 'hi'; 15 | 16 | const WrappedComponent = withWindowSize<{ hello: string }>( 17 | props => expect(props).toEqual({ width: 1024, height: 768, hello }) || null 18 | ); 19 | 20 | ReactDOM.render(, node); 21 | }); 22 | 23 | it('renders elements and passes thru props', () => { 24 | const hello = 'hi'; 25 | 26 | const WrappedComponent = withWindowSize<{ hello: string }>(props => ( 27 |
28 | x: {props.height}, y: {props.width} {hello} 29 |
30 | )); 31 | 32 | ReactDOM.render(, node); 33 | 34 | expect(node.innerHTML.includes('1024')).toBe(true); 35 | expect(node.innerHTML.includes('768')).toBe(true); 36 | expect(node.innerHTML.includes('hi')).toBe(true); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/WindowSize/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './WindowSize'; 9 | export * from './withWindowSize'; 10 | -------------------------------------------------------------------------------- /src/WindowSize/withWindowSize.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | import { WindowSize, WindowSizeProps } from './WindowSize'; 10 | import { hoistNonReactStatics } from '../hoistStatics'; 11 | 12 | export function withWindowSize( 13 | Component: React.ComponentType 14 | ) { 15 | const S: React.SFC = props => { 16 | return ( 17 | } 19 | /> 20 | ); 21 | }; 22 | 23 | return hoistNonReactStatics( 24 | S as any, 25 | Component as React.ComponentClass 26 | ) as React.ComponentType; 27 | } 28 | -------------------------------------------------------------------------------- /src/hoistStatics.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './utils/isEmptyChildren'; 9 | 10 | import { ComponentClass } from 'react'; 11 | 12 | const REACT_STATICS: any = { 13 | childContextTypes: true, 14 | contextTypes: true, 15 | defaultProps: true, 16 | displayName: true, 17 | getDefaultProps: true, 18 | mixins: true, 19 | propTypes: true, 20 | type: true, 21 | }; 22 | 23 | const KNOWN_STATICS: any = { 24 | name: true, 25 | length: true, 26 | prototype: true, 27 | caller: true, 28 | callee: true, 29 | arguments: true, 30 | arity: true, 31 | }; 32 | 33 | const getOwnPropertySymbols = Object.getOwnPropertySymbols; 34 | const propIsEnumerable = Object.prototype.propertyIsEnumerable; 35 | const getPrototypeOf = Object.getPrototypeOf; 36 | const objectPrototype = getPrototypeOf && getPrototypeOf(Object); 37 | const getOwnPropertyNames = Object.getOwnPropertyNames; 38 | 39 | export function hoistNonReactStatics

( 40 | targetComponent: ComponentClass

, 41 | sourceComponent: ComponentClass, 42 | blacklist?: { [name: string]: boolean } 43 | ): ComponentClass

{ 44 | if (typeof sourceComponent !== 'string') { 45 | // don't hoist over string (html) components 46 | 47 | if (objectPrototype) { 48 | let inheritedComponent = getPrototypeOf(sourceComponent); 49 | if (inheritedComponent && inheritedComponent !== objectPrototype) { 50 | hoistNonReactStatics(targetComponent, inheritedComponent, blacklist); 51 | } 52 | } 53 | 54 | let keys = getOwnPropertyNames(sourceComponent); 55 | 56 | if (getOwnPropertySymbols) { 57 | keys = keys.concat(getOwnPropertySymbols(sourceComponent) as any); 58 | } 59 | 60 | for (let i = 0; i < keys.length; ++i) { 61 | let key: string = keys[i]; 62 | if ( 63 | !REACT_STATICS[key] && 64 | !KNOWN_STATICS[key] && 65 | (!blacklist || !blacklist[key]) 66 | ) { 67 | // Only hoist enumerables and non-enumerable functions 68 | if ( 69 | propIsEnumerable.call(sourceComponent, key) || 70 | typeof (sourceComponent as any)[key] === 'function' 71 | ) { 72 | try { 73 | // Avoid failures from read-only properties 74 | (targetComponent as any)[key] = (sourceComponent as any)[key]; 75 | // tslint:disable-next-line:no-empty 76 | } catch (e) {} 77 | } 78 | } 79 | } 80 | 81 | return targetComponent; 82 | } 83 | 84 | return targetComponent; 85 | } 86 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './Scroll'; 9 | export * from './DeviceMotion'; 10 | export * from './DeviceOrientation'; 11 | export * from './Network'; 12 | export * from './GeoPosition'; 13 | export * from './Media'; 14 | export * from './WindowSize'; 15 | export * from './Locales'; 16 | -------------------------------------------------------------------------------- /src/types.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | 10 | export interface SharedRenderProps

{ 11 | debug?: boolean; 12 | component?: React.ComponentType

; 13 | render?: ((props: P) => React.ReactNode); 14 | children?: ((props: P) => React.ReactNode) | React.ReactNode; 15 | } 16 | 17 | export type CompositeComponent

= 18 | | React.ComponentClass

19 | | React.StatelessComponent

; 20 | 21 | export interface ComponentDecorator { 22 | (component: CompositeComponent): React.ComponentClass< 23 | TOwnProps 24 | >; 25 | } 26 | 27 | export interface InferableComponentDecorator { 28 | >(component: T): T; 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/debounce.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | /** 9 | * Returns a function, that, as long as it continues to be invoked, will not 10 | * be triggered. The function will be called after it stops being called for 11 | * N milliseconds. If `immediate` is passed, trigger the function on the 12 | * leading edge, instead of the trailing. 13 | * 14 | * @param func a function 15 | * @param wait time to wait 16 | * @param immediate should it be called immediately 17 | */ 18 | export const debounce = (func: Function, wait: number, immediate?: boolean) => { 19 | let timeout: any; 20 | return function(this: any) { 21 | var context = this, 22 | args = arguments; 23 | var later = function() { 24 | timeout = null; 25 | if (!immediate) func.apply(context, args); 26 | }; 27 | var callNow = immediate && !timeout; 28 | clearTimeout(timeout); 29 | timeout = setTimeout(later, wait); 30 | if (callNow) func.apply(context, args); 31 | }; 32 | }; 33 | -------------------------------------------------------------------------------- /src/utils/featureDetection.ts: -------------------------------------------------------------------------------- 1 | export let supportsPassiveListener = false; 2 | 3 | const noop = () => {}; 4 | 5 | try { 6 | const opts = Object.defineProperty({}, 'passive', { 7 | get: function() { 8 | supportsPassiveListener = true; 9 | }, 10 | }); 11 | (window as EventTarget).addEventListener('testPassive', noop, opts); 12 | (window as EventTarget).removeEventListener('testPassive', noop, opts); 13 | } catch (e) {} 14 | -------------------------------------------------------------------------------- /src/utils/index.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | export * from './isEmptyChildren'; 9 | export * from './debounce'; 10 | export * from './throttle'; 11 | -------------------------------------------------------------------------------- /src/utils/isEmptyChildren.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present Jared Palmer 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | import * as React from 'react'; 9 | 10 | export const isEmptyChildren = (children: any) => 11 | React.Children.count(children) === 0; 12 | -------------------------------------------------------------------------------- /src/utils/throttle.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Throttling enforces a maximum number of times a function 3 | * can be called over time. 4 | * 5 | * @param func a function 6 | * @param wait time 7 | */ 8 | 9 | export function throttle(this: any, func: Function, wait: Number) { 10 | let timeout: number | null = null; 11 | let callbackArgs: IArguments | null = null; 12 | const context = this; 13 | 14 | const later = () => { 15 | func.apply(context, callbackArgs); 16 | timeout = null; 17 | }; 18 | 19 | return function() { 20 | if (!timeout) { 21 | callbackArgs = arguments; 22 | timeout = setTimeout(later, wait); 23 | } 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "declaration": true, 5 | "declarationDir": "dist", 6 | "emitDecoratorMetadata": false, 7 | "experimentalDecorators": false, 8 | "importHelpers": true, 9 | "jsx": "react", 10 | "lib": ["es2015", "dom"], 11 | "module": "es2015", 12 | "moduleResolution": "node", 13 | "noFallthroughCasesInSwitch": true, 14 | "noImplicitAny": true, 15 | "noImplicitReturns": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "outDir": "compiled", 19 | "removeComments": true, 20 | "sourceMap": true, 21 | "pretty": true, 22 | "strict": true, 23 | 24 | "stripInternal": true, 25 | "target": "es5", 26 | "typeRoots": ["node_modules/@types"] 27 | }, 28 | "include": ["src", "./typings.d.ts"], 29 | "exclude": ["node_modules", "dist", "src/**/*.test.tsx"] 30 | } 31 | -------------------------------------------------------------------------------- /website/core/Footer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | const React = require("react"); 11 | 12 | const highlighterCode = ` 13 | function fn() { 14 | Array.prototype.forEach.call( 15 | document.querySelectorAll("pre"), 16 | hljs.highlightBlock 17 | ); 18 | }; 19 | if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){ 20 | fn(); 21 | } else { 22 | document.addEventListener('DOMContentLoaded', fn); 23 | } 24 | `; 25 | 26 | class Footer extends React.Component { 27 | render() { 28 | return ( 29 | 30 | 31 | 32 | 50 | 51 | ); 52 | const currentYear = new Date().getFullYear(); 53 | return ( 54 |

119 | ); 120 | } 121 | } 122 | 123 | module.exports = Footer; 124 | -------------------------------------------------------------------------------- /website/deploy.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Copyright (c) 2017-present, Facebook, Inc. 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE file in the root directory of this source tree. 8 | */ 9 | 10 | const shell = require("shelljs"); 11 | 12 | const GIT_USER = process.env.GIT_USER; 13 | const CIRCLE_BRANCH = process.env.CIRCLE_BRANCH; 14 | const CIRCLE_PROJECT_USERNAME = process.env.CIRCLE_PROJECT_USERNAME; 15 | const CIRCLE_PROJECT_REPONAME = process.env.CIRCLE_PROJECT_REPONAME; 16 | const CI_PULL_REQUEST = process.env.CI_PULL_REQUEST; 17 | 18 | const USE_SSH = process.env.USE_SSH; 19 | 20 | let remoteBranch; 21 | if (USE_SSH === "true") { 22 | remoteBranch = `git@github.com:${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}.git`; 23 | } else { 24 | remoteBranch = `https://${GIT_USER}@github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}.git`; 25 | } 26 | 27 | // build static html files, then push to master branch of specified repo 28 | 29 | if (!shell.which("git")) { 30 | shell.echo("Sorry, this script requires git"); 31 | shell.exit(1); 32 | } 33 | 34 | if (CI_PULL_REQUEST) { 35 | shell.echo("Skipping deploy on a pull request"); 36 | shell.exit(0); 37 | } 38 | 39 | if (CIRCLE_BRANCH === "master") { 40 | shell.echo("Cannot deploy from a master branch. Only to it"); 41 | shell.exit(1); 42 | } 43 | 44 | if (shell.exec(`node ${__dirname}/node_modules/docusaurus/lib/build-files.js`).code) { 45 | shell.echo("Error: generating html failed"); 46 | shell.exit(1); 47 | } 48 | 49 | shell.cd(process.cwd()); 50 | shell.cd("build"); 51 | 52 | if ( 53 | shell.exec(`git clone ${remoteBranch} ${CIRCLE_PROJECT_REPONAME}-master`) 54 | .code !== 0 55 | ) { 56 | shell.echo("Error: git clone failed"); 57 | shell.exit(1); 58 | } 59 | 60 | shell.cd(`${CIRCLE_PROJECT_REPONAME}-master`); 61 | 62 | if (shell.exec("git checkout origin/master").code !== 0) { 63 | if (shell.exec("git checkout --orphan master").code !== 0) { 64 | shell.echo("Error: Git checkout master failed"); 65 | shell.exit(1); 66 | } 67 | } else { 68 | if ( 69 | shell.exec("git checkout -b master").code + 70 | shell.exec("git branch --set-upstream-to=origin/master").code !== 71 | 0 72 | ) { 73 | shell.echo("Error: Git checkout master failed"); 74 | shell.exit(1); 75 | } 76 | } 77 | 78 | shell.exec("git rm -rf ."); 79 | 80 | shell.cd("../.."); 81 | 82 | shell.cp( 83 | "-R", 84 | `build/${CIRCLE_PROJECT_REPONAME}/*`, 85 | `build/${CIRCLE_PROJECT_REPONAME}-master/` 86 | ); 87 | shell.cd(`build/${CIRCLE_PROJECT_REPONAME}-master`); 88 | 89 | const currentCommit = shell.exec('git rev-parse HEAD').stdout.trim(); 90 | 91 | shell.exec("git add --all"); 92 | shell.exec(`git commit -m "Deploy website" -m "Deploy website version based on ${currentCommit}"`); 93 | if (shell.exec("git push origin master").code !== 0) { 94 | shell.echo("Error: Git push failed"); 95 | shell.exit(1); 96 | } else { 97 | shell.echo( 98 | `Website is live at: https://${CIRCLE_PROJECT_USERNAME}.github.io/${CIRCLE_PROJECT_REPONAME}/` 99 | ); 100 | shell.exit(0); 101 | } 102 | -------------------------------------------------------------------------------- /website/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "_comment": "This file is auto-generated by write-translations.js", 3 | "localized-strings": { 4 | "next": "Next", 5 | "previous": "Previous", 6 | "tagline": "React Components for common Web APIs", 7 | "api": "API Reference", 8 | "how-it-works": "How it works", 9 | "installation": "Installation", 10 | "Docs": "Docs", 11 | "GitHub": "GitHub", 12 | "Intro": "Intro", 13 | "API Reference": "API Reference" 14 | }, 15 | "pages-strings": { 16 | "Learn more using the [documentation on this site.](/test-site/docs/en/doc1.html)|no description given": "Learn more using the [documentation on this site.](/test-site/docs/en/doc1.html)", 17 | "Browse Docs|no description given": "Browse Docs", 18 | "Ask questions about the documentation and project|no description given": "Ask questions about the documentation and project", 19 | "Join the community|no description given": "Join the community", 20 | "Find out what's new with this project|no description given": "Find out what's new with this project", 21 | "Stay up to date|no description given": "Stay up to date", 22 | "Need help?|no description given": "Need help?", 23 | "This project is maintained by a dedicated group of people.|statement made to reader": "This project is maintained by a dedicated group of people.", 24 | "Get Started|no description given": "Get Started", 25 | "Help Translate|recruit community translators for your project": "Help Translate", 26 | "Edit this Doc|recruitment message asking to edit the doc source": "Edit", 27 | "Translate this Doc|recruitment message asking to translate the docs": "Translate" 28 | } 29 | } -------------------------------------------------------------------------------- /website/languages.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | const languages = [ 11 | { 12 | enabled: false, 13 | name: "日本語", 14 | tag: "ja" 15 | }, 16 | { 17 | enabled: true, 18 | name: "English", 19 | tag: "en" 20 | }, 21 | { 22 | enabled: false, 23 | name: "العربية", 24 | tag: "ar" 25 | }, 26 | { 27 | enabled: false, 28 | name: "Bosanski", 29 | tag: "bs-BA" 30 | }, 31 | { 32 | enabled: false, 33 | name: "Català", 34 | tag: "ca" 35 | }, 36 | { 37 | enabled: false, 38 | name: "Čeština", 39 | tag: "cs" 40 | }, 41 | { 42 | enabled: false, 43 | name: "Dansk", 44 | tag: "da" 45 | }, 46 | { 47 | enabled: false, 48 | name: "Deutsch", 49 | tag: "de" 50 | }, 51 | { 52 | enabled: false, 53 | name: "Ελληνικά", 54 | tag: "el" 55 | }, 56 | { 57 | enabled: false, 58 | name: "Español", 59 | tag: "es-ES" 60 | }, 61 | { 62 | enabled: false, 63 | name: "فارسی", 64 | tag: "fa-IR" 65 | }, 66 | { 67 | enabled: false, 68 | name: "Suomi", 69 | tag: "fi" 70 | }, 71 | { 72 | enabled: true, 73 | name: "Français", 74 | tag: "fr" 75 | }, 76 | { 77 | enabled: false, 78 | name: "עִברִית", 79 | tag: "he" 80 | }, 81 | { 82 | enabled: false, 83 | name: "Magyar", 84 | tag: "hu" 85 | }, 86 | { 87 | enabled: false, 88 | name: "Bahasa Indonesia", 89 | tag: "id-ID" 90 | }, 91 | { 92 | enabled: false, 93 | name: "Italiano", 94 | tag: "it" 95 | }, 96 | { 97 | enabled: false, 98 | name: "Afrikaans", 99 | tag: "af" 100 | }, 101 | { 102 | enabled: false, 103 | name: "한국어", 104 | tag: "ko" 105 | }, 106 | { 107 | enabled: false, 108 | name: "मराठी", 109 | tag: "mr-IN" 110 | }, 111 | { 112 | enabled: false, 113 | name: "Nederlands", 114 | tag: "nl" 115 | }, 116 | { 117 | enabled: false, 118 | name: "Norsk", 119 | tag: "no-NO" 120 | }, 121 | { 122 | enabled: false, 123 | name: "Polskie", 124 | tag: "pl" 125 | }, 126 | { 127 | enabled: false, 128 | name: "Português", 129 | tag: "pt-PT" 130 | }, 131 | { 132 | enabled: false, 133 | name: "Português (Brasil)", 134 | tag: "pt-BR" 135 | }, 136 | { 137 | enabled: false, 138 | name: "Română", 139 | tag: "ro" 140 | }, 141 | { 142 | enabled: true, 143 | name: "Русский", 144 | tag: "ru" 145 | }, 146 | { 147 | enabled: false, 148 | name: "Slovenský", 149 | tag: "sk-SK" 150 | }, 151 | { 152 | enabled: false, 153 | name: "Српски језик (Ћирилица)", 154 | tag: "sr" 155 | }, 156 | { 157 | enabled: false, 158 | name: "Svenska", 159 | tag: "sv-SE" 160 | }, 161 | { 162 | enabled: false, 163 | name: "Türkçe", 164 | tag: "tr" 165 | }, 166 | { 167 | enabled: false, 168 | name: "Українська", 169 | tag: "uk" 170 | }, 171 | { 172 | enabled: false, 173 | name: "Tiếng Việt", 174 | tag: "vi" 175 | }, 176 | { 177 | enabled: true, 178 | name: "中文", 179 | tag: "zh-CN" 180 | }, 181 | { 182 | enabled: false, 183 | name: "繁體中文", 184 | tag: "zh-Hant" 185 | }, 186 | { 187 | enabled: false, 188 | name: "ɥsᴉlƃuƎ uʍopǝpᴉsd∩", 189 | tag: "en-UD" 190 | } 191 | ]; 192 | module.exports = languages; 193 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bucklescript-site", 3 | "scripts": { 4 | "start": "docusaurus-start", 5 | "build": "docusaurus-build", 6 | "write-translations": "docusaurus-write-translations", 7 | "crowdin-upload": 8 | "export CROWDIN_DOCUSAURUS_PROJECT_ID=\"esy-website\"; export CROWDIN_DOCUSAURUS_API_KEY=$ESY_CROWDIN_API_KEY; crowdin --config ../crowdin.yaml upload sources --auto-update -b master", 9 | "crowdin-download": 10 | "export CROWDIN_DOCUSAURUS_PROJECT_ID=\"esy-website\"; export CROWDIN_DOCUSAURUS_API_KEY=$ESY_CROWDIN_API_KEY; crowdin --config ../crowdin.yaml download -b master" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/jaredpalmer/react-fns.git" 18 | }, 19 | "dependencies": { 20 | "docusaurus": "^1.0.0-beta.15" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /website/pages/en/help-with-translations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | const React = require("react"); 11 | 12 | const CompLibrary = require("../../core/CompLibrary.js"); 13 | const Container = CompLibrary.Container; 14 | const GridBlock = CompLibrary.GridBlock; 15 | 16 | const translate = require("../../server/translate.js").translate; 17 | 18 | const siteConfig = require(process.cwd() + "/siteConfig.js"); 19 | 20 | class Help extends React.Component { 21 | render() { 22 | const supportLinks = [ 23 | { 24 | content: ( 25 | 26 | Learn more using the [documentation on this site.](/test-site/docs/en/doc1.html) 27 | 28 | ), 29 | title: Browse Docs 30 | }, 31 | { 32 | content: ( 33 | 34 | Ask questions about the documentation and project 35 | 36 | ), 37 | title: Join the community 38 | }, 39 | { 40 | content: Find out what's new with this project, 41 | title: Stay up to date 42 | } 43 | ]; 44 | 45 | return ( 46 |
47 | 48 |
49 |
50 |

51 | Need help? 52 |

53 |
54 |

55 | 56 | This project is maintained by a dedicated group of people. 57 | 58 |

59 | 60 |
61 |
62 |
63 | ); 64 | } 65 | } 66 | 67 | Help.defaultProps = { 68 | language: "en" 69 | }; 70 | 71 | module.exports = Help; 72 | -------------------------------------------------------------------------------- /website/pages/en/index.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | 3 | const CompLibrary = require('../../core/CompLibrary.js'); 4 | const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */ 5 | const Container = CompLibrary.Container; 6 | const GridBlock = CompLibrary.GridBlock; 7 | 8 | const translate = require('../../server/translate.js').translate; 9 | 10 | const siteConfig = require(process.cwd() + '/siteConfig.js'); 11 | 12 | class Button extends React.Component { 13 | render() { 14 | return ( 15 | 24 | ); 25 | } 26 | } 27 | 28 | Button.defaultProps = { 29 | target: '_self', 30 | }; 31 | const pre = '```'; 32 | const code = '`'; 33 | 34 | const quickStart = `${pre}bash 35 | #Install esy 36 | npm install -g esy 37 | 38 | # Clone the example 39 | git clone git@github.com:esy-ocaml/esy-ocaml-project.git 40 | cd esy-ocaml-project 41 | 42 | # Install project's dependencies: 43 | esy install 44 | 45 | # Perform an initial build: 46 | esy build 47 | ${pre}`; 48 | 49 | class HomeSplash extends React.Component { 50 | render() { 51 | let promoSection = ( 52 |
53 |
54 |
55 | 66 | 69 |
70 |
71 |
72 | ); 73 | 74 | return ( 75 |
76 |
77 |
78 |
{siteConfig.title}
79 | 80 |
81 |
{siteConfig.tagline}
82 |
83 | 84 | {promoSection} 85 |
86 |
87 |
88 | ); 89 | } 90 | } 91 | 92 | class Index extends React.Component { 93 | render() { 94 | let language = this.props.language || 'en'; 95 | 96 | return ( 97 |
98 | 99 |
100 | 101 | 120 | 121 |
122 |
123 | ); 124 | } 125 | } 126 | 127 | module.exports = Index; 128 | -------------------------------------------------------------------------------- /website/sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": { 3 | "Intro": ["installation", "how-it-works"], 4 | "API Reference": ["api"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /website/siteConfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | const siteConfig = { 11 | title: 'react-fns', 12 | tagline: 'React Components for common Web APIs', 13 | url: 'https://react-fns.netlify.com', 14 | editUrl: 'https://github.com/jaredpalmer/react-fns/tree/source/docs/', 15 | translationRecruitingLink: 'https://crowdin.com/project/react-fns-website', 16 | sourceCodeButton: null, 17 | baseUrl: '/', 18 | projectName: '', 19 | headerLinks: [ 20 | { doc: 'installation', label: 'Docs' }, 21 | 22 | { languages: true }, 23 | { search: true }, 24 | { href: 'https://github.com/jaredpalmer/react-fns', label: 'GitHub' }, 25 | ], 26 | headerIcon: 'img/reason-react-white.svg', 27 | // footerIcon: "img/logo.svg", 28 | favicon: 'img/reason-react-red.svg', 29 | /* colors for website */ 30 | colors: { 31 | primaryColor: '#05a', 32 | secondaryColor: '#05a', 33 | codeColor: 34 | 'rgba(0, 85, 170, 0.03)' /* primaryColor in rgba form, with 0.03 alpha */, 35 | }, 36 | highlight: { 37 | theme: 'github', 38 | }, 39 | algolia: { 40 | apiKey: '3a8847aa925527a21be796bd981526c6', 41 | indexName: 'react-fns', 42 | }, 43 | markdownPlugins: [ 44 | // ignore `` before passing into Docusaurus to avoid mis-parsing (#3322) 45 | md => { 46 | md.block.ruler.before( 47 | 'htmlblock', 48 | 'prettierignore', 49 | (state, startLine) => { 50 | const pos = state.bMarks[startLine]; 51 | const max = state.eMarks[startLine]; 52 | if (//.test(state.src.slice(pos, max))) { 53 | state.line += 1; 54 | return true; 55 | } 56 | return false; 57 | } 58 | ); 59 | }, 60 | ], 61 | }; 62 | 63 | module.exports = siteConfig; 64 | -------------------------------------------------------------------------------- /website/static/circle.yml: -------------------------------------------------------------------------------- 1 | # CircleCI by default tests every push to every branch. This mean automated release from source to master will trigger CI testing master. 2 | # we disable that. Piece and quiet: https://circleci.com/docs/1.0/configuration/#branches 3 | # master is cleared out on every push, so we can't manually put a circle.yml there. So we piggy back off static/ being copied over to master during deployment. 4 | general: 5 | branches: 6 | ignore: 7 | - master 8 | -------------------------------------------------------------------------------- /website/static/css/custom.css: -------------------------------------------------------------------------------- 1 | .fixedHeaderContainer header > a > img { 2 | /* spinning logo */ 3 | transition: transform 0.5s cubic-bezier(0.18, 0.89, 0.31, 1.16); 4 | /* make sure it's a square. Spins at the center */ 5 | height: 34px; 6 | width: 34px; 7 | } 8 | .fixedHeaderContainer header > a:hover > img { 9 | transform: scale(1.3); 10 | } 11 | 12 | /* only show language icon */ 13 | #languages-menu { 14 | font-size: 0px; 15 | padding: 0; 16 | } 17 | #languages-menu .languages-icon { 18 | margin: 0; 19 | padding: 6px 10px; 20 | } 21 | #languages-dropdown { 22 | width: auto; 23 | right: 0; 24 | } 25 | @media only screen and (min-width: 1024px) { 26 | /* only targets language dropdown li, i swear */ 27 | .nav-site > span > li { 28 | position: relative; 29 | } 30 | } 31 | 32 | #docsNav.container.docsNavContainer .toc::-webkit-scrollbar { 33 | display: none; 34 | } 35 | 36 | .spinner { 37 | margin: auto; 38 | position: absolute; 39 | width: 800px; 40 | left: 0px; 41 | bottom: -400px; 42 | animation: spin 750s linear infinite; 43 | transition: opacity 2s; 44 | } 45 | 46 | @media (max-width: 1024px) { 47 | .spinner { 48 | opacity: 0; 49 | } 50 | } 51 | 52 | @keyframes spin { 53 | 100% { 54 | transform: rotate(360deg); 55 | } 56 | } 57 | 58 | /* overrides. Most of these should be upstreamed into docusaurus */ 59 | 60 | .button { 61 | /* these buttons really shouldn't have any margin */ 62 | margin: 0; 63 | } 64 | .read-more { 65 | /* ... the margin belongs to the wrapper */ 66 | margin: 20px 0 0 0; 67 | } 68 | .edit-page-link { 69 | /* header H1 height at small size is 27, button is 34, center it (-3.5px) */ 70 | margin-top: -3.5px; 71 | } 72 | @media (min-width: 736px) { 73 | .edit-page-link { 74 | /* doc header H1 height at big size is 45, button is 34, center it (5.5px) */ 75 | margin-top: 5.5px; 76 | } 77 | } 78 | 79 | .navPusher { 80 | height: auto; 81 | } 82 | 83 | .navGroup { 84 | background-color: #f6f4f4 !important; 85 | } 86 | 87 | .projectTitle { 88 | font-size: 50px; 89 | font-weight: bold; 90 | font-family: proxima-nova, sans-serif; 91 | } 92 | 93 | .quickStartAndExamples .blockImage { 94 | max-width: 226px; 95 | box-shadow: 0px 0 20px lightgrey; 96 | margin: 0; 97 | } 98 | 99 | .quickStartAndExamples .blockContent h2 { 100 | margin: 0; 101 | } 102 | 103 | .quickStartAndExamples .blockimage img { 104 | border-radius: 3px; 105 | } 106 | 107 | /* end override */ 108 | -------------------------------------------------------------------------------- /website/static/css/reason.css: -------------------------------------------------------------------------------- 1 | /* These should be upstreamed to Docusaurus */ 2 | 3 | table { 4 | border: 1px solid #eee; 5 | } 6 | 7 | table thead { 8 | border-bottom: 1px solid #eee; 9 | } 10 | 11 | table tr:nth-of-type(odd) { 12 | background: $codeColor; 13 | } 14 | 15 | table tr th, 16 | table tr td { 17 | border-right: 1px solid #eee; 18 | } 19 | 20 | /* Below are styles that should be global to all Reason-ecosystem sites 21 | but which we have no current plans to upstream to Docusaurus. */ 22 | 23 | /* 1. Independent scrolling of docs sidebar menu (requires pjax) */ 24 | @media only screen and (min-width: 1024px) { 25 | /* makes desktop nav scroll independently of content */ 26 | #docsNav.container.docsNavContainer .toc { 27 | position: fixed; 28 | overflow-y: scroll; 29 | bottom: 0; 30 | top: 0; 31 | padding: 0; 32 | padding-top: 90px; 33 | } 34 | /* safari fix */ 35 | #docsNav.container.docsNavContainer { 36 | position: static; 37 | } 38 | } 39 | 40 | /* 2. Custom code block styling */ 41 | /* code */ 42 | code { 43 | padding: 2px 0; 44 | background-color: rgba(0, 85, 170, 0.1); 45 | box-shadow: 2px 0 rgba(0, 85, 170, 0.1), -2px 0 rgba(0, 85, 170, 0.1); 46 | color: inherit; 47 | border-radius: 0.3em; 48 | /* avoids scale issues on mobile */ 49 | word-break: break-word; 50 | } 51 | pre code { 52 | background-color: unset; 53 | box-shadow: none; 54 | } 55 | .hljs { 56 | /* this -15px margin offsets the usual margin given to a page. This way it looks like this: 57 | | | 58 | | myCode | 59 | | | 60 | 61 | instead of: 62 | 63 | | | 64 | | myCode | 65 | | | 66 | 67 | might not be clear as ascii comment, but the **code words inside the code 68 | block** are aligned with the page's boundaries; previously the code block 69 | itself starts at the boundary, then has extra padding. Now the code words 70 | themselves are aligned with the rest of the page's paragraphs' starting 71 | point 72 | */ 73 | margin-left: -15px; 74 | margin-right: -15px; 75 | border: 1px solid #eee; 76 | border-radius: 6px; 77 | padding: 15px; 78 | overflow: auto; 79 | background-color: $codeColor; 80 | font-size: 15px; 81 | line-height: 23px; 82 | 83 | /* respect the section's max width, from .mainContainer .wrapper p */ 84 | max-width: 50rem; 85 | } 86 | .hljs.javascript { 87 | background-color: rgba(0, 85, 170, 0.03); 88 | } 89 | .hljs .comment { 90 | opacity: 0.7; 91 | } 92 | 93 | @media (max-width: 736px) { 94 | .hljs { 95 | margin-left: -20px; 96 | margin-right: -20px; 97 | padding: 20px; 98 | /* on small screens, the code block completely occupies the width of the 99 | view. No need for left/right borders */ 100 | border-radius: 0; 101 | border-left: none; 102 | border-right: none; 103 | } 104 | } 105 | 106 | /* 3. Homepage changes */ 107 | #redirectMessage { 108 | font-size: 30px; 109 | line-height: 30px; 110 | padding-top: 100px; 111 | margin: auto; 112 | max-width: 800px; 113 | text-align: center; 114 | } 115 | 116 | #redirectBanner { 117 | background: #808080; 118 | color: #fff; 119 | display: none; 120 | font-weight: 300; 121 | margin: auto; 122 | padding: 10px 0; 123 | text-align: center; 124 | } 125 | 126 | #redirectBanner a { 127 | color: #fff; 128 | opacity: 0.6; 129 | } 130 | 131 | #redirectBanner a:hover { 132 | opacity: 1; 133 | } 134 | 135 | .homeWrapperWrapper { 136 | background-color: #f6f4f4; 137 | overflow: hidden; 138 | position: relative; 139 | } 140 | 141 | .homeWrapperInner { 142 | display: flex; 143 | align-items: center; 144 | justify-content: space-evenly; 145 | padding: 20px 0px 20px 0px; 146 | } 147 | 148 | .homeCodeSnippet { 149 | background-color: transparent; 150 | border: none; 151 | text-align: initial; 152 | padding-top: 20px; 153 | } 154 | 155 | .homeTagLine { 156 | font-size: 24px; 157 | font-weight: 300; 158 | max-width: 500px; 159 | line-height: 1.5em; 160 | } 161 | 162 | .homeThreePoints { 163 | padding-top: 20px; 164 | } 165 | 166 | .quickStartAndExamples { 167 | /* homeThreePoints + blockElement have 30 padding, so we should have 30 here too */ 168 | padding-top: 30px; 169 | /* block padding of 80px is too huge */ 170 | padding-bottom: 30px; 171 | } 172 | 173 | .quickStartAndExamples .wrapper { 174 | display: flex; 175 | flex-direction: row; 176 | justify-content: space-around; 177 | max-width: 1024px; 178 | } 179 | 180 | @media (max-width: 736px) { 181 | .quickStartAndExamples .wrapper { 182 | flex-direction: column; 183 | } 184 | } 185 | 186 | /* companies page */ 187 | .addCompanyButton { 188 | margin-top: 2em; 189 | } 190 | 191 | @media (max-width: 1024px) { 192 | .homeWrapperInner { 193 | flex-direction: column; 194 | } 195 | 196 | .projectTitle { 197 | font-size: 40px; 198 | } 199 | } 200 | /* mobile */ 201 | @media (max-width: 480px) { 202 | .projectTitle { 203 | font-size: 30px; 204 | } 205 | 206 | .homeCodeSnippet .hljs { 207 | font-size: 13px; 208 | } 209 | } 210 | 211 | .homeCodeSnippet .hljs { 212 | background-color: transparent; 213 | border: none; 214 | padding-top: 0; 215 | padding-bottom: 0; 216 | margin: 0; 217 | color: rgb(57, 57, 57); 218 | font-size: 14px; 219 | } 220 | 221 | .getStarted { 222 | background-color: $primaryColor; 223 | color: white; 224 | /* buttons have 10px margin. Make the right padding 10px too so that it looks nicely spaced beside the Tutorial button */ 225 | margin: 0 10px 0 0; 226 | } 227 | -------------------------------------------------------------------------------- /website/static/img/reason-react-red.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/static/img/reason-react-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /website/static/js/redirect.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is not run through any build step! Don't add any fancy stuff 3 | */ 4 | 5 | (function() { 6 | var faq = { 7 | '#frequently-asked-questions-common-type-errors': 'im-having-a-type-error.html', 8 | '#frequently-asked-questions-how-do-i-do-props-spreading-div-thisprops': 'props-spread.html', 9 | default: 'im-having-a-type-error.html' 10 | }; 11 | var examples = { 12 | '#examples-simple': 'simple.html', 13 | '#examples-counter': 'counter.html', 14 | '#examples-reasonreact-using-reactjs': 'retained-props.html', 15 | '#examples-reasonreact-using-reactjs': 'reason-using-js.html', 16 | '#examples-reactjs-using-reasonreact': 'js-using-reason.html', 17 | default: 'simple.html' 18 | }; 19 | var gettingStarted = { 20 | '#getting-started': 'installation.html', 21 | '#getting-started-bsb': 'installation.html#bsb', 22 | '#getting-started-reason-scripts': 'installation.html#reason-scripts', 23 | default: 'installation.html' 24 | }; 25 | // redirects[page][hash] => new page; 26 | // yarn start only supports faq.html format, but gh pages upens up the other two. 27 | var redirects = { 28 | 'faq.html': faq, 29 | 'faq': faq, 30 | 'faq/': faq, 31 | 'examples.html': examples, 32 | 'examples': examples, 33 | 'examples/': examples, 34 | 'gettingStarted.html': gettingStarted, 35 | 'gettingStarted': gettingStarted, 36 | 'gettingStarted/': gettingStarted, 37 | }; 38 | var hash = window.location.hash; 39 | var base = '/reason-react/docs/en/'; 40 | var path = window.location.pathname.split('/'); 41 | var page = path[path.length - 1]; 42 | if (redirects[page]) { 43 | var link = document.getElementById('redirectLink'); 44 | var location = base + 45 | (redirects[page][hash] || redirects[page].default); 46 | link.textContent = 'https://reasonml.github.io' + location; 47 | link.href = location; 48 | } 49 | })(); 50 | -------------------------------------------------------------------------------- /website/static/js/redirectBlog.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is not run through any build step! Don't add any fancy stuff 3 | */ 4 | 5 | (function() { 6 | var path = window.location.pathname.split('/'); 7 | var page = path[path.length - 1]; 8 | if (page.indexOf('blog') !== 0) { 9 | return; 10 | } 11 | // redirects[page][hash] => new page; 12 | var redirects = { 13 | '#reducers-are-here': '2017/09/01/reducers.html', 14 | '#reducers-are-here-design-decisions': '2017/09/01/reducers.html#design-decisions', 15 | '#021-released': '2017/07/05/021.html', 16 | '#015-released': '2017/06/21/015.html', 17 | '#major-new-release': '2017/06/09/major-release.html' 18 | }; 19 | 20 | var hash = window.location.hash 21 | var base = '/reason-react/blog/'; 22 | Object.keys(redirects).forEach(function(redirect) { 23 | if (redirect === hash) { 24 | // setup html 25 | var bannerString = '
Hello! This particular blog post has moved to . Please update the URLs to reflect it. Thanks!
'; 26 | var div = document.createElement('div'); 27 | div.innerHTML = bannerString; 28 | var redirectBanner = div.firstChild; 29 | var navPusher = document.querySelector('.navPusher'); 30 | navPusher.insertBefore(redirectBanner, navPusher.firstChild); 31 | 32 | var newHash = redirect.split(hash + '-')[1] || ''; 33 | newHash = newHash ? '#' + newHash : newHash; 34 | var link = document.getElementById('redirectLink'); 35 | var banner = document.getElementById('redirectBanner'); 36 | var location = base + redirects[redirect] + newHash; 37 | 38 | link.textContent = 'https://reasonml.github.io' + location; 39 | link.href = location; 40 | banner.style.display = 'block'; 41 | } 42 | }); 43 | })(); 44 | -------------------------------------------------------------------------------- /website/static/js/redirectIndex.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This code is not run through any build step! Don't add any fancy stuff 3 | */ 4 | 5 | (function() { 6 | // redirects[page][hash] => new page; 7 | var redirects = { 8 | 'reason-react': 'reason-react.html', 9 | 'intro-example': 'intro-example.html', 10 | 'jsx': 'jsx.html', 11 | 'component-creation': 'creation-props-self.html', 12 | 'react-element': 'render.html', 13 | 'interop-with-existing-javascript-components': 'interop.html', 14 | 'events': 'event.html', 15 | 'styles': 'style.html', 16 | 'cloneelement': 'clone-element.html', 17 | 'working-with-children': 'children.html', 18 | 'working-with-dom': 'dom.html', 19 | 'convert-over-reactjs-idioms': 'convert.html', 20 | 'miscellaneous': 'context-mixins.html', 21 | 'common-type-errors': 'im-having-a-type-error.html', 22 | }; 23 | // they all start with reason-react 24 | var hash = window.location.hash 25 | if (hash.indexOf('reason-react') !== 1) { 26 | return; 27 | } 28 | if (hash === '#reason-react') { 29 | hash = 'reason-react'; 30 | } else { 31 | hash = hash.split('reason-react-')[1]; 32 | } 33 | var path = window.location.pathname.split('/'); 34 | var page = path[path.length - 1]; 35 | var base = '/reason-react/docs/en/'; 36 | Object.keys(redirects).forEach(function(redirect) { 37 | if (redirect.indexOf(hash) === 0) { 38 | var newHash = redirect.split(hash + '-')[1] || ''; 39 | newHash = newHash ? '#' + newHash : newHash; 40 | var link = document.getElementById('redirectLink'); 41 | var banner = document.getElementById('redirectBanner'); 42 | var location = base + redirects[redirect] + newHash; 43 | 44 | link.textContent = 'https://reasonml.github.io' + location; 45 | link.href = location; 46 | banner.style.display = 'block'; 47 | } 48 | }); 49 | })(); 50 | --------------------------------------------------------------------------------