From 28b3e5186e00ce964cf6422cafb4d53a262794f0 Mon Sep 17 00:00:00 2001 From: Thiago Bellini Date: Fri, 31 Aug 2018 14:24:51 -0300 Subject: [PATCH] Upgrade to the stable babel 7 release (#460) * Upgrade to the stable babel 7 release * fix the CI * [core] Upgrade the dependencies --- .babelrc | 80 - .circleci/config.yml | 14 +- .eslintignore | 11 +- .eslintrc.js | 51 +- babel.config.js | 163 + docs/src/modules/components/AppContent.js | 19 +- docs/src/modules/components/AppDrawer.js | 39 +- .../modules/components/AppDrawerNavItem.js | 2 +- docs/src/modules/components/AppFrame.js | 59 +- .../modules/components/AppTableOfContents.js | 193 + docs/src/modules/components/AppWrapper.js | 38 +- docs/src/modules/components/Demo.js | 300 +- docs/src/modules/components/Head.js | 44 + docs/src/modules/components/MarkdownDocs.js | 127 +- .../src/modules/components/MarkdownElement.js | 51 +- docs/src/modules/components/PaginationDot.js | 2 +- docs/src/modules/components/SupportTouch.js | 6 +- docs/src/modules/components/withRoot.js | 1 + .../{getContext.js => getPageContext.js} | 2 +- docs/src/modules/utils/parseMarkdown.js | 17 +- docs/src/pages/api/api.md | 2 + docs/src/pages/demos/DemoAnimateHeight.js | 12 +- docs/src/pages/demos/demos.md | 2 + .../pages/getting-started/example-projects.md | 2 +- .../src/pages/getting-started/installation.md | 2 + .../getting-started/supported-platforms.md | 2 + docs/src/pages/getting-started/usage.md | 2 + docs/webpackBaseConfig.js | 39 + native/docs/src/Footer.native.js | 9 +- .../src/SwipeableViews.animated.js | 260 +- .../src/SwipeableViews.scroll.js | 166 +- next.config.js | 3 +- package.json | 51 +- .../react-swipeable-views-core/.npmignore | 2 +- .../react-swipeable-views-core/package.json | 2 +- ...ounds.spec.js => checkIndexBounds.test.js} | 0 ...puteIndex.spec.js => computeIndex.test.js} | 0 ...de.spec.js => getDisplaySameSlide.test.js} | 0 .../src/{mod.spec.js => mod.test.js} | 0 .../react-swipeable-views-utils/.npmignore | 2 +- .../react-swipeable-views-utils/package.json | 2 +- .../src/autoPlay.js | 112 +- .../{autoPlay.spec.js => autoPlay.test.js} | 0 ...dKeyboard.spec.js => bindKeyboard.test.js} | 0 .../src/virtualize.js | 100 +- ...{virtualize.spec.js => virtualize.test.js} | 0 packages/react-swipeable-views/.npmignore | 2 +- packages/react-swipeable-views/package.json | 2 +- .../src/SwipeableViews.js | 82 +- ...leViews.spec.js => SwipeableViews.test.js} | 0 pages/_document.js | 59 +- scripts/babel-preset-env.js | 20 - test/dom.js | 31 +- test/mocha.opts | 2 +- yarn.lock | 5583 ++++++++--------- 55 files changed, 4139 insertions(+), 3633 deletions(-) delete mode 100644 .babelrc create mode 100644 babel.config.js create mode 100644 docs/src/modules/components/AppTableOfContents.js create mode 100644 docs/src/modules/components/Head.js rename docs/src/modules/styles/{getContext.js => getPageContext.js} (97%) create mode 100644 docs/webpackBaseConfig.js rename packages/react-swipeable-views-core/src/{checkIndexBounds.spec.js => checkIndexBounds.test.js} (100%) rename packages/react-swipeable-views-core/src/{computeIndex.spec.js => computeIndex.test.js} (100%) rename packages/react-swipeable-views-core/src/{getDisplaySameSlide.spec.js => getDisplaySameSlide.test.js} (100%) rename packages/react-swipeable-views-core/src/{mod.spec.js => mod.test.js} (100%) rename packages/react-swipeable-views-utils/src/{autoPlay.spec.js => autoPlay.test.js} (100%) rename packages/react-swipeable-views-utils/src/{bindKeyboard.spec.js => bindKeyboard.test.js} (100%) rename packages/react-swipeable-views-utils/src/{virtualize.spec.js => virtualize.test.js} (100%) rename packages/react-swipeable-views/src/{SwipeableViews.spec.js => SwipeableViews.test.js} (100%) delete mode 100644 scripts/babel-preset-env.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 3f18c56a..00000000 --- a/.babelrc +++ /dev/null @@ -1,80 +0,0 @@ -{ - "presets": [ - "./scripts/babel-preset-env", - ["@babel/preset-stage-1", { "loose": true }], - "@babel/preset-react" - ], - "plugins": [ - "@babel/plugin-transform-object-assign", - ["@babel/transform-runtime", { "polyfill": false, "useBuiltIns": true }] - ], - "env": { - "docs-development": { - "plugins": [ - "./scripts/babel-plugin-preval", - [ - "module-resolver", - { - "alias": { - "pages": "./pages", - "react-swipeable-views": "./packages/react-swipeable-views/src", - "react-swipeable-views-utils": "./packages/react-swipeable-views-utils/src", - "react-swipeable-views-core": "./packages/react-swipeable-views-core/src", - "docs": "./docs" - } - } - ] - ] - }, - "docs-production": { - "plugins": [ - "./scripts/babel-plugin-preval", - [ - "module-resolver", - { - "alias": { - "pages": "./pages", - "react-swipeable-views": "./packages/react-swipeable-views/src", - "react-swipeable-views-utils": "./packages/react-swipeable-views-utils/src", - "react-swipeable-views-core": "./packages/react-swipeable-views-core/src", - "docs": "./docs" - } - } - ], - "transform-react-constant-elements", - "transform-dev-warning", - ["transform-react-remove-prop-types", { "mode": "remove" }] - ] - }, - "test": { - "plugins": [ - [ - "module-resolver", - { - "alias": { - "pages": "./pages", - "react-swipeable-views": "./packages/react-swipeable-views/src", - "react-swipeable-views-utils": "./packages/react-swipeable-views-utils/src", - "react-swipeable-views-core": "./packages/react-swipeable-views-core/src", - "docs": "./docs" - } - } - ] - ] - }, - "production": { - "plugins": [ - "transform-react-constant-elements", - "transform-dev-warning", - [ - "transform-react-remove-prop-types", - { - "mode": "wrap" - } - ] - ], - "ignore": ["**/*.test*"] - } - }, - "ignore": ["scripts/*.js"] -} diff --git a/.circleci/config.yml b/.circleci/config.yml index 6fc8c7f2..098b19af 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,16 +3,21 @@ defaults: &defaults docker: - image: circleci/node:8 restore_repo: &restore_repo - restore_cache: - keys: - - v1-repo-{{ .Branch }}-{{ .Revision }} + # The cache logic of CircleCI has been disabled for Security reasons. + # We can no longer use it. + # restore_cache: + # keys: + # - v1-repo-{{ .Branch }}-{{ .Revision }} + run: + name: Install js dependencies + command: yarn version: 2 jobs: checkout: <<: *defaults steps: - - *restore_repo - checkout + - *restore_repo - run: name: Check versions and env command: | @@ -41,6 +46,7 @@ jobs: test_unit: <<: *defaults steps: + - checkout - *restore_repo - run: name: Lint diff --git a/.eslintignore b/.eslintignore index 1b07e74b..025f9f63 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,8 +1,9 @@ .git -/.next/ -node_modules +/.next +/coverage +/docs/export +/docs/src/modules/utils/find.js /flow/interfaces -lib/ /next.config.js -/docs/src/modules/utils/find.js -/docs/export/ +lib/ +node_modules diff --git a/.eslintrc.js b/.eslintrc.js index ba556c4b..ce3dae5b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,5 @@ +const path = require('path'); + module.exports = { // So parent files don't get applied root: true, @@ -20,18 +22,20 @@ module.exports = { settings: { 'import/resolver': { webpack: { - config: './docs/webpackBaseConfig.js', + config: path.join(__dirname, './docs/webpackBaseConfig.js'), }, }, }, rules: { - 'linebreak-style': 'off', // Don't play nicely with Windows. - 'arrow-body-style': 'off', // Not our taste? + 'linebreak-style': 'off', // Don't play nicely with Windows + 'arrow-body-style': 'off', // Incompatible with prettier 'arrow-parens': 'off', // Incompatible with prettier 'object-curly-newline': 'off', // Incompatible with prettier 'function-paren-newline': 'off', // Incompatible with prettier indent: 'off', // Incompatible with prettier + 'implicit-arrow-linebreak': 'off', // Incompatible with prettier 'space-before-function-paren': 'off', // Incompatible with prettier + 'no-confusing-arrow': 'off', // Incompatible with prettier 'no-mixed-operators': 'off', // Incompatible with prettier 'consistent-this': ['error', 'self'], 'max-len': [ @@ -43,25 +47,29 @@ module.exports = { }, ], // airbnb is allowing some edge cases 'no-console': 'error', // airbnb is using warn + 'prefer-destructuring': 'off', // airbnb is using error. destructuring harm grep potential. 'no-alert': 'error', // airbnb is using warn - 'no-param-reassign': 'off', // Not our taste? + 'no-param-reassign': 'off', // airbnb use error 'no-prototype-builtins': 'off', // airbnb use error - 'object-curly-spacing': 'off', // use babel plugin rule - 'no-restricted-properties': 'off', // To remove once react-docgen support ** operator. - 'prefer-destructuring': 'off', // To remove once react-docgen support ** operator. - - 'babel/object-curly-spacing': ['error', 'always'], + 'operator-linebreak': 'off', // airbnb use error - 'import/unambiguous': 'off', // scripts - 'import/namespace': ['error', { allowComputed: true }], + // It would be better to enable this rule, but it might slow us down. 'import/no-extraneous-dependencies': 'off', - 'import/extensions': 'off', - 'import/no-unresolved': 'off', + 'import/namespace': ['error', { allowComputed: true }], + 'import/order': [ + 'error', + { + groups: [['index', 'sibling', 'parent', 'internal', 'external', 'builtin']], + 'newlines-between': 'never', + }, + ], + 'import/no-unresolved': 'off', // To fix at some point 'react/jsx-indent': 'off', // Incompatible with prettier 'react/jsx-closing-bracket-location': 'off', // Incompatible with prettier 'react/jsx-wrap-multilines': 'off', // Incompatible with prettier 'react/jsx-indent-props': 'off', // Incompatible with prettier + 'react/jsx-one-expression-per-line': 'off', // Incompatible with prettier 'react/jsx-handler-names': [ 'error', { @@ -70,16 +78,15 @@ module.exports = { eventHandlerPropPrefix: 'on', }, ], - 'react/require-default-props': 'off', // airbnb use error + 'react/jsx-curly-brace-presence': 'off', // airbnb use error, it's buggy 'react/forbid-prop-types': 'off', // airbnb use error + 'react/require-default-props': 'off', // airbnb use error, it's buggy + 'react/destructuring-assignment': 'off', // airbnb use error 'react/jsx-filename-extension': ['error', { extensions: ['.js'] }], // airbnb is using .jsx 'react/no-danger': 'error', // airbnb is using warn - 'react/no-direct-mutation-state': 'error', // airbnb is disabling this rule - 'react/no-find-dom-node': 'off', // I don't know - 'react/no-unused-prop-types': 'off', // Is still buggy - 'react/sort-prop-types': 'error', // airbnb do nothing here. - 'react/default-props-match-prop-types': 'off', // Buggy - 'react/jsx-curly-brace-presence': 'off', // Buggy + 'react/no-direct-mutation-state': 'error', // airbnb is using off + 'react/no-find-dom-node': 'off', // airbnb use error + 'react/sort-prop-types': 'error', // airbnb use off 'mocha/handle-done-callback': 'error', 'mocha/no-exclusive-tests': 'error', @@ -87,6 +94,10 @@ module.exports = { 'mocha/no-pending-tests': 'error', 'mocha/no-skipped-tests': 'error', + 'jsx-a11y/label-has-associated-control': 'off', + 'jsx-a11y/label-has-for': 'off', + 'jsx-a11y/no-autofocus': 'off', // We are a library, people do what they want. + 'prettier/prettier': ['error'], }, }; diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000..9a87a23f --- /dev/null +++ b/babel.config.js @@ -0,0 +1,163 @@ +let defaultPresets; + +// We release a ES version of Material-UI. +// It's something that matches the latest official supported features of JavaScript. +// Nothing more (stage-1, etc), nothing less (require, etc). +if (process.env.BABEL_ENV === 'es') { + defaultPresets = []; +} else { + defaultPresets = [ + [ + '@babel/preset-env', + { + targets: { + ie: 10, + edge: 14, + firefox: 28, + chrome: 29, + safari: 9, + node: '6.11', + }, + modules: ['modules', 'production-umd'].includes(process.env.BABEL_ENV) ? false : 'commonjs', + }, + ], + ]; +} + +module.exports = { + presets: defaultPresets.concat(['@babel/preset-react']), + plugins: [ + ['@babel/plugin-proposal-class-properties', { loose: true }], + [ + '@babel/plugin-proposal-object-rest-spread', + { + // Workaround for /~https://github.com/babel/babel/issues/8323 + loose: process.env.BABEL_ENV !== 'es', + }, + ], + '@babel/plugin-transform-object-assign', + '@babel/plugin-transform-runtime', + ], + env: { + coverage: { + plugins: [ + 'babel-plugin-istanbul', + [ + 'babel-plugin-module-resolver', + { + root: ['./'], + alias: { + pages: './pages', + 'react-swipeable-views': './packages/react-swipeable-views/src', + 'react-swipeable-views-utils': './packages/react-swipeable-views-utils/src', + 'react-swipeable-views-core': './packages/react-swipeable-views-core/src', + docs: './docs', + }, + }, + ], + ], + }, + development: {}, + 'docs-development': { + plugins: [ + 'babel-plugin-preval', + [ + 'babel-plugin-module-resolver', + { + alias: { + 'react-swipeable-views': './packages/react-swipeable-views/src', + 'react-swipeable-views-core': './packages/react-swipeable-views-core/src', + 'react-swipeable-views-utils': './packages/react-swipeable-views-utils/src', + docs: './docs', + pages: './pages', + }, + }, + ], + ], + }, + 'docs-production': { + plugins: [ + 'babel-plugin-preval', + [ + 'babel-plugin-module-resolver', + { + alias: { + 'react-swipeable-views': './packages/react-swipeable-views/src', + 'react-swipeable-views-core': './packages/react-swipeable-views-core/src', + 'react-swipeable-views-utils': './packages/react-swipeable-views-utils/src', + docs: './docs', + pages: './pages', + }, + }, + ], + 'transform-react-constant-elements', + 'transform-dev-warning', + ['react-remove-properties', { properties: ['data-mui-test'] }], + ['transform-react-remove-prop-types', { mode: 'remove' }], + ], + }, + es: { + plugins: [ + 'transform-react-constant-elements', + 'transform-dev-warning', + ['react-remove-properties', { properties: ['data-mui-test'] }], + [ + 'transform-react-remove-prop-types', + { + mode: 'wrap', + }, + ], + ], + // It's most likely a babel bug. + // We are using this ignore option in the CLI command but that has no effect. + ignore: ['**/*.test.js'], + }, + production: { + plugins: [ + 'transform-react-constant-elements', + 'transform-dev-warning', + ['react-remove-properties', { properties: ['data-mui-test'] }], + [ + 'transform-react-remove-prop-types', + { + mode: 'wrap', + }, + ], + ], + // It's most likely a babel bug. + // We are using this ignore option in the CLI command but that has no effect. + ignore: ['**/*.test.js'], + }, + 'production-umd': { + plugins: [ + 'transform-react-constant-elements', + 'transform-dev-warning', + ['react-remove-properties', { properties: ['data-mui-test'] }], + [ + 'transform-react-remove-prop-types', + { + mode: 'wrap', + }, + ], + ], + }, + test: { + sourceMaps: 'both', + plugins: [ + [ + 'babel-plugin-module-resolver', + { + root: ['./'], + alias: { + 'react-swipeable-views': './packages/react-swipeable-views/src', + 'react-swipeable-views-core': './packages/react-swipeable-views-core/src', + 'react-swipeable-views-utils': './packages/react-swipeable-views-utils/src', + docs: './docs', + pages: './pages', + }, + }, + ], + ], + }, + }, +}; diff --git a/docs/src/modules/components/AppContent.js b/docs/src/modules/components/AppContent.js index a19b1e57..c9024c00 100644 --- a/docs/src/modules/components/AppContent.js +++ b/docs/src/modules/components/AppContent.js @@ -4,15 +4,22 @@ import classNames from 'classnames'; import { withStyles } from '@material-ui/core/styles'; const styles = theme => ({ - root: theme.mixins.gutters({ + root: { paddingTop: 80, flex: '1 1 100%', maxWidth: '100%', margin: '0 auto', - }), - [theme.breakpoints.up(900 + theme.spacing.unit * 6)]: { - root: { - maxWidth: 900, + paddingLeft: theme.spacing.unit * 2, + paddingRight: theme.spacing.unit * 2, + [theme.breakpoints.up('sm')]: { + paddingLeft: theme.spacing.unit * 4, + paddingRight: theme.spacing.unit * 4, + maxWidth: 'calc(100% - 162px)', + }, + [theme.breakpoints.up('lg')]: { + paddingLeft: theme.spacing.unit * 5, + paddingRight: theme.spacing.unit * 9, + maxWidth: 'calc(100% - 240px - 162px)', }, }, }); @@ -20,7 +27,7 @@ const styles = theme => ({ function AppContent(props) { const { className, classes, children } = props; - return
{children}
; + return
{children}
; } AppContent.propTypes = { diff --git a/docs/src/modules/components/AppDrawer.js b/docs/src/modules/components/AppDrawer.js index 031b02ff..9dd1f8ff 100644 --- a/docs/src/modules/components/AppDrawer.js +++ b/docs/src/modules/components/AppDrawer.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { withStyles } from '@material-ui/core/styles'; import List from '@material-ui/core/List'; import Drawer from '@material-ui/core/Drawer'; +import SwipeableDrawer from '@material-ui/core/SwipeableDrawer'; import Typography from '@material-ui/core/Typography'; import Divider from '@material-ui/core/Divider'; import Hidden from '@material-ui/core/Hidden'; @@ -13,7 +14,7 @@ import { pageToTitle } from 'docs/src/modules/utils/helpers'; const styles = theme => ({ paper: { - width: 250, + width: 240, backgroundColor: theme.palette.background.paper, }, title: { @@ -55,16 +56,20 @@ function renderNavItems({ pages, ...params }) { } function reduceChildRoutes({ props, activePage, items, page, depth }) { + if (page.displayNav === false) { + return items; + } + if (page.children && page.children.length > 1) { const title = pageToTitle(page); - const openImmediately = activePage.pathname.indexOf(page.pathname) === 0; + const openImmediately = activePage.pathname.indexOf(`${page.pathname}/`) === 0; items.push( {renderNavItems({ props, pages: page.children, activePage, depth: depth + 1 })} , ); - } else if (page.title !== false) { + } else { const title = pageToTitle(page); page = page.children && page.children.length === 1 ? page.children[0] : page; @@ -82,11 +87,13 @@ function reduceChildRoutes({ props, activePage, items, page, depth }) { return items; } -const GITHUB_RELEASE_BASE_URL = - '/~https://github.com/oliviertassinari/react-swipeable-views/releases/tag/'; +// iOS is hosted on high-end devices. We can enable the backdrop transition without +// dropping frames. The performance will be good enough. +// So: +const iOS = process.browser && /iPad|iPhone|iPod/.test(navigator.userAgent); function AppDrawer(props, context) { - const { classes, className, disablePermanent, mobileOpen, onClose } = props; + const { classes, className, disablePermanent, mobileOpen, onClose, onOpen } = props; const drawer = (
@@ -94,14 +101,11 @@ function AppDrawer(props, context) {
- Documentation + Material-UI {process.env.LIB_VERSION ? ( - + {`v${process.env.LIB_VERSION}`} ) : null} @@ -113,21 +117,23 @@ function AppDrawer(props, context) { ); return ( -
- - + + {drawer} - + {disablePermanent ? null : ( @@ -142,7 +148,7 @@ function AppDrawer(props, context) { )} -
+ ); } @@ -152,6 +158,7 @@ AppDrawer.propTypes = { disablePermanent: PropTypes.bool.isRequired, mobileOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, + onOpen: PropTypes.func.isRequired, }; AppDrawer.contextTypes = { diff --git a/docs/src/modules/components/AppDrawerNavItem.js b/docs/src/modules/components/AppDrawerNavItem.js index 7b7c5e59..c92efa4e 100644 --- a/docs/src/modules/components/AppDrawerNavItem.js +++ b/docs/src/modules/components/AppDrawerNavItem.js @@ -57,7 +57,7 @@ class AppDrawerNavItem extends React.Component { } handleClick = () => { - this.setState({ open: !this.state.open }); + this.setState(state => ({ open: !state.open })); }; render() { diff --git a/docs/src/modules/components/AppFrame.js b/docs/src/modules/components/AppFrame.js index 9f5a0aa0..30e26d24 100644 --- a/docs/src/modules/components/AppFrame.js +++ b/docs/src/modules/components/AppFrame.js @@ -2,14 +2,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import NProgress from 'nprogress'; import Router from 'next/router'; +import { withStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import AppBar from '@material-ui/core/AppBar'; -import Github from '@material-ui/docs/svgIcons/GitHub'; -import NProgressBar from '@material-ui/docs/NProgressBar'; +import Toolbar from '@material-ui/core/Toolbar'; import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; +import CssBaseline from '@material-ui/core/CssBaseline'; import MenuIcon from '@material-ui/icons/Menu'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; -import { withStyles } from '@material-ui/core/styles'; +import NProgressBar from '@material-ui/docs/NProgressBar'; +import GithubIcon from '@material-ui/docs/svgIcons/GitHub'; import AppDrawer from 'docs/src/modules/components/AppDrawer'; import { pageToTitle } from 'docs/src/modules/utils/helpers'; @@ -28,9 +30,6 @@ Router.onRouteChangeError = () => { const styles = theme => ({ root: { display: 'flex', - alignItems: 'stretch', - minHeight: '100vh', - width: '100%', }, grow: { flex: '1 1 auto', @@ -50,12 +49,12 @@ const styles = theme => ({ }, appBarShift: { [theme.breakpoints.up('lg')]: { - width: 'calc(100% - 250px)', + width: 'calc(100% - 240px)', }, }, drawer: { [theme.breakpoints.up('lg')]: { - width: 250, + width: 240, }, }, navIconHide: { @@ -70,8 +69,12 @@ class AppFrame extends React.Component { mobileOpen: false, }; - handleDrawerToggle = () => { - this.setState({ mobileOpen: !this.state.mobileOpen }); + handleDrawerOpen = () => { + this.setState({ mobileOpen: true }); + }; + + handleDrawerClose = () => { + this.setState({ mobileOpen: false }); }; render() { @@ -100,12 +103,13 @@ class AppFrame extends React.Component { return (
+ @@ -116,20 +120,23 @@ class AppFrame extends React.Component { )}
- - - + + + + + {children} @@ -146,9 +153,7 @@ AppFrame.propTypes = { AppFrame.contextTypes = { activePage: PropTypes.shape({ pathname: PropTypes.string.isRequired, - }), + }).isRequired, }; -export default withStyles(styles, { - name: 'AppFrame', -})(AppFrame); +export default withStyles(styles)(AppFrame); diff --git a/docs/src/modules/components/AppTableOfContents.js b/docs/src/modules/components/AppTableOfContents.js new file mode 100644 index 00000000..7e70ecf0 --- /dev/null +++ b/docs/src/modules/components/AppTableOfContents.js @@ -0,0 +1,193 @@ +/* eslint-disable react/no-danger */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import Link from 'docs/src/modules/components/Link'; +import marked from 'marked'; +import throttle from 'lodash/throttle'; +import EventListener from 'react-event-listener'; +import { withStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import { textToHash } from 'docs/src/modules/components/MarkdownElement'; + +const renderer = new marked.Renderer(); + +let itemsServer = null; +renderer.heading = (text, level) => { + if (level === 1 || level > 3) { + return; + } + + if (level === 2) { + itemsServer.push({ + text, + level, + hash: textToHash(text), + children: [], + }); + } + + if (level === 3) { + itemsServer[itemsServer.length - 1].children.push({ + text, + level, + hash: textToHash(text), + }); + } +}; + +const styles = theme => ({ + root: { + top: 70, + width: 162, + flexShrink: 0, + order: 2, + position: 'sticky', + wordBreak: 'break-word', + height: 'calc(100vh - 70px)', + overflowY: 'auto', + padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 2}px ${theme.spacing.unit * + 2}px 0`, + display: 'none', + [theme.breakpoints.up('sm')]: { + display: 'block', + }, + }, + contents: { + marginTop: theme.spacing.unit * 2, + }, + ul: { + padding: 0, + margin: 0, + listStyleType: 'none', + }, + item: { + fontSize: 13, + padding: `${theme.spacing.unit / 2}px 0`, + }, +}); + +class AppTableOfContents extends React.Component { + handleScroll = throttle(() => { + this.findActiveIndex(); + }, 166); // Corresponds to 10 frames at 60 Hz. + + constructor(props) { + super(props); + itemsServer = []; + marked(props.contents.join(''), { + renderer, + }); + } + + state = { + active: null, + }; + + componentDidMount() { + this.itemsClient = []; + + itemsServer.forEach(item2 => { + this.itemsClient.push({ + ...item2, + node: document.getElementById(item2.hash), + }); + + if (item2.children.length > 0) { + item2.children.forEach(item3 => { + this.itemsClient.push({ + ...item3, + node: document.getElementById(item3.hash), + }); + }); + } + }); + + this.findActiveIndex(); + } + + componentWillUnmount() { + this.handleScroll.cancel(); + } + + findActiveIndex = () => { + let active; + + for (let i = 0; i < this.itemsClient.length; i += 1) { + const item = this.itemsClient[i]; + if ( + document.documentElement.scrollTop < item.node.offsetTop + 100 || + i === this.itemsClient.length - 1 + ) { + active = item; + break; + } + } + + if (active && this.state.active !== active.hash) { + this.setState({ + active: active.hash, + }); + } + }; + + render() { + const { classes } = this.props; + const { active } = this.state; + + return ( + + ); + } +} + +AppTableOfContents.propTypes = { + classes: PropTypes.object.isRequired, + contents: PropTypes.array.isRequired, +}; + +export default withStyles(styles)(AppTableOfContents); diff --git a/docs/src/modules/components/AppWrapper.js b/docs/src/modules/components/AppWrapper.js index 6dc355cc..2bb91acb 100644 --- a/docs/src/modules/components/AppWrapper.js +++ b/docs/src/modules/components/AppWrapper.js @@ -3,11 +3,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { MuiThemeProvider } from '@material-ui/core/styles'; -import CssBaseline from '@material-ui/core/CssBaseline'; -import getContext from 'docs/src/modules/styles/getContext'; import JssProvider from 'react-jss/lib/JssProvider'; -import AppFrame from 'docs/src/modules/components/AppFrame'; import { lightTheme, setPrismTheme } from 'docs/src/modules/components/prism'; +import getPageContext from 'docs/src/modules/styles/getPageContext'; // Inject the insertion-point-jss after docssearch if (process.browser && !global.__INSERTION_POINT__) { @@ -21,9 +19,7 @@ if (process.browser && !global.__INSERTION_POINT__) { } class AppWrapper extends React.Component { - componentWillMount() { - this.styleContext = getContext(); - } + state = {}; componentDidMount() { // Remove the server-side injected CSS. @@ -35,23 +31,28 @@ class AppWrapper extends React.Component { setPrismTheme(lightTheme); } - styleContext = null; + static getDerivedStateFromProps(nextProps, prevState) { + if (typeof prevState.pageContext === 'undefined') { + return { + pageContext: nextProps.pageContext || getPageContext(), + }; + } + + return null; + } render() { - const { children, sheetsRegistry } = this.props; + const { children } = this.props; + const { pageContext } = this.state; return ( - - - {children} + + {children} ); @@ -60,7 +61,8 @@ class AppWrapper extends React.Component { AppWrapper.propTypes = { children: PropTypes.node.isRequired, - sheetsRegistry: PropTypes.object, + // eslint-disable-next-line react/no-unused-prop-types + pageContext: PropTypes.object, }; export default AppWrapper; diff --git a/docs/src/modules/components/Demo.js b/docs/src/modules/components/Demo.js index 1ab0946b..76cdd7af 100644 --- a/docs/src/modules/components/Demo.js +++ b/docs/src/modules/components/Demo.js @@ -1,46 +1,105 @@ import React from 'react'; -import PropTypes from 'prop-types'; import LZString from 'lz-string'; -import CodeIcon from '@material-ui/icons/Code'; -import Collapse from '@material-ui/core/Collapse'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import copy from 'clipboard-copy'; +import { withStyles } from '@material-ui/core/styles'; import IconButton from '@material-ui/core/IconButton'; -import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; -import ModeEditIcon from '@material-ui/icons/ModeEdit'; +import Collapse from '@material-ui/core/Collapse'; +import EditIcon from '@material-ui/icons/Edit'; +import CodeIcon from '@material-ui/icons/Code'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; import Tooltip from '@material-ui/core/Tooltip'; -import { withStyles } from '@material-ui/core/styles'; +import Github from '@material-ui/docs/svgIcons/GitHub'; +import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; +import { getDependencies } from 'docs/src/modules/utils/helpers'; + +function compress(object) { + return LZString.compressToBase64(JSON.stringify(object)) + .replace(/\+/g, '-') // Convert '+' to '-' + .replace(/\//g, '_') // Convert '/' to '_' + .replace(/=+$/, ''); // Remove ending '=' +} + +function addHiddenInput(form, name, value) { + const input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + input.value = value; + form.appendChild(input); +} + +function getDemo(props) { + return { + title: 'Material demo', + description: props.githubLocation, + dependencies: getDependencies(props.raw), + files: { + 'demo.js': props.raw, + 'index.js': ` +import React from 'react'; +import { render } from 'react-dom'; +import Demo from './demo'; + +const rootElement = document.querySelector('#root'); +if (rootElement) { + render(, rootElement); +} + `, + 'index.html': ` + + + +
+ + `, + }, + }; +} const styles = theme => ({ root: { position: 'relative', - backgroundColor: theme.palette.background.contentFrame, marginBottom: 40, marginLeft: -theme.spacing.unit * 2, marginRight: -theme.spacing.unit * 2, [theme.breakpoints.up('sm')]: { + padding: `0 ${theme.spacing.unit}px`, marginLeft: 0, marginRight: 0, }, }, - demo: { - ...theme.mixins.gutters({ - paddingTop: theme.spacing.unit * 2, - paddingBottom: theme.spacing.unit * 2, - }), + demo: theme.mixins.gutters({ + borderRadius: theme.shape.borderRadius, + backgroundColor: + theme.palette.type === 'light' ? theme.palette.grey[200] : theme.palette.grey[900], + display: 'flex', + justifyContent: 'center', + paddingTop: theme.spacing.unit * 2, + paddingBottom: theme.spacing.unit * 2, [theme.breakpoints.up('sm')]: { paddingLeft: theme.spacing.unit * 3, paddingRight: theme.spacing.unit * 3, paddingTop: theme.spacing.unit * 6, + paddingBottom: theme.spacing.unit * 3, + }, + }), + demoHiddenHeader: { + paddingTop: theme.spacing.unit * 2, + [theme.breakpoints.up('sm')]: { + paddingTop: theme.spacing.unit * 3, }, }, - codeButton: { - flip: false, + header: { display: 'none', - zIndex: 10, - position: 'absolute', - top: 2, - right: 7, [theme.breakpoints.up('sm')]: { - display: 'block', + display: 'flex', + flip: false, + position: 'absolute', + top: 0, + right: theme.spacing.unit, }, }, code: { @@ -56,114 +115,160 @@ const styles = theme => ({ borderRadius: '0px !important', }, }, - codesandbox: { - display: 'none', - [theme.breakpoints.up('sm')]: { - display: 'block', - flip: false, - zIndex: 10, - position: 'absolute', - top: 2, - right: 48, - }, - }, }); -function compress(object) { - return LZString.compressToBase64(JSON.stringify(object)) - .replace(/\+/g, '-') // Convert '+' to '-' - .replace(/\//g, '_') // Convert '/' to '_' - .replace(/=+$/, ''); // Remove ending '=' -} - class Demo extends React.Component { state = { + anchorEl: null, codeOpen: false, }; - codesandboxForm = null; + handleClickMore = event => { + this.setState({ anchorEl: event.currentTarget }); + }; + + handleCloseMore = () => { + this.setState({ anchorEl: null }); + }; handleClickCodeOpen = () => { - this.setState({ - codeOpen: !this.state.codeOpen, - }); + this.setState(state => ({ + codeOpen: !state.codeOpen, + })); }; - handleClickCodesandbox = () => { - const codesandboxValue = { + handleClickCodeSandbox = () => { + const demo = getDemo(this.props); + const parameters = compress({ files: { 'package.json': { content: { - dependencies: { - react: 'latest', - 'react-dom': 'latest', - 'react-swipeable-views': 'latest', - 'react-swipeable-views-utils': 'latest', - }, + title: demo.title, + description: demo.description, + dependencies: demo.dependencies, }, }, 'demo.js': { - content: this.props.raw, + content: demo.files['demo.js'], }, 'index.js': { - content: ` -import React from 'react'; -import { render } from 'react-dom'; -import Demo from './demo'; - -const rootElement = document.querySelector('#root'); -if (rootElement) { - render(, rootElement); -} - `, + content: demo.files['index.js'], }, 'index.html': { - content: ` - - -
- - `, + content: demo.files['index.html'], }, }, - }; + }); - this.codesandboxForm.querySelector('[name="parameters"]').value = compress(codesandboxValue); - this.codesandboxForm.submit(); + const form = document.createElement('form'); + form.method = 'POST'; + form.target = '_blank'; + form.action = 'https://codeSandbox.io/api/v1/sandboxes/define'; + addHiddenInput(form, 'parameters', parameters); + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); + }; + + handleClickCopy = async () => { + try { + await copy(this.props.raw); + } finally { + this.handleCloseMore(); + } + }; + + handleClickStackBlitz = () => { + const demo = getDemo(this.props); + const form = document.createElement('form'); + form.method = 'POST'; + form.target = '_blank'; + form.action = 'https://stackblitz.com/run'; + addHiddenInput(form, 'project[template]', 'javascript'); + addHiddenInput(form, 'project[title]', demo.title); + addHiddenInput(form, 'project[description]', demo.description); + addHiddenInput(form, 'project[dependencies]', JSON.stringify(demo.dependencies)); + Object.entries(demo.files).forEach(([key, value]) => { + addHiddenInput(form, `project[files][${key}]`, value); + }); + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); + this.handleCloseMore(); }; render() { - const { classes, js: DemoComponent, raw } = this.props; - const { codeOpen } = this.state; + const { classes, demoOptions, githubLocation, index, js: DemoComponent, raw } = this.props; + const { anchorEl, codeOpen } = this.state; return (
- -
- - - -
{ - this.codesandboxForm = node; - }} - method="get" - action="https://codesandbox.io/api/v1/sandboxes/define" - target="_blank" - > - -
+ {demoOptions.hideHeader ? null : ( +
+
+ + + + + + {demoOptions.hideEditButton ? null : ( + + + + + + )} + + + + + + + + + + Copy the source + {demoOptions.hideEditButton ? null : ( + Edit in StackBlitz + )} + +
+ + +
- - - - - - - - - -
+ )} +
@@ -173,6 +278,9 @@ if (rootElement) { Demo.propTypes = { classes: PropTypes.object.isRequired, + demoOptions: PropTypes.object.isRequired, + githubLocation: PropTypes.string.isRequired, + index: PropTypes.number.isRequired, js: PropTypes.func.isRequired, raw: PropTypes.string.isRequired, }; diff --git a/docs/src/modules/components/Head.js b/docs/src/modules/components/Head.js new file mode 100644 index 00000000..3172c308 --- /dev/null +++ b/docs/src/modules/components/Head.js @@ -0,0 +1,44 @@ +import React from 'react'; +import NextHead from 'next/head'; +import PropTypes from 'prop-types'; + +function Head(props) { + const { title, description } = props; + + return ( + + {title} + + {/* Twitter */} + + + + + + {/* Facebook */} + + + + + + + ); +} + +Head.propTypes = { + description: PropTypes.string, + title: PropTypes.string, +}; + +Head.defaultProps = { + description: 'A React component for swipeable views.', + title: 'react-swipeable-views', +}; + +export default Head; diff --git a/docs/src/modules/components/MarkdownDocs.js b/docs/src/modules/components/MarkdownDocs.js index b6c748bb..8e794e14 100644 --- a/docs/src/modules/components/MarkdownDocs.js +++ b/docs/src/modules/components/MarkdownDocs.js @@ -2,15 +2,22 @@ import React from 'react'; import PropTypes from 'prop-types'; import kebabCase from 'lodash/kebabCase'; import warning from 'warning'; -import Head from 'next/head'; import { withStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button'; -import AppContent from 'docs/src/modules/components/AppContent'; import MarkdownElement from 'docs/src/modules/components/MarkdownElement'; +import Head from 'docs/src/modules/components/Head'; +import AppContent from 'docs/src/modules/components/AppContent'; import Demo from 'docs/src/modules/components/Demo'; -import { getHeaders, getContents, getTitle } from 'docs/src/modules/utils/parseMarkdown'; +import AppFrame from 'docs/src/modules/components/AppFrame'; +import AppTableOfContents from 'docs/src/modules/components/AppTableOfContents'; +import { + getHeaders, + getContents, + getTitle, + getDescription, +} from 'docs/src/modules/utils/parseMarkdown'; -const styles = { +const styles = theme => ({ root: { marginBottom: 100, }, @@ -19,70 +26,106 @@ const styles = { flexDirection: 'column', alignItems: 'flex-end', }, -}; + markdownElement: { + marginTop: theme.spacing.unit * 2, + marginBottom: theme.spacing.unit * 2, + padding: `0 ${theme.spacing.unit}px`, + }, +}); -const demoRegexp = /^demo='(.*)'$/; +const demoRegexp = /^"demo": "(.*)"/; const SOURCE_CODE_ROOT_URL = '/~https://github.com/oliviertassinari/react-swipeable-views/tree/master/'; function MarkdownDocs(props, context) { - const { classes, demos, markdown, sourceLocation: sourceLocationProp } = props; + const { classes, demos, disableAd, markdown, markdownLocation: markdownLocationProp } = props; const contents = getContents(markdown); const headers = getHeaders(markdown); - let sourceLocation = sourceLocationProp || context.activePage.pathname; + let markdownLocation = markdownLocationProp || context.activePage.pathname; + + if (!markdownLocationProp) { + const token = markdownLocation.split('/'); + token.push(token[token.length - 1]); + markdownLocation = token.join('/'); - if (!sourceLocationProp) { if (headers.filename) { - sourceLocation = headers.filename; + markdownLocation = headers.filename; } else { - sourceLocation = `/docs/src/pages${sourceLocation}.md`; + markdownLocation = `/docs/src/pages${markdownLocation}.md`; } } - return ( - - - {`${getTitle(markdown)} - react-swipeable-views`} - -
- -
- {contents.map(content => { - const match = content.match(demoRegexp); - - if (match && demos) { - const name = match[1]; - warning(demos && demos[name], `Missing demo: ${name}.`); - return ; - } - - return ; - })} - {headers.components.length > 0 ? ( - 0) { + const section = markdownLocation.split('/')[4]; + contents.push(` ## API ${headers.components - .map(component => `- [<${component} />](/api/${kebabCase(component)})`) - .join('\n')} - `} - /> - ) : null} -
+ .map( + component => + `- [<${component} />](${section === 'lab' ? '/lab/api' : '/api'}/${kebabCase( + component, + )})`, + ) + .join('\n')} + `); + } + + return ( + + + + +
+ +
+ {contents.map((content, index) => { + const match = content.match(demoRegexp); + + if (match && demos) { + const demoOptions = JSON.parse(`{${content}}`); + + const name = demoOptions.demo; + warning(demos && demos[name], `Missing demo: ${name}.`); + return ( + + ); + } + + return ( + + ); + })} +
+
); } MarkdownDocs.propTypes = { classes: PropTypes.object.isRequired, demos: PropTypes.object, + disableAd: PropTypes.bool, markdown: PropTypes.string.isRequired, // You can define the direction location of the markdown file. // Otherwise, we try to determine it with an heuristic. - sourceLocation: PropTypes.string, + markdownLocation: PropTypes.string, +}; + +MarkdownDocs.defaultProps = { + disableAd: false, }; MarkdownDocs.contextTypes = { diff --git a/docs/src/modules/components/MarkdownElement.js b/docs/src/modules/components/MarkdownElement.js index 8cccd6cc..19b22a31 100644 --- a/docs/src/modules/components/MarkdownElement.js +++ b/docs/src/modules/components/MarkdownElement.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import marked from 'marked'; import { withStyles } from '@material-ui/core/styles'; -import prism from 'docs/src/modules/components/prism'; +import prism from './prism'; // Monkey patch to preserve non-breaking spaces // /~https://github.com/chjj/marked/blob/6b0416d10910702f73da9cb6bb3d4c8dcb7dead7/lib/marked.js#L142-L150 @@ -18,6 +18,13 @@ marked.Lexer.prototype.lex = function lex(src) { const renderer = new marked.Renderer(); +export function textToHash(text) { + return text + .toLowerCase() + .replace(/=>|<| \/>||<\/code>/g, '') + .replace(/[^\w]+/g, '-'); +} + renderer.heading = (text, level) => { // Small title. No need for an anchor. // It's reducing the risk of duplicated id and it's less elements in the DOM. @@ -41,7 +48,7 @@ renderer.heading = (text, level) => { ); }; -marked.setOptions({ +const markedOptions = { gfm: true, tables: true, breaks: false, @@ -75,7 +82,7 @@ marked.setOptions({ return prism.highlight(code, language); }, renderer, -}); +}; const styles = theme => ({ root: { @@ -83,14 +90,14 @@ const styles = theme => ({ fontSize: 16, color: theme.palette.text.primary, '& .anchor-link': { - marginTop: -theme.spacing.unit * 12, // Offset for the anchor. + marginTop: -96, // Offset for the anchor. position: 'absolute', }, '& pre, & pre[class*="language-"]': { - margin: `${theme.spacing.unit * 3}px 0`, + margin: '24px 0', padding: '12px 18px', backgroundColor: theme.palette.background.paper, - borderRadius: 3, + borderRadius: theme.shape.borderRadius, overflow: 'auto', WebkitOverflowScrolling: 'touch', // iOS momentum scrolling. }, @@ -110,22 +117,26 @@ const styles = theme => ({ '& h1': { ...theme.typography.display2, color: theme.palette.text.secondary, - margin: '0.7em 0', + margin: '32px 0 16px', + }, + '& .description': { + ...theme.typography.headline, + margin: '0 0 40px', }, '& h2': { ...theme.typography.display1, color: theme.palette.text.secondary, - margin: '1em 0 0.7em', + margin: '32px 0 24px', }, '& h3': { ...theme.typography.headline, color: theme.palette.text.secondary, - margin: '1em 0 0.7em', + margin: '32px 0 24px', }, '& h4': { ...theme.typography.title, color: theme.palette.text.secondary, - margin: '1em 0 0.7em', + margin: '24px 0 16px', }, '& p, & ul, & ol': { lineHeight: 1.6, @@ -145,7 +156,7 @@ const styles = theme => ({ '&:hover .anchor-link-style': { display: 'inline-block', opacity: 1, - padding: `0 ${theme.spacing.unit}px`, + padding: '0 8px', color: theme.palette.text.hint, '&:hover': { color: theme.palette.text.secondary, @@ -195,16 +206,14 @@ const styles = theme => ({ }, '& td': { borderBottom: `1px solid ${theme.palette.divider}`, - padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px ${theme.spacing.unit}px ${ - theme.spacing.unit - }px`, + padding: '8px 16px 8px 8px', textAlign: 'left', }, '& td:last-child': { - paddingRight: theme.spacing.unit * 3, + paddingRight: 24, }, '& td compact': { - paddingRight: theme.spacing.unit * 3, + paddingRight: 24, }, '& td code': { fontSize: 13, @@ -214,11 +223,11 @@ const styles = theme => ({ whiteSpace: 'pre', borderBottom: `1px solid ${theme.palette.divider}`, fontWeight: theme.typography.fontWeightMedium, - padding: `0 ${theme.spacing.unit * 2}px 0 ${theme.spacing.unit}px`, + padding: '0 16px 0 8px', textAlign: 'left', }, '& th:last-child': { - paddingRight: theme.spacing.unit * 3, + paddingRight: 24, }, '& tr': { height: 48, @@ -232,8 +241,8 @@ const styles = theme => ({ '& blockquote': { borderLeft: `5px solid ${theme.palette.text.hint}`, backgroundColor: theme.palette.background.paper, - padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit * 3}px`, - margin: `${theme.spacing.unit * 3}px 0`, + padding: '4px 24px', + margin: '24px 0', }, '& a, & a code': { // Style taken from the Link component @@ -256,7 +265,7 @@ function MarkdownElement(props) { return (
); diff --git a/docs/src/modules/components/PaginationDot.js b/docs/src/modules/components/PaginationDot.js index 9f96f20b..57db63fa 100644 --- a/docs/src/modules/components/PaginationDot.js +++ b/docs/src/modules/components/PaginationDot.js @@ -39,7 +39,7 @@ class PaginationDot extends React.Component { } return ( - ); diff --git a/docs/src/modules/components/SupportTouch.js b/docs/src/modules/components/SupportTouch.js index e7ac0733..b8e4758f 100644 --- a/docs/src/modules/components/SupportTouch.js +++ b/docs/src/modules/components/SupportTouch.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import NoSSR from '@material-ui/docs/NoSSR'; +import NoSsr from '@material-ui/core/NoSsr'; const supportsTouch = 'ontouchstart' in global; @@ -9,7 +9,7 @@ function SupportTouch(props) { return ( - + {!supportsTouch && ( You need a touch device to swipe between the slides. @@ -17,7 +17,7 @@ function SupportTouch(props) {
)} -
+
{children}
); diff --git a/docs/src/modules/components/withRoot.js b/docs/src/modules/components/withRoot.js index 3817f948..20e29991 100644 --- a/docs/src/modules/components/withRoot.js +++ b/docs/src/modules/components/withRoot.js @@ -44,6 +44,7 @@ const pages = [ }, { pathname: '/', + displayNav: false, title: false, }, ]; diff --git a/docs/src/modules/styles/getContext.js b/docs/src/modules/styles/getPageContext.js similarity index 97% rename from docs/src/modules/styles/getContext.js rename to docs/src/modules/styles/getPageContext.js index d00af14d..98f64f76 100644 --- a/docs/src/modules/styles/getContext.js +++ b/docs/src/modules/styles/getPageContext.js @@ -43,7 +43,7 @@ function createContext() { }; } -export default function getContext() { +export default function getPageContext() { // Make sure to create a new store for every server-side request so that data // isn't shared between connections (which would be bad) if (!process.browser) { diff --git a/docs/src/modules/utils/parseMarkdown.js b/docs/src/modules/utils/parseMarkdown.js index de60b988..410232a4 100644 --- a/docs/src/modules/utils/parseMarkdown.js +++ b/docs/src/modules/utils/parseMarkdown.js @@ -1,5 +1,6 @@ const headerRegExp = /---[\r\n]([\s\S]*)[\r\n]---/; const titleRegExp = /# (.*)[\r\n]/; +const descriptionRegExp = /

(.*)<\/p>[\r\n]/; const headerKeyValueRegExp = /(.*): (.*)/g; const emptyRegExp = /^\s*$/; @@ -41,5 +42,19 @@ export function getContents(markdown) { export function getTitle(markdown) { const matches = markdown.match(titleRegExp); - return matches ? matches[1] : 'react-swipeable-views'; + if (!matches || !matches[1]) { + throw new Error('Missing title in the page'); + } + + return matches[1]; +} + +export function getDescription(markdown) { + const matches = markdown.match(descriptionRegExp); + + if (!matches || !matches[1]) { + throw new Error('Missing description in the page'); + } + + return matches[1]; } diff --git a/docs/src/pages/api/api.md b/docs/src/pages/api/api.md index 472de852..e02a36de 100644 --- a/docs/src/pages/api/api.md +++ b/docs/src/pages/api/api.md @@ -1,5 +1,7 @@ # API +

The API documentation.

+ ## `SwipeableViews` | Name | Type | Default | Platform | Description | diff --git a/docs/src/pages/demos/DemoAnimateHeight.js b/docs/src/pages/demos/DemoAnimateHeight.js index 84c4ab28..2883846d 100644 --- a/docs/src/pages/demos/DemoAnimateHeight.js +++ b/docs/src/pages/demos/DemoAnimateHeight.js @@ -36,10 +36,6 @@ for (let i = 0; i < 30; i += 1) { } class Slide4 extends PureComponent { - static contextTypes = { - swipeableViews: PropTypes.object.isRequired, - }; - state = { large: false, }; @@ -49,8 +45,8 @@ class Slide4 extends PureComponent { } handleClick = () => { - this.setState(() => ({ - large: !this.state.large, + this.setState(state => ({ + large: !state.large, })); }; @@ -66,6 +62,10 @@ class Slide4 extends PureComponent { } } +Slide4.contextTypes = { + swipeableViews: PropTypes.object.isRequired, +}; + function DemoAnimateHeight() { return ( diff --git a/docs/src/pages/demos/demos.md b/docs/src/pages/demos/demos.md index 2720964e..db8fcc28 100644 --- a/docs/src/pages/demos/demos.md +++ b/docs/src/pages/demos/demos.md @@ -1,5 +1,7 @@ # Demos +

Demonstrate some use cases we are supporting.

+ ## Simple A Simple case. diff --git a/docs/src/pages/getting-started/example-projects.md b/docs/src/pages/getting-started/example-projects.md index b92a7aba..6ffd5b3d 100644 --- a/docs/src/pages/getting-started/example-projects.md +++ b/docs/src/pages/getting-started/example-projects.md @@ -1,6 +1,6 @@ # Example Projects -Are you looking for an example project to get started? +

Are you looking for an example project to get started?

We host some example projects which you can find in the [GitHub repository](/~https://github.com/oliviertassinari/react-swipeable-views) under the [`/examples`](/~https://github.com/oliviertassinari/react-swipeable-views/tree/master/examples) folder: - [Create React App](/~https://github.com/oliviertassinari/react-swipeable-views/tree/master/examples/create-react-app) diff --git a/docs/src/pages/getting-started/installation.md b/docs/src/pages/getting-started/installation.md index 9c75e09d..eb0d3ade 100644 --- a/docs/src/pages/getting-started/installation.md +++ b/docs/src/pages/getting-started/installation.md @@ -1,5 +1,7 @@ # Installation +

Install react-swipeable-views.

+ react-swipeable-views is available as a npm packages. | Package | Version | Download | Size (kB gzipped) | diff --git a/docs/src/pages/getting-started/supported-platforms.md b/docs/src/pages/getting-started/supported-platforms.md index ca46dec3..4b32a942 100644 --- a/docs/src/pages/getting-started/supported-platforms.md +++ b/docs/src/pages/getting-started/supported-platforms.md @@ -1,5 +1,7 @@ ## Supported platforms +

Learn about the platforms, from modern to old, that are supported by Material-UI.

+ The API is as consistent as possible between the three platforms so the same component can be used independently on where it's running. diff --git a/docs/src/pages/getting-started/usage.md b/docs/src/pages/getting-started/usage.md index f65a83e6..c4fcc1cb 100644 --- a/docs/src/pages/getting-started/usage.md +++ b/docs/src/pages/getting-started/usage.md @@ -1,5 +1,7 @@ # Usage +

Get started with react-swipeable-views in no time.

+ ## Simple example ![usage](/static/usage.gif) diff --git a/docs/webpackBaseConfig.js b/docs/webpackBaseConfig.js new file mode 100644 index 00000000..f528d879 --- /dev/null +++ b/docs/webpackBaseConfig.js @@ -0,0 +1,39 @@ +const path = require('path'); + +// This module isn't used to build the documentation. We use Next.js for that. +// This module is used by the visual regression tests to run the demos. +module.exports = { + context: path.resolve(__dirname), + resolve: { + modules: [path.join(__dirname, '../'), 'node_modules'], + alias: { + 'react-swipeable-views': path.resolve(__dirname, '../packages/react-swipeable-views/src'), + 'react-swipeable-views-core': path.resolve( + __dirname, + '../packages/react-swipeable-views-core/src', + ), + 'react-swipeable-views-utils': path.resolve( + __dirname, + '../packages/react-swipeable-views-utils/src', + ), + docs: __dirname, + }, + }, + output: { + path: path.join(__dirname, 'build'), + filename: 'bundle.js', + publicPath: '/build/', + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader', + query: { + cacheDirectory: true, + }, + }, + ], + }, +}; diff --git a/native/docs/src/Footer.native.js b/native/docs/src/Footer.native.js index b4041f5b..3891c133 100644 --- a/native/docs/src/Footer.native.js +++ b/native/docs/src/Footer.native.js @@ -16,12 +16,7 @@ const styles = StyleSheet.create({ }); function Footer(props) { - const { - maintainerName, - // maintainerUrl, - repositoryName, - // repositoryUrl, - } = props; + const { maintainerName, repositoryName } = props; return ( @@ -37,9 +32,7 @@ function Footer(props) { Footer.propTypes = { maintainerName: PropTypes.string.isRequired, - maintainerUrl: PropTypes.string.isRequired, repositoryName: PropTypes.string.isRequired, - repositoryUrl: PropTypes.string.isRequired, }; export default Footer; diff --git a/native/packages/react-swipeable-views-native/src/SwipeableViews.animated.js b/native/packages/react-swipeable-views-native/src/SwipeableViews.animated.js index 9e3d4f71..113fee5d 100644 --- a/native/packages/react-swipeable-views-native/src/SwipeableViews.animated.js +++ b/native/packages/react-swipeable-views-native/src/SwipeableViews.animated.js @@ -33,118 +33,11 @@ function getAnimatedValue(animated) { } class SwipeableViews extends React.Component { - static propTypes = { - /** - * If `true`, the height of the container will be animated to match the current slide height. - * Animating another style property has a negative impact regarding performance. - */ - animateHeight: PropTypes.bool, - /** - * If `false`, changes to the index prop will not cause an animated transition. - */ - animateTransitions: PropTypes.bool, - /** - * The axis on which the slides will slide. - */ - axis: PropTypes.oneOf(['x', 'x-reverse', 'y', 'y-reverse']), - /** - * Use this property to provide your slides. - */ - children: PropTypes.node.isRequired, - /** - * This is the inlined style that will be applied - * to each slide container. - */ - containerStyle: Animated.View.propTypes.style, - /** - * If `true`, it will disable touch events. - * This is useful when you want to prohibit the user from changing slides. - */ - disabled: PropTypes.bool, - /** - * Configure hysteresis between slides. This value determines how far - * should user swipe to switch slide. - */ - hysteresis: PropTypes.number, - /** - * This is the index of the slide to show. - * This is useful when you want to change the default slide shown. - * Or when you have tabs linked to each slide. - */ - index: PropTypes.number, - /** - * This is callback prop. It's call by the - * component when the shown slide change after a swipe made by the user. - * This is useful when you have tabs linked to each slide. - * - * @param {integer} index This is the current index of the slide. - * @param {integer} fromIndex This is the oldest index of the slide. - */ - onChangeIndex: PropTypes.func, - /** - * This is callback prop. It's called by the - * component when the slide switching. - * This is useful when you want to implement something - * corresponding to the current slide position. - * - * @param {integer} index This is the current index of the slide. - * @param {string} type Can be either `move` or `end`. - */ - onSwitching: PropTypes.func, - /** - * @ignore - */ - onTouchEnd: PropTypes.func, - /** - * @ignore - */ - onTouchStart: PropTypes.func, - /** - * The callback that fires when the animation comes to a rest. - * This is useful to defer CPU intensive task. - */ - onTransitionEnd: PropTypes.func, - /** - * If `true`, it will add bounds effect on the edges. - */ - resistance: PropTypes.bool, - /** - * This is the inlined style that will be applied - * on the slide component. - */ - slideStyle: View.propTypes.style, - /** - * This is the config given to Animated for the spring. - * This is useful to change the dynamic of the transition. - */ - springConfig: PropTypes.shape({ - tension: PropTypes.number, - friction: PropTypes.number, - }), - /** - * This is the inlined style that will be applied - * on the root component. - */ - style: View.propTypes.style, - /** - * This is the threshold used for detecting a quick swipe. - * If the computed speed is above this value, the index change. - */ - threshold: PropTypes.number, - }; + panResponder = undefined; - static defaultProps = { - animateTransitions: true, - disabled: false, - hysteresis: 0.6, - index: 0, - resistance: false, - springConfig: { - tension: 300, - friction: 30, - }, - threshold: 5, - }; + startX = 0; + + startIndex = 0; state = {}; @@ -211,20 +104,6 @@ class SwipeableViews extends React.Component { } } - animateIndexCurrent(index) { - // Avoid starting an animation when we are already on the right value. - if (getAnimatedValue(this.state.indexCurrent) !== index) { - Animated.spring(this.state.indexCurrent, { - toValue: index, - ...this.props.springConfig, - }).start(this.handleAnimationFinished); - } else { - this.handleAnimationFinished({ - finished: true, - }); - } - } - handleAnimationFinished = params => { // The animation can be aborted. // We only want to call onTransitionEnd when the animation is finished. @@ -233,10 +112,6 @@ class SwipeableViews extends React.Component { } }; - panResponder = undefined; - startX = 0; - startIndex = 0; - handleTouchStart = (event, gestureState) => { if (this.props.onTouchStart) { this.props.onTouchStart(event, gestureState); @@ -332,6 +207,20 @@ class SwipeableViews extends React.Component { } }; + animateIndexCurrent(index) { + // Avoid starting an animation when we are already on the right value. + if (getAnimatedValue(this.state.indexCurrent) !== index) { + Animated.spring(this.state.indexCurrent, { + toValue: index, + ...this.props.springConfig, + }).start(this.handleAnimationFinished); + } else { + this.handleAnimationFinished({ + finished: true, + }); + } + } + render() { const { children, @@ -387,4 +276,117 @@ We are expecting a valid React Element`, } } +SwipeableViews.propTypes = { + /** + * If `true`, the height of the container will be animated to match the current slide height. + * Animating another style property has a negative impact regarding performance. + */ + animateHeight: PropTypes.bool, + /** + * If `false`, changes to the index prop will not cause an animated transition. + */ + animateTransitions: PropTypes.bool, + /** + * The axis on which the slides will slide. + */ + axis: PropTypes.oneOf(['x', 'x-reverse', 'y', 'y-reverse']), + /** + * Use this property to provide your slides. + */ + children: PropTypes.node.isRequired, + /** + * This is the inlined style that will be applied + * to each slide container. + */ + containerStyle: Animated.View.propTypes.style, + /** + * If `true`, it will disable touch events. + * This is useful when you want to prohibit the user from changing slides. + */ + disabled: PropTypes.bool, + /** + * Configure hysteresis between slides. This value determines how far + * should user swipe to switch slide. + */ + hysteresis: PropTypes.number, + /** + * This is the index of the slide to show. + * This is useful when you want to change the default slide shown. + * Or when you have tabs linked to each slide. + */ + index: PropTypes.number, + /** + * This is callback prop. It's call by the + * component when the shown slide change after a swipe made by the user. + * This is useful when you have tabs linked to each slide. + * + * @param {integer} index This is the current index of the slide. + * @param {integer} fromIndex This is the oldest index of the slide. + */ + onChangeIndex: PropTypes.func, + /** + * This is callback prop. It's called by the + * component when the slide switching. + * This is useful when you want to implement something + * corresponding to the current slide position. + * + * @param {integer} index This is the current index of the slide. + * @param {string} type Can be either `move` or `end`. + */ + onSwitching: PropTypes.func, + /** + * @ignore + */ + onTouchEnd: PropTypes.func, + /** + * @ignore + */ + onTouchStart: PropTypes.func, + /** + * The callback that fires when the animation comes to a rest. + * This is useful to defer CPU intensive task. + */ + onTransitionEnd: PropTypes.func, + /** + * If `true`, it will add bounds effect on the edges. + */ + resistance: PropTypes.bool, + /** + * This is the inlined style that will be applied + * on the slide component. + */ + slideStyle: View.propTypes.style, + /** + * This is the config given to Animated for the spring. + * This is useful to change the dynamic of the transition. + */ + springConfig: PropTypes.shape({ + friction: PropTypes.number, + tension: PropTypes.number, + }), + /** + * This is the inlined style that will be applied + * on the root component. + */ + style: View.propTypes.style, + /** + * This is the threshold used for detecting a quick swipe. + * If the computed speed is above this value, the index change. + */ + threshold: PropTypes.number, +}; + +SwipeableViews.defaultProps = { + animateTransitions: true, + disabled: false, + hysteresis: 0.6, + index: 0, + resistance: false, + springConfig: { + tension: 300, + friction: 30, + }, + threshold: 5, +}; + export default SwipeableViews; diff --git a/native/packages/react-swipeable-views-native/src/SwipeableViews.scroll.js b/native/packages/react-swipeable-views-native/src/SwipeableViews.scroll.js index 182f3562..99ecd8db 100644 --- a/native/packages/react-swipeable-views-native/src/SwipeableViews.scroll.js +++ b/native/packages/react-swipeable-views-native/src/SwipeableViews.scroll.js @@ -1,3 +1,5 @@ +/* eslint-disable react/no-access-state-in-setstate */ + // This is an alternative version that use `ScrollView` and `ViewPagerAndroid`. // I'm not sure what version give the best UX experience. // I'm keeping the two versions here until we figured out. @@ -25,86 +27,7 @@ const styles = StyleSheet.create({ }); class SwipeableViews extends React.Component { - static propTypes = { - /** - * If `true`, the height of the container will be animated to match the current slide height. - * Animating another style property has a negative impact regarding performance. - */ - animateHeight: PropTypes.bool, - /** - * If `false`, changes to the index prop will not cause an animated transition. - */ - animateTransitions: PropTypes.bool, - /** - * The axis on which the slides will slide. - */ - axis: PropTypes.oneOf(['x', 'x-reverse', 'y', 'y-reverse']), - /** - * Use this property to provide your slides. - */ - children: PropTypes.node, - /** - * This is the inlined style that will be applied - * to each slide container. - */ - containerStyle: ScrollView.propTypes.style, - /** - * If `true`, it will disable touch events. - * This is useful when you want to prohibit the user from changing slides. - */ - disabled: PropTypes.bool, - /** - * This is the index of the slide to show. - * This is useful when you want to change the default slide shown. - * Or when you have tabs linked to each slide. - */ - index: PropTypes.number, - /** - * This is callback prop. It's call by the - * component when the shown slide change after a swipe made by the user. - * This is useful when you have tabs linked to each slide. - * - * @param {integer} index This is the current index of the slide. - * @param {integer} fromIndex This is the oldest index of the slide. - */ - onChangeIndex: PropTypes.func, - /** - * This is callback prop. It's called by the - * component when the slide switching. - * This is useful when you want to implement something - * corresponding to the current slide position. - * - * @param {integer} index This is the current index of the slide. - * @param {string} type Can be either `move` or `end`. - */ - onSwitching: PropTypes.func, - /** - * The callback that fires when the animation comes to a rest. - * This is useful to defer CPU intensive task. - */ - onTransitionEnd: PropTypes.func, - /** - * If `true`, it will add bounds effect on the edges. - */ - resistance: PropTypes.bool, - /** - * This is the inlined style that will be applied - * on the slide component. - */ - slideStyle: View.propTypes.style, - /** - * This is the inlined style that will be applied - * on the root component. - */ - style: View.propTypes.style, - }; - - static defaultProps = { - animateTransitions: true, - disabled: false, - index: 0, - resistance: false, - }; + scrollViewNode = null; state = {}; @@ -157,8 +80,6 @@ class SwipeableViews extends React.Component { } } - scrollViewNode = null; - handleScroll = event => { // Filters out when changing the children if (this.state.displaySameSlide) { @@ -271,4 +192,85 @@ We are expecting a valid React Element`, } } +SwipeableViews.propTypes = { + /** + * If `true`, the height of the container will be animated to match the current slide height. + * Animating another style property has a negative impact regarding performance. + */ + animateHeight: PropTypes.bool, + /** + * If `false`, changes to the index prop will not cause an animated transition. + */ + animateTransitions: PropTypes.bool, + /** + * The axis on which the slides will slide. + */ + axis: PropTypes.oneOf(['x', 'x-reverse', 'y', 'y-reverse']), + /** + * Use this property to provide your slides. + */ + children: PropTypes.node, + /** + * This is the inlined style that will be applied + * to each slide container. + */ + containerStyle: ScrollView.propTypes.style, + /** + * If `true`, it will disable touch events. + * This is useful when you want to prohibit the user from changing slides. + */ + disabled: PropTypes.bool, + /** + * This is the index of the slide to show. + * This is useful when you want to change the default slide shown. + * Or when you have tabs linked to each slide. + */ + index: PropTypes.number, + /** + * This is callback prop. It's call by the + * component when the shown slide change after a swipe made by the user. + * This is useful when you have tabs linked to each slide. + * + * @param {integer} index This is the current index of the slide. + * @param {integer} fromIndex This is the oldest index of the slide. + */ + onChangeIndex: PropTypes.func, + /** + * This is callback prop. It's called by the + * component when the slide switching. + * This is useful when you want to implement something + * corresponding to the current slide position. + * + * @param {integer} index This is the current index of the slide. + * @param {string} type Can be either `move` or `end`. + */ + onSwitching: PropTypes.func, + /** + * The callback that fires when the animation comes to a rest. + * This is useful to defer CPU intensive task. + */ + onTransitionEnd: PropTypes.func, + /** + * If `true`, it will add bounds effect on the edges. + */ + resistance: PropTypes.bool, + /** + * This is the inlined style that will be applied + * on the slide component. + */ + slideStyle: View.propTypes.style, + /** + * This is the inlined style that will be applied + * on the root component. + */ + style: View.propTypes.style, +}; + +SwipeableViews.defaultProps = { + animateTransitions: true, + disabled: false, + index: 0, + resistance: false, +}; + export default SwipeableViews; diff --git a/next.config.js b/next.config.js index 314bd91f..1b57d45d 100644 --- a/next.config.js +++ b/next.config.js @@ -3,7 +3,6 @@ const pkg = require('./packages/react-swipeable-views/package.json'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); const { findPages } = require('./docs/src/modules/utils/find'); -const ENABLE_STATS = false; process.env.LIB_VERSION = pkg.version; module.exports = { @@ -16,7 +15,7 @@ module.exports = { }), ]); - if (ENABLE_STATS) { + if (process.env.DOCS_STATS_ENABLED) { plugins.push( // For all options see /~https://github.com/th0r/webpack-bundle-analyzer#as-plugin new BundleAnalyzerPlugin({ diff --git a/package.json b/package.json index 4b35a646..75fbd9ea 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "react-swipeable-views-workspace", "private": true, "scripts": { - "start": "yarn docs:dev", "lint": "eslint . && echo \"eslint: no lint errors\"", "test:unit": "cross-env NODE_ENV=test mocha", "test:watch": "yarn test:unit -w", @@ -16,7 +15,7 @@ "size": "size-limit", "size:why": "size-limit --why packages/react-swipeable-views/lib/index.js", "watch": "lerna exec --concurrency 99 -- babel src --out-dir lib --watch", - "build": "rm -rf packages/*/lib && cross-env NODE_ENV=production lerna exec -- babel src --out-dir lib --ignore spec.js", + "build": "rm -rf packages/*/lib && cross-env NODE_ENV=production lerna exec -- babel --config-file ../../babel.config.js src --out-dir lib --ignore test.js", "release": "yarn build && yarn lerna exec yarn prepublish && lerna publish", "postrelease": "yarn docs:deploy" }, @@ -27,22 +26,24 @@ "url": "/~https://github.com/oliviertassinari/react-swipeable-views.git" }, "devDependencies": { - "@babel/cli": "7.0.0-beta.42", - "@babel/core": "7.0.0-beta.42", - "@babel/plugin-transform-object-assign": "7.0.0-beta.42", - "@babel/plugin-transform-runtime": "7.0.0-beta.42", - "@babel/preset-react": "7.0.0-beta.42", - "@babel/preset-stage-1": "7.0.0-beta.42", - "@babel/register": "7.0.0-beta.42", - "@babel/runtime": "7.0.0-beta.42", - "@material-ui/core": "^1.0.0", + "@babel/cli": "7.0.0", + "@babel/core": "7.0.0", + "@babel/plugin-transform-object-assign": "7.0.0", + "@babel/plugin-transform-runtime": "7.0.0", + "@babel/preset-react": "7.0.0", + "@babel/preset-stage-1": "7.0.0", + "@babel/register": "7.0.0", + "@babel/runtime": "7.0.0", + "@material-ui/core": "^3.0.0", "@material-ui/docs": "^1.0.0-alpha.3", - "@material-ui/icons": "^1.0.0", + "@material-ui/icons": "^3.0.0", "animated": "^0.2.1", - "babel-eslint": "^8.0.2", - "babel-loader": "8.0.0-beta.2", + "autoprefixer": "^9.1.3", + "babel-eslint": "^9.0.0", + "babel-loader": "^8.0.0", + "babel-plugin-istanbul": "^5.0.1", "babel-plugin-module-resolver": "^3.0.0", - "babel-plugin-preval": "^1.6.4", + "babel-plugin-preval": "^2.0.0", "babel-plugin-react-remove-properties": "^0.2.5", "babel-plugin-transform-dev-warning": "^0.1.0", "babel-plugin-transform-react-constant-elements": "^6.23.0", @@ -50,11 +51,12 @@ "chai": "^4.1.2", "chai-shallow-deep-equal": "^1.4.6", "clean-css": "^4.1.9", + "clipboard-copy": "^2.0.0", "cross-env": "^5.1.1", "enzyme": "^3.2.0", "enzyme-adapter-react-16": "^1.1.0", - "eslint": "^4.11.0", - "eslint-config-airbnb": "^16.1.0", + "eslint": "^5.4.0", + "eslint-config-airbnb": "^17.0.0", "eslint-plugin-babel": "^5.0.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-jsx-a11y": "^6.0.2", @@ -62,21 +64,22 @@ "eslint-plugin-prettier": "^2.3.1", "eslint-plugin-react": "^7.5.1", "glob": "^7.1.2", - "jsdom": "^11.4.0", - "lerna": "^2.5.1", + "jsdom": "^12.0.0", + "lerna": "^3.2.1", "lz-string": "^1.4.4", "mocha": "^5.0.0", - "next": "^6.0.1", - "nyc": "^12.0.0", + "next": "^7.0.0-canary.5", + "nyc": "^13.0.0", + "postcss": "^7.0.2", "prettier": "^1.8.2", "prop-types": "^15.6.0", "raw-loader": "^0.5.1", "react": "^16.3.0", "react-dom": "^16.3.0", - "recompose": "^0.27.0", + "recompose": "^0.29.0", "sinon": "^6.0.0", - "size-limit": "^0.18.2", - "webpack": "3.10.0", + "size-limit": "^0.19.0", + "webpack": "4.16.3", "webpack-bundle-analyzer": "^2.9.1" }, "engines": { diff --git a/packages/react-swipeable-views-core/.npmignore b/packages/react-swipeable-views-core/.npmignore index d4de1c8e..a3a82f6d 100644 --- a/packages/react-swipeable-views-core/.npmignore +++ b/packages/react-swipeable-views-core/.npmignore @@ -1,4 +1,4 @@ /* !/src/*.js !/lib/*.js -*.spec.js +*.test.js diff --git a/packages/react-swipeable-views-core/package.json b/packages/react-swipeable-views-core/package.json index ffbcc545..d85a8d2e 100644 --- a/packages/react-swipeable-views-core/package.json +++ b/packages/react-swipeable-views-core/package.json @@ -15,7 +15,7 @@ "url": "/~https://github.com/oliviertassinari/react-swipeable-views/issues" }, "dependencies": { - "@babel/runtime": "7.0.0-beta.42", + "@babel/runtime": "7.0.0", "warning": "^4.0.1" }, "devDependencies": { diff --git a/packages/react-swipeable-views-core/src/checkIndexBounds.spec.js b/packages/react-swipeable-views-core/src/checkIndexBounds.test.js similarity index 100% rename from packages/react-swipeable-views-core/src/checkIndexBounds.spec.js rename to packages/react-swipeable-views-core/src/checkIndexBounds.test.js diff --git a/packages/react-swipeable-views-core/src/computeIndex.spec.js b/packages/react-swipeable-views-core/src/computeIndex.test.js similarity index 100% rename from packages/react-swipeable-views-core/src/computeIndex.spec.js rename to packages/react-swipeable-views-core/src/computeIndex.test.js diff --git a/packages/react-swipeable-views-core/src/getDisplaySameSlide.spec.js b/packages/react-swipeable-views-core/src/getDisplaySameSlide.test.js similarity index 100% rename from packages/react-swipeable-views-core/src/getDisplaySameSlide.spec.js rename to packages/react-swipeable-views-core/src/getDisplaySameSlide.test.js diff --git a/packages/react-swipeable-views-core/src/mod.spec.js b/packages/react-swipeable-views-core/src/mod.test.js similarity index 100% rename from packages/react-swipeable-views-core/src/mod.spec.js rename to packages/react-swipeable-views-core/src/mod.test.js diff --git a/packages/react-swipeable-views-utils/.npmignore b/packages/react-swipeable-views-utils/.npmignore index d4de1c8e..a3a82f6d 100644 --- a/packages/react-swipeable-views-utils/.npmignore +++ b/packages/react-swipeable-views-utils/.npmignore @@ -1,4 +1,4 @@ /* !/src/*.js !/lib/*.js -*.spec.js +*.test.js diff --git a/packages/react-swipeable-views-utils/package.json b/packages/react-swipeable-views-utils/package.json index acd67e4c..5ce15ab7 100644 --- a/packages/react-swipeable-views-utils/package.json +++ b/packages/react-swipeable-views-utils/package.json @@ -15,7 +15,7 @@ "url": "/~https://github.com/oliviertassinari/react-swipeable-views/issues" }, "dependencies": { - "@babel/runtime": "7.0.0-beta.42", + "@babel/runtime": "7.0.0", "fbjs": "^0.8.4", "keycode": "^2.1.7", "prop-types": "^15.6.0", diff --git a/packages/react-swipeable-views-utils/src/autoPlay.js b/packages/react-swipeable-views-utils/src/autoPlay.js index c527e2d1..5a717588 100644 --- a/packages/react-swipeable-views-utils/src/autoPlay.js +++ b/packages/react-swipeable-views-utils/src/autoPlay.js @@ -5,50 +5,11 @@ import { mod } from 'react-swipeable-views-core'; export default function autoPlay(MyComponent) { class AutoPlay extends React.Component { - static propTypes = { - /** - * If `false`, the auto play behavior is disabled. - */ - autoplay: PropTypes.bool, - /** - * @ignore - */ - children: PropTypes.node, - /** - * This is the auto play direction. - */ - direction: PropTypes.oneOf(['incremental', 'decremental']), - /** - * @ignore - */ - index: PropTypes.number, - /** - * Delay between auto play transitions (in ms). - */ - interval: PropTypes.number, - /** - * @ignore - */ - onChangeIndex: PropTypes.func, - /** - * @ignore - */ - onSwitching: PropTypes.func, - /** - * @ignore - */ - slideCount: PropTypes.number, - }; - - static defaultProps = { - autoplay: true, - direction: 'incremental', - interval: 3000, - }; + timer = null; - constructor(props, context) { - super(props, context); - this.state.index = this.props.index || 0; + constructor(props) { + super(props); + this.state.index = props.index || 0; } state = {}; @@ -90,18 +51,6 @@ export default function autoPlay(MyComponent) { clearInterval(this.timer); } - timer = null; - - startInterval() { - const { autoplay, interval } = this.props; - - clearInterval(this.timer); - - if (autoplay) { - this.timer = setInterval(this.handleInterval, interval); - } - } - handleInterval = () => { const { children, direction, onChangeIndex, slideCount } = this.props; @@ -156,12 +105,22 @@ export default function autoPlay(MyComponent) { } }; + startInterval() { + const { autoplay, interval } = this.props; + + clearInterval(this.timer); + + if (autoplay) { + this.timer = setInterval(this.handleInterval, interval); + } + } + render() { const { autoplay, direction, - interval, index: indexProp, + interval, onChangeIndex, ...other } = this.props; @@ -183,5 +142,46 @@ export default function autoPlay(MyComponent) { } } + AutoPlay.propTypes = { + /** + * If `false`, the auto play behavior is disabled. + */ + autoplay: PropTypes.bool, + /** + * @ignore + */ + children: PropTypes.node, + /** + * This is the auto play direction. + */ + direction: PropTypes.oneOf(['incremental', 'decremental']), + /** + * @ignore + */ + index: PropTypes.number, + /** + * Delay between auto play transitions (in ms). + */ + interval: PropTypes.number, + /** + * @ignore + */ + onChangeIndex: PropTypes.func, + /** + * @ignore + */ + onSwitching: PropTypes.func, + /** + * @ignore + */ + slideCount: PropTypes.number, + }; + + AutoPlay.defaultProps = { + autoplay: true, + direction: 'incremental', + interval: 3000, + }; + return AutoPlay; } diff --git a/packages/react-swipeable-views-utils/src/autoPlay.spec.js b/packages/react-swipeable-views-utils/src/autoPlay.test.js similarity index 100% rename from packages/react-swipeable-views-utils/src/autoPlay.spec.js rename to packages/react-swipeable-views-utils/src/autoPlay.test.js diff --git a/packages/react-swipeable-views-utils/src/bindKeyboard.spec.js b/packages/react-swipeable-views-utils/src/bindKeyboard.test.js similarity index 100% rename from packages/react-swipeable-views-utils/src/bindKeyboard.spec.js rename to packages/react-swipeable-views-utils/src/bindKeyboard.test.js diff --git a/packages/react-swipeable-views-utils/src/virtualize.js b/packages/react-swipeable-views-utils/src/virtualize.js index ba204082..bc69c53e 100644 --- a/packages/react-swipeable-views-utils/src/virtualize.js +++ b/packages/react-swipeable-views-utils/src/virtualize.js @@ -4,54 +4,7 @@ import { mod } from 'react-swipeable-views-core'; export default function virtualize(MyComponent) { class Virtualize extends PureComponent { - static propTypes = { - /** - * @ignore - */ - children: (props, propName) => { - if (props[propName] !== undefined) { - return new Error("The children property isn't supported."); - } - - return null; - }, - /** - * @ignore - */ - index: PropTypes.number, - /** - * @ignore - */ - onChangeIndex: PropTypes.func, - /** - * @ignore - */ - onTransitionEnd: PropTypes.func, - /** - * Number of slide to render after the visible slide. - */ - overscanSlideAfter: PropTypes.number, - /** - * Number of slide to render before the visible slide. - */ - overscanSlideBefore: PropTypes.number, - /** - * When set, it's adding a limit to the number of slide: [0, slideCount]. - */ - slideCount: PropTypes.number, - /** - * Responsible for rendering a slide given an index. - * ({ index: number }): node. - */ - slideRenderer: PropTypes.func.isRequired, - }; - - static defaultProps = { - overscanSlideAfter: 2, - // Render one more slide for going backward as it's more difficult to - // keep the window up to date. - overscanSlideBefore: 3, - }; + timer = null; constructor(props, context) { super(props, context); @@ -141,8 +94,6 @@ export default function virtualize(MyComponent) { }); } - timer = null; - handleChangeIndex = (indexContainer, indexLatest) => { const { slideCount, onChangeIndex } = this.props; @@ -213,5 +164,54 @@ export default function virtualize(MyComponent) { } } + Virtualize.propTypes = { + /** + * @ignore + */ + children: (props, propName) => { + if (props[propName] !== undefined) { + return new Error("The children property isn't supported."); + } + + return null; + }, + /** + * @ignore + */ + index: PropTypes.number, + /** + * @ignore + */ + onChangeIndex: PropTypes.func, + /** + * @ignore + */ + onTransitionEnd: PropTypes.func, + /** + * Number of slide to render after the visible slide. + */ + overscanSlideAfter: PropTypes.number, + /** + * Number of slide to render before the visible slide. + */ + overscanSlideBefore: PropTypes.number, + /** + * When set, it's adding a limit to the number of slide: [0, slideCount]. + */ + slideCount: PropTypes.number, + /** + * Responsible for rendering a slide given an index. + * ({ index: number }): node. + */ + slideRenderer: PropTypes.func.isRequired, + }; + + Virtualize.defaultProps = { + overscanSlideAfter: 2, + // Render one more slide for going backward as it's more difficult to + // keep the window up to date. + overscanSlideBefore: 3, + }; + return Virtualize; } diff --git a/packages/react-swipeable-views-utils/src/virtualize.spec.js b/packages/react-swipeable-views-utils/src/virtualize.test.js similarity index 100% rename from packages/react-swipeable-views-utils/src/virtualize.spec.js rename to packages/react-swipeable-views-utils/src/virtualize.test.js diff --git a/packages/react-swipeable-views/.npmignore b/packages/react-swipeable-views/.npmignore index d4de1c8e..a3a82f6d 100644 --- a/packages/react-swipeable-views/.npmignore +++ b/packages/react-swipeable-views/.npmignore @@ -1,4 +1,4 @@ /* !/src/*.js !/lib/*.js -*.spec.js +*.test.js diff --git a/packages/react-swipeable-views/package.json b/packages/react-swipeable-views/package.json index 263484d4..8c069f0e 100644 --- a/packages/react-swipeable-views/package.json +++ b/packages/react-swipeable-views/package.json @@ -21,7 +21,7 @@ "url": "/~https://github.com/oliviertassinari/react-swipeable-views/issues" }, "dependencies": { - "@babel/runtime": "7.0.0-beta.42", + "@babel/runtime": "7.0.0", "dom-helpers": "^3.2.1", "prop-types": "^15.5.4", "react-swipeable-views-core": "^0.12.16", diff --git a/packages/react-swipeable-views/src/SwipeableViews.js b/packages/react-swipeable-views/src/SwipeableViews.js index 750b6a83..a862193b 100644 --- a/packages/react-swipeable-views/src/SwipeableViews.js +++ b/packages/react-swipeable-views/src/SwipeableViews.js @@ -224,6 +224,36 @@ export function findNativeHandler(params) { } class SwipeableViews extends React.Component { + rootNode = null; + + containerNode = null; + + ignoreNextScrollEvents = false; + + viewLength = 0; + + startX = 0; + + lastX = 0; + + vx = 0; + + startY = 0; + + isSwiping = undefined; + + started = false; + + startIndex = 0; + + transitionListener = null; + + touchMoveListener = null; + + activeSlide = null; + + indexCurrent = null; + constructor(props, context) { super(props, context); @@ -350,22 +380,6 @@ class SwipeableViews extends React.Component { this.updateHeight(); }; - rootNode = null; - containerNode = null; - ignoreNextScrollEvents = false; - viewLength = 0; - startX = 0; - lastX = 0; - vx = 0; - startY = 0; - isSwiping = undefined; - started = false; - startIndex = 0; - transitionListener = null; - touchMoveListener = null; - activeSlide = null; - indexCurrent = null; - handleSwipeStart = event => { const { axis } = this.props; @@ -638,23 +652,6 @@ class SwipeableViews extends React.Component { } }; - handleTransitionEnd() { - if (!this.props.onTransitionEnd) { - return; - } - - // Filters out when changing the children - if (this.state.displaySameSlide) { - return; - } - - // The rest callback is triggered when swiping. It's just noise. - // We filter it out. - if (!this.state.isDragging) { - this.props.onTransitionEnd(); - } - } - handleScroll = event => { if (this.props.onScroll) { this.props.onScroll(event); @@ -699,6 +696,23 @@ class SwipeableViews extends React.Component { } }; + handleTransitionEnd() { + if (!this.props.onTransitionEnd) { + return; + } + + // Filters out when changing the children + if (this.state.displaySameSlide) { + return; + } + + // The rest callback is triggered when swiping. It's just noise. + // We filter it out. + if (!this.state.isDragging) { + this.props.onTransitionEnd(); + } + } + render() { const { action, @@ -988,9 +1002,9 @@ SwipeableViews.propTypes = { * This is useful to change the dynamic of the transition. */ springConfig: PropTypes.shape({ + delay: PropTypes.string, duration: PropTypes.string, easeFunction: PropTypes.string, - delay: PropTypes.string, }), /** * This is the inlined style that will be applied diff --git a/packages/react-swipeable-views/src/SwipeableViews.spec.js b/packages/react-swipeable-views/src/SwipeableViews.test.js similarity index 100% rename from packages/react-swipeable-views/src/SwipeableViews.spec.js rename to packages/react-swipeable-views/src/SwipeableViews.test.js diff --git a/pages/_document.js b/pages/_document.js index cdb1a856..08247405 100644 --- a/pages/_document.js +++ b/pages/_document.js @@ -1,33 +1,36 @@ import React from 'react'; import Document, { Head, Main, NextScript } from 'next/document'; -import getContext from 'docs/src/modules/styles/getContext'; +import getPageContext from 'docs/src/modules/styles/getPageContext'; +// You can find a benchmark of the available CSS minifiers under +// /~https://github.com/GoalSmashers/css-minification-benchmark +// We have found that clean-css is faster than cssnano but the output is larger. +// Waiting for /~https://github.com/cssinjs/jss/issues/279 +// 4% slower but 12% smaller output than doing it in a single step. +// +// It's using .browserslistrc +let prefixer; let cleanCSS; if (process.env.NODE_ENV === 'production') { + const postcss = require('postcss'); + const autoprefixer = require('autoprefixer'); const CleanCSS = require('clean-css'); + + prefixer = postcss([autoprefixer]); cleanCSS = new CleanCSS(); } -const title = 'react-swipeable-views'; -const description = 'A React component for swipeable views.'; - class MyDocument extends Document { render() { - const { stylesContext } = this.props; + const { pageContext } = this.props; return ( - {title} - - {/* Use minimum-scale=1 to enable GPU rasterization */} {/* manifest.json provides metadata used when your web app is added to the @@ -35,31 +38,31 @@ class MyDocument extends Document { */} {/* PWA primary color */} - + + + {/* + Preconnect allows the browser to setup early connections before an HTTP request + is actually sent to the server. + This includes DNS lookups, TLS negotiations, TCP handshakes. + */} +