From 8cae589f24147def7e3414aa753d9619522e6f40 Mon Sep 17 00:00:00 2001 From: Brian Lovin Date: Sat, 12 Jan 2019 23:00:23 -0800 Subject: [PATCH] First section implemenation pass --- components/Card/index.js | 8 +- components/Card/style.js | 8 - components/Checklist/index.js | 21 ++ components/Checklist/style.js | 9 + components/ChecklistItem/App.js | 82 ++++++++ components/ChecklistItem/Apps.js | 17 ++ components/ChecklistItem/Heading.js | 16 ++ components/ChecklistItem/Resource.js | 20 ++ components/ChecklistItem/Resources.js | 18 ++ components/ChecklistItem/index.js | 69 +++++++ components/ChecklistItem/style.js | 240 +++++++++++++++++++++++ components/ChecklistItem/utils.js | 10 + components/Icon/index.js | 27 +++ components/LoadingChecklistItem/index.js | 74 +++++++ components/LoadingChecklistItem/style.js | 58 ++++++ components/Page/style.js | 6 +- config/data.js | 5 + config/passwordManager.js | 47 +++++ pages/index.js | 13 +- static/img/1password.jpg | Bin 0 -> 23971 bytes static/img/lastpass.jpg | Bin 0 -> 9133 bytes types/index.js | 27 +++ 22 files changed, 761 insertions(+), 14 deletions(-) create mode 100644 components/Checklist/index.js create mode 100644 components/Checklist/style.js create mode 100644 components/ChecklistItem/App.js create mode 100644 components/ChecklistItem/Apps.js create mode 100644 components/ChecklistItem/Heading.js create mode 100644 components/ChecklistItem/Resource.js create mode 100644 components/ChecklistItem/Resources.js create mode 100644 components/ChecklistItem/index.js create mode 100644 components/ChecklistItem/style.js create mode 100644 components/ChecklistItem/utils.js create mode 100644 components/LoadingChecklistItem/index.js create mode 100644 components/LoadingChecklistItem/style.js create mode 100644 config/data.js create mode 100644 config/passwordManager.js create mode 100644 static/img/1password.jpg create mode 100644 static/img/lastpass.jpg diff --git a/components/Card/index.js b/components/Card/index.js index 0be2ca7..ac88284 100644 --- a/components/Card/index.js +++ b/components/Card/index.js @@ -8,6 +8,10 @@ type Props = { }; export default function Card(props: Props) { - const { style, children } = props; - return {children}; + const { style, children, ...rest } = props; + return ( + + {children} + + ); } diff --git a/components/Card/style.js b/components/Card/style.js index 109117d..2f789ae 100644 --- a/components/Card/style.js +++ b/components/Card/style.js @@ -7,12 +7,4 @@ export const StyledCard = styled.div` background: ${props => props.theme.bg.default}; border-radius: 6px; ${Shadows.default}; - - &:hover { - ${Shadows.hover}; - } - - &:active { - ${Shadows.active}; - } `; diff --git a/components/Checklist/index.js b/components/Checklist/index.js new file mode 100644 index 0000000..41c8f81 --- /dev/null +++ b/components/Checklist/index.js @@ -0,0 +1,21 @@ +// @flow +import React from 'react'; +import data from '../../config/data'; +import ChecklistItem from '../ChecklistItem'; +import { Grid } from './style'; + +class Checklist extends React.Component<{}> { + render() { + const keys = Object.keys(data); + const resources = keys.map(k => data[k]); + return ( + + {resources.map(resource => ( + + ))} + + ); + } +} + +export default Checklist; diff --git a/components/Checklist/style.js b/components/Checklist/style.js new file mode 100644 index 0000000..535ac4f --- /dev/null +++ b/components/Checklist/style.js @@ -0,0 +1,9 @@ +// @flow +import styled from 'styled-components'; + +export const Grid = styled.div` + display: grid; + grid-template-columns: 1fr; + margin-top: 32px; + width: 100%; +`; diff --git a/components/ChecklistItem/App.js b/components/ChecklistItem/App.js new file mode 100644 index 0000000..bb477ac --- /dev/null +++ b/components/ChecklistItem/App.js @@ -0,0 +1,82 @@ +// @flow +import React from 'react'; +import type { App } from '../../types'; +import { + AppMeta, + AppRowContainer, + AppIcon, + AppName, + AppSourcesList, + AppSourcesListItem, + AppSourcesLabel, +} from './style'; +import Icon from '../Icon'; + +type Props = { + app: App, +}; + +export const AppRow = ({ app }: Props) => { + const sourcesKeys = Object.keys(app.sources); + + const renderSourceIcon = (key: string) => { + const sourceUrl = app.sources[key]; + const appUrl = app.url; + + if (key === 'windows') { + return ( + + + + Windows + + + ); + } + + if (key === 'ios') { + return ( + + + + iOS + + + ); + } + + if (key === 'macos') { + return ( + + + + macOS + + + ); + } + + if (key === 'android') { + return ( + + + + Android + + + ); + } + + return null; + }; + + return ( + + + + {app.name} + + {sourcesKeys.map(renderSourceIcon)} + + ); +}; diff --git a/components/ChecklistItem/Apps.js b/components/ChecklistItem/Apps.js new file mode 100644 index 0000000..a37496c --- /dev/null +++ b/components/ChecklistItem/Apps.js @@ -0,0 +1,17 @@ +// @flow +import React from 'react'; +import { AppsContainer } from './style'; +import type { ChecklistResource } from '../../types'; +import { AppRow } from './App'; + +type Props = { + resource: ChecklistResource, +}; + +export const Apps = ({ resource }: Props) => ( + + {resource.apps.map(app => ( + + ))} + +); diff --git a/components/ChecklistItem/Heading.js b/components/ChecklistItem/Heading.js new file mode 100644 index 0000000..7e3add0 --- /dev/null +++ b/components/ChecklistItem/Heading.js @@ -0,0 +1,16 @@ +// @flow +import React from 'react'; +import { Title, Description } from './style'; +import type { ChecklistResource } from '../../types'; + +type Props = { + resource: ChecklistResource, + isChecked: boolean, +}; + +export const Heading = ({ resource, isChecked }: Props) => ( + + {resource.title} + {resource.description} + +); diff --git a/components/ChecklistItem/Resource.js b/components/ChecklistItem/Resource.js new file mode 100644 index 0000000..dd20fff --- /dev/null +++ b/components/ChecklistItem/Resource.js @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import type { Resource } from '../../types'; +import { ResourceRowContainer, ResourceName } from './style'; +import Icon from '../Icon'; + +type Props = { + resource: Resource, +}; + +export const ResourceRow = ({ resource }: Props) => ( + + + {resource.name} + +); diff --git a/components/ChecklistItem/Resources.js b/components/ChecklistItem/Resources.js new file mode 100644 index 0000000..824c25e --- /dev/null +++ b/components/ChecklistItem/Resources.js @@ -0,0 +1,18 @@ +// @flow +import React from 'react'; +import { SectionHeading } from './style'; +import type { ChecklistResource } from '../../types'; +import { ResourceRow } from './Resource'; + +type Props = { + resource: ChecklistResource, +}; + +export const Resources = ({ resource }: Props) => ( + + More Resources + {resource.resources.map(r => ( + + ))} + +); diff --git a/components/ChecklistItem/index.js b/components/ChecklistItem/index.js new file mode 100644 index 0000000..5d51c14 --- /dev/null +++ b/components/ChecklistItem/index.js @@ -0,0 +1,69 @@ +// @flow +import React from 'react'; +import type { ChecklistResource } from '../../types'; +import { getCheckedStatusById, setCheckedStatusById } from './utils'; +import Card from '../Card'; +import LoadingChecklistItem from '../LoadingChecklistItem'; +import { Heading } from './Heading'; +import { Apps } from './Apps'; +import { Resources } from './Resources'; +import { + Container, + CheckboxContainer, + Checkbox, + CardContent, + ResourceContent, +} from './style'; + +type Props = { + resource: ChecklistResource, +}; + +type State = { + isChecked: boolean, + isLoading: boolean, +}; + +class ChecklistItem extends React.Component { + state = { isChecked: false, isLoading: true }; + + componentDidMount() { + const { resource } = this.props; + const isChecked = getCheckedStatusById(resource.id); + return this.setState({ isChecked, isLoading: false }); + } + + handleSetChecked = () => { + const { isChecked } = this.state; + const { resource } = this.props; + setCheckedStatusById(resource.id, !isChecked); + return this.setState({ isChecked: !isChecked }); + }; + + render() { + const { isChecked, isLoading } = this.state; + const { resource } = this.props; + + if (isLoading) return ; + + return ( + + + + + + + + + + + + + + + + ); + } +} + +export default ChecklistItem; diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js new file mode 100644 index 0000000..f518ed8 --- /dev/null +++ b/components/ChecklistItem/style.js @@ -0,0 +1,240 @@ +// @flow +import styled from 'styled-components'; +import { theme } from '../theme'; +import { Shadows } from '../globals'; + +export const Container = styled.div` + margin-bottom: 24px; +`; + +export const CardContent = styled.div` + padding: 24px 16px; + display: flex; + width: 100%; + + @media (max-width: 768px) { + flex-direction: column; + } +`; + +export const Title = styled.h3` + font-size: 18px; + font-weight: 500; + color: ${theme.text.default}; +`; + +export const Description = styled.h4` + font-size: 16px; + font-weight: 400; + color: ${theme.text.secondary}; +`; + +export const SectionHeading = styled.h5` + font-size: 12px; + font-weight: 500; + color: ${theme.text.tertiary}; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-top: 32px; +`; + +export const CheckboxContainer = styled.div` + display: flex; + align-items: flex-start; + width: 48px; + justify-content: center; +`; + +export const Checkbox = styled.span` + width: 32px; + height: 32px; + border-radius: 4px; + border: 1px solid + ${props => (props.isChecked ? theme.brand.default : theme.border.default)}; + background: ${props => + props.isChecked ? theme.brand.default : theme.bg.wash}; + cursor: pointer; + position: relative; + + &:hover { + ${Shadows.default}; + border: 1px solid + ${props => (props.isChecked ? theme.brand.default : theme.border.active)}; + } + + &:after { + content: ''; + position: absolute; + display: block; + left: 11px; + top: 6px; + width: 6px; + height: 12px; + border: solid + ${props => (props.isChecked ? theme.bg.default : theme.border.active)}; + border-width: 0 2px 2px 0; + transform: rotate(45deg); + } +`; + +export const ResourceContent = styled.div` + display: flex; + flex-direction: column; + padding-left: 16px; + width: 100%; + + @media (max-width: 768px) { + padding-left: 8px; + margin-top: 24px; + } +`; + +export const AppsContainer = styled.div` + margin-top: 8px; + + @media (max-width: 768px) { + margin-top: 24px; + } +`; + +export const AppMeta = styled.div` + display: flex; + align-items: center; + + @media (max-width: 768px) { + width: 100%; + padding-bottom: 16px; + margin-bottom: 16px; + } +`; + +export const AppRowContainer = styled.a` + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 8px; + width: 100%; + border-radius: 6px; + padding: 8px; + padding-left: 16px; + margin-left: -16px; + width: calc(100% + 12px); + flex-wrap: wrap; + + @media (max-width: 768px) { + border-top: 1px solid ${theme.bg.wash}; + border-radius: 0; + margin-top: 0px; + margin-left: -24px; + width: calc(100% + 40px); + padding: 24px; + + &:last-of-type { + border-bottom: 1px solid ${theme.bg.wash}; + } + } + + &:hover { + background: ${theme.bg.wash}; + } +`; + +export const AppIcon = styled.img` + width: 40px; + height: 40px; + border-radius: 8px; + margin-right: 12px; +`; + +export const AppName = styled.p` + font-size: 16px; + font-weight: 400; + color: ${theme.text.secondary}; +`; + +export const AppSourcesList = styled.ul` + list-style-type: none; + display: flex; + align-items: center; + margin-right: 8px; + + @media (max-width: 768px) { + width: 100%; + margin-right: 0; + justify-content: space-between; + } +`; + +export const AppSourcesListItem = styled.li` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: ${theme.text.tertiary}; + margin-left: 4px; + padding: 4px 8px; + border-radius: 4px; + min-width: 56px; + transition: all 0.1s ease-in-out; + + a { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .icon { + position: relative; + top: 4px; + left: 6px; + } + + &:hover { + color: ${theme.text.secondary}; + background: ${theme.bg.default}; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08); + transition: all 0.1s ease-in-out; + } +`; + +export const AppSourcesLabel = styled.span` + font-size: 12px; + font-weight: 400; + color: ${theme.text.tertiary}; + margin-top: 4px; +`; + +export const ResourceRowContainer = styled.a` + display: flex; + align-items: center; + margin-top: 4px; + width: 100%; + border-radius: 6px; + padding: 8px 12px; + margin-left: -12px; + color: ${theme.text.tertiary}; + width: calc(100% + 8px); + + @media (max-width: 768px) { + width: calc(100% + 16px); + } + + &:first-of-type { + margin-top: 8px; + } + + .icon { + margin-right: 8px; + } + + &:hover { + background: ${theme.bg.wash}; + color: ${theme.text.default}; + } +`; + +export const ResourceName = styled.p` + font-size: 16px; + font-weight: 400; +`; diff --git a/components/ChecklistItem/utils.js b/components/ChecklistItem/utils.js new file mode 100644 index 0000000..7058f71 --- /dev/null +++ b/components/ChecklistItem/utils.js @@ -0,0 +1,10 @@ +// @flow +import { + getItemFromStorage, + storeItem, + removeItemFromStorage, +} from '../../lib/localStorage'; + +export const getCheckedStatusById = (id: string) => getItemFromStorage(id); +export const setCheckedStatusById = (id: string, store: boolean) => + store ? storeItem(id, true) : removeItemFromStorage(id); diff --git a/components/Icon/index.js b/components/Icon/index.js index 1325476..7f8bbba 100644 --- a/components/Icon/index.js +++ b/components/Icon/index.js @@ -72,6 +72,33 @@ export const Glyph = ({ glyph }: GlyphProps): any => { ); + case 'apple': + return ( + + + + ); + case 'app-store': + return ( + + + + ); + case 'windows': + return ( + + + + ); + case 'android': + return ( + + + + + + + ); default: return null; } diff --git a/components/LoadingChecklistItem/index.js b/components/LoadingChecklistItem/index.js new file mode 100644 index 0000000..c1ec06a --- /dev/null +++ b/components/LoadingChecklistItem/index.js @@ -0,0 +1,74 @@ +// @flow +import React from 'react'; +import { ShimmerInboxThread, ShimmerBase, ShimmerLine, Cover } from './style'; +import Card from '../Card'; + +const LoadingChecklistItem = () => ( + + + + + + + + + + + + + + + + +); + +export default LoadingChecklistItem; diff --git a/components/LoadingChecklistItem/style.js b/components/LoadingChecklistItem/style.js new file mode 100644 index 0000000..c8b7d71 --- /dev/null +++ b/components/LoadingChecklistItem/style.js @@ -0,0 +1,58 @@ +// @flow +import styled, { keyframes } from 'styled-components'; +import { theme } from '../theme'; + +const placeHolderShimmer = keyframes` + 0%{ + transform: translateX(-100%) translateY(0%); + background-size: 100%; + opacity: 1; + } + 100%{ + transform: translateX(200%) translateY(0%); + background-size: 500%; + opacity: 0; + } +`; + +export const ShimmerInboxThread = styled.div` + padding: 16px; + + section { + min-height: 40px; + } + + &:last-of-type { + border-bottom: 0; + } +`; + +export const ShimmerBase = styled.section` + width: 100%; + height: 100%; + position: relative; + background: ${theme.bg.wash}; + overflow: hidden; +`; + +export const ShimmerLine = styled.span` + width: 100%; + height: 100%; + position: absolute; + animation-duration: 2.5s; + animation-fill-mode: forwards; + animation-iteration-count: infinite; + animation-timing-function: ease-in-out; + background: linear-gradient( + to right, + ${theme.bg.wash} 10%, + ${theme.border.active}, + ${theme.bg.wash} 30% + ); + animation-name: ${placeHolderShimmer}; +`; + +export const Cover = styled.span` + position: absolute; + background: ${theme.bg.default}; +`; diff --git a/components/Page/style.js b/components/Page/style.js index eed1b1a..eac5938 100644 --- a/components/Page/style.js +++ b/components/Page/style.js @@ -48,8 +48,9 @@ export const SectionHeading = styled.div` `; export const Heading = styled.h3` - font-size: 24px; + font-size: 32px; font-weight: 700; + line-height: 1.2; color: ${theme.text.default}; @media (max-width: 968px) { @@ -58,9 +59,10 @@ export const Heading = styled.h3` `; export const Subheading = styled.h4` - font-size: 18px; + font-size: 22px; font-weight: 400; color: ${theme.text.tertiary}; + margin-top: 8px; a { color: ${theme.text.default}; diff --git a/config/data.js b/config/data.js new file mode 100644 index 0000000..afb4ce4 --- /dev/null +++ b/config/data.js @@ -0,0 +1,5 @@ +import passwordManager from './passwordManager'; + +export default { + passwordManager, +}; diff --git a/config/passwordManager.js b/config/passwordManager.js new file mode 100644 index 0000000..a9f8a4e --- /dev/null +++ b/config/passwordManager.js @@ -0,0 +1,47 @@ +export default { + id: 'passwordManager', + title: 'Password Manager', + description: + 'A password manager helps you set unique passwords for each service you use. Having a unique password ensures that if one service you use is hacked, the leaked password won’t allow access to all of your accounts.', + apps: [ + { + name: '1Password', + image: '/static/img/1password.jpg', + url: 'https://1password.com/', + sources: { + windows: 'https://1password.com/downloads/windows/', + ios: 'https://1password.com/downloads/ios/', + android: 'https://1password.com/downloads/android/', + macos: 'https://1password.com/downloads/mac/', + website: 'https://1password.com/', + }, + }, + { + name: 'LastPass', + image: '/static/img/lastpass.jpg', + url: 'https://www.lastpass.com/', + sources: { + windows: + 'https://download.cloud.lastpass.com/windows_installer/lastpass.exe', + ios: + 'https://itunes.apple.com/us/app/lastpass-password-manager/id324613447', + android: 'https://lastpass.com/android_market.php', + macos: + 'https://itunes.apple.com/us/app/lastpass/id926036361?ls=1&mt=12', + website: 'https://www.lastpass.com/', + }, + }, + ], + resources: [ + { + name: 'How to use a password manager (and why you really should)', + url: + 'https://www.theverge.com/2017/7/24/15921282/best-password-manager-1password-lastpass-dashlane-how-to', + }, + { + name: 'How password managers work and why you should use one', + url: + 'https://motherboard.vice.com/en_us/article/59yv5x/how-password-managers-work-and-why-you-should-use-one', + }, + ], +}; diff --git a/pages/index.js b/pages/index.js index 02a101a..c442194 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,7 +1,8 @@ // @flow import * as React from 'react'; -import Page, { SectionHeading } from '../components/Page'; +import Page, { SectionHeading, Heading, Subheading } from '../components/Page'; import type { GetInitialProps } from '../types'; +import Checklist from '../components/Checklist'; class Index extends React.Component<{}> { static async getInitialProps({ res }: GetInitialProps) { @@ -17,7 +18,15 @@ class Index extends React.Component<{}> { render() { return ( - + + Improve your online privacy and security + + The follow resources are designed to improve everyday people’s + security and privacy while using the internet. + + + + ); } diff --git a/static/img/1password.jpg b/static/img/1password.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a1bd7e188f9f9df7b5e3d9013934ba807451ee12 GIT binary patch literal 23971 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 AX#fBK literal 0 HcmV?d00001 diff --git a/static/img/lastpass.jpg b/static/img/lastpass.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9fa0a332425467271403d9e932ae10e607786043 GIT binary patch literal 9133 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 NQQ-fs0?=*te*ub!Ie`EG literal 0 HcmV?d00001 diff --git a/types/index.js b/types/index.js index 2766434..a4c02f5 100644 --- a/types/index.js +++ b/types/index.js @@ -11,3 +11,30 @@ export type GetInitialProps = { getInitialProps?: (ctx: any) => void, }, }; + +export type Resource = { + name: string, + url: string, +}; + +export type AppSource = { + windows?: string, + ios?: string, + macos?: string, + anrdroid?: string, + website?: string, +}; + +export type App = { + name: string, + image: string, + sources: AppSource, +}; + +export type ChecklistResource = { + id: string, + title: string, + description: string, + apps?: ?Array, + resources?: ?Array, +};