├── public
├── favicon.ico
├── icon-128.png
├── icon-16.png
├── icon-24.png
├── icon-32.png
├── icon-48.png
├── fonts
│ ├── RobotoMono-Light.woff
│ ├── RobotoMono-Thin.woff
│ ├── RobotoMono-Thin.woff2
│ ├── RobotoMono-Light.woff2
│ ├── RobotoMono-Regular.woff
│ └── RobotoMono-Regular.woff2
├── manifest.json
└── index.html
├── src
├── App.test.js
├── reducers
│ ├── initialState.js
│ └── index.js
├── localStorage.js
├── actions
│ └── index.js
├── components
│ ├── SwitchButton.js
│ ├── styled-components
│ │ └── global.js
│ ├── button.js
│ ├── svg
│ │ └── dots.js
│ └── topBar.js
├── index.js
├── registerServiceWorker.js
└── App.js
├── .gitignore
├── index.html
├── README.md
└── package.json
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/icon-128.png
--------------------------------------------------------------------------------
/public/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/icon-16.png
--------------------------------------------------------------------------------
/public/icon-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/icon-24.png
--------------------------------------------------------------------------------
/public/icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/icon-32.png
--------------------------------------------------------------------------------
/public/icon-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/icon-48.png
--------------------------------------------------------------------------------
/public/fonts/RobotoMono-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/fonts/RobotoMono-Light.woff
--------------------------------------------------------------------------------
/public/fonts/RobotoMono-Thin.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/fonts/RobotoMono-Thin.woff
--------------------------------------------------------------------------------
/public/fonts/RobotoMono-Thin.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/fonts/RobotoMono-Thin.woff2
--------------------------------------------------------------------------------
/public/fonts/RobotoMono-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/fonts/RobotoMono-Light.woff2
--------------------------------------------------------------------------------
/public/fonts/RobotoMono-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/fonts/RobotoMono-Regular.woff
--------------------------------------------------------------------------------
/public/fonts/RobotoMono-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edkf/tabipsum/HEAD/public/fonts/RobotoMono-Regular.woff2
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 | build.pem
19 | build.crx
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/reducers/initialState.js:
--------------------------------------------------------------------------------
1 | import loremIpsum from 'lorem-ipsum'
2 |
3 | const content = loremIpsum({
4 | count: 11,
5 | units: 'words',
6 | sentenceLowerBound: 5,
7 | sentenceUpperBound: 15,
8 | paragraphLowerBound: 3,
9 | paragraphUpperBound: 7,
10 | format: 'plain',
11 | })
12 |
13 | export const initialState = {
14 | value: 14,
15 | darkmode: false,
16 | contentType: 'words',
17 | content
18 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
React App
--------------------------------------------------------------------------------
/src/localStorage.js:
--------------------------------------------------------------------------------
1 | export const loadState = () => {
2 | try {
3 | const serializedState = localStorage.getItem('state');
4 | if (serializedState == null) {
5 | return undefined
6 | }
7 | return JSON.parse(serializedState)
8 | } catch (err) {
9 | return undefined
10 | }
11 | }
12 |
13 | export const saveState = (state) => {
14 | try {
15 | const serializedState = JSON.stringify(state)
16 | localStorage.setItem('state', serializedState)
17 | } catch (err) {
18 |
19 | }
20 | }
--------------------------------------------------------------------------------
/src/actions/index.js:
--------------------------------------------------------------------------------
1 | export const CHANGE_UNIT = 'CHANGE_UNIT'
2 | export const UPDATE_VALUE = 'UPDATE_VALUE'
3 | export const SWITCH_UI_MODE = 'SWITCH_UI_MODE'
4 |
5 | export function changeUnit(contentType) {
6 | return {
7 | type: CHANGE_UNIT,
8 | contentType
9 | }
10 | }
11 |
12 | export function updateValue(value) {
13 | return {
14 | type: UPDATE_VALUE,
15 | value
16 | }
17 | }
18 |
19 | export function switchUIMode(darkmode) {
20 | return {
21 | type: SWITCH_UI_MODE,
22 | darkmode: !darkmode
23 | }
24 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |

3 |
4 |
5 | ## Get Tab Ipsum
6 |
7 | - **[Mac App](https://assets.edkf.com.br/TabIpsum-1.0.0.dmg.zip)**
8 | - **[Chrome extension](https://chrome.google.com/webstore/detail/tabipsum/kodnpaacnfpgeakliedgocnfoeiajghp)**
9 |
10 |
11 | ## Contribute
12 |
13 | - `yarn` (`npm run install`) Install dependencies.
14 | - `yarn start` (`npm run start`) Runs the app in development mode.
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tab-ipsum-app",
3 | "version": "1.0.0",
4 | "private": true,
5 | "dependencies": {
6 | "lorem-ipsum": "^1.0.4",
7 | "react": "^16.2.0",
8 | "react-clipboard.js": "^1.1.3",
9 | "react-dom": "^16.2.0",
10 | "react-input-range": "^1.3.0",
11 | "react-redux": "^5.0.7",
12 | "react-scripts": "1.1.1",
13 | "redux": "^4.0.0",
14 | "styled-components": "^3.2.3"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test --env=jsdom",
20 | "eject": "react-scripts eject"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "name": "TabIpsum",
4 | "short_name": "TabIpsum - Lorem Ipsum Generator",
5 | "version": "1.0",
6 | "description": "Just a Lorem Ipsum generator.",
7 | "author": "Edgard Kozlowski (http://edkf.com.br)",
8 | "commands": {
9 | "_execute_browser_action": {}
10 | },
11 | "icons": {
12 | "16": "icon-16.png",
13 | "32": "icon-32.png",
14 | "48": "icon-48.png",
15 | "128": "icon-128.png"
16 | },
17 | "browser_action": {
18 | "default_icon": {
19 | "16": "icon-16.png",
20 | "24": "icon-24.png",
21 | "32": "icon-32.png"
22 | },
23 | "default_popup": "index.html"
24 | },
25 | "permissions": [
26 | "tabs"
27 | ]
28 | }
--------------------------------------------------------------------------------
/src/components/SwitchButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | width: 35px;
6 | height: 20px;
7 | background: ${props => props.darkmode ? '#2FAADC' : '#EEEEEE'};
8 | border-radius: 20px;
9 | position: relative;
10 | `
11 |
12 | const Circle = styled.div`
13 | width: 16px;
14 | height: 16px;
15 | border-radius: 15px;
16 | background: #FFFFFF;
17 | position: relative;
18 | top: 2px;
19 | transition: transform .3s ease;
20 | transform: ${props => props.darkmode ? 'translateX(16px)' : 'translateX(3px)'};
21 | `
22 |
23 | const SwitchButton = (props) => {
24 | return (
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default SwitchButton
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | import React from 'react'
3 | import ReactDOM from 'react-dom'
4 | import { Provider } from 'react-redux'
5 | import { createStore } from 'redux'
6 | import { loadState, saveState } from './localStorage.js'
7 |
8 |
9 | // Components
10 | import App from './App'
11 |
12 | // Reducers
13 | import reducers from './reducers'
14 |
15 | // Styled-components
16 | import './components/styled-components/global.js'
17 |
18 | const persistedState = loadState()
19 |
20 | // Create Redux Store
21 | const store = createStore(
22 | reducers,
23 | persistedState,
24 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // Enable Redux Devtools
25 | )
26 |
27 | store.subscribe(() => {
28 | saveState(store.getState())
29 | })
30 |
31 | ReactDOM.render(
32 |
33 |
34 |
35 | , document.getElementById('root'));
36 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | import loremIpsum from 'lorem-ipsum'
3 |
4 | // Action types
5 | import {CHANGE_UNIT, UPDATE_VALUE, SWITCH_UI_MODE } from '../actions'
6 |
7 | // Initial state
8 | import { initialState } from './initialState'
9 |
10 | export default function reducers(state = initialState, action) {
11 | switch (action.type) {
12 | case CHANGE_UNIT:
13 | const { contentType } = action
14 | return {
15 | ...state,
16 | contentType,
17 | content: loremIpsum({
18 | ...state.content,
19 | units: contentType,
20 | count: state.value
21 | }),
22 | }
23 | case UPDATE_VALUE:
24 | const { value } = action
25 | return {
26 | ...state,
27 | value: value,
28 | content: loremIpsum({
29 | ...state.content,
30 | units: state.contentType,
31 | count: value
32 | })
33 | }
34 | case SWITCH_UI_MODE:
35 | return {
36 | ...state,
37 | darkmode: !state.darkmode
38 | }
39 | default:
40 | return state
41 | }
42 | }
--------------------------------------------------------------------------------
/src/components/styled-components/global.js:
--------------------------------------------------------------------------------
1 | import { injectGlobal } from 'styled-components'
2 |
3 | injectGlobal`
4 | *, *:before, *:after {
5 | -webkit-box-sizing: border-box;
6 | -moz-box-sizing: border-box;
7 | box-sizing: border-box;
8 | }
9 |
10 | @font-face {
11 | font-family: 'Roboto Mono';
12 | src: url('/fonts/RobotoMono-Thin.woff2') format('woff2'),
13 | url('/fonts/RobotoMono-Thin.woff') format('woff');
14 | font-weight: 100;
15 | font-style: normal;
16 | }
17 |
18 | @font-face {
19 | font-family: 'Roboto Mono';
20 | src: url('/fonts/RobotoMono-Light.woff2') format('woff2'),
21 | url('/fonts/RobotoMono-Light.woff') format('woff');
22 | font-weight: 300;
23 | font-style: normal;
24 | }
25 |
26 | @font-face {
27 | font-family: 'Roboto Mono';
28 | src: url('/fonts/RobotoMono-Regular.woff2') format('woff2'),
29 | url('/fonts/RobotoMono-Regular.woff') format('woff');
30 | font-weight: normal;
31 | font-style: normal;
32 | }
33 |
34 | body {
35 | font-family: 'Roboto Mono' ,-apple-system, system-ui, 'Helvetica Neue', Arial, sans-serif;
36 | -webkit-text-rendering: optimizeLegibility;
37 | -moz-text-rendering: optimizeLegibility;
38 | -ms-text-rendering: optimizeLegibility;
39 | -o-text-rendering: optimizeLegibility;
40 | text-rendering: optimizeLegibility;
41 | -webkit-font-smoothing: antialiased;
42 | -moz-osx-font-smoothing: grayscale;
43 | margin: 0;
44 | padding: 0;
45 | }
46 | `
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/components/button.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | import React, { Component } from 'react'
3 | import styled from 'styled-components'
4 | import Clipboard from 'react-clipboard.js'
5 |
6 | // Styled Components
7 | const ClipboardButton = styled(Clipboard)`
8 | font-family: 'Roboto Mono';
9 | color: #FFF;
10 | background-color: ${props => props.buttonColor};
11 | font-size: 24px;
12 | line-height: 1;
13 | font-weight: bold;
14 | padding: 30px;
15 | width: 100%;
16 | border: none;
17 | cursor: pointer;
18 | transition: .2s ease;
19 | `
20 |
21 | class Button extends Component {
22 |
23 | constructor (props) {
24 | super(props)
25 | this.onSuccess = this.onSuccess.bind(this)
26 | this.getRandomEmoji = this.getRandomEmoji.bind(this)
27 |
28 | this.state = {
29 | buttonLabel: 'Copy',
30 | buttonColor: '#000000'
31 | }
32 | }
33 |
34 | getRandomEmoji () {
35 | const emojis = ['😎', '🚀', '😀', '🙃', '😛', '🙏', '👍', '🙅', '💩', '📝']
36 | return emojis[Math.floor(Math.random()*emojis.length)]
37 | }
38 |
39 | onSuccess() {
40 | this.setState ({
41 | buttonLabel: 'Copied ' + this.getRandomEmoji(),
42 | buttonColor: '#00C492'
43 | })
44 | setTimeout(() => {
45 | this.setState({
46 | buttonLabel: 'Copy',
47 | buttonColor: '#000000'
48 | })
49 | }, 1000)
50 | }
51 |
52 | render () {
53 |
54 | const { buttonColor, buttonLabel } = this.state
55 | const { content } = this.props
56 |
57 | return (
58 |
63 | {buttonLabel}
64 |
65 | )
66 | }
67 | }
68 |
69 | export default Button
--------------------------------------------------------------------------------
/src/components/svg/dots.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled from 'styled-components'
3 |
4 | const Container = styled.div`
5 | padding: 5px;
6 |
7 | path {
8 | transition: .3s ease;
9 | }
10 |
11 | &:hover {
12 | path {
13 | fill: ${props => props.darkmode ? '#666666' : '#AAAAAA'};
14 | }
15 | }
16 | `
17 |
18 | const Dots = (props) => (
19 |
20 |
26 |
27 | )
28 |
29 | export default Dots
--------------------------------------------------------------------------------
/src/components/topBar.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | import React, { Component } from 'react'
3 | import styled from 'styled-components'
4 |
5 | // Assets
6 | import Dots from './svg/dots.js'
7 |
8 | import SwitchButton from './SwitchButton'
9 |
10 | const Container = styled.div`
11 | width: 100%;
12 | padding: 15px 25px;
13 | display: flex;
14 | justify-content: flex-end;
15 | `
16 |
17 | const List = styled.ul`
18 | list-style: none;
19 | margin: 0;
20 | padding: 10px;
21 | background-color: ${props => props.darkmode ? '#353638' : '#FFFFFF'};
22 | position: absolute;
23 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.1);
24 | pointer-events: none;
25 | right: 0;
26 | opacity: 0;
27 | `
28 |
29 | const Dropdown = styled.div`
30 | position: relative;
31 | &:hover {
32 | ${List} {
33 | pointer-events: auto;
34 | opacity: 1;
35 | }
36 | }
37 | `
38 |
39 | const Item = styled.li`
40 | width: 180px;
41 | font-family: 'Roboto Mono';
42 | cursor: pointer;
43 | padding: 10px 5px;
44 | font-size: 10px;
45 | list-style: none;
46 | color: #AAA;
47 | display: flex;
48 | justify-content: space-between;
49 | align-items: center;
50 |
51 | a {
52 | color: #AAA;
53 | text-decoration: none;
54 | }
55 | `
56 |
57 | const items = [
58 | {
59 | text: 'Visit Website',
60 | url: 'http://tabipsum.com'
61 | }
62 | ]
63 |
64 | class TopBar extends Component {
65 | render () {
66 |
67 | const { darkmode } = this.props
68 |
69 | return (
70 |
71 |
72 |
73 |
74 | -
77 | Enable dark mode
78 |
79 |
80 | {
81 | items.map((item) => (
82 | -
83 | {item.text}
84 |
85 | ))
86 | }
87 |
88 |
89 |
90 | )
91 | }
92 | }
93 |
94 | export default TopBar
--------------------------------------------------------------------------------
/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | // Dependencies
2 | import React, { Component } from 'react'
3 | import styled from 'styled-components'
4 | import InputRange from 'react-input-range'
5 | import { connect } from 'react-redux'
6 |
7 | // Actions
8 | import { changeUnit, updateValue, switchUIMode } from './actions'
9 |
10 | // Components
11 | import TopBar from './components/topBar'
12 | import Button from './components/button'
13 |
14 | // Styled Components
15 | const Container = styled.div`
16 | display: flex;
17 | flex-direction: column;
18 | justify-content: space-between;
19 | width: 320px;
20 | height: 420px;
21 | background: ${props => props.darkmode ? '#2E2F30' : '#FFFFFF'}
22 |
23 | input::-webkit-outer-spin-button,
24 | input::-webkit-inner-spin-button {
25 | /* display: none; <- Crashes Chrome on hover */
26 | -webkit-appearance: none;
27 | margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
28 | }
29 |
30 | input[type='number'] {
31 | -moz-appearance:textfield;
32 | }
33 |
34 | input::-webkit-outer-spin-button,
35 | input::-webkit-inner-spin-button {
36 | -webkit-appearance: none;
37 | }
38 |
39 | .input-range__slider {
40 | -webkit-appearance: none;
41 | -moz-appearance: none;
42 | appearance: none;
43 | background: ${props => props.darkmode ? '#DADADA' : '#000000'};
44 | border: 1px solid ${props => props.darkmode ? '#DADADA' : '#000000'};
45 | border-radius: 100%;
46 | cursor: pointer;
47 | display: block;
48 | height: 1rem;
49 | margin-left: -0.5rem;
50 | margin-top: -0.65rem;
51 | outline: none;
52 | position: absolute;
53 | top: 50%;
54 | -webkit-transition: box-shadow 0.3s ease-out, -webkit-transform 0.3s ease-out;
55 | transition: box-shadow 0.3s ease-out, -webkit-transform 0.3s ease-out;
56 | transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
57 | transition: transform 0.3s ease-out, box-shadow 0.3s ease-out, -webkit-transform 0.3s ease-out;
58 | width: 1rem; }
59 | .input-range__slider:active {
60 | -webkit-transform: scale(1.3);
61 | transform: scale(1.3); }
62 | .input-range__slider:focus {
63 | box-shadow: 0 0 0 5px rgba(63, 81, 181, 0.2); }
64 | .input-range--disabled .input-range__slider {
65 | background: #cccccc;
66 | border: 1px solid #cccccc;
67 | box-shadow: none;
68 | -webkit-transform: none;
69 | transform: none; }
70 |
71 | .input-range__slider-container {
72 | -webkit-transition: left 0.3s ease-out;
73 | transition: left 0.3s ease-out; }
74 |
75 | .input-range__label {
76 | color: #aaaaaa;
77 | font-family: "Helvetica Neue", san-serif;
78 | font-size: 0.8rem;
79 | -webkit-transform: translateZ(0);
80 | transform: translateZ(0);
81 | white-space: nowrap; }
82 |
83 | .input-range__label--min,
84 | .input-range__label--max {
85 | bottom: -1.4rem;
86 | position: absolute; }
87 |
88 | .input-range__label--min {
89 | left: 0; }
90 |
91 | .input-range__label--max {
92 | right: 0; }
93 |
94 | .input-range__label--value {
95 | position: absolute;
96 | top: -1.8rem; }
97 |
98 | .input-range__label-container {
99 | display: none;
100 | left: -50%;
101 | position: relative; }
102 | .input-range__label--max .input-range__label-container {
103 | left: 50%; }
104 |
105 | .input-range__track {
106 | background: ${props => props.darkmode ? '#222222' : '#EEEEEE'};
107 | border-radius: 0.3rem;
108 | cursor: pointer;
109 | display: block;
110 | height: 0.3rem;
111 | position: relative;
112 | -webkit-transition: left 0.3s ease-out, width 0.3s ease-out;
113 | transition: left 0.3s ease-out, width 0.3s ease-out; }
114 | .input-range--disabled .input-range__track {
115 | background: #EEEEEE; }
116 |
117 | .input-range__track--background {
118 | left: 0;
119 | margin-top: -0.15rem;
120 | position: absolute;
121 | right: 0;
122 | top: 50%; }
123 |
124 | .input-range__track--active {
125 | background: ${ props => props.darkmode ? '#DDDDDD' : '#333333'}; }
126 |
127 | .input-range {
128 | height: 1rem;
129 | position: relative;
130 | width: 100%; }
131 | `
132 |
133 | const MainContainer = styled.div`
134 | width: 80%;
135 | margin: 0 auto;
136 | text-align: center;
137 | `
138 |
139 | const Number = styled.input`
140 | width: 100%;
141 | font-family: 'Roboto Mono';
142 | font-size: 120px;
143 | font-weight: 100;
144 | text-align: center;
145 | letter-spacing: -10px;
146 | border: none;
147 | background-color: transparent;
148 | color: ${props => props.darkmode ? '#DADADA' : '#000000'}
149 |
150 | &:focus {
151 | outline: none;
152 | }
153 | `
154 |
155 | const List = styled.ul`
156 | padding: 0;
157 | margin: 15px 0;
158 | display: flex;
159 | justify-content: center;
160 | `
161 |
162 | const Item = styled.li`
163 | font-family: 'Roboto Mono';
164 | font-weight: 300;
165 | text-transform: capitalize;
166 | letter-spacing: -0.5px;
167 | cursor: pointer;
168 | padding: 10px;
169 | font-size: 14px;
170 | list-style: none;
171 | padding: 10px 5px;
172 | transition: .3s ease;
173 | color: ${props => props.darkmode ? (props.isSelected ? '#DADADA' : '#666666' ) : (props.isSelected ? '#000000' : '#AAAAAA')};
174 |
175 | &.is-selected {
176 | color: #000;
177 | font-weight: bold;
178 | }
179 | `
180 |
181 | const units = [ 'words', 'sentences', 'paragraphs']
182 |
183 | class App extends Component {
184 |
185 | constructor (props) {
186 | super(props)
187 | this.switchTheme = this.switchTheme.bind(this)
188 | }
189 |
190 | componentDidMount () {
191 | console.log(localStorage.getItem("darkmode"))
192 | }
193 |
194 | switchTheme () {
195 | localStorage.setItem('darkmode', !localStorage.getItem("darkmode"))
196 | }
197 |
198 | render() {
199 |
200 | const { content, value, contentType, darkmode } = this.props.state
201 | const { changeUnit, updateValue, updateFromNumber, switchUIMode } = this.props
202 |
203 | return (
204 |
205 |
206 |
207 |
215 |
216 | {units.map((unit, index) => (
217 | - {unit}
218 | ))}
219 |
220 |
227 |
228 |
229 |
230 | )
231 | }
232 | }
233 |
234 | const mapDispatchToProps = dispatch => {
235 | return {
236 | changeUnit: (event) => { dispatch(changeUnit(event.target.innerHTML))},
237 | updateValue: (value) => { dispatch(updateValue(value)) },
238 | updateFromNumber: (event) => { dispatch(updateValue(event.target.value === '' ? false : event.target.value)) },
239 | switchUIMode: (darkmode) => { dispatch(switchUIMode(darkmode)) }
240 | }
241 | }
242 |
243 | const mapStateToProps = state => {
244 | return {
245 | state
246 | }
247 | }
248 |
249 | export default connect(
250 | mapStateToProps,
251 | mapDispatchToProps
252 | )(App)
253 |
--------------------------------------------------------------------------------