diff --git a/.gitignore b/.gitignore index 5b8b70e2..22806fd1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,5 @@ yarn-debug.log* yarn-error.log* # Visual code -.vscode/ \ No newline at end of file +.vscode/ +.idea/ \ No newline at end of file diff --git a/package.json b/package.json index 0306d579..d3d5209c 100644 --- a/package.json +++ b/package.json @@ -2,20 +2,21 @@ "private": true, "name": "ferreiro.me", "engines": { - "node": "8.x", + "node": "10.16.x", "yarn": "1.x" }, "scripts": { "check": "npm-check", - "dev": "concurrently \"yarn backend:server\" \"yarn frontend:server:dev\"", + "seed-database": "node ./scripts/seedDatabase", + "dev": "concurrently \"yarn backend:server\" \"yarn backend:watch\" \"yarn frontend:server:dev\"", "frontend:server:dev": "yarn workspace @ferreiro/client start:dev", "backend:server": "yarn workspace @ferreiro/server start", + "backend:watch": "yarn workspace @ferreiro/server watch", "build:client": "yarn workspace @ferreiro/client build", "build:server": "yarn workspace @ferreiro/server build", "build": "yarn build:server && yarn build:client", - "start": "yarn build && yarn backend:server", - "test": "echo $'[TODO] Implement tests'", - "heroku-postbuild": "yarn build" + "start": "yarn backend:server", + "test": "echo $'[TODO] Implement tests'" }, "workspaces": [ "packages/*" @@ -25,7 +26,7 @@ "npm-check": "5.9.0" }, "dependencies": { - "xss-filters": "1.2.7", - "concurrently": "4.1.0" + "concurrently": "4.1.0", + "xss-filters": "1.2.7" } } diff --git a/packages/design-system/package.json b/packages/design-system/package.json index b07e0aaf..810570b8 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -2,12 +2,50 @@ "private": true, "name": "@ferreiro/design-system", "version": "1.0.0", + "engines": { + "node": "10.16.x", + "yarn": "1.x" + }, "description": "A minimalistic and powerful design system to start using in your projects", "main": "index.js", - "repository": "https://github.com/ferreiro/design-system.git", - "author": "Jorge Ferreiro ", - "license": "MIT", + "repository": "https://github.com/ferreiro/design-system.git", + "scripts": { + "build": "webpack --mode production", + "build:dev": "webpack --mode development", + "start:dev": "webpack-dev-server --mode development", + "start": "yarn build" + }, + "author": { + "name": "Jorge Ferreiro", + "email": "jorge@ferreiro.me", + "url": "https://www.ferreiro.me/" + }, + "engines": { + "node": "10.16.x", + "yarn": "1.x" + }, "dependencies": { - "react": "^16.8.6" + "react": "16.8.6", + "react-dom": "16.8.6", + "react-router-dom": "5.0.0", + "@ferreiro/design-system": "1.0.0", + "webpack": "4.30.0", + "webpack-cli": "3.3.0", + "webpack-dev-server": "3.3.1" + }, + "devDependencies": { + "@babel/core": "7.4.3", + "@babel/preset-env": "7.4.3", + "@babel/preset-react": "7.0.0", + "babel-loader": "8.0.5", + "copy-webpack-plugin": "5.0.2", + "css-loader": "2.1.1", + "imagemin-webpack-plugin": "2.4.2", + "mini-css-extract-plugin": "0.6.0", + "node-sass": "4.11.0", + "optimize-css-assets-webpack-plugin": "5.0.1", + "postcss-loader": "3.0.0", + "sass-loader": "7.1.0", + "style-loader": "0.23.1" } } diff --git a/packages/ferreiro-client/.babelrc b/packages/ferreiro-client/.babelrc index 294a8b3e..d548532c 100644 --- a/packages/ferreiro-client/.babelrc +++ b/packages/ferreiro-client/.babelrc @@ -1,3 +1,6 @@ { - "presets": ["@babel/env", "@babel/react"] + "presets": ["@babel/env", "@babel/react"], + "plugins": [ + "@babel/plugin-proposal-class-properties" + ] } \ No newline at end of file diff --git a/packages/ferreiro-client/package-lock.json b/packages/ferreiro-client/package-lock.json new file mode 100644 index 00000000..054968a2 --- /dev/null +++ b/packages/ferreiro-client/package-lock.json @@ -0,0 +1,126 @@ +{ + "name": "@ferreiro/client", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "httpplease": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/httpplease/-/httpplease-0.16.4.tgz", + "integrity": "sha1-04Lr4jDvUHkIC06f/r8xap51wNo=", + "dev": true, + "requires": { + "urllite": "~0.5.0", + "xmlhttprequest": "*", + "xtend": "~3.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "react-inlinesvg": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/react-inlinesvg/-/react-inlinesvg-0.8.4.tgz", + "integrity": "sha512-pMkYa09gsP+5mA5uYDon5TxJbu76rJqdPSQ9nTRZbVacH58Eo3tFxD0Z382cioxNrpeqWHI/hquzt00GaahnkA==", + "dev": true, + "requires": { + "httpplease": "^0.16.4", + "once": "^1.4.0" + } + }, + "simple-html-tokenizer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.1.1.tgz", + "integrity": "sha1-BcLuxXn//+FFoDCsJs/qYbmA+r4=", + "dev": true + }, + "svg-inline-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.8.0.tgz", + "integrity": "sha512-rynplY2eXFrdNomL1FvyTFQlP+dx0WqbzHglmNtA9M4IHRC3no2aPAl3ny9lUpJzFzFMZfWRK5YIclNU+FRePA==", + "dev": true, + "requires": { + "loader-utils": "^0.2.11", + "object-assign": "^4.0.1", + "simple-html-tokenizer": "^0.1.1" + } + }, + "urllite": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/urllite/-/urllite-0.5.0.tgz", + "integrity": "sha1-G3u5yj+w25Ug3hE0ZrvPfMNBRRo=", + "dev": true, + "requires": { + "xtend": "~4.0.0" + }, + "dependencies": { + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", + "dev": true + }, + "xtend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz", + "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=", + "dev": true + } + } +} diff --git a/packages/ferreiro-client/package.json b/packages/ferreiro-client/package.json index 3385fa07..ee10234c 100644 --- a/packages/ferreiro-client/package.json +++ b/packages/ferreiro-client/package.json @@ -2,37 +2,69 @@ "name": "@ferreiro/client", "version": "0.0.1", "private": true, + "main": "src/index.js", "scripts": { "build": "webpack --mode production", "build:dev": "webpack --mode development", "start:dev": "webpack-dev-server --mode development", "start": "yarn build" }, + "author": { + "name": "Jorge Ferreiro", + "email": "jorge@ferreiro.me", + "url": "https://www.ferreiro.me/" + }, "engines": { - "node": ">=10.16.0" + "node": "10.16.x", + "yarn": "1.x" }, "dependencies": { + "@babel/plugin-proposal-class-properties": "7.4.4", + "@ferreiro/design-system": "1.0.0", + "add": "2.0.6", + "case-sensitive-paths-webpack-plugin": "2.2.0", + "classnames": "2.2.6", + "dompurify": "1.0.10", + "email-validator": "2.0.4", + "emotion": "10.0.17", + "lodash": "4.17.11", + "prop-types": "15.7.2", + "query-string": "6.8.1", "react": "16.8.6", + "react-content-loader": "4.2.1", + "react-detect-offline": "2.4.0", "react-dom": "16.8.6", + "react-lazy-load": "3.0.13", + "react-lazy-load-image-component": "1.3.2", "react-router-dom": "5.0.0", - "@ferreiro/design-system": "1.0.0" + "react-router-hash-link": "1.2.2", + "react-sticky": "6.0.3", + "react-stickynode": "2.1.1", + "react-waypoint": "9.0.2", + "styled-components": "4.3.2", + "webpack": "4.30.0", + "webpack-cli": "3.3.0", + "webpack-dev-server": "3.3.1", + "yarn": "1.16.0" }, "devDependencies": { "@babel/core": "7.4.3", "@babel/preset-env": "7.4.3", "@babel/preset-react": "7.0.0", + "autoprefixer": "9.6.1", "babel-loader": "8.0.5", "copy-webpack-plugin": "5.0.2", "css-loader": "2.1.1", + "file-loader": "4.0.0", "imagemin-webpack-plugin": "2.4.2", "mini-css-extract-plugin": "0.6.0", "node-sass": "4.11.0", "optimize-css-assets-webpack-plugin": "5.0.1", "postcss-loader": "3.0.0", + "react-inlinesvg": "0.8.4", "sass-loader": "7.1.0", "style-loader": "0.23.1", - "webpack": "4.30.0", - "webpack-cli": "3.3.0", - "webpack-dev-server": "3.3.1" + "svg-inline-loader": "0.8.0", + "url-loader": "2.0.1" } -} \ No newline at end of file +} diff --git a/packages/ferreiro-client/postcss.config.js b/packages/ferreiro-client/postcss.config.js new file mode 100644 index 00000000..134417d3 --- /dev/null +++ b/packages/ferreiro-client/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: [ + require('autoprefixer') + ] +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/App.js b/packages/ferreiro-client/src/App.js index e69de29b..3f53fd06 100644 --- a/packages/ferreiro-client/src/App.js +++ b/packages/ferreiro-client/src/App.js @@ -0,0 +1,118 @@ +import React from 'react' +import { + Switch, + Route, + withRouter +} from 'react-router-dom' + +import {PageLayout} from './components/layout/PageLayout' + +import {Home} from './pages/home/Home' +import {About} from './pages/about/About' +import {AboutResume} from './pages/about/AboutResume' +import {BlogHome} from './pages/blog/home/Home' +import {BlogPost} from './pages/blog/post/Post' +import {Contact} from './pages/contact/Contact' +import {ContactTalk} from './pages/contact/ContactTalk' +import {Portfolio} from './pages/portfolio/Portfolio' +import {VideosHome} from './pages/videos/Home' +import {VideoDetailPage} from './pages/videos/DetailPage' +import {TalksHome} from './pages/talks/Home' +import {TalksInfo} from './pages/talks/Info' +import {Newsletter} from './pages/newsletter/Newsletter' + +import './common.scss'; + +const NoMatch = ({ location }) => ( + +

Page not found {location.pathname}

+
+) + +export const AppWithRoutes = () => ( + + + + + + + + + + + + + + + + + + +) + +export const App = withRouter(AppWithRoutes) \ No newline at end of file diff --git a/packages/ferreiro-client/src/ScrollToTop.js b/packages/ferreiro-client/src/ScrollToTop.js new file mode 100644 index 00000000..f54b19b2 --- /dev/null +++ b/packages/ferreiro-client/src/ScrollToTop.js @@ -0,0 +1,19 @@ +import React, {PureComponent} from 'react'; +import {withRouter} from 'react-router-dom'; + +// https://stackoverflow.com/questions/36904185/react-router-scroll-to-top-on-every-transition +class ScrollToTopWrapper extends PureComponent { + componentDidUpdate(prevProps) { + if (this.props.location !== prevProps.location) { + window.scrollTo(0, 0) + } + } + + render() { + return this.props.children + } +} + +export const ScrollToTop = withRouter( + ScrollToTopWrapper +) \ No newline at end of file diff --git a/packages/ferreiro-client/src/common.scss b/packages/ferreiro-client/src/common.scss new file mode 100644 index 00000000..b00de9f8 --- /dev/null +++ b/packages/ferreiro-client/src/common.scss @@ -0,0 +1,186 @@ +/* @import './normalize.scss'; */ +@import './components/spacing.scss'; +@import './components/mediaQueries.scss'; + +a, p, h1, h2, h3, h4 { + text-decoration: none; +} + +// TODO: Have a script that automatically generates this... +.spacing-1-ver { + margin-top: $spacing-1; + margin-bottom: $spacing-1; +} + +.spacing-1-hor { + margin-left: $spacing-1; + margin-right: $spacing-1; +} + +.spacing-1-bot { + margin-bottom: $spacing-1; +} + +.spacing-1-top { + margin-top: $spacing-1; +} + +.spacing-1-left { + margin-left: $spacing-1; +} + +.spacing-1-right { + margin-right: $spacing-1; +} + + +// TODO: Have a script that automatically generates this... +.spacing-2-ver { + margin-top: $spacing-2; + margin-bottom: $spacing-2; +} + +.spacing-2-hor { + margin-left: $spacing-2; + margin-right: $spacing-2; +} + +.spacing-2-bot { + margin-bottom: $spacing-2; +} + +.spacing-2-top { + margin-top: $spacing-2; +} + +.spacing-2-left { + margin-left: $spacing-2; +} + +.spacing-2-right { + margin-right: $spacing-2; +} + + +// TODO: Have a script that automatically generates this... +.spacing-3-ver { + margin-top: $spacing-3; + margin-bottom: $spacing-3; +} + +.spacing-3-hor { + margin-left: $spacing-3; + margin-right: $spacing-3; +} + +.spacing-3-bot { + margin-bottom: $spacing-3; +} + +.spacing-3-top { + margin-top: $spacing-3; +} + +.spacing-3-left { + margin-left: $spacing-3; +} + +.spacing-3-right { + margin-right: $spacing-3; +} + +// TODO: Have a script that automatically generates this... +.spacing-4-ver { + margin-top: $spacing-4; + margin-bottom: $spacing-4; +} + +.spacing-4-hor { + margin-left: $spacing-4; + margin-right: $spacing-4; +} + +.spacing-4-bot { + margin-bottom: $spacing-4; +} + +.spacing-4-top { + margin-top: $spacing-4; +} + +.spacing-4-left { + margin-left: $spacing-4; +} + +.spacing-4-right { + margin-right: $spacing-4; +} + + +// TODO: Have a script that automatically generates this... +.spacing-5-ver { + margin-top: $spacing-5; + margin-bottom: $spacing-5; +} + +.spacing-5-hor { + margin-left: $spacing-5; + margin-right: $spacing-5; +} + +.spacing-5-bot { + margin-bottom: $spacing-5; +} + +.spacing-5-top { + margin-top: $spacing-5; +} + +.spacing-5-left { + margin-left: $spacing-5; +} + +.spacing-5-right { + margin-right: $spacing-5; +} + + + +// TODO: Have a script that automatically generates this... +.spacing-6-ver { + margin-top: $spacing-6; + margin-bottom: $spacing-6; +} + +.spacing-6-hor { + margin-left: $spacing-6; + margin-right: $spacing-6; +} + +.spacing-6-bot { + margin-bottom: $spacing-6; +} + +.spacing-6-top { + margin-top: $spacing-6; +} + +.spacing-6-left { + margin-left: $spacing-6; +} + +.spacing-6-left-large { + @include large-up { + margin-left: $spacing-6; + } +} + +.spacing-6-right { + margin-right: $spacing-6; +} + +.spacing-6-right-large { + @include large-up { + margin-right: $spacing-6; + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/ads/ConferenceAd.js b/packages/ferreiro-client/src/components/ads/ConferenceAd.js new file mode 100644 index 00000000..3cb5872d --- /dev/null +++ b/packages/ferreiro-client/src/components/ads/ConferenceAd.js @@ -0,0 +1,73 @@ +import React, {PureComponent} from 'react' +import {Waypoint} from 'react-waypoint' +import {Link} from 'react-router-dom' +import classnames from 'classnames' + +import './ConferenceAd.scss' +import {translate} from '../../i18-me/i18-me'; + +export class ConferenceAd extends PureComponent { + state = { + isShown: false, + } + + static defaultProps = { + showMoreInfo: true, + } + + onEnter = () => { + if (this.state.isShown) { + return + } + + this.setState({isShown: true}) + } + + render = () => ( + +
+
+
+

+ Do you want me to speak in your congress, event or company? +

+
+ + {translate('Contact me!')} + + + {this.props.showMoreInfo && + + {translate('More info')} + + } +
+
+
+ Jorge Ferreiro. Frontend Software Engineer and public speaker on technical and motivational topics. +
+
+
+
+ ) +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/ads/ConferenceAd.scss b/packages/ferreiro-client/src/components/ads/ConferenceAd.scss new file mode 100644 index 00000000..fdc2edb6 --- /dev/null +++ b/packages/ferreiro-client/src/components/ads/ConferenceAd.scss @@ -0,0 +1,85 @@ +@import "../mediaQueries.scss"; + +.conference-ad { + width: 100%; + background: #f4f4f4; + + &__wrapper { + align-items: center; + display: flex; + margin: 0 auto; + max-width: 90%; + position: relative; + + @include large-up { + max-width: 620px; + } + } + + &__options { + display: block; + text-align: center; + margin: 0 auto; + position: relative; + z-index: 2; + } + + &__button { + background: #ca4622; + color: #fff; + display: inline-block; + font-weight: 600; + max-width: 90px; + padding: .8em 1.5em; + + &--more-info { + background: transparent; + color: #000; + } + } + + &__content { + flex: 1 1 auto; + text-align: center; + padding: 2em 0; + + @include large-up { + max-width: 370px; + margin: 0 auto; + align-self: center; + } + } + + &__title { + font-weight: 600; + font-size: 1.5em; + margin: 0; + margin-bottom: 1em; + + @include large-up { + font-size: 1.6em; + } + } + + &__image { + flex: 0 0 auto; + width: 200px; + align-self: flex-end; + display: none; + + @include large-up { + display: flex; + width: 250px; + position: absolute; + right: 0; + z-index: 0; + } + + img { + width: 100%; + object-fit: cover; + object-position: center; + height: 100%; + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/ads/GenericAd.js b/packages/ferreiro-client/src/components/ads/GenericAd.js new file mode 100644 index 00000000..b8b950aa --- /dev/null +++ b/packages/ferreiro-client/src/components/ads/GenericAd.js @@ -0,0 +1,39 @@ +import React from 'react' + +import './GenericAd.scss' +import {Link} from 'react-router-dom' + +export const GenericAd = ({ + title, + ctaText, + link, + useExternalLink = false, +}) => { + const buttonCta = useExternalLink ? ( + + {ctaText} + + ) : ( + + {ctaText} + + ) + + return ( +
+
+
+

+ {title} +

+ {buttonCta} +
+
+
+ ); +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/ads/GenericAd.scss b/packages/ferreiro-client/src/components/ads/GenericAd.scss new file mode 100644 index 00000000..426a056d --- /dev/null +++ b/packages/ferreiro-client/src/components/ads/GenericAd.scss @@ -0,0 +1,62 @@ +@import "../mediaQueries.scss"; + +.generic-ad { + width: 100%; + background: #f4f4f4; + margin-bottom: 2em; + + &__wrapper { + align-items: center; + display: flex; + margin: 0 auto; + max-width: 90%; + + @include large-up { + max-width: 620px; + } + } + + &__button { + background: #ca4622; + color: #fff; + display: block; + font-weight: 600; + max-width: 90px; + margin: 0 auto; + padding: .8em 1.5em; + } + + &__content { + flex: 1 1 auto; + text-align: center; + padding: 1em 0; + } + + &__title { + font-weight: 600; + font-size: 1.5em; + margin: 0; + margin-bottom: 1em; + + @include large-up { + font-size: 1.6em; + } + } + + &__image { + flex: 0 0 auto; + width: 200px; + align-self: flex-end; + + @include large-up { + width: 250px; + } + + img { + width: 100%; + object-fit: cover; + object-position: center; + height: 100%; + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/buttons/Button.js b/packages/ferreiro-client/src/components/buttons/Button.js new file mode 100644 index 00000000..8856ed56 --- /dev/null +++ b/packages/ferreiro-client/src/components/buttons/Button.js @@ -0,0 +1,92 @@ +import React from 'react' +import classNames from 'classnames' +import isEmpty from 'lodash/isEmpty' + +import './Button.scss' + +import {Link as ExternalLink} from '../link/Link'; +import {Link as InternalLink} from 'react-router-dom'; + +import { + BUTTON_STYLE_OUTLINE, + BUTTON_STYLE_FILL, + BUTTON_STYLE_NEUTRAL, + BUTTON_SIZE_SMALL, + BUTTON_SIZE_MEDIUM, + BUTTON_SIZE_BIG, + TARGET_SELF, + BUTTON_STYLE_YOUTUBE, + BUTTON_STYLE_LINK +} from '../constants' + +const BASE_CLASSNAME = 'button-subscribe' + +const STYLE_TO_CLASSNAME = { + [BUTTON_STYLE_LINK]: `${BASE_CLASSNAME}--style-link`, + [BUTTON_STYLE_OUTLINE]: `${BASE_CLASSNAME}--style-outline`, + [BUTTON_STYLE_FILL]: `${BASE_CLASSNAME}--style-fill`, + [BUTTON_STYLE_NEUTRAL]: `${BASE_CLASSNAME}--style-neutral`, + [BUTTON_STYLE_YOUTUBE]: `${BASE_CLASSNAME}--style-youtube`, +} + +const SIZE_TO_CLASSNAME = { + [BUTTON_SIZE_SMALL]: `${BASE_CLASSNAME}--size-small`, + [BUTTON_SIZE_MEDIUM]: `${BASE_CLASSNAME}--size-medium`, + [BUTTON_SIZE_BIG]: `${BASE_CLASSNAME}--size-big`, +} + +const getClassName = (style, size) => { + const styleClassName = STYLE_TO_CLASSNAME[style] + const sizeClassName = SIZE_TO_CLASSNAME[size] + + return classNames({ + [BASE_CLASSNAME]: true, + [styleClassName]: true, + [sizeClassName]: true, + }) +} + +// TODO: Rename into ButtonCta or something like that. +// This is not only used for subscribe +export const Button = ({ + onClick, + // TODO: Migrate into to, to be consistent with the link module + url = '/newsletter', + style = BUTTON_STYLE_NEUTRAL, + size = BUTTON_SIZE_MEDIUM, + target = TARGET_SELF, + text = 'Subscribe Newsletter', + title, + icon, +}) => { + const className = getClassName(style, size) + const _onClick = (event) => { + if (onClick === undefined) { + return + } + + event.preventDefault() + onClick() + } + + const LinkComponent = target === TARGET_SELF + ? InternalLink + : ExternalLink + + // TODO: Instead of opening a new tab + // open a modal. + return ( + + {icon && icon} + + {text} + + ) +} + diff --git a/packages/ferreiro-client/src/components/buttons/Button.scss b/packages/ferreiro-client/src/components/buttons/Button.scss new file mode 100644 index 00000000..c7da353f --- /dev/null +++ b/packages/ferreiro-client/src/components/buttons/Button.scss @@ -0,0 +1,110 @@ +@import '../variables.scss'; +@import '../spacing.scss'; +@import '../mediaQueries.scss'; + +.button-subscribe { + display: inline-block; + + &--style-youtube { + background: $color-youtube; + border-color: $color-youtube; + border-radius: 10px; + color: #fff; + font-weight: 600; + + &:hover { + background: $color-youtube-hover; + } + } + + /* 1. Removing margin added by border */ + &--style-link { + /* TODO: Instead of overriden with important, find an alternative */ + border-radius: 0 !important; + font-size: inherit !important; + padding: 0 !important; + margin: initial !important; + margin-bottom: -4px !important; /* 1. */ + border: 0 !important; + border-bottom: 4px solid $color-main-link !important; + + &:hover { + border-color: #000; + } + } + + /* Rename this into outline */ + &--style-outline { + border-style: solid; + border-color: $color-main-link; + color: $color-main; + font-weight: 600; + + &:hover { + background: $color-main-link; + color: #fff; + } + } + + &--style-fill { + background: $color-main; + color: #fff; + } + + &--style-neutral { + border-style: solid; + border-color: #000; + + &:hover { + background: #efefef; + color: $color-main; + border-color: $color-main; + } + } + + &--size-small { + border-radius: 60px; + + @include large-down { + border-width: 2px; + font-size: 14px; + padding: .4rem .8rem; + } + + @include large-up { + border-width: 2px; + font-size: 14px; + padding: .5rem .8rem; + } + } + + &--size-medium { + border-radius: 60px; + + @include large-down { + border-width: 2px; + font-size: 14px; + padding: .9rem 1rem; + } + + @include large-up { + border-width: 2px; + font-size: 16px; + padding: 0.6rem 1rem; + } + } + + &--size-big { + border-radius: 2px; + border-width: 3px; + font-size: 16px; + padding: .9rem 1rem; + + @include large-up { + border-radius: 4px; + border-width: 4px; + font-size: 18px; + padding: 1.2rem 1.8rem; + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/buttons/PlayButton.js b/packages/ferreiro-client/src/components/buttons/PlayButton.js new file mode 100644 index 00000000..6d4c249c --- /dev/null +++ b/packages/ferreiro-client/src/components/buttons/PlayButton.js @@ -0,0 +1,22 @@ +import React from 'react' +import styled from 'styled-components' + +const PlayIcon = styled.span` + font-Size: 70px; + position: absolute; + top: 50%; + left: 50%; + margin: -40px 0 0 -40px; + color: #fff; + + &:before { + -webkit-filter: drop-shadow(0 0 15px rgba(0, 0, 0, 0.9)); + filter: drop-shadow(0 0 15px rgba(0, 0, 0, 0.9)); + } +` + +export const PlayButton = () => ( + +) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/card/Card.js b/packages/ferreiro-client/src/components/card/Card.js new file mode 100644 index 00000000..35256a3e --- /dev/null +++ b/packages/ferreiro-client/src/components/card/Card.js @@ -0,0 +1,72 @@ +import React from 'react' +import {Link} from 'react-router-dom' +import LazyLoad from 'react-lazy-load' +import { LazyLoadImage } from 'react-lazy-load-image-component'; + +// import {Link} from '../atoms/link/Link' + +import './Card.scss' +const DEFAULT_MAX_SUMMARY_LENGTH = 150 + +export const Card = ({ + permalink, + title, + summary = '', + maxSummaryLength = DEFAULT_MAX_SUMMARY_LENGTH, + pic, + series, + ...rest +}) => { + const SEOTitle = `Jorge Ferreirto article ${title}` + const summaryShortened = summary.substr(0, maxSummaryLength) + + console.log('Card') + console.log(series) + console.log(rest) + console.log(pic) + + return ( + +
+
+ +
+
+

+ + {title} + +

+ +

+ {summaryShortened} +

+ + {series && ( +

+ This post belongs to {series.title.substr(0, 50)} series +

+ )} +{/* + + + */} + +
+
+ + ) +} diff --git a/packages/ferreiro-client/src/components/card/Card.scss b/packages/ferreiro-client/src/components/card/Card.scss new file mode 100644 index 00000000..0c8e65c8 --- /dev/null +++ b/packages/ferreiro-client/src/components/card/Card.scss @@ -0,0 +1,87 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + +.card { + align-items: center; + background: #fff; + border-radius: $card-radius; + display: flex; + + @include large-down { + flex-direction: column; + margin-bottom: 1.5em; + + border: 1px solid #e8e8e8; + border-radius: $card-radius; + box-shadow: 0 0 10px 0 rgba(0,0,0,.05); + } + + @include large-up { + flex-direction: row; + margin-bottom: 1.5em; + padding: 1em; + /* 1: Aligning left images to highlight card */ + margin-left: -1em; /* 1 */ + } + + &__image { + img { + object-fit: cover; + object-position: center; + } + + @include large-down { + width: 100%; + max-height: 260px; + + img { + width: 100%; + max-height: 260px; + border-top-left-radius: $card-radius; + border-top-right-radius: $card-radius; + } + } + + @include large-up { + flex: 0 0 auto; + width: 310px; + height: 240px; + + img { + border-radius: $card-radius; + height: 240px; + width: 310px; + } + } + } + + &__content { + padding: 1em; + + @include large-up { + padding: 2em; + flex: 1 1 auto; + } + } + + &__title { + @include large-down { + font-size: $font-size-title; + font-weight: 600; + } + + @include large-up { + max-width: 600px; + font-size: 2em; + line-height: 1.4em; + font-weight: 600; + } + } + + &__summary { + margin-top: .7rem; + opacity: 0.7; + font-size: $font-size-paragraph; + line-height: $font-size-paragraph * 1.4; + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/cardHighlight/CardHighlight.js b/packages/ferreiro-client/src/components/cardHighlight/CardHighlight.js new file mode 100644 index 00000000..60e7b8fd --- /dev/null +++ b/packages/ferreiro-client/src/components/cardHighlight/CardHighlight.js @@ -0,0 +1,50 @@ +import React from 'react' +import {Link} from 'react-router-dom' + +import './CardHighlight.scss' + +const DEFAULT_MAX_SUMMARY_LENGTH = 150 +const DEFAULT_MAX_TITLE_LENGTH = 60 + +export const CardHighlight = ({ + permalink, + title = '', + summary = '', + maxSummaryLength = DEFAULT_MAX_SUMMARY_LENGTH, + maxTitleLength = DEFAULT_MAX_TITLE_LENGTH, + image, +}) => { + const SEOTitle = `Jorge Ferreirto article ${title}` + const titleShortened = title.substr(0, maxTitleLength) + const summaryShortened = summary.substr(0, maxSummaryLength) + + return ( + +
+ {SEOTitle} +
+

+ + {titleShortened} + {titleShortened.length < title.length && '...'} + +

+

+ {summaryShortened} +

+
+
+ + ) +} diff --git a/packages/ferreiro-client/src/components/cardHighlight/CardHighlight.scss b/packages/ferreiro-client/src/components/cardHighlight/CardHighlight.scss new file mode 100644 index 00000000..32b92cf3 --- /dev/null +++ b/packages/ferreiro-client/src/components/cardHighlight/CardHighlight.scss @@ -0,0 +1,59 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + +.card-highlight { + background: #fff; + border-radius: $card-radius; + display: flex; + flex-direction: column; + margin-bottom: 1.5em; + + @include large-up { + flex-direction: row; + margin-bottom: 1.2em; + } + + &__image { + object-fit: cover; + object-position: center; + width: 100%; + max-height: 260px; + border-top-left-radius: $card-radius; + border-top-right-radius: $card-radius; + + @include large-up { + min-width: 60%; + height: 400px; + max-height: 400px; + border-radius: $card-radius; + } + } + + &__content { + padding: 1em; + + @include large-up { + padding: 2em; + } + } + + &__title { + @include large-down { + font-size: $font-size-title; + font-weight: 600; + } + + @include large-up { + font-size: 40px; + line-height: 50px; + font-weight: 600; + } + } + + &__summary { + margin-top: .7rem; + opacity: 0.7; + font-size: $font-size-paragraph; + line-height: $font-size-paragraph * 1.4; + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/cardRelatedPost/CardRelatedPost.js b/packages/ferreiro-client/src/components/cardRelatedPost/CardRelatedPost.js new file mode 100644 index 00000000..3a3d3c6f --- /dev/null +++ b/packages/ferreiro-client/src/components/cardRelatedPost/CardRelatedPost.js @@ -0,0 +1,45 @@ +import React from 'react' +import {Link} from 'react-router-dom' +import LazyLoad from 'react-lazy-load' + +import {getPostPermalink} from '../../utils/getPostPermalink' + +import './CardRelatedPost.scss' + +const DEFAULT_MAX_SUMMARY_LENGTH = 150 + +export const CardRelatedPost = ({ + maxSummaryLength = DEFAULT_MAX_SUMMARY_LENGTH, + post = {}, +}) => { + const { + permalink, + pic, + summary, + title, + } = post + + const summaryShortened = summary.substr(0, maxSummaryLength) + + return ( + +
+
+ + {title} + +
+
+

+ {title} +

+

+ {summaryShortened} +

+
+
+ + ) +} diff --git a/packages/ferreiro-client/src/components/cardRelatedPost/CardRelatedPost.scss b/packages/ferreiro-client/src/components/cardRelatedPost/CardRelatedPost.scss new file mode 100644 index 00000000..9cf74b95 --- /dev/null +++ b/packages/ferreiro-client/src/components/cardRelatedPost/CardRelatedPost.scss @@ -0,0 +1,55 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + +.card-related-post { + height: 100%; + background: #fff; + border-radius: $card-radius; + display: flex; + flex-direction: column; + margin-bottom: 1.5em; + border: 1px solid #e8e8e8; + border-radius: $card-radius; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, .05); + + &__image { + flex: 0 0 auto; + height: 200px; + width: 100%; + + img { + border-top-left-radius: $card-radius; + border-top-right-radius: $card-radius; + object-fit: cover; + object-position: center; + height: 200px; + width: 100%; + } + } + + &__content { + padding: 1em; + flex: 1 1 auto; + } + + &__title { + @include large-down { + font-size: $font-size-title; + font-weight: 600; + } + + @include large-up { + max-width: 600px; + font-size: 1.3em; + line-height: 1.4em; + font-weight: 600; + } + } + + &__summary { + margin-top: .7rem; + opacity: 0.7; + font-size: $font-size-paragraph; + line-height: $font-size-paragraph * 1.4; + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/cardRelatedVideo/CardRelatedVideo.js b/packages/ferreiro-client/src/components/cardRelatedVideo/CardRelatedVideo.js new file mode 100644 index 00000000..dd5dd315 --- /dev/null +++ b/packages/ferreiro-client/src/components/cardRelatedVideo/CardRelatedVideo.js @@ -0,0 +1,51 @@ +import React from 'react' +import {Link} from 'react-router-dom' +import LazyLoad from 'react-lazy-load' + +import {PlayButton} from '../buttons/PlayButton' +import {getVideoPermalink} from '../../utils/getVideoPermalink' + +import './CardRelatedVideo.scss' + +const DEFAULT_MAX_SUMMARY_LENGTH = 150 + +export const CardRelatedVideo = ({ + maxSummaryLength = DEFAULT_MAX_SUMMARY_LENGTH, + video = {}, +}) => { + const { + permalink, + image = {}, + subtitle, + title, + } = video + + const { + src, + alt + } = image + + return ( + +
+
+ + + {alt} + +
+
+

+ {title} +

+
+
+ + ) +} diff --git a/packages/ferreiro-client/src/components/cardRelatedVideo/CardRelatedVideo.scss b/packages/ferreiro-client/src/components/cardRelatedVideo/CardRelatedVideo.scss new file mode 100644 index 00000000..0d034248 --- /dev/null +++ b/packages/ferreiro-client/src/components/cardRelatedVideo/CardRelatedVideo.scss @@ -0,0 +1,59 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + +.card-related-video { + height: 100%; + background: #fff; + border-radius: $card-radius; + display: flex; + flex-direction: column; + border: 1px solid #e8e8e8; + border-radius: $card-radius; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, .05); + + @include large-up { + margin: 0 .8em; + } + + &__image { + flex: 0 0 auto; + height: 200px; + width: 100%; + position: relative; + + img { + border-top-left-radius: $card-radius; + border-top-right-radius: $card-radius; + object-fit: cover; + object-position: center; + height: 200px; + width: 100%; + } + } + + &__content { + padding: 1em; + flex: 1 1 auto; + } + + &__title { + @include large-down { + font-size: $font-size-title; + font-weight: 600; + } + + @include large-up { + max-width: 600px; + font-size: 1.3em; + line-height: 1.4em; + font-weight: 600; + } + } + + &__summary { + margin-top: .7rem; + opacity: 0.7; + font-size: $font-size-paragraph; + line-height: $font-size-paragraph * 1.4; + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/cardTalks/CardTalk.js b/packages/ferreiro-client/src/components/cardTalks/CardTalk.js new file mode 100644 index 00000000..e671226f --- /dev/null +++ b/packages/ferreiro-client/src/components/cardTalks/CardTalk.js @@ -0,0 +1,117 @@ +import React from 'react' +import {LazyLoadImage} from 'react-lazy-load-image-component'; + +import './CardTalk.scss' +import { Link } from '../link/Link'; +import { TARGET_BLANK } from '../constants'; + +const DEFAULT_MAX_SUMMARY_LENGTH = 150 + +const renderButton = ({ + url, + title, + type, +} = {}) => { + switch (type) { + case "video": + return ( + + +

+ Video +

+
+ ) + case "slides": + return ( + + +

+ Slides +

+
+ ) + case "image": + return ( + + +

+ {title} +

+
+ ) + default: + return null + } +} + +export const CardTalk = ({ + maxSummaryLength = DEFAULT_MAX_SUMMARY_LENGTH, + talk, +}) => { + const { + buttons = [], + permalink, + pic, + summary = '', + title, + } = talk + + const SEOTitle = `Jorge Ferreirto article ${title}` + const summaryShortened = summary.substr(0, maxSummaryLength) + + return ( + +
+
+ Loading... TODO: Replace with a facebook loader

+ } + /> +
+ +
+

+ + {title} + +

+

+ {summaryShortened} +

+
+ +
    + {buttons.map(renderButton)} +
+
+ + ) +} diff --git a/packages/ferreiro-client/src/components/cardTalks/CardTalk.scss b/packages/ferreiro-client/src/components/cardTalks/CardTalk.scss new file mode 100644 index 00000000..7250ff7a --- /dev/null +++ b/packages/ferreiro-client/src/components/cardTalks/CardTalk.scss @@ -0,0 +1,98 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; +@import '../mixins.scss'; + +$action_button_font_size: 1.2em; +$action_button_icon_size: 15px; +$action_button_icon_margin: 4px; + +.card-talk { + height: 100%; + background: #fff; + border-radius: $card-radius; + display: flex; + flex-direction: column; + border: 1px solid #e8e8e8; + border-radius: $card-radius; + box-shadow: 0 0 10px 0 rgba(0, 0, 0, .05); + + &__image { + flex: 0 0 auto; + height: 200px; + width: 100%; + + img { + border-top-left-radius: $card-radius; + border-top-right-radius: $card-radius; + object-fit: cover; + object-position: center; + height: 200px; + width: 100%; + } + } + + &__content { + padding: 1em; + flex: 1 1 auto; + } + + &__title { + @include large-down { + font-size: $font-size-title; + font-weight: 600; + } + + @include large-up { + max-width: 600px; + font-size: 1.3em; + line-height: 1.4em; + font-weight: 600; + } + } + + &__summary { + margin-top: .7rem; + opacity: 0.7; + font-size: $font-size-paragraph; + line-height: $font-size-paragraph * 1.4; + } + &__options { + margin: 0 1em 1em; + } +} + +.card-talk-button { + display: inline-flex; + align-items: center; + font-weight: 600; + padding: 0.6em 1em; + text-decoration: none; + color: #616161 !important; + margin-right: .5em; + + @include border-radius(60px); + @include transition(250ms); + + &:hover { + background: #f4f4f4; + } + + &__hidden { + display: none; + } + + span { + line-height: 0; + margin-right: 14px; + + &:before { + color: $color-main; + font-size: 22px; + } + } + + .legend { + text-transform: uppercase; + color: $color-main; + } + } \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/constants.js b/packages/ferreiro-client/src/components/constants.js new file mode 100644 index 00000000..edb5eeeb --- /dev/null +++ b/packages/ferreiro-client/src/components/constants.js @@ -0,0 +1,119 @@ +export const TARGET_BLANK = '_blank' +export const TARGET_SELF = '_self' + +export const LINKEDIN_URL = 'https://www.linkedin.com/in/jgferreiro/' +export const TWITTER_URL = 'https://twitter.com/JGFerreiro' +export const INSTAGRAM_URL = 'https://www.instagram.com/jgferreiro/' +export const GITHUB_URL = 'https://github.com/ferreiro' +export const SOCIAL_NETWORKS = [ + { + url: LINKEDIN_URL, + text: 'Linkedin', + icon: 'icon-linkedin', + }, + { + url: TWITTER_URL, + text: 'Twitter', + icon: 'icon-twitter', + }, + { + url: INSTAGRAM_URL, + text: 'Instagram', + icon: 'icon-instagram', + }, + { + url: GITHUB_URL, + text: 'Github', + icon: 'icon-github', + }, +] + +// BUTTON +export const BUTTON_STYLE_OUTLINE = 'link'; +export const BUTTON_STYLE_FILL = 'fill'; +export const BUTTON_STYLE_LINK = 'none'; +export const BUTTON_STYLE_NEUTRAL = 'neutral'; +export const BUTTON_STYLE_YOUTUBE = 'youtube'; +export const BUTTON_SIZE_SMALL = 'small'; +export const BUTTON_SIZE_MEDIUM = 'medium'; +export const BUTTON_SIZE_BIG = 'big'; + +// MENU +export const MENU_ITEMS = [ + { + "id": "about", + "icon": null, + "text" : "About", + "path" : "/about", + "target": "_self", + "submenu": [ + { + "id": "biography", + "icon": null, + "text" : "Biography", + "path" : "/about", + "target": "_self", + }, + { + "id": "portfolio", + "icon": null, + "text" : "Portfolio & Projects", + "path" : "/portfolio", + "target": "_self", + }, + { + "id": "resume", + "icon": null, + "text" : "Resume / CV", + "path" : "/about/resume", + "target": "_self", + }, + ] + }, + { + "id": "blog", + "icon": null, + "text" : "Blog", + "path" : "/blog", + "target": "_self" + }, + { + "id": "videos", + "icon": null, + "text" : "Videos", + "path" : "/videos", + "target": "_self" + }, + { + "id": "talks", + "icon": null, + "text" : "Conferences", + "path" : "/talks", + "target": "_self" + }, + // { + // "id": "portfolio", + // "icon": null, + // "text" : "Portfolio", + // "path" : "/portfolio", + // "target": "_self" + // }, + { + "id": "contact", + "icon": null, + "text" : "Contact", + "path" : "/contact", + "target": "_self" + } +] + +export const MOBILE_MENU_ITEMS = [ + { + "id": "home", + "hidden": false, + "text" : "Home", + "path" : "/", + "target": "_self" + }, + ...MENU_ITEMS +] \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/contentHeader/ContentHeader.js b/packages/ferreiro-client/src/components/contentHeader/ContentHeader.js new file mode 100644 index 00000000..c832e764 --- /dev/null +++ b/packages/ferreiro-client/src/components/contentHeader/ContentHeader.js @@ -0,0 +1,54 @@ +import React from 'react' +import {Waypoint} from 'react-waypoint' + +import './ContentHeader.scss' + +const onEnterViewport = () => { + console.log('On Enter') +} + +const onLeaveViewport = () => { + console.log('On leave') +} + +export const ContentHeader = ({ + options = null, + title, + subtitle, +}) => ( + +
+
+

+ {title} +

+ + {subtitle && ( +

+ )} +

+ + {options} +
+
+) diff --git a/packages/ferreiro-client/src/components/contentHeader/ContentHeader.scss b/packages/ferreiro-client/src/components/contentHeader/ContentHeader.scss new file mode 100644 index 00000000..e41b69cd --- /dev/null +++ b/packages/ferreiro-client/src/components/contentHeader/ContentHeader.scss @@ -0,0 +1,89 @@ +@import '../mediaQueries.scss'; + +.content-header { + align-items: center; + display: flex; + flex-wrap: wrap; + padding-bottom: 1.5em; + + &--contrast { + flex-direction: column; + color: $color-section-title-contrast; + background-size: cover; + background-repeat: no-repeat; + padding: 2em 1em; + padding-bottom: 100px !important; + } + + @include medium-up { + padding-bottom: 2rem; + } + + @include large-up { + padding: 1.5em 0 3em; + } + + &__title { + color: $color-section-title; + font-size: 30px; + + @include medium-up { + font-size: 60px; + } + + @include large-up { + font-size: 105px; + } + + &--contrast { + color: $color-section-title-contrast; + text-align: center; + + @include large-up { + font-size: 80px; + } + } + } + + &__subtitle { + max-width: 700px; + color: $color-section-title; + padding-top: 1rem; + word-break: break-word; + font-size: 18px; + padding-bottom: 1.5rem; + + + @include medium-up { + font-size: 24px; + padding-bottom: 1.5rem; + } + + @include large-up { + font-size: 30px; + } + + &--contrast { + color: $color-section-title-contrast; + padding: 0 auto; + text-align: center; + + @include large-up { + font-size: 26px; + } + } + + } + + &__after-content { + &--contrast { + width: calc(100% - 4em); + margin: 0 2em; + display: flex; + background: red; + margin-bottom: -140px; + margin-top: 24px; + } + } +} + diff --git a/packages/ferreiro-client/src/components/contentHeader/ContentHeaderContrast.js b/packages/ferreiro-client/src/components/contentHeader/ContentHeaderContrast.js new file mode 100644 index 00000000..b09078ef --- /dev/null +++ b/packages/ferreiro-client/src/components/contentHeader/ContentHeaderContrast.js @@ -0,0 +1,61 @@ +import React from 'react' +import {Waypoint} from 'react-waypoint' + +import './ContentHeader.scss' + +const onEnterViewport = () => { + console.log('On Enter') +} + +const onLeaveViewport = () => { + console.log('On leave') +} + +export const ContentHeaderContrast = ({ + afterContent = null, + options = null, + title, + subtitle, + backgroundImageUrl = '', + backgroundColor, +}) => ( + +
+
+

+ {title} +

+ + {subtitle && +

+ } + + {options} + + {afterContent} +

+
+
+) diff --git a/packages/ferreiro-client/src/components/footer/Footer.js b/packages/ferreiro-client/src/components/footer/Footer.js new file mode 100644 index 00000000..1e567ac4 --- /dev/null +++ b/packages/ferreiro-client/src/components/footer/Footer.js @@ -0,0 +1,49 @@ +import React from 'react' + +import './footer.scss' + +export const Footer = () => ( +
+
+ +
+
+

jorge@ferreiro.me

+
+
+
+
+

Newsletter

+
+
+
+
+

Twitter

+
+
+
+
+

Linkedin

+
+
+
+
+

Github

+
+
+
+
+

Instagram

+
+
+
+
+

Designed & Programmed by:

+

Jorge Ferreiro

+

 © Jorge Ferreiro | Source code

+
+ +

Newsletter form here!

+
+
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/footer/footer.scss b/packages/ferreiro-client/src/components/footer/footer.scss new file mode 100644 index 00000000..e6467407 --- /dev/null +++ b/packages/ferreiro-client/src/components/footer/footer.scss @@ -0,0 +1,134 @@ +@import '../mediaQueries.scss'; +@import '../mixins.scss'; +@import '../variables.scss'; + +.footer { + width: 100%; + position: relative; + background: #fff; + display: block !important; + border: 0; + border-top: 1px solid #f4f4f4; + + @include large-up { + padding:1.7em 0; + } + + &_content { + width: 100%; + text-align: center; + display: block; + margin: 0; + padding: 0; + border: 0; + } + + &_separator { + width: 100%; + display: none; + border-top: 2px solid #f4f4f4; + margin: 0; + + @include medium { + margin: 2em 0; + display: block; + } + + @include large-up { + display: block; + margin-bottom: 2em; + } + } + + &__logo { + height: 40px; + margin-bottom: 1em; + display: inline-block; + + img { + height: 40px; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); + opacity: 0.5; + filter: alpha(opacity =50); + @include border-radius(5px); + } + } + + &_entry { + width: 48%; + border-bottom:1px solid #f4f4f4; + display: inline-block; + padding: 1em 0; + position: relative; + color: rgba(0, 0, 0, 0.9); + + &:hover { + .footer_entry__social { + p, a, span{ + color: $color-main !important; + } + } + } + + &__link { + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + } + + @include medium { + width: 33%; + } + + @include large-up { + border: 0; + width: auto !important; + padding:0 2em; + border-left:1px solid #f4f4f4; + border-right:1px solid #f4f4f4; + } + + h3 { + margin: 0.5em 0; + margin-top: 1em; + font-size: 0.8em; + color: rgba(0, 0, 0, 0.6); + @include transition(200ms); + } + h2 { + font-size: .8em; + color: rgba(0, 0, 0, 0.6); + @include transition(200ms); + } + h1 { + font-size: 1.6em; + color:#676767; + @include transition(200ms); + } + p { + margin-top: 5px; + font-size: 1em; + color: #000; + text-decoration: none; + @include transition(200ms); + } + span { + border: 0; + text-decoration: none; + font-size: 25px; + @include transition(200ms); + } + a { + text-decoration: none; + @include transition(200ms); + } + } + &_credits { + border: 0; + } + + } + \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/header/Header.js b/packages/ferreiro-client/src/components/header/Header.js new file mode 100644 index 00000000..197780bd --- /dev/null +++ b/packages/ferreiro-client/src/components/header/Header.js @@ -0,0 +1,290 @@ +import React, {PureComponent} from 'react' +import classNames from 'classnames' +import {Link} from 'react-router-dom' + +import { + MENU_ITEMS, + MOBILE_MENU_ITEMS, + SOCIAL_NETWORKS, +} from '../constants' + +import './Header.scss'; + +const isReactEnabledForPage = (item) => ( + item.id === 'about' + || item.id === 'blog' + || item.id === 'portfolio' + || item.id === 'videos' + || item.id === 'talks' + || item.id === 'contact' +) + +const renderMenuItemMobile = (item, currentPath = '', itemClassName, selectedItemClassName) => { + const onClick = () => { + document.body.classList.remove('mobile-header-is-showing'); + } + const className = classNames({ + [itemClassName]: true, + [selectedItemClassName]: currentPath.includes(item.id) + }) + + if (item.hidden === true) { + // SKIP: do not add hide items + return null + } + + // TODO: Remove this once all the migration into react + // is done. + if (isReactEnabledForPage(item)) { + return ( + + {item.icon && ( +

+ )} + {item.text && ( +

{item.text}

+ )} + + ) + } + + return ( + + {item.icon && ( +

+ )} + {item.text && ( +

{item.text}

+ )} +
+ ) +} + +const renderMenuItem = ({ + currentPath = '', + isShownAboutDropdown, + item, + itemClassName, + selectedItemClassName, + toggleAboutDropdownMenu +}) => { + const onClick = () => { + document.body.classList.remove('mobile-header-is-showing'); + } + const className = classNames({ + [itemClassName]: true, + [selectedItemClassName]: currentPath.includes(item.id) + }) + + if (item.hidden === true) { + // SKIP: do not add hide items + return null + } + + // TODO: Remove this once all the migration into react + // is done. + if (isReactEnabledForPage(item)) { + const {submenu = null} = item + const renderItem = ({ + item, + className, + selectedClass, + onClick, + options + }) => { + const {path, text, id, icon} = item + + return ( + + {icon && ( +

+ )} + {text && ( +

{text}

+ )} + {options} + + ) + } + + if (submenu) { + return ( +
+ {renderItem({ + item, + className, + options: , + })} + + {isShownAboutDropdown && ( +
+ {submenu.map((item) => renderItem({ + item, + className: 'submenu-dropdown__item', + selectedClass: 'submenu-dropdown__item--selected', + onClick + }))} +
+ )} +
+ ) + } + + return renderItem({item, className, onClick}) + } + + return ( + + {item.icon && ( +

+ )} + {item.text && ( +

{item.text}

+ )} +
+ ) +} + +const renderSocialItem = (item, itemClassName) => { + const className = classNames({ + [itemClassName]: true + }) + + return ( + + {item.icon && ( + + )} + + ); +} + +export class Header extends PureComponent { + state = { + isShown: false, + isShownAboutDropdown: false, + } + + toggleAboutDropdownMenu = () => { + this.setState((prevState) => ({ + isShownAboutDropdown: !prevState.isShownAboutDropdown + })) + } + + toggleMenu = () => { + const isShown = this.state.isShown + + // TODO: Set body to not have overflow, and don't + // allow scrolling. + + this.setState((prevState, props) => ({ + isShown: !prevState.isShown + })) + + if (isShown) { + document.body.classList.remove('mobile-header-is-showing'); + } else { + document.body.classList.add('mobile-header-is-showing'); + } + } + + render() { + const {currentPath} = this.props + const { + isShownAboutDropdown + } = this.state + + // TODO: delete menu, menu__wrapper and container_inner + return ( +
+ {this.state.isShown && ( +
+ +
+ {SOCIAL_NETWORKS.map((item) => + renderSocialItem(item, 'main-header-dropdown__social-item') + )} +
+
+ )} + +
+
+ + Jorge Ferreiro Frontend Software Engineer and Entrepreneur + +
+ + + +
+ {SOCIAL_NETWORKS.map((item) => renderSocialItem(item, 'main-header__social-item'))} +
+ +
+
+
+
+ ) + } +} diff --git a/packages/ferreiro-client/src/components/header/Header.scss b/packages/ferreiro-client/src/components/header/Header.scss new file mode 100644 index 00000000..7812ba41 --- /dev/null +++ b/packages/ferreiro-client/src/components/header/Header.scss @@ -0,0 +1,215 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + +// This class is added to the body +// and it is used to hide scrolling on mobile +.mobile-header-is-showing { + overflow: hidden; +} + +.main-header { + border-bottom: 1px solid #dedede; + + &-dropdown { + background: rgba(255, 255, 255, .98); + display: flex; + flex-direction: column; + width: 100%; + position: fixed; + top: 77px; + bottom: 0; + z-index: 1000; + padding: 2em 0; + + @include extra-large-up { + display: none; + } + + &__links { + flex-direction: column; + align-items: center; + display: flex; + flex: 1 1 auto; + overflow-y: scroll; + justify-content: center; + padding-bottom: 2em; + } + + &__item { + padding: 1em; + display: flex; + text-align: center; + font-size: 1.6em; + font-weight: 600; + width: 100%; + + p { + text-align: center; + margin: 0 2em; + width: calc(100% - 4em); + } + + &--selected { + p { + color: #c94621; + } + } + } + + &__social { + display: flex; + flex: 0 0 auto; + justify-content: center; + flex-wrap: wrap; + + &-item { + align-self: center; + width: 60px; + height: 60px; + border-radius: 100%; + border: 2px solid #cecece; + margin-left: 12px; + flex: 0 0 auto; + + display: flex; + justify-content: center; + align-items: center; + } + } + } + + &-wrapper { + align-items: center; + display: flex; + padding: .8em 1.5em; + + @include medium-up { + padding: .8em 1.5em; + } + } + + &__logo { + display: flex; + flex: 1 1 auto; + + img { + height: 50px; + width: auto; + } + } + + &__menu { + align-items: stretch; + display: flex; + + @include extra-large-down { + display: none; + } + } + + .submenu { + position: relative; + + &-dropdown { + box-shadow: 1px 1px 10px rgba(0,0,0,.2); + top: 100%; + width: 240px; + display: flex; + flex-direction: column; + background: #fff; + position: absolute; + z-index: 100; + + &__item { + padding: 1em 2em; + flex: 1 1 auto; + + p { + border-bottom: 2px solid transparent; + } + + &:hover { + background: #f7f7f7; + + p { + color: $color-main; + } + } + + &:nth-child(1) { + padding-top: 2em; + } + + &:nth-last-child(1) { + padding-bottom: 2em; + } + + &--selected { + background: #efeaea; + } + } + } + } + + &__item { + align-items: center; + font-size: 16px; + padding: 18px 24px; + display: flex; + border-radius: 60px; + + p { + color: rgba(0,0,0,.6); + font-weight: 600; + font-size: 1.2em; + } + + &--selected { + p, span { + color: rgb(201, 70, 33); + } + background: #f7f7f7; + } + } + + &__social { + height: 50px; + align-items: center; + display: flex; + + @include extra-large-down { + display: none; + } + + &-item { + align-items: center; + border-radius: 100%; + border: 2px solid #cecece; + display: flex; + justify-content: center; + height: 50px; + margin-left: 12px; + width: 50px; + } + } + + &__mobile-button { + align-items: flex-start; + display: flex; + flex: 0 0 auto; + + button { + width: 80px; + height: 50px; + border: 0; + background: url("/images/burger-icon.svg"); + background-size: 40px; + background-position: center; + background-repeat: no-repeat; + } + + @include extra-large-up { + display: none; + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/layout/LayoutWithSidebar.js b/packages/ferreiro-client/src/components/layout/LayoutWithSidebar.js new file mode 100644 index 00000000..d761b2c8 --- /dev/null +++ b/packages/ferreiro-client/src/components/layout/LayoutWithSidebar.js @@ -0,0 +1,66 @@ +import React from 'react' +import {StickyContainer, Sticky} from 'react-sticky' +import classnames from 'classnames' + +import {OfflineWarning} from '../offlineWarning/OfflineWarning' + +import './LayoutWithSidebar.scss' + +// TODO: Remove contentHeader... Not needed. We can simply have +// content with all the required data. +export const LayoutWithSidebar = ({ + afterContent, + beforeContent, + content, + contentHeader, + header, + isHeaderFullWidth = false, + panel, + style, + wrapperClassName, +}) => ( + +
+ + + {isHeaderFullWidth === true ? ( + header + ) : ( +
+ {header} +
+ )} + +
+ {panel && ( +
+ + {({style}) => ( +
+ {panel} +
+ )} +
+
+ )} +
+ {beforeContent} + +
+ {contentHeader} + {content} +
+ + {afterContent} +
+
+
+
+) diff --git a/packages/ferreiro-client/src/components/layout/LayoutWithSidebar.scss b/packages/ferreiro-client/src/components/layout/LayoutWithSidebar.scss new file mode 100644 index 00000000..88946e1a --- /dev/null +++ b/packages/ferreiro-client/src/components/layout/LayoutWithSidebar.scss @@ -0,0 +1,53 @@ +@import '../mediaQueries.scss'; +@import '../variables.scss'; + +.layout-with-sidebar { + display: flex; + flex-direction: column; + + &__wrapper { + align-items: stretch; + display: flex; + flex-direction: row; + } + + &__sidebar { + background: #fff; + z-index: 10; + + @include extra-large-down { + display: none; + } + + @include large-up { + box-shadow: 5px 0px 5px -2px rgba(0, 0, 0, .1); + flex: 0 0 auto; + min-height: 100vh; + width: 240px; + } + } + + &__content { + min-height: 100vh; + flex: 1 1 auto; + + &-wrapper { + padding: 1.5em; + + @include medium { + max-width: 500px; + margin: 0 auto; + } + + @include large-up { + padding: 1.5em 2.5em; + } + + @include extra-large-up { + padding: 1.5em 3em; + max-width: 1000px; + margin: 0 auto; + } + } + } +} diff --git a/packages/ferreiro-client/src/components/layout/PageLayout.js b/packages/ferreiro-client/src/components/layout/PageLayout.js new file mode 100644 index 00000000..cc92e4c2 --- /dev/null +++ b/packages/ferreiro-client/src/components/layout/PageLayout.js @@ -0,0 +1,97 @@ +import React, {PureComponent} from 'react' +import classNames from 'classnames' + +import {Header} from '../header/Header'; +import {Footer} from '../footer/Footer' + +import './PageLayout.scss' + +export class PageLayout extends PureComponent { + updatePageTitle = (title) => { + const defaultTitle = 'Jorge Ferreiro - Frontend Software Engineer at Eventbrite, Entrepreneur and former Amazon'; + + document.title = title + ? `${title} - ${defaultTitle}` + : defaultTitle + } + + componentDidMount() { + this.updatePageTitle(this.props.title) + } + + componentDidUpdate(prevProps) { + this.updatePageTitle(prevProps.title) + } + + render() { + const { + children, + currentPath = '', + showHeader = true, + isHeaderFix = false, + } = this.props + + const headerClassName = classNames({ + 'page-layout__header': true, + 'page-layout__header--fixed': isHeaderFix === true, + }) + + return ( +
+ {showHeader === true && ( +
+
+
+ )} + +
+ {children} +
+ +
+
+
+
+ ) + } +} + +// export const PageLayout = ({ +// currentPath = null, +// showHeader = true, +// isHeaderFix = false, +// children +// }) => { +// const headerClassName = classNames({ +// 'page-layout__header': true, +// 'page-layout__header--fixed': isHeaderFix === true, +// }) + +// class PageLayoutWithContent extends PureComponent { +// render() { +// return ( +//
+// {showHeader === true && ( +//
+//
+//
+// )} + +//
+// {children} +//
+ +//
+//
+//
+//
+// ) +// } +// } + +// return +// } \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/layout/PageLayout.scss b/packages/ferreiro-client/src/components/layout/PageLayout.scss new file mode 100644 index 00000000..0492c013 --- /dev/null +++ b/packages/ferreiro-client/src/components/layout/PageLayout.scss @@ -0,0 +1,4 @@ +.page-layout__content { + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/link/Link.js b/packages/ferreiro-client/src/components/link/Link.js new file mode 100644 index 00000000..e87d14f8 --- /dev/null +++ b/packages/ferreiro-client/src/components/link/Link.js @@ -0,0 +1,35 @@ +import React from 'react' +import {Link as RouterLink} from 'react-router-dom' +import { + TARGET_BLANK, + TARGET_SELF +} from '../constants' + +export const Link = ({ + // TODO: Delete url, and only use to + url, + to, + children, + target = TARGET_SELF, + onClick, + className, + title, +}) => { + const rel = target === TARGET_BLANK ? 'noopener noreferrer' : '' + const LinkComponent = target === TARGET_SELF + ? RouterLink + : 'a'; + + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/loader/Loader.js b/packages/ferreiro-client/src/components/loader/Loader.js new file mode 100644 index 00000000..ce13394e --- /dev/null +++ b/packages/ferreiro-client/src/components/loader/Loader.js @@ -0,0 +1,7 @@ +import React from 'react' + +import './Loader.scss' + +export const Loader = () => ( +
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/loader/Loader.scss b/packages/ferreiro-client/src/components/loader/Loader.scss new file mode 100644 index 00000000..671dbea4 --- /dev/null +++ b/packages/ferreiro-client/src/components/loader/Loader.scss @@ -0,0 +1,10 @@ +.page-loader { + background-image: url(/images/three-dots.svg); + background-repeat: no-repeat; + background-size: 60px auto; + background-position: 50% 50%; + height: 100vh; + left: 0; + top: 0; + width: 100%; +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderCard.js b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderCard.js new file mode 100644 index 00000000..69043412 --- /dev/null +++ b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderCard.js @@ -0,0 +1,7 @@ +import React from 'react' + +import './LoadingPlaceholderCard.scss' + +export const LoadingPlaceholderCard = () => ( +
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderCard.scss b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderCard.scss new file mode 100644 index 00000000..da6f14a4 --- /dev/null +++ b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderCard.scss @@ -0,0 +1,9 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + +.loading-placeholder-card { + background: #fff; + border-radius: $card-radius; + height: 260px; + margin-top: 25px; +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderHightlight.js b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderHightlight.js new file mode 100644 index 00000000..1ec94225 --- /dev/null +++ b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderHightlight.js @@ -0,0 +1,9 @@ +import React from 'react' + +import './LoadingPlaceholderHightlight.scss' + +export const LoadingPlaceholderHightlight = () => ( +
+
+
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderHightlight.scss b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderHightlight.scss new file mode 100644 index 00000000..efb789e8 --- /dev/null +++ b/packages/ferreiro-client/src/components/loadingPlaceholder/LoadingPlaceholderHightlight.scss @@ -0,0 +1,41 @@ +@import '../variables.scss'; +@import '../mediaQueries.scss'; + + +.animated-background { + animation-duration: 1.25s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-name: placeHolderShimmer; + animation-timing-function: linear; + background: #F6F6F6; + background: linear-gradient(to right, #F6F6F6 8%, #F0F0F0 18%, #F6F6F6 33%); + background-size: 800px 104px; + height: 100%; + position: relative; +} + + +.loading-placeholder-highlight { + background: #fff; + border-radius: $card-radius; + display: flex; + height: 400px; + + &__image { + width: 60%; + height: 100%; + background: #F6F6F6; + flex: 0 0 auto; + @extend .animated-background; + } +} + +@keyframes placeHolderShimmer{ + 0%{ + background-position: -468px 0 + } + 100%{ + background-position: 468px 0 + } +} diff --git a/packages/ferreiro-client/src/components/mediaQueries.scss b/packages/ferreiro-client/src/components/mediaQueries.scss new file mode 100644 index 00000000..11a3062a --- /dev/null +++ b/packages/ferreiro-client/src/components/mediaQueries.scss @@ -0,0 +1,61 @@ +@import './variables.scss'; + +@mixin small { + @media all and (max-width: $medium - 1px) { + @content; + } +} + +@mixin medium { + @media all and (min-width: $medium) and (max-width: $large) { + @content; + } +} + +@mixin medium-down { + @media all and (max-width: $medium - 1px) { + @content; + } +} + +@mixin medium-up { + @media all and (min-width: $medium) { + @content; + } +} + +@mixin large { + @media all and (min-width: $large) and (max-width: $extra-large - 1px) { + @content; + } +} + +@mixin large-down { + @media all and (max-width: $large - 1px) { + @content; + } +} + +@mixin large-up { + @media all and (min-width: $large) { + @content; + } +} + +@mixin extra-large { + @media all and (min-width: $extra-large) { + @content; + } +} + +@mixin extra-large-down { + @media all and (max-width: $extra-large - 1px) { + @content; + } +} + +@mixin extra-large-up { + @media all and (min-width: $extra-large) { + @content; + } +} diff --git a/packages/ferreiro-client/src/components/mixins.scss b/packages/ferreiro-client/src/components/mixins.scss new file mode 100644 index 00000000..64c52ed2 --- /dev/null +++ b/packages/ferreiro-client/src/components/mixins.scss @@ -0,0 +1,154 @@ +// Source: https://css-tricks.com/snippets/css/useful-css3-less-mixins/ + +@mixin text-shadow($string: 0 1px 3px rgba(0, 0, 0, 0.25)) { + text-shadow: $string; +} + +@mixin box-shadow($string) { + -webkit-box-shadow: $string; + -moz-box-shadow: $string; + box-shadow: $string; +} + +@mixin drop-shadow($x: 0, $y: 1px, $blur: 2px, $spread: 0, $alpha: 0.25) { + -webkit-box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha); + -moz-box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha); + box-shadow: $x $y $blur $spread rgba(0, 0, 0, $alpha); +} + +@mixin inner-shadow($x: 0, $y: 1px, $blur: 2px, $spread: 0, $alpha: 0.25) { + -webkit-box-shadow: inset $x $y $blur $spread rgba(0, 0, 0, $alpha); + -moz-box-shadow: inset $x $y $blur $spread rgba(0, 0, 0, $alpha); + box-shadow: inset $x $y $blur $spread rgba(0, 0, 0, $alpha); +} + +@mixin box-sizing($type: border-box) { + -webkit-box-sizing: $type; + -moz-box-sizing: $type; + box-sizing: $type; +} + +@mixin border-radius($radius: 5px) { + -webkit-border-radius: $radius; + -moz-border-radius: $radius; + border-radius: $radius; + + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} + +@mixin border-radiuses($topleft: 0, $topright: 0, $bottomleft: 0, $bottomright: 0) { + -webkit-border-top-right-radius: $topright; + -webkit-border-bottom-right-radius: $bottomright; + -webkit-border-bottom-left-radius: $bottomleft; + -webkit-border-top-left-radius: $topleft; + + -moz-border-radius-topright: $topright; + -moz-border-radius-bottomright: $bottomright; + -moz-border-radius-bottomleft: $bottomleft; + -moz-border-radius-topleft: $topleft; + + border-top-right-radius: $topright; + border-bottom-right-radius: $bottomright; + border-bottom-left-radius: $bottomleft; + border-top-left-radius: $topleft; + + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} + +@mixin opacity($opacity: 0.5) { + -webkit-opacity: $opacity; + -moz-opacity: $opacity; + opacity: $opacity; +} + +@mixin gradient($startColor: #eee, $endColor: white) { + background-color: $startColor; + background: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)); + background: -webkit-linear-gradient(top, $startColor, $endColor); + background: -moz-linear-gradient(top, $startColor, $endColor); + background: -ms-linear-gradient(top, $startColor, $endColor); + background: -o-linear-gradient(top, $startColor, $endColor); +} + +@mixin horizontal-gradient($startColor: #eee, $endColor: white) { + background-color: $startColor; + background-image: -webkit-gradient(linear, left top, right top, from($startColor), to($endColor)); + background-image: -webkit-linear-gradient(left, $startColor, $endColor); + background-image: -moz-linear-gradient(left, $startColor, $endColor); + background-image: -ms-linear-gradient(left, $startColor, $endColor); + background-image: -o-linear-gradient(left, $startColor, $endColor); +} + +@mixin animation($name, $duration: 300ms, $delay: 0, $ease: ease) { + -webkit-animation: $name $duration $delay $ease; + -moz-animation: $name $duration $delay $ease; + -ms-animation: $name $duration $delay $ease; +} + +@mixin transition($transition) { + -webkit-transition: $transition; + -moz-transition: $transition; + -ms-transition: $transition; + -o-transition: $transition; +} + +@mixin transform($string) { + -webkit-transform: $string; + -moz-transform: $string; + -ms-transform: $string; + -o-transform: $string; +} + +@mixin scale($factor) { + -webkit-transform: scale($factor); + -moz-transform: scale($factor); + -ms-transform: scale($factor); + -o-transform: scale($factor); +} + +@mixin rotate($deg) { + -webkit-transform: rotate($deg); + -moz-transform: rotate($deg); + -ms-transform: rotate($deg); + -o-transform: rotate($deg); +} + +@mixin skew($deg, $deg2) { + -webkit-transform: skew($deg, $deg2); + -moz-transform: skew($deg, $deg2); + -ms-transform: skew($deg, $deg2); + -o-transform: skew($deg, $deg2); +} + +@mixin translate($x, $y:0) { + -webkit-transform: translate($x, $y); + -moz-transform: translate($x, $y); + -ms-transform: translate($x, $y); + -o-transform: translate($x, $y); +} + +@mixin translate3d($x, $y: 0, $z: 0) { + -webkit-transform: translate3d($x, $y, $z); + -moz-transform: translate3d($x, $y, $z); + -ms-transform: translate3d($x, $y, $z); + -o-transform: translate3d($x, $y, $z); +} + +@mixin perspective($value: 1000) { + -webkit-perspective: $value; + -moz-perspective: $value; + -ms-perspective: $value; + perspective: $value; +} + +@mixin transform-origin($x:center, $y:center) { + -webkit-transform-origin: $x $y; + -moz-transform-origin: $x $y; + -ms-transform-origin: $x $y; + -o-transform-origin: $x $y; +} + diff --git a/packages/ferreiro-client/src/components/offlineWarning/OfflineWarning.js b/packages/ferreiro-client/src/components/offlineWarning/OfflineWarning.js new file mode 100644 index 00000000..f69f83fb --- /dev/null +++ b/packages/ferreiro-client/src/components/offlineWarning/OfflineWarning.js @@ -0,0 +1,16 @@ +import React from 'react' +import {Offline} from 'react-detect-offline' + +import './OfflineWarning.scss' + +export const OfflineWarning = ({ + +}) => ( + +
+
+ You're offline right now. Check your connection. +
+
+
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/offlineWarning/OfflineWarning.scss b/packages/ferreiro-client/src/components/offlineWarning/OfflineWarning.scss new file mode 100644 index 00000000..c2764035 --- /dev/null +++ b/packages/ferreiro-client/src/components/offlineWarning/OfflineWarning.scss @@ -0,0 +1,17 @@ +@import "../../components/variables.scss"; +@import "../../components/spacing.scss"; + +.offline-warning { + background: rgb(199, 0, 0); + color: #fff; + text-align: center; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + z-index: 1000; + + &__wrapper { + padding: $spacing-3 $spacing-4; + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/oneColumnLayout/OneColumnLayout.js b/packages/ferreiro-client/src/components/oneColumnLayout/OneColumnLayout.js new file mode 100644 index 00000000..57a1de7a --- /dev/null +++ b/packages/ferreiro-client/src/components/oneColumnLayout/OneColumnLayout.js @@ -0,0 +1,24 @@ +import React, {PureComponent} from 'react' + +import './OneColumnLayout.scss'; + +export class OneColumnLayout extends PureComponent { + render() { + const { + items, + renderingCallback, + } = this.props; + + return ( +
+ {items.map((item) => ( +
+
+ {renderingCallback(item)} +
+
+ ))} +
+ ) + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/oneColumnLayout/OneColumnLayout.scss b/packages/ferreiro-client/src/components/oneColumnLayout/OneColumnLayout.scss new file mode 100644 index 00000000..6da62de2 --- /dev/null +++ b/packages/ferreiro-client/src/components/oneColumnLayout/OneColumnLayout.scss @@ -0,0 +1,30 @@ +@import '../mediaQueries.scss'; + + +.one-column-layout { + display: flex; + flex-direction: row; + flex-wrap: wrap; + + @include large-up { + width: calc(100% + 1em); + margin: 0 -.5em; + } + + &__item { + align-content: stretch; + margin-bottom: 1.5em; + width: 100%; + + @include large-up { + margin-bottom: 1.5em; + } + } + + &__item-wrapper { + @include large-up { + height: 100%; + padding: 0 .5em; + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/pagination/Pagination.js b/packages/ferreiro-client/src/components/pagination/Pagination.js new file mode 100644 index 00000000..c5c099b0 --- /dev/null +++ b/packages/ferreiro-client/src/components/pagination/Pagination.js @@ -0,0 +1,43 @@ +import React from 'react' +import {Link} from 'react-router-dom' + +const shouldDisplayPagination = (prevPageToken, nextPageToken) => ( + prevPageToken !== 'start' || nextPageToken !== "end" +) + +export const Pagination = ({ + prevPageToken, + nextPageToken, +}) => { + if (!shouldDisplayPagination(prevPageToken, nextPageToken)) { + return

No more articles

+ } + + const shouldDisplayPrevPage = ( + prevPageToken !== "start" && prevPageToken > 0 + ) + const shouldDisplayNextPage = ( + nextPageToken !== "end" + ) + + return ( +
+ {shouldDisplayPrevPage && ( + + Previous page + + )} + + {shouldDisplayNextPage && ( + + Next page + + )} +
+ ) +} + + // //- Add the category id (only if is not the main page) + // - blogLink = '/blog' + // if blogCategory !== 'all' + // - blogLink += '/category/' + (blogCategory ? blogCategory : '') diff --git a/packages/ferreiro-client/src/components/profileCard/ProfileCard.js b/packages/ferreiro-client/src/components/profileCard/ProfileCard.js new file mode 100644 index 00000000..e1bf74f3 --- /dev/null +++ b/packages/ferreiro-client/src/components/profileCard/ProfileCard.js @@ -0,0 +1,45 @@ +import React from 'react' +import {Link} from '../link/Link' + +import { + SOCIAL_NETWORKS, + TARGET_BLANK, +} from '../constants' + +import './ProfileCard.scss' + +export const ProfileCard = () => ( +
+ Jorge Ferreiro is a Frontend Software Engineer, Entrepreneur and blog writer. + +

+ Jorge Ferreiro +

+ +

+ Software Engineer and Entrepreneur +

+ +
    + {SOCIAL_NETWORKS.map(({icon, text, url}) => ( +
  • + + +

    {text}

    + +
  • + ))} +
+
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/profileCard/ProfileCard.scss b/packages/ferreiro-client/src/components/profileCard/ProfileCard.scss new file mode 100644 index 00000000..13df7383 --- /dev/null +++ b/packages/ferreiro-client/src/components/profileCard/ProfileCard.scss @@ -0,0 +1,45 @@ +.profile-card { + padding: 2em 1.5em; + text-align: center; + + &__image { + border-radius: 100%; + } + + &__title { + margin: .5em 0; + font-weight: 600; + } + + &__links { + display: flex; + flex-direction: column; + margin: 0; + margin-top: 2em; + padding: 0; + } + + &__link { + list-style: none; + text-align: left; + + a { + align-items: center; + display: flex; + width: 100%; + padding-bottom: .8em; + + span { + flex: 0 0 auto; + border-radius: 100%; + border: 1px solid #585858; + padding: .3em; + margin-right: 1em; + } + + p { + flex: 1 1 auto; + } + } + } +} diff --git a/packages/ferreiro-client/src/components/projectCard/ProjectCard-pendingToMigrate.md b/packages/ferreiro-client/src/components/projectCard/ProjectCard-pendingToMigrate.md new file mode 100644 index 00000000..d91c2c51 --- /dev/null +++ b/packages/ferreiro-client/src/components/projectCard/ProjectCard-pendingToMigrate.md @@ -0,0 +1,274 @@ + +// /* &_next { +// width: 50px; +// height: 100%; +// display: none; +// position: absolute; +// right: 0; +// background: blue; +// z-index: 10; +// } + +// &_wrapper { +// //width: calc(100% - 2em - #{$projectHeaderGalleryGradientWidth}); +// width: 100%; +// height: 100%; +// padding: 0; +// // padding-right: calc(1em + #{$projectHeaderGalleryGradientWidth}); +// overflow: hidden; +// display: inline-block; +// position: absolute; +// top: 0; +// left: 0; +// } + +// &_list { +// width: calc(100% - 2em); +// padding: 1em; +// height: calc(100% - 2em); +// position: relative; +// overflow-x: scroll; +// overflow-y: hidden; + +// display: flex; +// flex-direction: row; +// flex-wrap: nowrap; +// justify-content: flex-start; +// align-items: center; + +// &__offset { +// height: 100%; +// display: block; +// visibility: hidden; +// width: $projectHeaderGalleryGradientWidth - 1em !important; +// display: inline-block; +// } +// } + +// &_entry { +// width: 115px; +// height: 90px; +// cursor: pointer; +// overflow: hidden; +// margin-right: 1em; +// display: inline-block !important; // Lazy adds display:block; so this prevents that. +// background-size: cover; +// background-position: center center; + +// @include transition(100ms); +// @include opacity(0.6); +// @include border-radius($projectHeaderGalleryRadius); +// @include flexbox_flex(0, 0, auto); +// @include flexbox_alignSelf(auto); + +// &:hover { +// @include opacity(0.8); +// } +// } +// } + +// &_body { +// z-index: 12; +// height: 100%; +// position: relative; +// background: #fff; +// padding: $projectBodyPadding_mobile; +// padding-bottom: $projectBodyPadding_mobile + $projectLinksHeight; + +// @include large-up { +// padding: $projectBodyPadding_desktop; +// padding-bottom: $projectBodyPadding_desktop + $projectLinksHeight + $projectLinksTopMargin; +// } + +// // When a card does not have links (we don't put the buttons down) +// &_noLinks { +// padding: $projectBodyPadding_mobile; +// @include large-up { +// padding: $projectBodyPadding_desktop; +// } +// } + +// &_title { +// color: rgba(0, 0, 0, 1); +// font-size: 1.4em; +// font-weight: 500; +// margin-bottom: 0.5em; +// @include large-up { +// margin-bottom: 0.6em; +// } +// } + +// &_subtitle { +// color: rgba(0, 0, 0, 1); +// font-size: 1em; +// font-weight: 500; +// margin-bottom: 0.5em; +// @include large-up { +// margin-bottom: 0.6em; +// } +// } + +// &_text { +// font-size: 1em; +// font-weight: 400; +// line-height: 1.2em; +// color: rgba(0,0,0,.8); +// color: rgba(0, 0, 0, .7); +// margin-bottom: 1em; +// line-height: 1.3em; +// } +// } + +// &_highlight { +// display: block; +// margin-bottom: 1em; + +// &_list { +// list-style: none; +// vertical-align: top; +// margin: -0.3em 0; +// display: block; +// } + +// &_entry { +// width: 100%; +// min-height: 18px; +// // max-height: 36px; +// display: inline-block; + // vertical-align: top; + // position: relative; + // margin: 0.5em 0; + // margin-right: 0; + + // @include large-up {} + + // &_icon { + // left: 0; + // top: 3px; + // position: absolute; + // display: inline-block; + // color: $primaryColor; + // vertical-align: top; + // } + // &_text { + // display: inline-block; + // vertical-align: middle; + // margin-left: 25px; + // font-size: 1em; + // line-height: 1.3em; + // position: relative; + // color: #333; + // color: rgba(0, 0, 0, .7); + + // a { + // font-weight: 300; + // color: rgba(255, 255, 255, 0.8); + // color: $primaryColor; + // } + // } + // } + // } + + // &_stack { + // // margin-bottom: 0.5em; + // margin-top: 1.3em; + // display: block; + + // &_list { + // list-style: none; + // display: block; + // margin-top: -1em; // remove first entry margin top + // } + // &_entry { + // margin-top: 1em; + // margin-right: 1em; + // display: inline-block; + // color: rgba(0, 0, 0, 0.6); + // text-align: center; + // font-size: 0.8em; + // } + // &_title { + // font-size: 0.9em; + // color: rgba(0, 0, 0, 0.6); + // text-transform: capitalize; + // } + // } + + // &_people { + // display: inline-block; + // margin-bottom: 1.5em; + // vertical-align: top; + + // &_list { + // list-style: none; + // display: block; + // } + // &_entry { + // margin: 0.3em; + // margin-right: 0.4em; + // display: inline-block; + + // &_avatar { + // width: 36px; + // height: 36px; + // display: inline-block; + // vertical-align: middle; + // background-color: #f4f4f4; + // @include border-radius(100%); + // } + // } + // &_link { + // display: block; + // border: 1px solid #f4f4f4; + // @include border-radius(60px); + // } + // &_title { + // margin-left: 10px; + // margin-right: 12px; + // font-size: 12px; + // color: rgba(0, 0, 0, .8); + // display: inline-block; + // vertical-align: middle; + // text-transform: capitalize; + // } + // } + + // &_links { + // z-index: 12; + // overflow: hidden; + // position: absolute; + // min-height: $projectLinksHeight; + // left: $projectBodyPadding_mobile; + // bottom: $projectBodyPadding_mobile; + // @include large-up { + // left: $projectBodyPadding_desktop; + // bottom: $projectBodyPadding_desktop; + // } + // a:last-child { + // margin-bottom: 0; + // } + // a { + // color: #fff; + // font-weight: 600; + // margin-right: 1em; + // padding: 0.8em 1em; + // position: relative; + // text-align: center; + // display: inline-block; + // text-decoration: none; + // border: 2px solid #d83902; + // @include border-radius(60px); + // @include transition(all 0.5s); + // &:hover { + // background: #f7f7f7; + // } + // } + // &_code { + // // background: #f4f4f4 !important; + // color: #8b8b8b !important; + // border-color:#cecece !important; + // &:hover { + // background: #ebebeb !important; + // } + // } + // } diff --git a/packages/ferreiro-client/src/components/projectCard/ProjectCard.js b/packages/ferreiro-client/src/components/projectCard/ProjectCard.js new file mode 100644 index 00000000..ff1ddc60 --- /dev/null +++ b/packages/ferreiro-client/src/components/projectCard/ProjectCard.js @@ -0,0 +1,248 @@ +import React from 'react' +import isEmpty from 'lodash/isEmpty' + +import './ProjectCard.scss' +import { Link } from '../link/Link'; + +const renderFeatures = (features = [], hex) => ( +
+

+ Features +

+
    + {features.map((feature) => ( +
  • +
    + +
    +

    + {feature} +

    +
  • + ))} +
+
+) + +const renderHightlight = (highlights = [], hex) => ( +
+

+ Highlights +

+
    + {highlights.map((highlight) => ( +
  • +
    + +
    +

    +

  • + ))} +
+
+) + +const renderStack = (stack) => ( +
+

+ Technology stack +

+
    +
  • + {stack.map((technology) => { + if (technology === "phonegap") { + return ( + + ) + } else if (technology === "bower") { + return + } else if (technology === "less") { + return + } else { + return + } +

    + {technology} +

    + })} +
  • +
+
+) + +export const ProjectCard = ({ + about, + avatar, + color: { + hex, + rgba_gradient_start, + rgba_gradient_end, + } = {}, + date, + images, + title, + links, + people, + stack, + features, + highlights, +}) => { + + console.log(avatar) + + return ( +
+
+ {hex && ( +
+ )} +
+
+
+ {avatar && ( +
+ +
+ )} +
+
+
+ {title && ( +
+ {links ? ( + + {title} + + ) : ( + + {title} + + )} +
+ )} + {date && ( + + {date} + + )} +
+
+ +
+
+
+ + {isEmpty(images) && ( +
+
+
+
+

+ +
+
+ {project.images.map(({ + small, + large, + }) => ( +
+ +
+ ))} + +
+
+
+ )} + +
+ {about && ( +

+ About {title} +

+ )} + + {about.length > 0 ? ( + about.map((paragraph) => ( +

+ )) + ) : ( +

+ {about} +

+ )} + + {people && people.hidden === false && ( +
+

+ Team +

+ +
+ )} + + {highlights && renderHightlight(highlights, hex)} + {features && renderFeatures(features, hex)} + {stack && renderStack(stack)} + + + //- Project Body + if project.links + //-a.action_button(href= project.links.website, target="_blank") Check out the project! + .project_links + if project.links.website + a(href= project.links.website, target="_blank").project_links_website(style="color: " + project.color.hex + "; border-color:" + project.color.hex + ";") Check this out + if project.links.code + a(href= project.links.code, target="_blank").project_links_code Source code + +
+
+ ) +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/projectCard/ProjectCard.scss b/packages/ferreiro-client/src/components/projectCard/ProjectCard.scss new file mode 100644 index 00000000..1d6aaf62 --- /dev/null +++ b/packages/ferreiro-client/src/components/projectCard/ProjectCard.scss @@ -0,0 +1,212 @@ +@import "../mediaQueries.scss"; +@import "../mixins.scss"; + +$projectBorderRadious: 10px; +$projectHeaderHeight: 60px; +$projectHeaderMargin: 1em; +$projectHeaderAvatarSize: 60px; +$projectHeaderTitleSize: 1.2em; +$projectHeaderDateSize: 1em; +$projectHeaderGalleryHeight: 122px; +$projectHeaderGalleryGradientWidth: 2em; +$projectHeaderGalleryRadius: 7px; +$projectBodyPadding_mobile: 16px; +$projectBodyPadding_desktop: 24px; +$projectLinksHeight: 52px; +$projectLinksTopMargin: 20px; + +.projects { + @include large-up { + -webkit-column-count: 2; + -webkit-column-gap: 1.5em; + -moz-column-count: 2; + -moz-column-gap: 1.5em; + column-count: 2; + column-gap: 1.5em; + } +} + +.project { + border: 1px solid #f0f0f0; + border-radius: $projectBorderRadious; + display: flex; + height: 100%; + overflow: hidden; + position: relative; + margin-bottom: 1.5em; + flex-direction: column; + + @include large { + background: #fefefe; + margin: 0; + } + + &_bg { + top: 0; + left: 0; + z-index: 0; + width: 100%; + height: 100%; + overflow: hidden; + position: absolute; + border-radius: $projectBorderRadious; + + &_image { + width: 100%; + height: 100%; + background-size: cover; + -webkit-filter: blur($projectBorderRadious); + -moz-filter: blur($projectBorderRadious); + -ms-filter: blur($projectBorderRadious); + -o-filter: blur($projectBorderRadious); + filter: blur($projectBorderRadious); + + &::before { + width: 100%; + height: 100%; + content: ""; + background: rgba(0, 0, 0, 0.2); + } + } + + &_solid { + width: 100%; + height: 100%; + opacity: 0.9; + position: absolute; + top: 0; + } + } + + &_header { + z-index: 3; + position: relative; + min-height: $projectHeaderHeight; + padding: $projectHeaderMargin; + background: rgba(0, 0, 0, 0.3); + + @include border-radiuses($projectBorderRadious, $projectBorderRadious, 0, 0); + + @include large-up { + padding: $projectHeaderMargin; + } + + &_left { + left: 1em; + position: absolute; + width: $projectHeaderHeight; + max-width: $projectHeaderHeight; + } + + &_right { + margin-left: $projectHeaderHeight + 14px; + } + + &_avatar { + border-radius: 100%; + width: $projectHeaderAvatarSize; + height: $projectHeaderAvatarSize; + overflow: hidden; + background-size: cover; + display: inline-block; + vertical-align: middle; + margin-right: $projectHeaderMargin; + + @include large-up { + width: $projectHeaderAvatarSize; + height: $projectHeaderAvatarSize; + } + + img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + } + } + + &_data { + width: 100%; + display: inline-block; + vertical-align: middle; + margin-top: 5px; + } + + &_title { + color: #fff; + font-size: 1.2em; + font-weight: 600; + margin-bottom: 0.3em; + text-decoration: none; + + @include large-up { + font-size: $projectHeaderTitleSize; + } + + &_clickable:hover { + text-decoration: underline; + } + } + + &_date { + color: #fff; + font-weight: 300; + @include large-up { + font-size: $projectHeaderDateSize; + } + } + + &_share { + background: rgba(255, 255, 255, 0.3); + border-radius: $projectBorderRadious; + cursor: pointer; + top: 50%; + right: 1em; + padding: 10px; + display: none; + position: absolute; + transform: translate(0, -50%); + + @include large-up { + font-size: 1.2em; + padding: 1em; + } + } + } + + &_gallery { + z-index: 10; + position: relative; + height: $projectHeaderGalleryHeight; + background: rgba(0, 0, 0, 0.5); + + &_gradient { + width: $projectHeaderGalleryGradientWidth; + height: 100%; + position: absolute; + z-index: 100; + right: 0; + top: 0; + + @include opacity(0.3); + + &_solid { + width: $projectHeaderGalleryGradientWidth; + height: 100%; + position: absolute; + z-index: 100; + right: 0; + top: 0; + } + &:after { + content: ''; + width: $projectHeaderGalleryGradientWidth; + height: 100%; + position: absolute; + z-index: 100; + right: 0; + top: 0; + } + } + } +} diff --git a/packages/ferreiro-client/src/components/recentArticles/RecentArticles.js b/packages/ferreiro-client/src/components/recentArticles/RecentArticles.js new file mode 100644 index 00000000..bfd55c74 --- /dev/null +++ b/packages/ferreiro-client/src/components/recentArticles/RecentArticles.js @@ -0,0 +1,102 @@ +import React, {PureComponent} from 'react' +import PropTypes from 'prop-types' +import isEmpty from 'lodash/isEmpty' +import {Waypoint} from 'react-waypoint'; + +import {CardRelatedPost} from '../cardRelatedPost/CardRelatedPost'; +import {fetchApi} from '../../utils/fetchApi'; + +import {ERROR_FETCHING_CONTENT} from '../../types/enums'; + +import './RecentArticles.scss' + +export class RecentArticles extends PureComponent { + static propTypes = { + permalink: PropTypes.string.isRequired, + } + + state = { + posts: [], + isLoading: false, + error: {}, + } + + onEnterWaypoint = () => { + const {posts} = this.state + + if (isEmpty(posts)) { + this.loadposts() + } + } + + startFetchingPosts = () => { + this.setState({isLoading: true}) + } + + stopFetchingPosts = () => { + this.setState({isLoading: false}) + } + + loadposts = () => { + // TODO: Cache results first, so avoid re-fetching + // posts lists in the same session. + // TODO: Put the url in CONSTANT + fetchApi('/api/v1/blog/list?limit=3', { + onStart: () => { + this.startFetchingPosts() + }, + onSuccess: ({posts = []}) => { + this.setState({posts}) + }, + onError: (_) => { + this.setState({ + error: ERROR_FETCHING_CONTENT + }) + }, + onFinish: () => { + this.stopFetchingPosts() + } + }) + } + + renderPosts = (posts) => ( + posts.map((post) => ( +
+ +
+ )) + ) + + render() { + const {posts, isLoading} = this.state + + return ( + +
+ {isLoading && ( +

Loading...

+ )} + + {posts.length === 0 && ( +

Currently we don't have more related posts.

+ )} + + {posts.length > 0 && ( +
+
+ {this.renderPosts(posts)} +
+
+ )} +
+
+ ) + } +} diff --git a/packages/ferreiro-client/src/components/recentArticles/RecentArticles.scss b/packages/ferreiro-client/src/components/recentArticles/RecentArticles.scss new file mode 100644 index 00000000..3768c37b --- /dev/null +++ b/packages/ferreiro-client/src/components/recentArticles/RecentArticles.scss @@ -0,0 +1,34 @@ +@import '../mediaQueries.scss'; + +.recent-article { + min-height: 300px; + display: block; + + @include large-up { + margin: 0 -.8em; + } + + &__items { + align-items: stretch; + flex-wrap: wrap; + flex-direction: column; + display: flex; + + @include large-up { + flex-direction: row; + min-height: 370px; + } + } + + &__item { + flex: 1 1 auto; + + @include large-up { + max-width: 33%; + + .card-related-post { + margin: 0 .8em; + } + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/recentTalks/RecentTalks.js b/packages/ferreiro-client/src/components/recentTalks/RecentTalks.js new file mode 100644 index 00000000..bd4e4e52 --- /dev/null +++ b/packages/ferreiro-client/src/components/recentTalks/RecentTalks.js @@ -0,0 +1,7 @@ +import React, {PureComponent} from 'react' + +export const RecentTalks = () => ( +
+ List of talks... +
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/recentVideos/RecentVideos.js b/packages/ferreiro-client/src/components/recentVideos/RecentVideos.js new file mode 100644 index 00000000..51cba1e8 --- /dev/null +++ b/packages/ferreiro-client/src/components/recentVideos/RecentVideos.js @@ -0,0 +1,60 @@ +import './RecentVideos.scss' + +import React, {PureComponent} from 'react' +import {Waypoint} from 'react-waypoint' + +import {CardRelatedVideo} from '../cardRelatedVideo/CardRelatedVideo' +import {getPageData, PAGE_ENTITIES} from '../../content/english'; +import {PATH_VIDEOS} from '../../pages/constants'; + +const MAX_RECENT_VIDEOS = 3 + +const videosEntities = getPageData(PATH_VIDEOS)[PAGE_ENTITIES] +const videos = Object.values(videosEntities).slice(0, MAX_RECENT_VIDEOS) + +const renderVideos = (videos = []) => ( + videos.map((video) => ( +
+ +
+ )) +) + +export class RecentVideos extends PureComponent { + state = { + isVisible: false, + } + + onEnter = () => { + this.setState({isVisible: true}) + } + + renderContent = () => ( +
+ {videos.length === 0 && ( +

Currently we don't have any other video.

+ )} + + {videos.length > 0 && ( +
+
+ {renderVideos(videos)} +
+
+ )} +
+ ) + + render() { + return ( + + {this.state.isVisible && ( + this.renderContent() + )} + + ) + } +} + \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/recentVideos/RecentVideos.scss b/packages/ferreiro-client/src/components/recentVideos/RecentVideos.scss new file mode 100644 index 00000000..d61c1059 --- /dev/null +++ b/packages/ferreiro-client/src/components/recentVideos/RecentVideos.scss @@ -0,0 +1,36 @@ +@import '../mediaQueries.scss'; +@import '../spacing.scss'; + +.recent-videos { + min-height: 300px; + display: block; + + @include large-up { + margin: 0 -.8em; + } + + &__items { + align-items: stretch; + flex-wrap: wrap; + flex-direction: column; + display: flex; + + @include large-up { + flex-direction: row; + } + } + + &__item { + flex: 1 1 auto; + margin-bottom: $spacing-4; + + @include large-up { + max-width: 33%; + margin-bottom: 0; + + .card-related-post { + margin: 0 .8em; + } + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/sidebarMenu/SidebarMenu.js b/packages/ferreiro-client/src/components/sidebarMenu/SidebarMenu.js new file mode 100644 index 00000000..2c396a1d --- /dev/null +++ b/packages/ferreiro-client/src/components/sidebarMenu/SidebarMenu.js @@ -0,0 +1,40 @@ +import React from 'react' +import {Link} from 'react-router-dom' + +import './SidebarMenu.scss' + +export const SidebarMenu = ({ + onClick, + selectedCategory, + title = null, + items, +}) => { + return ( +
+ {title && ( +

+ {title} +

+ )} + +
    + {items.map(({ + text, + icon, + path, + category + }) => { + const classNames = category === selectedCategory && 'selected' + + return ( +
  • + + {text} + +
  • + ) + })} +
+
+ ) +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/sidebarMenu/SidebarMenu.scss b/packages/ferreiro-client/src/components/sidebarMenu/SidebarMenu.scss new file mode 100644 index 00000000..05b0092d --- /dev/null +++ b/packages/ferreiro-client/src/components/sidebarMenu/SidebarMenu.scss @@ -0,0 +1,37 @@ +.sidebar-menu { + width: calc(100% - 1em); + margin: 2em 0 2em; + margin-left: 1em; + + &__title { + font-size: 20px; + margin-bottom: 18px; + margin-left: 24px; + } + + &__list { + display: flex; + flex-direction: column; + } + + &__item { + list-style: none; + flex: 1 1 auto; + + a { + display: block; + padding: 1em 1.5em; + } + } + + .selected { + border-top-left-radius: 100px; + border-bottom-left-radius: 100px; + background: #d8f2ff; + + a { + color: #33454e; + font-weight: 600; + } + } +} \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/sidebarNewsletterAd/SidebarNewsletterAd.js b/packages/ferreiro-client/src/components/sidebarNewsletterAd/SidebarNewsletterAd.js new file mode 100644 index 00000000..cefc3529 --- /dev/null +++ b/packages/ferreiro-client/src/components/sidebarNewsletterAd/SidebarNewsletterAd.js @@ -0,0 +1,23 @@ +import React from 'react' +// import {Link} from 'react-router-dom' + +// TODO: Substitute Link with react-router-dom +import {Link} from '../link/Link' + +export const SidebarNewsletterAd = ({ + referrer = '' +}) => ( + + + + Subscribe to the blog + + +) diff --git a/packages/ferreiro-client/src/components/sidebarSeparator/SidebarSeparator.js b/packages/ferreiro-client/src/components/sidebarSeparator/SidebarSeparator.js new file mode 100644 index 00000000..4cd07ef7 --- /dev/null +++ b/packages/ferreiro-client/src/components/sidebarSeparator/SidebarSeparator.js @@ -0,0 +1,9 @@ +import React from 'react' + +export const SidebarSeparator = () => ( +
+) \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/spacing.scss b/packages/ferreiro-client/src/components/spacing.scss new file mode 100644 index 00000000..5a17104a --- /dev/null +++ b/packages/ferreiro-client/src/components/spacing.scss @@ -0,0 +1,9 @@ +$spacing-1: 4px; +$spacing-2: 8px; +$spacing-3: 16px; +$spacing-4: 24px; +$spacing-5: 32px; +$spacing-6: 40px; +$spacing-7: 64px; +$spacing-8: 86px; +$spacing-9: 128px; \ No newline at end of file diff --git a/packages/ferreiro-client/src/components/talkListItem/TalkListItem.js b/packages/ferreiro-client/src/components/talkListItem/TalkListItem.js new file mode 100644 index 00000000..e6d01d61 --- /dev/null +++ b/packages/ferreiro-client/src/components/talkListItem/TalkListItem.js @@ -0,0 +1,163 @@ +import React from 'react' +import {LazyLoadImage} from 'react-lazy-load-image-component' +import isEmpty from 'lodash/isEmpty' +import {translate} from '../../i18-me/i18-me' + +import {BUTTON_SIZE_MEDIUM, TARGET_BLANK, BUTTON_STYLE_OUTLINE} from '../../components/constants' +import {Button} from '../../components/buttons/Button' + +import {BUTTON_TYPE_SLIDES, BUTTON_TYPE_VIDEO} from '../../content/english' +import {Link} from '../link/Link' + +import './TalkListItem.scss' + +const renderButton = ({title, type, url}, SEOTitle) => { + if (type === BUTTON_TYPE_SLIDES) { + const text = translate('Read slides') + // icon-stack + const icon = ( + + ) + + return ( +