diff --git a/.eslintrc.json b/.eslintrc.json index cb9fedf2c2..9a9cf75b86 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,7 +3,8 @@ "eslint:recommended", "plugin:prettier/recommended", "plugin:react/recommended", - "plugin:jsx-a11y/recommended" + "plugin:jsx-a11y/recommended", + "plugin:json/recommended" ], "parser": "babel-eslint", "env": { diff --git a/app.json b/app.json index 05991c8a23..29767ea5b1 100644 --- a/app.json +++ b/app.json @@ -1,21 +1,17 @@ { - "addons": [ - - ], + "addons": [], "buildpacks": [ { "url": "heroku/nodejs" } ], - "env": { - }, + "env": {}, "formation": { "web": { "quantity": 1 } }, "name": "dvc.org", - "scripts": { - }, + "scripts": {}, "stack": "heroku-18" } diff --git a/package.json b/package.json index 6b2cc58983..f2768f9108 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,9 @@ "test": "jest", "start": "NODE_ENV=production node server.js", "format-staged": "pretty-quick --staged --no-restage --bail", - "format-check": "prettier --check '{.,pages/**,public/static/docs/**,src/**}/*.{js,md}'", - "lint-check": "eslint src pages", - "format-all": "prettier --write '{.,pages/**,public/static/docs/**,src/**}/*.{js,md}'", + "format-check": "prettier --check '{.,pages/**,public/static/docs/**,src/**}/*.{js,md,json}'", + "lint-check": "eslint --ext .json,.js src pages", + "format-all": "prettier --write '{.,pages/**,public/static/docs/**,src/**}/*.{js,md,json}'", "format": "prettier --write", "link-check": "scripts/link-check-git-all.sh", "link-check-diff": "scripts/link-check-git-diff.sh" @@ -28,9 +28,11 @@ }, "homepage": "https://github.com/iterative/dvc.org#readme", "dependencies": { + "@octokit/graphql": "^4.3.1", "@sentry/browser": "^5.12.1", "@zeit/next-source-maps": "^0.0.3", "color": "^3.1.2", + "date-fns": "^2.8.1", "dom-scroll-into-view": "^2.0.1", "github-markdown-css": "^3.0.1", "isomorphic-fetch": "^2.2.1", @@ -41,19 +43,20 @@ "lodash.topairs": "^4.3.0", "micro-cors": "^0.1.1", "next": "^9.1.6", + "node-cache": "^5.1.0", "perfect-scrollbar": "^1.4.0", "prop-types": "^15.7.2", "react": "^16.12.0", - "react-collapse": "^4.0.3", + "react-collapse": "^5.0.1", "react-collapsible": "^2.6.2", "react-dom": "^16.12.0", "react-ga": "^2.7.0", "react-markdown": "^4.2.2", - "react-motion": "^0.5.2", "react-popover": "^0.5.10", "react-scroll": "^1.7.13", "react-slick": "^0.25.2", "react-syntax-highlighter": "^11.0.2", + "react-use": "^13.24.0", "request": "^2.88.0", "styled-components": "^4.4.1", "styled-reset": "^4.0.8", @@ -67,21 +70,22 @@ "babel-plugin-transform-object-assign": "^6.22.0", "eslint": "^6.7.2", "eslint-config-prettier": "^6.7.0", + "eslint-plugin-json": "^2.0.1", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-prettier": "^3.1.1", "eslint-plugin-react": "^7.17.0", - "husky": "^3.1.0", + "husky": "^4.0.10", "jest": "^24.9.0", - "lint-staged": "^10.0.0-1", + "lint-staged": "^10.0.0", "prettier": "^1.19.1", "pretty-quick": "^2.0.1" }, "husky": { "hooks": { - "pre-commit": "yarn format-staged && lint-staged" + "pre-commit": "yarn format-staged && yarn lint-staged" } }, "lint-staged": { - "*.js": "eslint" + "*.{js,json}": "eslint" } } diff --git a/pages/_app.js b/pages/_app.js index 39bcd60c70..10f9f70d27 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -9,16 +9,6 @@ Sentry.init({ }) class MyApp extends App { - static async getInitialProps({ Component, ctx }) { - let pageProps = {} - - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx) - } - - return { pageProps } - } - componentDidCatch(error, errorInfo) { Sentry.withScope(scope => { Object.keys(errorInfo).forEach(key => { diff --git a/pages/api/blog.js b/pages/api/blog.js new file mode 100644 index 0000000000..ce1d3f4e33 --- /dev/null +++ b/pages/api/blog.js @@ -0,0 +1,19 @@ +import fetch from 'isomorphic-fetch' + +export default async (_, res) => { + try { + const response = await fetch(`https://blog.dvc.org/api/posts.json`) + + if (response.status !== 200) { + res.status(502).json({ error: 'Unexpected response from Blog' }) + + return + } + + const data = await response.text() + + res.status(200).json(data) + } catch { + res.status(502) + } +} diff --git a/pages/api/comments.js b/pages/api/comments.js index 405901f132..1b131e8655 100644 --- a/pages/api/comments.js +++ b/pages/api/comments.js @@ -1,3 +1,5 @@ +/* eslint-env node */ + /* * This API endpoint is used by our blog to get comments count for the post, it * gets discuss.dvc.org topic URL as a param and returns comments count or @@ -7,17 +9,22 @@ * potential ability to cache comments count in the future. */ +import fetch from 'isomorphic-fetch' import Cors from 'micro-cors' -import request from 'request' +import NodeCache from 'node-cache' import { BLOG_URL, FORUM_URL } from '../../src/consts' +const cache = new NodeCache({ stdTTL: 900 }) + +const dev = process.env.NODE_ENV === 'development' + const cors = Cors({ allowedMethods: ['GET', 'HEAD'], origin: BLOG_URL }) -const getCommentCount = (req, res) => { +const getCommentCount = async (req, res) => { const { query: { url } } = req @@ -28,26 +35,42 @@ const getCommentCount = (req, res) => { return } - request(`${url}.json`, (error, response, body) => { - if (error || response.statusCode !== 200) { + if (cache.get(url) !== undefined) { + if (dev) console.log(`Using cache for ${url}`) + + res.status(200).json({ count: cache.get(url) }) + + return + } else { + if (dev) console.log(`Not using cache for ${url}`) + } + + try { + const response = await fetch(`${url}.json`) + + if (response.status !== 200) { res.status(502).json({ error: 'Unexpected response from Forum' }) return } - const json = JSON.parse(body) + const data = await response.json() - if (!json.posts_count) { + if (!data.posts_count) { res.status(502).json({ error: 'Unexpected payload from Forum' }) return } // post_count return all posts including topic itself - const count = json.posts_count - 1 + const count = data.posts_count - 1 + + cache.set(url, count) res.status(200).json({ count }) - }) + } catch { + res.status(404) + } } export default cors(getCommentCount) diff --git a/pages/api/discourse.js b/pages/api/discourse.js new file mode 100644 index 0000000000..ba5463c69a --- /dev/null +++ b/pages/api/discourse.js @@ -0,0 +1,51 @@ +/* eslint-env node */ + +import fetch from 'isomorphic-fetch' +import NodeCache from 'node-cache' + +import { FORUM_URL } from '../../src/consts' + +const cache = new NodeCache({ stdTTL: 900 }) + +const dev = process.env.NODE_ENV === 'development' + +export default async (_, res) => { + if (cache.get('topics')) { + if (dev) console.log('Using cache for "topics"') + + res.status(200).json(cache.get('topics')) + + return + } else { + if (dev) console.log('Not using cache for "topics"') + } + + try { + const response = await fetch(`${FORUM_URL}/latest.json?order=created`) + + if (response.status !== 200) { + res.status(502).json({ error: 'Unexpected response from Forum' }) + + return + } + + const data = await response.text() + + const { + topic_list: { topics: original_topics } + } = JSON.parse(data) + + const topics = original_topics.slice(0, 3).map(item => ({ + title: item.title, + comments: item.posts_count - 1, + date: item.last_posted_at, + url: `${FORUM_URL}/t/${item.slug}/${item.id}` + })) + + cache.set('topics', { topics }) + + res.status(200).json({ topics }) + } catch { + res.status(404) + } +} diff --git a/pages/api/github.js b/pages/api/github.js new file mode 100644 index 0000000000..1a7165b0fe --- /dev/null +++ b/pages/api/github.js @@ -0,0 +1,67 @@ +/* eslint-env node */ + +import { graphql } from '@octokit/graphql' +import NodeCache from 'node-cache' + +const cache = new NodeCache({ stdTTL: 900 }) + +const dev = process.env.NODE_ENV === 'development' + +export default async (_, res) => { + if (!process.env.GITHUB_TOKEN) { + res.status(200).json({ issues: [] }) + } else { + if (cache.get('issues')) { + if (dev) console.log('Using cache for "issues"') + + res.status(200).json({ issues: cache.get('issues') }) + } else { + console.log('Not using cache for "issues"') + } + + try { + const { repository } = await graphql( + ` + { + repository(owner: "iterative", name: "dvc") { + issues( + first: 3 + states: OPEN + orderBy: { field: CREATED_AT, direction: DESC } + ) { + edges { + node { + title + createdAt + url + comments { + totalCount + } + } + } + } + } + } + `, + { + headers: { + authorization: `token ${process.env.GITHUB_TOKEN}` + } + } + ) + + const issues = repository.issues.edges.map(({ node }) => ({ + title: node.title, + url: node.url, + comments: node.comments.totalCount, + date: node.createdAt + })) + + cache.set('issues', issues) + + res.status(200).json({ issues }) + } catch (e) { + res.status(404) + } + } +} diff --git a/pages/community.js b/pages/community.js new file mode 100644 index 0000000000..c4a2a88d98 --- /dev/null +++ b/pages/community.js @@ -0,0 +1,46 @@ +import React from 'react' +import Head from 'next/head' + +import { + getLatestIssues, + getLatestTopics, + getLatestPosts +} from '../src/utils/api' + +import Community from '../src/components/Community' + +import { META_BASE_TITLE } from '../src/consts' + +export default function CommunityPage(props) { + return ( + <> + + + + Community | {META_BASE_TITLE} + + + + ) +} + +CommunityPage.getInitialProps = async ({ req }) => { + const issues = await getLatestIssues(req) + const posts = await getLatestPosts(req) + const topics = await getLatestTopics(req) + + return { + issues, + posts, + topics + } +} diff --git a/pages/doc.js b/pages/doc.js index aacf29815a..0521bf3258 100644 --- a/pages/doc.js +++ b/pages/doc.js @@ -8,6 +8,7 @@ import Head from 'next/head' import Documentation from '../src/components/Documentation' import { getItemByPath } from '../src/utils/sidebar' +import { makeAbsoluteURL } from '../src/utils/api' import { META_BASE_TITLE } from '../src/consts' @@ -43,11 +44,8 @@ DocumentationPage.getInitialProps = async ({ asPath, req }) => { } } - const host = req ? req.headers['host'] : window.location.host - const protocol = host.indexOf('localhost') > -1 ? 'http:' : 'https:' - try { - const res = await fetch(`${protocol}//${host}${item.source}`) + const res = await fetch(makeAbsoluteURL(req, item.source)) if (res.status !== 200) { return { @@ -62,7 +60,6 @@ DocumentationPage.getInitialProps = async ({ asPath, req }) => { markdown } } catch { - console.error(`Can't fetch ${protocol}//${host}${item.source}`) window.location.reload() } } diff --git a/public/static/fonts/fonts.css b/public/static/fonts/fonts.css index df1b2fdea5..d7ceae837b 100644 --- a/public/static/fonts/fonts.css +++ b/public/static/fonts/fonts.css @@ -5,6 +5,13 @@ font-weight: normal; } +@font-face { + font-family: BrandonGrotesqueBlack; + src: url('/static/fonts/Brandon_blk.otf'); + font-style: normal; + font-weight: normal; +} + @font-face { font-family: BrandonGrotesqueBold; src: url('/static/fonts/Brandon_bld.otf'); diff --git a/public/static/img/community/banner-mobile.png b/public/static/img/community/banner-mobile.png new file mode 100644 index 0000000000..75c4edd41f Binary files /dev/null and b/public/static/img/community/banner-mobile.png differ diff --git a/public/static/img/community/banner.png b/public/static/img/community/banner.png new file mode 100644 index 0000000000..1a1cab5c55 Binary files /dev/null and b/public/static/img/community/banner.png differ diff --git a/public/static/img/community/contribute.svg b/public/static/img/community/contribute.svg new file mode 100644 index 0000000000..a062d24710 --- /dev/null +++ b/public/static/img/community/contribute.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/contribute_bg.jpg b/public/static/img/community/contribute_bg.jpg new file mode 100644 index 0000000000..b29aee8b61 Binary files /dev/null and b/public/static/img/community/contribute_bg.jpg differ diff --git a/public/static/img/community/discord.svg b/public/static/img/community/discord.svg new file mode 100644 index 0000000000..c8c8672f38 --- /dev/null +++ b/public/static/img/community/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/discourse.svg b/public/static/img/community/discourse.svg new file mode 100644 index 0000000000..d0dfb79741 --- /dev/null +++ b/public/static/img/community/discourse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/event-placeholder.svg b/public/static/img/community/event-placeholder.svg new file mode 100644 index 0000000000..0a8f81edcb --- /dev/null +++ b/public/static/img/community/event-placeholder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/events.svg b/public/static/img/community/events.svg new file mode 100644 index 0000000000..61f89b6b3d --- /dev/null +++ b/public/static/img/community/events.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/events/divops.jpg b/public/static/img/community/events/divops.jpg new file mode 100644 index 0000000000..52f70f88f3 Binary files /dev/null and b/public/static/img/community/events/divops.jpg differ diff --git a/public/static/img/community/events/mlprague.jpg b/public/static/img/community/events/mlprague.jpg new file mode 100644 index 0000000000..db1ddd00c2 Binary files /dev/null and b/public/static/img/community/events/mlprague.jpg differ diff --git a/public/static/img/community/github.svg b/public/static/img/community/github.svg new file mode 100644 index 0000000000..fb15a78330 --- /dev/null +++ b/public/static/img/community/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/icon-community.svg b/public/static/img/community/icon-community.svg new file mode 100644 index 0000000000..0cc21f8ab2 --- /dev/null +++ b/public/static/img/community/icon-community.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/icon-contribute.svg b/public/static/img/community/icon-contribute.svg new file mode 100644 index 0000000000..488e9148ef --- /dev/null +++ b/public/static/img/community/icon-contribute.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/icon-discord.svg b/public/static/img/community/icon-discord.svg new file mode 100644 index 0000000000..c26d05bb39 --- /dev/null +++ b/public/static/img/community/icon-discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/icon-events.svg b/public/static/img/community/icon-events.svg new file mode 100644 index 0000000000..28de0b192f --- /dev/null +++ b/public/static/img/community/icon-events.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/icon-github.svg b/public/static/img/community/icon-github.svg new file mode 100644 index 0000000000..d7687bda5f --- /dev/null +++ b/public/static/img/community/icon-github.svg @@ -0,0 +1 @@ + diff --git a/public/static/img/community/icon-learn.svg b/public/static/img/community/icon-learn.svg new file mode 100644 index 0000000000..71d6f40a69 --- /dev/null +++ b/public/static/img/community/icon-learn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/static/img/community/icon-mail.svg b/public/static/img/community/icon-mail.svg new file mode 100644 index 0000000000..569ad4ab5b --- /dev/null +++ b/public/static/img/community/icon-mail.svg @@ -0,0 +1 @@ + diff --git a/public/static/img/community/icon-twitter.svg b/public/static/img/community/icon-twitter.svg new file mode 100644 index 0000000000..450f448afd --- /dev/null +++ b/public/static/img/community/icon-twitter.svg @@ -0,0 +1 @@ + diff --git a/public/static/img/community/learn.svg b/public/static/img/community/learn.svg new file mode 100644 index 0000000000..846d20acac --- /dev/null +++ b/public/static/img/community/learn.svg @@ -0,0 +1 @@ + diff --git a/public/static/img/community/learn_bg.jpg b/public/static/img/community/learn_bg.jpg new file mode 100644 index 0000000000..f71b4f045a Binary files /dev/null and b/public/static/img/community/learn_bg.jpg differ diff --git a/public/static/img/community/meet.svg b/public/static/img/community/meet.svg new file mode 100644 index 0000000000..a424990c60 --- /dev/null +++ b/public/static/img/community/meet.svg @@ -0,0 +1 @@ + diff --git a/public/static/img/community/meet_bg.jpg b/public/static/img/community/meet_bg.jpg new file mode 100644 index 0000000000..7423eb31be Binary files /dev/null and b/public/static/img/community/meet_bg.jpg differ diff --git a/public/static/img/community/menu.png b/public/static/img/community/menu.png new file mode 100644 index 0000000000..a472453df6 Binary files /dev/null and b/public/static/img/community/menu.png differ diff --git a/public/static/img/community/ugc/codecentric.png b/public/static/img/community/ugc/codecentric.png new file mode 100644 index 0000000000..92c6b0aac0 Binary files /dev/null and b/public/static/img/community/ugc/codecentric.png differ diff --git a/public/static/img/community/ugc/donuts.png b/public/static/img/community/ugc/donuts.png new file mode 100644 index 0000000000..35a2c3dbb8 Binary files /dev/null and b/public/static/img/community/ugc/donuts.png differ diff --git a/public/static/img/community/ugc/fowler_icon.ico b/public/static/img/community/ugc/fowler_icon.ico new file mode 100644 index 0000000000..782a130df1 Binary files /dev/null and b/public/static/img/community/ugc/fowler_icon.ico differ diff --git a/public/static/img/community/ugc/medium_logo.png b/public/static/img/community/ugc/medium_logo.png new file mode 100644 index 0000000000..1f7a84f1c1 Binary files /dev/null and b/public/static/img/community/ugc/medium_logo.png differ diff --git a/public/static/img/community/ugc/ml-axis-of-change.png b/public/static/img/community/ugc/ml-axis-of-change.png new file mode 100644 index 0000000000..ec03c75a4b Binary files /dev/null and b/public/static/img/community/ugc/ml-axis-of-change.png differ diff --git a/public/static/img/community/ugc/tds_logo.png b/public/static/img/community/ugc/tds_logo.png new file mode 100644 index 0000000000..7d2362fec7 Binary files /dev/null and b/public/static/img/community/ugc/tds_logo.png differ diff --git a/src/components/Community/Block/index.js b/src/components/Community/Block/index.js new file mode 100644 index 0000000000..3aea85cd9d --- /dev/null +++ b/src/components/Community/Block/index.js @@ -0,0 +1,28 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import { Action, Content, Icon, Header, Wrapper } from './styles' + +export default function Block({ action, children, icon, title }) { + const hasAction = !!action + + return ( + + {title && ( +
+ {title} + {icon && } +
+ )} + {children} + {hasAction && {action}} +
+ ) +} + +Block.propTypes = { + action: PropTypes.node, + children: PropTypes.node, + icon: PropTypes.string, + title: PropTypes.node +} diff --git a/src/components/Community/Block/styles.js b/src/components/Community/Block/styles.js new file mode 100644 index 0000000000..75bfa96a52 --- /dev/null +++ b/src/components/Community/Block/styles.js @@ -0,0 +1,52 @@ +import styled from 'styled-components' +import { media } from '../../../styles' + +export const Action = styled.div` + margin-top: 20px; +` + +export const Content = styled.div` + flex-grow: 1; + font-size: 16px; + line-height: 24px; + color: #838d93; +` + +export const Header = styled.div` + display: flex; + justify-content: space-between; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #d8dfe3; + font-family: BrandonGrotesqueMed; + font-size: 24px; + line-height: 34px; + color: #24292e; + + ${media.tablet` + margin: 0; + border: none; + font-size: 20px; + line-height: 30px; + `} +` + +export const Icon = styled.img` + margin: 0 -2px 0 0; + + ${media.tablet` + display: none; + `} +` + +export const Wrapper = styled.div` + box-sizing: border-box; + position: relative; + display: flex; + flex-direction: column; + justify-content: stretch; + width: 100%; + padding: 10px 20px 20px; + border-radius: 20px; + background: #eef4f8; +` diff --git a/src/components/Community/Button/index.js b/src/components/Community/Button/index.js new file mode 100644 index 0000000000..97df73f6ff --- /dev/null +++ b/src/components/Community/Button/index.js @@ -0,0 +1,26 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import { Wrapper } from './styles' + +function CommunityButton({ theme, children, forwardedRef, ...props }) { + return ( + + {children} + + ) +} + +CommunityButton.propTypes = { + children: PropTypes.node, + theme: PropTypes.shape({ + backgroundColor: PropTypes.string, + color: PropTypes.string + }), + forwardedRef: PropTypes.func +} + +// eslint-disable-next-line react/display-name +export default React.forwardRef((props, ref) => ( + +)) diff --git a/src/components/Community/Button/styles.js b/src/components/Community/Button/styles.js new file mode 100644 index 0000000000..dd7fe14fa9 --- /dev/null +++ b/src/components/Community/Button/styles.js @@ -0,0 +1,19 @@ +import styled from 'styled-components' + +export const Wrapper = styled.a` + display: block; + height: 38px; + border-radius: 4px; + font-size: 16px; + font-family: BrandonGrotesqueMed; + line-height: 38px; + text-decoration: none; + text-align: center; + color: ${({ color }) => (color ? color : '#999')}; + background-color: ${({ backgroundColor }) => + backgroundColor ? backgroundColor : '#ddd'}; + + &:hover { + opacity: 0.7; + } +` diff --git a/src/components/Community/Contribute/index.js b/src/components/Community/Contribute/index.js new file mode 100644 index 0000000000..46a6c3480b --- /dev/null +++ b/src/components/Community/Contribute/index.js @@ -0,0 +1,121 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import { logEvent } from '../../../utils/ga' + +import CommunityButton from '../Button' +import CommunityBlock from '../Block' +import CommunitySection from '../Section' + +import data from '../data' + +import { Item, Items, Wrapper } from '../styles' + +const { description, mobileDescription, title } = data.section.contribute + +const logPR = () => logEvent('community', 'contribute-pr') +const logBlogpost = () => logEvent('community', 'contribute-blogpost') +const logTalk = () => logEvent('community', 'contribute-talk') +const logAmbassador = () => logEvent('community', 'contribute-ambassador') + +export default function CommunityContribute({ theme }) { + return ( + + + + + + Go to Github + + } + > + Become DVC contributor and let us build something great together. + + + + + Let’s talk! + + } + > + Have something cool on your mind? Suggest a text and we'll + publish it in our blog. + + + + + Let’s talk! + + } + > + We support speakers all over the world and help with preparation, + speaker training and expenses. + + + + + Let’s talk! + + } + > + Get perks and benefits for significant contributions, creating + content or hosting meetups. + + + + + + ) +} + +CommunityContribute.propTypes = { + theme: PropTypes.shape({ + backgroundColor: PropTypes.string, + color: PropTypes.string + }) +} diff --git a/src/components/Community/Events/index.js b/src/components/Community/Events/index.js new file mode 100644 index 0000000000..5f90be6b0f --- /dev/null +++ b/src/components/Community/Events/index.js @@ -0,0 +1,125 @@ +import React, { useCallback } from 'react' +import PropTypes from 'prop-types' +import format from 'date-fns/format' + +import { logEvent } from '../../../utils/ga' + +import CommunityBlock from '../Block' +import CommunityButton from '../Button' +import CommunitySection from '../Section' + +import data from '../data' + +import { Item, Items, Line, Link, Wrapper } from '../styles' + +import { Image, ImageWrapper, Meta } from './styles' + +const { description, mobileDescription, title } = data.section.events +const { events } = data + +const modifiedEvents = events.length > 3 ? events.slice(0, 3) : events +const eventPlaceholders = new Array(3 - modifiedEvents.length).fill(Item) + +function CommunityEvent({ + theme, + city, + date, + description, + pictureUrl, + title, + url +}) { + const logEventClick = useCallback( + () => logEvent('community', 'event', title), + [title] + ) + + return ( + + + Event Info + + } + > + + + + + + {title} + + + {description} + + {city}, {format(new Date(date), 'MMMM d')} + + + + + ) +} + +CommunityEvent.propTypes = { + theme: PropTypes.object, + city: PropTypes.string, + date: PropTypes.string, + description: PropTypes.string, + pictureUrl: PropTypes.string, + title: PropTypes.string, + url: PropTypes.string +} + +export default function CommunityEvents({ theme }) { + if (!events.length) return '' + + return ( + + + + {modifiedEvents.map(event => ( + + ))} + {eventPlaceholders.map((Component, key) => ( + + ))} + + + + ) +} + +CommunityEvents.propTypes = { + theme: PropTypes.shape({ + backgroundColor: PropTypes.string, + color: PropTypes.string + }) +} diff --git a/src/components/Community/Events/styles.js b/src/components/Community/Events/styles.js new file mode 100644 index 0000000000..bea64fab3a --- /dev/null +++ b/src/components/Community/Events/styles.js @@ -0,0 +1,17 @@ +import styled from 'styled-components' + +import { Meta as ParentMeta } from '../styles' + +export const Image = styled.img` + width: 100%; + border-radius: 20px 20px 0 0; +` + +export const ImageWrapper = styled.a` + display: block; + margin: -10px -20px 0; +` + +export const Meta = styled(ParentMeta)` + margin-top: 10px; +` diff --git a/src/components/Community/Hero/index.js b/src/components/Community/Hero/index.js new file mode 100644 index 0000000000..52b5c99c71 --- /dev/null +++ b/src/components/Community/Hero/index.js @@ -0,0 +1,32 @@ +import React from 'react' + +import { logEvent } from '../../../utils/ga' + +import { OnlyDesktop, OnlyMobile } from '../../../styles' +import { Link, Picture, Wrapper } from './styles' + +import data from '../data.json' + +const logHero = () => logEvent('community', 'hero') + +export default function CommunityHero() { + if (!data.hero) return '' + + return ( + + + + + + + + + + + ) +} diff --git a/src/components/Community/Hero/styles.js b/src/components/Community/Hero/styles.js new file mode 100644 index 0000000000..3f007e8291 --- /dev/null +++ b/src/components/Community/Hero/styles.js @@ -0,0 +1,23 @@ +import styled from 'styled-components' +import { media } from '../../../styles' + +export const Wrapper = styled.div` + margin: 0 auto; + padding: 40px 0 20px; + max-width: 1000px; + + ${media.tablet` + padding: 0 0 10px; + `} +` + +export const Link = styled.a` + &:hover { + opacity: 0.7; + } +` + +export const Picture = styled.img` + max-width: 1000px; + width: 100%; +` diff --git a/src/components/Community/Learn/index.js b/src/components/Community/Learn/index.js new file mode 100644 index 0000000000..0b34b74060 --- /dev/null +++ b/src/components/Community/Learn/index.js @@ -0,0 +1,294 @@ +import React, { useCallback, useEffect, useState } from 'react' +import PropTypes from 'prop-types' +import format from 'date-fns/format' + +import LocalLink from '../../LocalLink' + +import { logEvent } from '../../../utils/ga' + +import CommunityBlock from '../Block' +import CommunityButton from '../Button' +import CommunitySection from '../Section' + +import { pluralizeComments } from '../../../utils/i18n' + +import { + Comments, + HeaderLink, + ImageLine, + Item, + Items, + Line, + Link, + Meta, + NbspWrapper, + Placeholder, + TextWrapper, + Wrapper +} from '../styles' + +import { Image } from './styles' + +import data from '../data' + +const { description, mobileDescription, title } = data.section.learn +const { documentation, userContent } = data + +const logPostAll = () => logEvent('community', 'blog', 'all') +const logDocumentationAll = () => logEvent('community', 'documentation', 'all') + +function CommunityBlogPost({ + url, + title, + date, + color, + commentsUrl, + pictureUrl +}) { + const [count, setCount] = useState() + const loaded = count !== undefined + const logPost = useCallback(() => logEvent('community', 'blog', title), [ + title + ]) + + useEffect(() => { + if (commentsUrl) { + fetch(`/api/comments?url=${commentsUrl}`) + .then(result => result.json()) + .then(data => setCount(data.count)) + } + }, []) + + return ( + + {pictureUrl && ( + + + + )} + + + {title} + + + {loaded && ( + <> + + {pluralizeComments(count)} + + {' • '} + + )} + {format(new Date(date), 'MMM, d')} + + + + ) +} + +CommunityBlogPost.propTypes = { + color: PropTypes.string, + commentsUrl: PropTypes.string, + pictureUrl: PropTypes.string, + date: PropTypes.string, + title: PropTypes.string, + url: PropTypes.string +} + +function CommunityUserContent({ url, title, author, date, color, pictureUrl }) { + const logUserContent = useCallback( + () => logEvent('community', 'user-content', title), + [title] + ) + + return ( + + {pictureUrl && ( + + + + )} + + + {title} + + + {author} •{' '} + {format(new Date(date), 'MMM, d')} + + + + ) +} + +CommunityUserContent.propTypes = { + author: PropTypes.string, + color: PropTypes.string, + date: PropTypes.string, + pictureUrl: PropTypes.string, + title: PropTypes.string, + url: PropTypes.string +} + +function CommunityDocumentation({ url, title, description, color }) { + const logDocumentation = useCallback( + () => logEvent('community', 'documentation', title), + [title] + ) + + return ( + + + {title} + + {description} + + ) +} + +CommunityDocumentation.propTypes = { + color: PropTypes.string, + description: PropTypes.string, + title: PropTypes.string, + url: PropTypes.string +} + +export default function CommunityLearn({ posts, theme }) { + return ( + + + + + + Documentation + + } + action={ + + See all docs + + } + > + {documentation.map(documentation => ( + + ))} + + + + + DVC Blog + + } + action={ + posts.length && ( + + See all Posts + + ) + } + > + {posts.length ? ( + posts.map(post => ( + + )) + ) : ( + Blog is unavailable right now + )} + + + + + {userContent.map(userContent => ( + + ))} + + + + + + ) +} + +CommunityLearn.propTypes = { + posts: PropTypes.array, + theme: PropTypes.shape({ + backgroundColor: PropTypes.string, + color: PropTypes.string + }) +} diff --git a/src/components/Community/Learn/styles.js b/src/components/Community/Learn/styles.js new file mode 100644 index 0000000000..6479afc7fd --- /dev/null +++ b/src/components/Community/Learn/styles.js @@ -0,0 +1,9 @@ +import styled from 'styled-components' + +export const Image = styled.img` + width: 80px; + height: 80px; + border-radius: 5px; + float: left; + margin: 5px 10px 0 0; +` diff --git a/src/components/Community/Meet/index.js b/src/components/Community/Meet/index.js new file mode 100644 index 0000000000..bcddb9c897 --- /dev/null +++ b/src/components/Community/Meet/index.js @@ -0,0 +1,260 @@ +import React, { useCallback } from 'react' +import PropTypes from 'prop-types' +import formatDistanceToNow from 'date-fns/formatDistanceToNow' + +import CommunityBlock from '../Block' +import CommunityButton from '../Button' +import CommunitySection from '../Section' + +import { pluralizeComments } from '../../../utils/i18n' +import { logEvent } from '../../../utils/ga' + +import data from '../data' + +const { description, mobileDescription, title } = data.section.meet + +import { Stats, StatLabel, StatLine, StatValue } from './styles' + +import { + Comments, + HeaderLink, + Item, + Items, + Line, + Link, + Meta, + Placeholder, + Wrapper +} from '../styles' + +const logIssueAll = () => logEvent('community', 'issue', 'all') +const logTopicAll = () => logEvent('community', 'topic', 'all') +const logDiscord = () => logEvent('community', 'discord') + +function CommunityTopic({ url, title, date, comments, color }) { + const logTopic = useCallback(() => logEvent('community', 'forum', title), [ + title + ]) + + return ( + + + {title} + + + + {pluralizeComments(comments)} + {' '} + • last activity {formatDistanceToNow(new Date(date), 'MMM, d') + ' '} + ago + + + ) +} + +CommunityTopic.propTypes = { + url: PropTypes.string, + title: PropTypes.string, + date: PropTypes.string, + comments: PropTypes.number, + color: PropTypes.string +} + +function CommunityIssue({ url, title, date, comments, color }) { + const logIssue = useCallback(() => logEvent('community', 'issue', title), [ + title + ]) + + return ( + + + {title} + + + + {pluralizeComments(comments)} + + {' •'} opened {formatDistanceToNow(new Date(date), 'MMM, d')} ago + + + ) +} + +CommunityIssue.propTypes = { + url: PropTypes.string, + title: PropTypes.string, + date: PropTypes.string, + comments: PropTypes.number, + color: PropTypes.string +} + +export default function CommunityMeet({ issues, theme, topics }) { + return ( + + + + + + Join the Dev Chat + + } + action={ + + Open Chat + + } + icon="/static/img/community/discord.svg" + > + + Need urgent help? Ask advice from experienced developers online + + + + {data.stats.users} + registered developers + + + {data.stats.messages} + messages posted over the past month + + + + + + + Ask a Question + + } + action={ + topics.length && ( + + Read All Topics + + ) + } + icon="/static/img/community/discourse.svg" + > + {topics.length ? ( + topics.map(topic => ( + + )) + ) : ( + Forum is unavailable right now + )} + + + + + Post an Issue + + } + action={ + issues.length && ( + + Read All Issues + + ) + } + icon="/static/img/community/github.svg" + > + {issues.length ? ( + issues.map(issue => ( + + )) + ) : ( + Github is unavailable right now + )} + + + + + + ) +} + +CommunityMeet.propTypes = { + issues: PropTypes.array, + theme: PropTypes.shape({ + backgroundColor: PropTypes.string, + color: PropTypes.string + }), + topics: PropTypes.array +} diff --git a/src/components/Community/Meet/styles.js b/src/components/Community/Meet/styles.js new file mode 100644 index 0000000000..d802178770 --- /dev/null +++ b/src/components/Community/Meet/styles.js @@ -0,0 +1,50 @@ +import styled from 'styled-components' +import { media } from '../../../styles' + +export const Stats = styled.div` + ${media.tablet` + display: flex; + flex-direction: row; + margin-top: 15px; + `} +` + +export const StatLabel = styled.div` + font-size: 16px; + line-height: 20px; + color: #838d93; +` + +export const StatLine = styled.div` + display: flex; + align-items: center; + margin-top: 25px; + + ${media.tablet` + flex-direction: column; + flex-basis: 50%; + align-items: flex-start; + margin: 0; + + & + & { + margin-left: 25px; + } + `} +` + +export const StatValue = styled.div` + flex: 0 0 124px; + margin-right: 20px; + font-size: 40px; + font-family: BrandonGrotesqueBlack; + text-align: right; + line-height: 50px; + color: #24292e; + + ${media.tablet` + flex: initial; + font-size: 30px; + font-family: BrandonGrotesque; + line-height: 40px; + `} +` diff --git a/src/components/Community/Section/index.js b/src/components/Community/Section/index.js new file mode 100644 index 0000000000..8cc4871144 --- /dev/null +++ b/src/components/Community/Section/index.js @@ -0,0 +1,95 @@ +import PropTypes from 'prop-types' +import React, { useCallback, useEffect, useState } from 'react' +import { Collapse } from 'react-collapse' +import { useWindowSize } from 'react-use' + +import Router from 'next/router' + +import { + DesktopDescription, + Header, + Icon, + MobileDescription, + Picture, + Title, + Wrapper +} from './styles' + +import { sizes } from '../../../styles' + +export default function CommunitySection({ + anchor, + background, + color, + contentVisible = false, + children, + description, + icon, + mobileDescription, + title +}) { + const [isTablet, setIsTablet] = useState(false) + const [isContentVisible, setIsContentVisible] = useState(contentVisible) + const toggleVisibility = useCallback( + () => setIsContentVisible(!isContentVisible), + [isContentVisible] + ) + + const { width } = useWindowSize() + + useEffect(() => { + const updateVisibility = () => { + const { hash } = window.location + + if (anchor && hash === `#${anchor}`) { + setIsContentVisible(true) + } + } + + updateVisibility() + + Router.events.on('hashChangeComplete', updateVisibility) + + return () => { + Router.events.off('hashChangeComplete', updateVisibility) + } + }, []) + + useEffect(() => setIsTablet(width <= sizes.tablet), [width]) + + return ( + +
+ +
+ {title} + {description} + {mobileDescription} +
+
+ {background && } + + {isTablet ? ( + {children} + ) : ( + children + )} +
+ ) +} + +CommunitySection.propTypes = { + anchor: PropTypes.string, + background: PropTypes.string, + color: PropTypes.string, + contentVisible: PropTypes.bool, + children: PropTypes.node, + description: PropTypes.string, + icon: PropTypes.string, + mobileDescription: PropTypes.string, + title: PropTypes.string +} diff --git a/src/components/Community/Section/styles.js b/src/components/Community/Section/styles.js new file mode 100644 index 0000000000..8580487ab0 --- /dev/null +++ b/src/components/Community/Section/styles.js @@ -0,0 +1,105 @@ +import styled from 'styled-components' +import { media } from '../../../styles' + +export const Wrapper = styled.div` + position: relative; + margin: 50px -50px; + padding: ${({ hasBg }) => (hasBg ? '0 50px 260px' : '0 50px')}; + + ${media.desktop` + padding: ${({ hasBg }) => (hasBg ? '0 65px 260px' : '0 65px')}; + `} + + ${media.tablet` + margin: 0; + padding: 20px 0; + `} + + .ReactCollapse--collapse { + transition: height 500ms; + } +` + +export const Header = styled.div` + display: flex; + color: ${({ color }) => color}; +` + +export const Title = styled.div` + font-size: 40px; + font-family: BrandonGrotesqueMed; + line-height: 60px; + + ${media.tablet` + font-size: 30px; + line-height: 35px; + cursor: pointer; + + &::after { + content: ''; + + position: relative; + display: inline-block; + width: 0; + height: 0; + margin-left: 10px; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 12px solid currentColor; + transition: transform 200ms; + + ${({ isContentVisible }) => + isContentVisible && + `transform: rotate(-180deg) + `} + + } + `} +` + +export const Icon = styled.img` + width: 50px; + height: 50px; + margin: 5px 10px 0 0; +` + +const Description = styled.div` + max-width: 600px; + font-size: 16px; + line-height: 24px; + color: #838d93; + + ${media.tablet` + display: none; + `}; +` + +export const DesktopDescription = styled(Description)` + max-width: 600px; + + ${media.tablet` + display: none; + `}; +` + +export const MobileDescription = styled(Description)` + display: none; + + ${media.tablet` + display: block; + `}; +` + +export const Picture = styled.img` + z-index: -1; + position: absolute; + bottom: 0; + left: 50%; + width: 1100px; + height: 450px; + margin-left: -550px; + + ${media.tablet` + display: none; + `} +` diff --git a/src/components/Community/data.json b/src/components/Community/data.json new file mode 100644 index 0000000000..ca3d264cac --- /dev/null +++ b/src/components/Community/data.json @@ -0,0 +1,98 @@ +{ + "hero": { + "pictureDesktop": "/static/img/community/banner.png", + "pictureMobile": "/static/img/community/banner-mobile.png", + "url": "https://www.mlprague.com/#schedule-saturday" + }, + "section": { + "contribute": { + "title": "Contribute", + "description": "DVC is 100% open-source and we welcome contributions of all sorts to our code and docs.", + "mobileDescription": "PRs, Blog Posts, Talks, Ambassadors" + }, + "events": { + "title": "Events", + "description": "Conferences, meetups and talks to learn more about DVC or meet in person.", + "mobileDescription": "Come to hear more about DVC" + }, + "learn": { + "title": "Learn", + "description": "Every month we are sharing here our news, findings, interesting reads, community takeaways, and everything along the way.", + "mobileDescription": "Docs, Blog, User Content" + }, + "meet": { + "title": "Community", + "description": "Ask questions, find answers, share your ideas and connect with DVC community.", + "mobileDescription": "Chat, Forum, Github" + } + }, + "documentation": [ + { + "url": "/doc/get-started", + "title": "Get started", + "description": "Learn how to set up your project" + }, + { + "url": "/doc/tutorials", + "title": "Tutorials", + "description": "Learn how to solve most typical tasks" + }, + { + "url": "/doc/changelog", + "title": "Changelog", + "description": "See what's new in DVC core " + } + ], + "userContent": [ + { + "url": "https://blog.codecentric.de/en/2020/01/remote-training-gitlab-ci-dvc/", + "title": "Remote training with GitLab-CI and DVC", + "author": "Marcel Mikl and Bert Besser", + "date": "2020-01-28", + "pictureUrl": "/static/img/community/ugc/codecentric.png" + }, + { + "url": "https://martinfowler.com/articles/cd4ml.html", + "title": "Continuous Delivery for Machine Learning", + "author": "Danilo Sato, Arif Wider and Christoph Windheuser", + "date": "2019-09-20", + "pictureUrl": "/static/img/community/ugc/fowler_icon.ico" + }, + { + "url": "https://towardsdatascience.com/mlops-reducing-the-technical-debt-of-machine-learning-dac528ef39de", + "title": "MLOps: Reducing the Technical Debt of Machine Learning", + "author": "Saurav Chakravorty", + "date": "2019-12-21", + "pictureUrl": "/static/img/community/ugc/tds_logo.png" + } + ], + "events": [ + { + "url": "https://www.widsconference.org/", + "title": "Women in Data Science San Diego", + "description": "Elle O'Brien is talking about data catalogs and feature stores.", + "city": "San Diego", + "date": "2020-05-10" + }, + { + "url": "https://www.mlprague.com/#schedule-saturday", + "title": "Machine Learning Prague 2020", + "description": "Paweł Redzyński will talk about open source tools for versioning machine learning projects", + "city": "Prague", + "date": "2020-03-20", + "pictureUrl": "/static/img/community/events/mlprague.jpg" + }, + { + "url": "https://divops.org", + "title": "DivOps", + "description": "Elle O'Brien is talking about open source software in the growing field of MLOps.", + "city": "Remote", + "date": "2020-03-25", + "pictureUrl": "/static/img/community/events/divops.jpg" + } + ], + "stats": { + "users": "1380+", + "messages": "50K+" + } +} diff --git a/src/components/Community/index.js b/src/components/Community/index.js new file mode 100644 index 0000000000..b73c2be15a --- /dev/null +++ b/src/components/Community/index.js @@ -0,0 +1,40 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Page from '../Page' +import Subscribe from '../Subscribe' + +import CommunityContribute from './Contribute' +import CommunityEvents from './Events' +import CommunityHero from './Hero' +import CommunityLearn from './Learn' +import CommunityMeet from './Meet' + +import { PageWrapper } from './styles' + +const themes = { + green: { backgroundColor: '#C2E6EE', color: '#13ADC7' }, + orange: { backgroundColor: '#EFD8D1', color: '#F46737' }, + purple: { backgroundColor: '#DCD6F1', color: '#955DD6' } +} + +export default function Community({ issues, posts, topics }) { + return ( + + + + + + + + + + + ) +} + +Community.propTypes = { + issues: PropTypes.array, + posts: PropTypes.array, + topics: PropTypes.array +} diff --git a/src/components/Community/styles.js b/src/components/Community/styles.js new file mode 100644 index 0000000000..8515ab1524 --- /dev/null +++ b/src/components/Community/styles.js @@ -0,0 +1,122 @@ +import styled from 'styled-components' +import { media } from '../../styles' + +export const Comments = styled.a` + text-decoration: none; + color: inherit; + + &:hover { + opacity: 0.7; + } +` + +export const Item = styled.div` + display: flex; + flex-grow: 1; + flex-basis: 0; + align-items: stretch; + + & + & { + margin-left: 30px; + + ${media.desktop` + margin-left: 15px; + `} + + ${media.tablet` + display: block; + margin: 15px 0 0 0; + `} + } +` + +export const Items = styled.div` + display: flex; + align-items: stretch; + padding-top: 40px; + + ${media.phablet` + padding-top: 20px; + `} + + ${media.tablet` + display: block; + `} +` + +export const Line = styled.div` + overflow: hidden; + + & + & { + margin-top: 20px; + } +` +export const ImageLine = styled(Line)` + display: flex; +` + +export const Link = styled.a` + font-size: ${({ large }) => (large ? '24px' : '16px')}; + font-family: BrandonGrotesqueBold; + line-height: ${({ large }) => (large ? '34px' : '18px')}; + text-decoration: none; + word-break: break-word; + overflow-wrap: anywhere; + color: ${({ color }) => color}; + + &:hover { + opacity: 0.7; + } +` + +export const Meta = styled.div` + line-height: 20px; +` + +export const Placeholder = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 100%; +` + +export const Wrapper = styled.div` + max-width: 1000px; + margin: 0 auto; + + ${media.tablet` + margin: 0 15px; + + & + & { + border-top: 1px solid #e6e8e9; + } + `} +` + +export const TextWrapper = styled.div`` + +export const NbspWrapper = styled.span` + white-space: nowrap; +` + +export const HeaderLink = styled.a` + font-family: BrandonGrotesqueMed; + font-size: 24px; + line-height: 34px; + color: #24292e; + text-decoration: none; + + &:hover { + opacity: 0.7; + } +` + +export const PageWrapper = styled.div` + ${media.tablet` + padding-bottom: 30px; + `} + + ${media.phablet` + padding-bottom: 0; + `} +` diff --git a/src/components/Documentation/Markdown/index.js b/src/components/Documentation/Markdown/index.js index 299613c5c5..3ea4ab591c 100644 --- a/src/components/Documentation/Markdown/index.js +++ b/src/components/Documentation/Markdown/index.js @@ -145,7 +145,7 @@ const Link = ({ children, href, ...props }) => { externalLink && children && typeof children[0].props.children === 'string' const modifiedProps = externalLink - ? { ...props, target: '_blank', rel: 'noopener nofollow' } + ? { ...props, target: '_blank', rel: 'noreferrer noopener' } : props if (showIcon) { @@ -227,7 +227,7 @@ export default class Markdown extends React.PureComponent { )} - + Edit on GitHub {icons[k] && } diff --git a/src/components/Footer/index.js b/src/components/Footer/index.js index 5bde6af5f3..38f8443f6d 100644 --- a/src/components/Footer/index.js +++ b/src/components/Footer/index.js @@ -63,9 +63,9 @@ export default function Footer(props) { Get started - - Chat - + + Community + Documentation diff --git a/src/components/HamburgerMenu/index.js b/src/components/HamburgerMenu/index.js index d2f81b9cda..44d0ea2f53 100644 --- a/src/components/HamburgerMenu/index.js +++ b/src/components/HamburgerMenu/index.js @@ -1,5 +1,4 @@ -import React, { Component } from 'react' -import PropTypes from 'prop-types' +import React, { useCallback, useState } from 'react' import Hamburger from '../Hamburger' import LocalLink from '../LocalLink' @@ -8,181 +7,169 @@ import { logEvent } from '../../utils/ga' import { Button, - Column, - Columns, - Heading, + Image, + ImageComment, + ImageLink, Link, - Links, + LinkButton, Logo, Menu, Section, + Subsection, Top, Wrapper } from './styles' -const SocialLink = ({ src, href, click, children }) => ( - - {children} - -) +export default function HamburgerMenu() { + const [menu, setMenu] = useState(false) + const [clicked, setClicked] = useState(false) -SocialLink.propTypes = { - src: PropTypes.string.isRequired, - href: PropTypes.string.isRequired, - click: PropTypes.func.isRequired, - children: PropTypes.node.isRequired -} - -export default class HamburgerMenu extends Component { - state = { - menu: false, - clicked: false - } - - toggleMobileMenu = () => { - if (!this.state.clicked) { + const toggleMobileMenu = useCallback(() => { + if (clicked) { logEvent('hamburger', 'open') } - this.setState(prevState => ({ - menu: !prevState.menu, - clicked: false - })) - } - - close = () => - this.setState({ - menu: false - }) - - itemClick = item => () => { - this.close() - logEvent('hamburger', item) - } - scrollToTop = () => { - window.scrollTo(0, 0) - } + setMenu(!menu) + setClicked(false) + }, [clicked, menu]) - render() { - const { menu } = this.state + const close = useCallback(() => setMenu(false), []) - return ( -
- + const itemClick = useCallback( + item => () => { + close() + logEvent('hamburger', item) + }, + [] + ) - - -
- - - dvc.org - - + return ( +
+ - - - Product - - - Overview - - - Features - - - - - Help - - - Support - - - Get started - - - Chat - - - Documentation - - - - - Company - - - Blog - - - Iterative.ai - - - - - Social - - - Twitter - - - GitHub - - - Discord - - - - -
-
-
-
- ) - } + + + + dvc.org + + + +
+ + Features + +
+
+ + Doc + +
+
+ + Blog + +
+
+ + Community + + + + + Meet Us + + + + Contribute + + + + Learn + + + + Events + + +
+
+ + Support + + + + + E-Mail + + + + GitHub + + + + Discord + + + + Twitter + + +
+
+ + Get started + +
+ + ) } diff --git a/src/components/HamburgerMenu/styles.js b/src/components/HamburgerMenu/styles.js index 5696c90cda..62f5e96706 100644 --- a/src/components/HamburgerMenu/styles.js +++ b/src/components/HamburgerMenu/styles.js @@ -3,7 +3,7 @@ import styled from 'styled-components' import { media } from '../../styles' export const Wrapper = styled.div` - padding: 25px 31px 20px 31px; + padding: 25px 15px 15px; display: none; position: fixed; z-index: 10; @@ -13,7 +13,6 @@ export const Wrapper = styled.div` left: 0px; right: 0px; top: 0px; - bottom: 0px; background-color: #40364d; color: #fff; @@ -35,10 +34,7 @@ export const Button = styled.button` right: 15px; top: 25px; - - width: 46px; - height: 36px; - + padding: 0; border: none; background: transparent; @@ -47,77 +43,84 @@ export const Button = styled.button` `}; ` -export const Menu = styled.div` - display: flex; - height: 100%; - flex-direction: column; - justify-content: space-between; +export const Menu = styled.div`` + +export const Image = styled.img` + display: block; + margin: 0 auto 5px; +` + +export const ImageComment = styled.div` + margin: 0 -30px; color: #fff; + opacity: 0.5; + line-height: 20px; + text-align: center; + white-space: nowrap; ` -export const Section = styled.div`` +export const ImageLink = styled.a` + flex: 1 1 0; + cursor: pointer; + text-decoration: none; -export const Top = styled.div` - height: 40px; - margin-bottom: 40.7px; -` + & + & { + margin-left: 15px; + } -export const Logo = styled.a` - margin-top: 5px; - display: block; - -webkit-transform-style: preserve-3d; - transform-style: preserve-3d; - transform: translate3d(0, 0, 0); + &:hover { + opacity: 0.7; + } ` -export const Columns = styled.div` - display: flex; - flex-direction: row; - flex-flow: wrap; -` +export const Section = styled.div` + padding: 15px 0; -export const Column = styled.div` - display: flex; - flex-direction: column; - margin-bottom: 24px; - flex-basis: 50%; + & + & { + border-top: 1px solid #4d465a; + } ` -export const Links = styled.div` +export const Subsection = styled.div` display: flex; - flex-direction: column; + margin-top: 15px; ` -export const Heading = styled.h2` - opacity: 0.61; - color: #fff; - font-size: 20px; - font-weight: 100; +export const Top = styled.div`` + +export const Logo = styled.a` + display: block; + width: 34px; + margin-top: 5px; ` export const Link = styled.a` - font-size: 18px; - padding: 8px 0px; - display: flex; - color: #fff; + font-family: BrandonGrotesqueBold; + font-size: 13px; + line-height: 20px; + text-transform: uppercase; text-decoration: none; + color: #fff; &:hover { - color: #fff; + opacity: 0.7; } +` - ${props => - props.src && - ` - &::before { - margin-right: 14px; - width: 26px; - height: 26px; - content: ''; - background-image: url(${props.src}); - background-repeat: no-repeat; - background-position: center center; - background-size: contain; - } - `}; +export const LinkButton = styled.a` + display: block; + margin-top: 15px; + height: 38px; + border-radius: 4px; + background-color: #fff; + font-family: BrandonGrotesqueMed; + font-size: 16px; + line-height: 38px; + text-align: center; + text-decoration: none; + color: #24292e; + + &:hover { + opacity: 0.7; + } ` diff --git a/src/components/Nav/index.js b/src/components/Nav/index.js index 951584e894..7e97e6c329 100644 --- a/src/components/Nav/index.js +++ b/src/components/Nav/index.js @@ -5,7 +5,18 @@ import LocalLink from '../LocalLink' import { logEvent } from '../../utils/ga' -import { GetStartedButton, Link, Links, Wrapper } from './styles' +import { + Dropdown, + DropdownInset, + DropdownLink, + DropdownWrapper, + GetStartedButton, + ImageLink, + Image, + Link, + Links, + Wrapper +} from './styles' export default function Nav({ mobile = false }) { return ( @@ -31,15 +42,47 @@ export default function Nav({ mobile = false }) { > Blog - logEvent('menu', 'chat')}> - Chat - - logEvent('menu', 'github')} - > - GitHub - + + logEvent('menu', 'community')} + > + Community + + + + logEvent('menu', 'community')} + > + Meet the Community + + logEvent('menu', 'community')} + > + Contribute + + logEvent('menu', 'community')} + > + Learn + + logEvent('menu', 'community')} + > + Events + + + + Support + + + + + + {header} - - {children} - + {children} {!isOpened && More...} ) diff --git a/src/components/TextCollapse/styles.js b/src/components/TextCollapse/styles.js index 671fea5a3a..884ee0c856 100644 --- a/src/components/TextCollapse/styles.js +++ b/src/components/TextCollapse/styles.js @@ -6,6 +6,10 @@ export const Wrapper = styled.button` appearance: none; background: none; font-family: BrandonGrotesque, Tahoma, Arial; + + .ReactCollapse--collapse { + transition: height 500ms; + } ` export const MoreText = styled.div` diff --git a/src/components/TopMenu/styles.js b/src/components/TopMenu/styles.js index c7c9fd862d..57d85b5f37 100644 --- a/src/components/TopMenu/styles.js +++ b/src/components/TopMenu/styles.js @@ -13,7 +13,10 @@ export const Wrapper = styled.div` background-color: #ffffff; box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.15); - overflow-y: scroll; + + &::-webkit-scrollbar { + visibility: hidden; + } ` export const Container = styled.section` diff --git a/src/utils/api.js b/src/utils/api.js new file mode 100644 index 0000000000..773209d351 --- /dev/null +++ b/src/utils/api.js @@ -0,0 +1,56 @@ +import fetch from 'isomorphic-fetch' + +export function makeAbsoluteURL(req, uri) { + const host = req ? req.headers['host'] : window.location.host + const protocol = host.indexOf('localhost') > -1 ? 'http:' : 'https:' + + return `${protocol}//${host}${uri}` +} + +export async function getLatestIssues(req) { + try { + const res = await fetch(makeAbsoluteURL(req, '/api/github')) + + if (res.status !== 200) return [] + + const { issues } = await res.json() + + return issues + } catch (e) { + console.error(e) + + return [] + } +} + +export async function getLatestPosts(req) { + try { + const res = await fetch(makeAbsoluteURL(req, '/api/blog')) + + if (res.status !== 200) return [] + + const { posts } = await res.json() + + return posts + } catch (e) { + console.error(e) + + return [] + } +} + +export async function getLatestTopics(req) { + try { + const res = await fetch(makeAbsoluteURL(req, '/api/discourse')) + + if (res.status !== 200) return [] + + const { topics } = await res.json() + + return topics + } catch (e) { + console.error(e) + + return [] + } +} diff --git a/src/utils/format.js b/src/utils/format.js new file mode 100644 index 0000000000..ceae7420f4 --- /dev/null +++ b/src/utils/format.js @@ -0,0 +1,7 @@ +export function formatNumber(number) { + try { + return number.toLocaleString('en-US') + } catch { + return number + } +} diff --git a/src/utils/ga.js b/src/utils/ga.js index abd0fdec67..07d7392ac9 100644 --- a/src/utils/ga.js +++ b/src/utils/ga.js @@ -9,8 +9,10 @@ export const logPageView = () => { ReactGA.pageview(window.location.pathname) } -export const logEvent = (category = '', action = '') => { - if (category && action) { +export const logEvent = (category, action, label) => { + if (category && action && label) { + ReactGA.event({ category, action, label }) + } else if (category && action) { ReactGA.event({ category, action }) } } diff --git a/src/utils/i18n.js b/src/utils/i18n.js new file mode 100644 index 0000000000..f95414ae25 --- /dev/null +++ b/src/utils/i18n.js @@ -0,0 +1,20 @@ +export function pluralize(entry, count) { + let key + + if (count === 0 && entry.zero) { + key = 'zero' + } else if (count === 1) { + key = 'one' + } else { + key = 'other' + } + + return entry[key].replace('{count}', count.toString()) +} + +export function pluralizeComments(count) { + return pluralize( + { zero: 'No comments', one: '{count} comment', other: '{count} comments' }, + count + ) +} diff --git a/yarn.lock b/yarn.lock index ddb3e900d1..029b09536f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -869,6 +869,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.1.2": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308" + integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.4": version "7.7.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.7.tgz#194769ca8d6d7790ec23605af9ee3e42a0aa79cf" @@ -1087,6 +1094,54 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" +"@octokit/endpoint@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.5.1.tgz#2eea81e110ca754ff2de11c79154ccab4ae16b3f" + integrity sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg== + dependencies: + "@octokit/types" "^2.0.0" + is-plain-object "^3.0.0" + universal-user-agent "^4.0.0" + +"@octokit/graphql@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.3.1.tgz#9ee840e04ed2906c7d6763807632de84cdecf418" + integrity sha512-hCdTjfvrK+ilU2keAdqNBWOk+gm1kai1ZcdjRfB30oA3/T6n53UVJb7w0L5cR3/rhU91xT3HSqCd+qbvH06yxA== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^2.0.0" + universal-user-agent "^4.0.0" + +"@octokit/request-error@^1.0.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-1.2.0.tgz#a64d2a9d7a13555570cd79722de4a4d76371baaa" + integrity sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg== + dependencies: + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request@^5.3.0": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.3.1.tgz#3a1ace45e6f88b1be4749c5da963b3a3b4a2f120" + integrity sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg== + dependencies: + "@octokit/endpoint" "^5.5.0" + "@octokit/request-error" "^1.0.1" + "@octokit/types" "^2.0.0" + deprecation "^2.0.0" + is-plain-object "^3.0.0" + node-fetch "^2.3.0" + once "^1.4.0" + universal-user-agent "^4.0.0" + +"@octokit/types@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-2.0.2.tgz#0888497f5a664e28b0449731d5e88e19b2a74f90" + integrity sha512-StASIL2lgT3TRjxv17z9pAqbnI7HGu9DrJlg3sEBFfCLaMEqp+O3IQPUF6EZtQ4xkAu2ml6kMBBCtGxjvmtmuQ== + dependencies: + "@types/node" ">= 8" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -1204,15 +1259,20 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/js-cookie@2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.4.tgz#f79720b4755aa197c2e15e982e2f438f5748e348" + integrity sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA== + "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/normalize-package-data@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" - integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/node@>= 8": + version "12.12.19" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.19.tgz#6133aa2a765accdec89ad7792b651a0830f7a34e" + integrity sha512-OXw80IpKyLeuZ5a8r2XCxVNnRAtS3lRDHBleSUQmbgu3C6eKqRsz7/5XNBU0EvK0RTVfotvYFgvRwwe2jeoiKw== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1387,6 +1447,11 @@ "@webassemblyjs/wast-parser" "1.8.5" "@xtuc/long" "4.2.2" +"@xobotyi/scrollbar-width@1.8.2": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.8.2.tgz#056946ac41ade4885c576619c8d70c46c77e9683" + integrity sha512-RV6+4hR29oMaPCvSYFUvzOvlsrg2s2k5NE9tNERs+4nFIC9dRXxs+lL2CcaRTbl3yQxKwAZ8Cd+qMI8aUu9TFw== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -1970,6 +2035,11 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bowser@^1.7.3: + version "1.9.4" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2386,6 +2456,11 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +clone@2.x: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" @@ -2590,6 +2665,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-to-clipboard@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.1.tgz#b1a1137100e5665d5a96015cb579e30e90e07c44" + integrity sha512-btru1Q6RD9wbonIvEU5EfnhIRGHLo//BGXQ1hNAD2avIs/nBZlpbOeKtv3mhoUByN4DB9Cb6/vXBymj1S43KmA== + dependencies: + toggle-selection "^1.0.6" + core-js-compat@^3.1.1: version "3.6.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.1.tgz#39638c935c83c93a793abb628b252ec43e85783a" @@ -2613,7 +2695,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^5.0.0, cosmiconfig@^5.2.1: +cosmiconfig@^5.0.0: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== @@ -2722,6 +2804,14 @@ css-has-pseudo@^0.10.0: postcss "^7.0.6" postcss-selector-parser "^5.0.0-rc.4" +css-in-js-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" + integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-loader@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.3.0.tgz#65f889807baec3197313965d6cda9899f936734d" @@ -2756,6 +2846,14 @@ css-to-react-native@^2.2.2: css-color-keywords "^1.0.0" postcss-value-parser "^3.3.0" +css-tree@^1.0.0-alpha.28: + version "1.0.0-alpha.39" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" + integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== + dependencies: + mdn-data "2.0.6" + source-map "^0.6.1" + css-vendor@^0.3.1: version "0.3.8" resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa" @@ -2815,6 +2913,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +csstype@^2.5.5: + version "2.6.8" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431" + integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA== + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -2846,6 +2949,11 @@ date-fns@^1.27.2: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== +date-fns@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.8.1.tgz#2109362ccb6c87c3ca011e9e31f702bc09e4123b" + integrity sha512-EL/C8IHvYRwAHYgFRse4MGAPSqlJVlOrhVYZ75iQBKrnv+ZedmYsgwH3t+BCDuZDXpoo07+q9j4qgSSOa7irJg== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2943,6 +3051,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +deprecation@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -3170,6 +3283,13 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + dependencies: + stackframe "^1.1.1" + es-abstract@^1.17.0, es-abstract@^1.17.0-next.1: version "1.17.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" @@ -3230,6 +3350,14 @@ eslint-plugin-eslint-plugin@^2.1.0: resolved "https://registry.yarnpkg.com/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-2.1.0.tgz#a7a00f15a886957d855feacaafee264f039e62d5" integrity sha512-kT3A/ZJftt28gbl/Cv04qezb/NQ1dwYIbi8lyf806XMxkus7DvOVCLIfTXMrorp322Pnoez7+zabXH29tADIDg== +eslint-plugin-json@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-2.0.1.tgz#825ba21fed9522a7080044f0692ffffffbc237e3" + integrity sha512-IxKZIlMyBn0tvxlBj2viW0N/UBuoey1oYpV+SNVuNFmy4xsNuwgeoOjzEeFDnVXL0FpIo7UbQSeZ+lfh2D/nLQ== + dependencies: + lodash "^4.17.15" + vscode-json-languageservice "^3.3.5" + eslint-plugin-jsx-a11y@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" @@ -3552,6 +3680,16 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" + integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== + +fastest-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" + integrity sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg= + fault@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.3.tgz#4da88cf979b6b792b4e13c7ec836767725170b7e" @@ -3815,11 +3953,6 @@ get-stdin@^6.0.0: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== -get-stdin@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" - integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== - get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -4143,22 +4276,24 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== -husky@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/husky/-/husky-3.1.0.tgz#5faad520ab860582ed94f0c1a77f0f04c90b57c0" - integrity sha512-FJkPoHHB+6s4a+jwPqBudBDvYZsoQW5/HBuMSehC8qDiCe50kpcxeqFoDSlow+9I6wg47YxBoT3WxaURlrDIIQ== +husky@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/husky/-/husky-4.0.10.tgz#659b52c404d3163b943a73f6c1d454708c0226d8" + integrity sha512-Ptm4k2DqOwxeK/kzu5RaJmNRoGvESrgDXObFcZ8aJZcyXyMBHhM2FqZj6zYKdetadmP3wCwxEHCBuB9xGlRp8A== dependencies: - chalk "^2.4.2" + chalk "^3.0.0" ci-info "^2.0.0" - cosmiconfig "^5.2.1" - execa "^1.0.0" - get-stdin "^7.0.0" + cosmiconfig "^6.0.0" opencollective-postinstall "^2.0.2" pkg-dir "^4.2.0" please-upgrade-node "^3.2.0" - read-pkg "^5.2.0" - run-node "^1.0.0" slash "^3.0.0" + which-pm-runs "^1.0.0" + +hyphenate-style-name@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" + integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" @@ -4280,6 +4415,14 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +inline-style-prefixer@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz#d390957d26f281255fe101da863158ac6eb60911" + integrity sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg== + dependencies: + bowser "^1.7.3" + css-in-js-utils "^2.0.0" + inquirer@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.1.tgz#13f7980eedc73c689feff3994b109c4e799c6ebb" @@ -4549,6 +4692,13 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928" + integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg== + dependencies: + isobject "^4.0.0" + is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" @@ -4645,6 +4795,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + isomorphic-fetch@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" @@ -5057,6 +5212,11 @@ jest@^24.9.0: import-local "^2.0.0" jest-cli "^24.9.0" +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" + integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== + js-levenshtein@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" @@ -5178,6 +5338,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonc-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.2.0.tgz#f206f87f9d49d644b7502052c04e82dd6392e9ef" + integrity sha512-4fLQxW1j/5fWj6p78vAlAafoCKtuBm6ghv+Ij5W2DrDx0qE+ZdEl2c6Ko1mgJNF5ftX1iEWQQ4Ap7+3GlhjkOA== + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -5256,10 +5421,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^10.0.0-1: - version "10.0.0-beta.14" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.0-beta.14.tgz#cbf8b28ed30ab349a0fc0511ed69517610cca2f8" - integrity sha512-AdlmRw9G6MLOh9RVzKQ78yWcs1SaNpNs5y5tEetwujFgqIp/fXMZY53KnJByHjtcg6uKSN/DPNxdjpRlkVw12Q== +lint-staged@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.0.0.tgz#2560394062b6e0be77247761bba5fde26025fb89" + integrity sha512-/MrZOLMnljjMHakxlRd1Z5Kr8wWWlrWFasye7HaTv5tx56icwzT/STRty8flMKsyzBGTfTa9QszNVPsDS/yOug== dependencies: chalk "^3.0.0" commander "^4.0.1" @@ -5516,6 +5681,11 @@ lru-cache@5.1.1, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +macos-release@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" + integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -5583,6 +5753,11 @@ mdast-add-list-metadata@1.0.1: dependencies: unist-util-visit-parents "1.1.2" +mdn-data@2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" + integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== + memoize-one@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" @@ -5819,6 +5994,20 @@ nan@^2.12.1: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nano-css@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.2.1.tgz#73b8470fa40b028a134d3393ae36bbb34b9fa332" + integrity sha512-T54okxMAha0+de+W8o3qFtuWhTxYvqQh2ku1cYEqTTP9mR62nWV2lLK9qRuAGWmoaYWhU7K4evT9Lc1iF65wuw== + dependencies: + css-tree "^1.0.0-alpha.28" + csstype "^2.5.5" + fastest-stable-stringify "^1.0.1" + inline-style-prefixer "^4.0.0" + rtl-css-js "^1.9.0" + sourcemap-codec "^1.4.1" + stacktrace-js "^2.0.0" + stylis "3.5.0" + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -5950,7 +6139,14 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-fetch@2.6.0: +node-cache@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.0.tgz#266786c28dcec0fd34385ee29c383e6d6f1aa5de" + integrity sha512-gFQwYdoOztBuPlwg6DKQEf50G+gkK69aqLnw4djkmlHCzeVrLJfwvg9xl4RCAGviTIMUVoqcyoZ/V/wPEu/VVg== + dependencies: + clone "2.x" + +node-fetch@2.6.0, node-fetch@^2.3.0: version "2.6.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== @@ -6020,7 +6216,7 @@ node-releases@^1.1.42: dependencies: semver "^6.3.0" -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -6259,6 +6455,14 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= +os-name@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-name/-/os-name-3.1.0.tgz#dec19d966296e1cd62d701a5a66ee1ddeae70801" + integrity sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg== + dependencies: + macos-release "^2.2.0" + windows-release "^3.1.0" + os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -6523,11 +6727,6 @@ perfect-scrollbar@^1.4.0: resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.4.0.tgz#5d014ef9775e1f43058a1dbae9ed1daf0e7091f1" integrity sha512-/2Sk/khljhdrsamjJYS5NjrH+GKEHEwh7zFSiYyxROyYKagkE4kSn2zDQDRTOMo8mpT2jikxx6yI1dG7lNP/hw== -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -7196,13 +7395,6 @@ querystring@0.2.0, querystring@^0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -raf@^3.1.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" - integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== - dependencies: - performance-now "^2.1.0" - ramda@^0.26: version "0.26.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" @@ -7238,12 +7430,10 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -react-collapse@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/react-collapse/-/react-collapse-4.0.3.tgz#b96de959ed0092a43534630b599a4753dd76d543" - integrity sha512-OO4NhtEqFtz+1ma31J1B7+ezdRnzHCZiTGSSd/Pxoks9hxrZYhzFEddeYt05A/1477xTtdrwo7xEa2FLJyWGCQ== - dependencies: - prop-types "^15.5.8" +react-collapse@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/react-collapse/-/react-collapse-5.0.1.tgz#f763b7207aee73baa1ff778b2d0b4ffb49d900a0" + integrity sha512-cN2tkxBWizhPQ2JHfe0aUSJtmMthKA17NZkTElpiQ2snQAAi1hssXZ2fv88rAPNNvG5ss4t0PbOZT0TIl9Lk3Q== react-collapsible@^2.6.2: version "2.6.2" @@ -7265,6 +7455,11 @@ react-error-overlay@5.1.6: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.6.tgz#0cd73407c5d141f9638ae1e0c63e7b2bf7e9929d" integrity sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q== +react-fast-compare@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-ga@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.7.0.tgz#24328f157f31e8cffbf4de74a3396536679d8d7c" @@ -7294,15 +7489,6 @@ react-markdown@^4.2.2: unist-util-visit "^1.3.0" xtend "^4.0.1" -react-motion@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" - integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ== - dependencies: - performance-now "^0.2.0" - prop-types "^15.5.8" - raf "^3.1.0" - react-popover@^0.5.10: version "0.5.10" resolved "https://registry.yarnpkg.com/react-popover/-/react-popover-0.5.10.tgz#40d5e854300a96722ffc1620e49d840cb1ad5db6" @@ -7343,6 +7529,25 @@ react-syntax-highlighter@^11.0.2: prismjs "^1.8.4" refractor "^2.4.1" +react-use@^13.24.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.24.0.tgz#f4574e26cfaaad65e3f04c0d5ff80c1836546236" + integrity sha512-p8GsZuMdz8OeIGzuYLm6pzJysKOhNyQjCUG6SHrQGk6o6ghy/RVGSqnmxVacNbN9166S0+9FsM1N1yH9GzWlgg== + dependencies: + "@types/js-cookie" "2.2.4" + "@xobotyi/scrollbar-width" "1.8.2" + copy-to-clipboard "^3.2.0" + fast-shallow-equal "^1.0.0" + js-cookie "^2.2.1" + nano-css "^5.2.1" + react-fast-compare "^2.0.4" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^2.1.0" + ts-easing "^0.2.0" + tslib "^1.10.0" + react@^16.12.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" @@ -7378,16 +7583,6 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -7597,7 +7792,7 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -resize-observer-polyfill@^1.5.0: +resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== @@ -7689,6 +7884,13 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +rtl-css-js@^1.9.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d" + integrity sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg== + dependencies: + "@babel/runtime" "^7.1.2" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -7696,11 +7898,6 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" -run-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" - integrity sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A== - run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -7782,6 +7979,11 @@ schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.6.0: ajv "^6.10.2" ajv-keywords "^3.4.1" +screenfull@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.1.tgz#873052411eb9096bc1c8e615f5badad119e6e42c" + integrity sha512-NgQH4KKh2V3zlj2u90l7TUcSFxr9qL/64QEvhAvCN/fu1YS39YLTBKIqZqiS3STj3QD8sN6XnsK/8jk3hRq4WA== + select@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" @@ -7836,6 +8038,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -8005,6 +8212,11 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -8020,6 +8232,11 @@ source-map@^0.5.0, source-map@^0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +sourcemap-codec@^1.4.1: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + space-separated-tokens@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.4.tgz#27910835ae00d0adfcdbd0ad7e611fb9544351fa" @@ -8085,11 +8302,40 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +stack-generator@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36" + integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q== + dependencies: + stackframe "^1.1.1" + stack-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== +stackframe@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.1.tgz#ffef0a3318b1b60c3b58564989aca5660729ec71" + integrity sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ== + +stacktrace-gps@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a" + integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg== + dependencies: + source-map "0.5.6" + stackframe "^1.1.1" + +stacktrace-js@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" + integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== + dependencies: + error-stack-parser "^2.0.6" + stack-generator "^2.0.5" + stacktrace-gps "^3.0.4" + state-toggle@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.2.tgz#75e93a61944116b4959d665c8db2d243631d6ddc" @@ -8341,6 +8587,11 @@ stylis-rule-sheet@0.0.10, stylis-rule-sheet@^0.0.10: resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw== +stylis@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" + integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== + stylis@3.5.4, stylis@^3.5.0: version "3.5.4" resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" @@ -8450,6 +8701,11 @@ throat@^4.0.0: resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= +throttle-debounce@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5" + integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg== + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -8534,6 +8790,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -8582,12 +8843,17 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.4.tgz#3b52b1f13924f460c3fbfd0df69b587dbcbc762e" integrity sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q== +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + ts-pnp@^1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.5.tgz#840e0739c89fce5f3abd9037bb091dbff16d9dec" integrity sha512-ti7OGMOUOzo66wLF3liskw6YQIaSsBgc4GOAlWRnIEj8htCxJUxskanMUoJOD6MDCRAXo36goXJZch+nOS0VMA== -tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== @@ -8616,11 +8882,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -8781,6 +9042,13 @@ unist-util-visit@^1.1.0, unist-util-visit@^1.3.0: dependencies: unist-util-visit-parents "^2.0.0" +universal-user-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-4.0.0.tgz#27da2ec87e32769619f68a14996465ea1cb9df16" + integrity sha512-eM8knLpev67iBDizr/YtqkJsF3GK8gzDc6st/WKzrTuPtcsOKW/0IdL4cnMBsU69pOx0otavLWBDGTwg+dB0aA== + dependencies: + os-name "^3.1.0" + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -8920,6 +9188,37 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +vscode-json-languageservice@^3.3.5: + version "3.4.12" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.12.tgz#e7c96a1824896a624cc7bb14f46fbf9cb7e6c5a3" + integrity sha512-+tA0KPVM1pDfORZqsQen7bY5buBpQGDTVYEobm5MoGtXNeZY2Kn0iy5wIQqXveb28LRv/I5xKE87dmNJTEaijQ== + dependencies: + jsonc-parser "^2.2.0" + vscode-languageserver-textdocument "^1.0.1-next.1" + vscode-languageserver-types "^3.15.0" + vscode-nls "^4.1.1" + vscode-uri "^2.1.1" + +vscode-languageserver-textdocument@^1.0.1-next.1: + version "1.0.1-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1-next.1.tgz#c8f2f792c7c88d33ea8441ca04bfb8376896b671" + integrity sha512-Cmt0KsNxouns+d7/Kw/jWtWU9Z3h56z1qAA8utjDOEqrDcrTs2rDXv3EJRa99nuKM3wVf6DbWym1VqL9q71XPA== + +vscode-languageserver-types@^3.15.0: + version "3.15.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz#17be71d78d2f6236d414f0001ce1ef4d23e6b6de" + integrity sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ== + +vscode-nls@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" + integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== + +vscode-uri@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90" + integrity sha512-eY9jmGoEnVf8VE8xr5znSah7Qt1P/xsCdErz+g8HYZtJ7bZqKH5E3d+6oVNm1AC/c6IHUDokbmVXKOi4qPAC9A== + w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" @@ -9076,6 +9375,11 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -9090,6 +9394,13 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +windows-release@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" + integrity sha512-QTlz2hKLrdqukrsapKsINzqMgOUpQW268eJ0OaOpJN32h272waxR9fkB9VoWRtK7uKHG5EHJcTXQBD8XZVJkFA== + dependencies: + execa "^1.0.0" + word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"