├── README.md └── colors-app ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── App.js ├── App.test.js ├── ColorBox.js ├── ColorPickerForm.js ├── DraggableColorBox.js ├── DraggableColorList.js ├── MiniPalette.js ├── Navbar.js ├── NewPaletteForm.js ├── Page.js ├── Palette.js ├── PaletteFooter.js ├── PaletteFormNav.js ├── PaletteList.js ├── PaletteMetaForm.js ├── SingleColorPalette.js ├── colorHelpers.js ├── constants.js ├── index.css ├── index.js ├── logo.svg ├── seedColors.js ├── serviceWorker.js └── styles │ ├── ColorBoxStyles.js │ ├── ColorPickerFormStyles.js │ ├── DraggableColorBoxStyles.js │ ├── MiniPaletteStyles.js │ ├── NavbarStyles.js │ ├── NewPaletteFormStyles.js │ ├── Page.css │ ├── PaletteFooterStyles.js │ ├── PaletteFormNavStyles.js │ ├── PaletteListStyles.js │ ├── PaletteStyles.js │ ├── bg.svg │ └── sizes.js └── yarn.lock /README.md: -------------------------------------------------------------------------------- 1 | # React Colors Project 2 | 3 | - A clone of websites like [Flat UI Colors](https://flatuicolors.com/) and [Material UI Colors](http://materialuicolors.co/?utm_source=launchers). 4 | - One of the projects for my Modern React Bootcamp course. 5 | - Each video has its own corresponding commit that you can follow along with. 6 | 7 | ![image](https://i.imgur.com/9x1F9At.png) 8 | ![image](https://i.imgur.com/GM0etHA.png) 9 | 10 | ![image](https://i.imgur.com/QB2zRzf.png) 11 | ![image](https://i.imgur.com/aFowgNg.png) 12 | 13 | -------------------------------------------------------------------------------- /colors-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | .DS_Store 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /colors-app/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /colors-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colors-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^3.9.2", 7 | "@material-ui/icons": "^3.0.2", 8 | "@material-ui/styles": "^3.0.0-alpha.10", 9 | "chroma-js": "^2.0.3", 10 | "emoji-mart": "^2.10.0", 11 | "rc-slider": "^8.6.6", 12 | "react": "^16.8.4", 13 | "react-color": "^2.17.0", 14 | "react-copy-to-clipboard": "^5.0.1", 15 | "react-dom": "^16.8.4", 16 | "react-material-ui-form-validator": "^2.0.7", 17 | "react-router-dom": "^4.3.1", 18 | "react-router-transition": "^1.3.0", 19 | "react-scripts": "2.1.8", 20 | "react-sortable-hoc": "^1.7.1", 21 | "react-transition-group": "^2.6.1" 22 | }, 23 | "scripts": { 24 | "start": "react-scripts start", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "eject": "react-scripts eject" 28 | }, 29 | "eslintConfig": { 30 | "extends": "react-app" 31 | }, 32 | "browserslist": [ 33 | ">0.2%", 34 | "not dead", 35 | "not ie <= 11", 36 | "not op_mini all" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /colors-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Colt/react-colors/c358775e0e23e32f47085df1fefc34e38ab0b6be/colors-app/public/favicon.ico -------------------------------------------------------------------------------- /colors-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 14 | 15 | 19 | 20 | 29 | React App 30 | 31 | 32 | 33 |
34 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /colors-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /colors-app/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Route, Switch } from "react-router-dom"; 3 | import { TransitionGroup, CSSTransition } from "react-transition-group"; 4 | import Palette from "./Palette"; 5 | import PaletteList from "./PaletteList"; 6 | import SingleColorPalette from "./SingleColorPalette"; 7 | import Page from "./Page"; 8 | import NewPaletteForm from "./NewPaletteForm"; 9 | import seedColors from "./seedColors"; 10 | import { generatePalette } from "./colorHelpers"; 11 | 12 | class App extends Component { 13 | constructor(props) { 14 | super(props); 15 | const savedPalettes = JSON.parse(window.localStorage.getItem("palettes")); 16 | this.state = { palettes: savedPalettes || seedColors }; 17 | this.savePalette = this.savePalette.bind(this); 18 | this.findPalette = this.findPalette.bind(this); 19 | this.deletePalette = this.deletePalette.bind(this); 20 | } 21 | findPalette(id) { 22 | return this.state.palettes.find(function(palette) { 23 | return palette.id === id; 24 | }); 25 | } 26 | deletePalette(id) { 27 | this.setState( 28 | st => ({ palettes: st.palettes.filter(palette => palette.id !== id) }), 29 | this.syncLocalStorage 30 | ); 31 | } 32 | savePalette(newPalette) { 33 | this.setState( 34 | { palettes: [...this.state.palettes, newPalette] }, 35 | this.syncLocalStorage 36 | ); 37 | } 38 | syncLocalStorage() { 39 | //save palettes to local storage 40 | window.localStorage.setItem( 41 | "palettes", 42 | JSON.stringify(this.state.palettes) 43 | ); 44 | } 45 | render() { 46 | return ( 47 | ( 49 | 50 | 51 | 52 | ( 56 | 57 | 62 | 63 | )} 64 | /> 65 | ( 69 | 70 | 76 | 77 | )} 78 | /> 79 | ( 83 | 84 | 89 | 90 | )} 91 | /> 92 | ( 96 | 97 | 102 | 103 | )} 104 | /> 105 | ( 107 | 108 | 113 | 114 | )} 115 | /> 116 | 117 | 118 | 119 | )} 120 | /> 121 | ); 122 | } 123 | } 124 | 125 | export default App; 126 | -------------------------------------------------------------------------------- /colors-app/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 | -------------------------------------------------------------------------------- /colors-app/src/ColorBox.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { CopyToClipboard } from "react-copy-to-clipboard"; 3 | import { Link } from "react-router-dom"; 4 | import classNames from "classnames"; 5 | import { withStyles } from "@material-ui/styles"; 6 | import styles from "./styles/ColorBoxStyles"; 7 | 8 | class ColorBox extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { copied: false }; 12 | this.changeCopyState = this.changeCopyState.bind(this); 13 | } 14 | changeCopyState() { 15 | this.setState({ copied: true }, () => { 16 | setTimeout(() => this.setState({ copied: false }), 1500); 17 | }); 18 | } 19 | 20 | render() { 21 | const { 22 | name, 23 | background, 24 | moreUrl, 25 | showingFullPalette, 26 | classes 27 | } = this.props; 28 | 29 | const { copied } = this.state; 30 | return ( 31 | 32 |
33 |
39 | 40 |
45 |

copied!

46 |

{background}

47 |
48 |
49 |
50 | {name} 51 |
52 | 53 |
54 | {showingFullPalette && ( 55 | e.stopPropagation()}> 56 | MORE 57 | 58 | )} 59 |
60 | 61 | ); 62 | } 63 | } 64 | export default withStyles(styles)(ColorBox); 65 | -------------------------------------------------------------------------------- /colors-app/src/ColorPickerForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import { ValidatorForm, TextValidator } from "react-material-ui-form-validator"; 4 | import { ChromePicker } from "react-color"; 5 | import { withStyles } from "@material-ui/core/styles"; 6 | import styles from "./styles/ColorPickerFormStyles"; 7 | 8 | class ColorPickerForm extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { currentColor: "teal", newColorName: "" }; 12 | this.updateCurrentColor = this.updateCurrentColor.bind(this); 13 | this.handleChange = this.handleChange.bind(this); 14 | this.handleSubmit = this.handleSubmit.bind(this); 15 | } 16 | componentDidMount() { 17 | ValidatorForm.addValidationRule("isColorNameUnique", value => 18 | this.props.colors.every( 19 | ({ name }) => name.toLowerCase() !== value.toLowerCase() 20 | ) 21 | ); 22 | ValidatorForm.addValidationRule("isColorUnique", value => 23 | this.props.colors.every(({ color }) => color !== this.state.currentColor) 24 | ); 25 | } 26 | updateCurrentColor(newColor) { 27 | this.setState({ currentColor: newColor.hex }); 28 | } 29 | handleChange(evt) { 30 | this.setState({ 31 | [evt.target.name]: evt.target.value 32 | }); 33 | } 34 | handleSubmit() { 35 | const newColor = { 36 | color: this.state.currentColor, 37 | name: this.state.newColorName 38 | }; 39 | this.props.addNewColor(newColor); 40 | this.setState({ newColorName: "" }); 41 | } 42 | 43 | render() { 44 | const { paletteIsFull, classes } = this.props; 45 | const { currentColor, newColorName } = this.state; 46 | return ( 47 |
48 | 53 | 58 | 73 | 85 | 86 |
87 | ); 88 | } 89 | } 90 | export default withStyles(styles)(ColorPickerForm); 91 | -------------------------------------------------------------------------------- /colors-app/src/DraggableColorBox.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SortableElement } from "react-sortable-hoc"; 3 | import { withStyles } from "@material-ui/styles"; 4 | import DeleteIcon from "@material-ui/icons/Delete"; 5 | import styles from "./styles/DraggableColorBoxStyles"; 6 | 7 | const DraggableColorBox = SortableElement(props => { 8 | const { classes, handleClick, name, color } = props; 9 | return ( 10 |
11 |
12 | {name} 13 | 14 |
15 |
16 | ); 17 | }); 18 | export default withStyles(styles)(DraggableColorBox); 19 | -------------------------------------------------------------------------------- /colors-app/src/DraggableColorList.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import DraggableColorBox from "./DraggableColorBox"; 3 | import { SortableContainer } from "react-sortable-hoc"; 4 | 5 | const DraggableColorList = SortableContainer(({ colors, removeColor }) => { 6 | return ( 7 |
8 | {colors.map((color, i) => ( 9 | removeColor(color.name)} 15 | /> 16 | ))} 17 |
18 | ); 19 | }); 20 | export default DraggableColorList; 21 | -------------------------------------------------------------------------------- /colors-app/src/MiniPalette.js: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import styles from "./styles/MiniPaletteStyles"; 4 | import DeleteIcon from "@material-ui/icons/Delete"; 5 | 6 | class MiniPalette extends PureComponent { 7 | constructor(props) { 8 | super(props); 9 | this.deletePalette = this.deletePalette.bind(this); 10 | this.handleClick = this.handleClick.bind(this); 11 | } 12 | deletePalette(e) { 13 | e.stopPropagation(); 14 | this.props.openDialog(this.props.id); 15 | } 16 | handleClick() { 17 | this.props.goToPalette(this.props.id); 18 | } 19 | render() { 20 | const { classes, paletteName, emoji, colors } = this.props; 21 | 22 | const miniColorBoxes = colors.map(color => ( 23 |
28 | )); 29 | return ( 30 |
31 | 36 | 37 |
{miniColorBoxes}
38 |
39 | {paletteName} {emoji} 40 |
41 |
42 | ); 43 | } 44 | } 45 | 46 | export default withStyles(styles)(MiniPalette); 47 | -------------------------------------------------------------------------------- /colors-app/src/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { withStyles } from "@material-ui/styles"; 4 | import Select from "@material-ui/core/Select"; 5 | import MenuItem from "@material-ui/core/MenuItem"; 6 | import Snackbar from "@material-ui/core/Snackbar"; 7 | import IconButton from "@material-ui/core/IconButton"; 8 | import CloseIcon from "@material-ui/icons/Close"; 9 | import Slider from "rc-slider"; 10 | import "rc-slider/assets/index.css"; 11 | import styles from "./styles/NavbarStyles"; 12 | 13 | class Navbar extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { format: "hex", open: false }; 17 | this.handleFormatChange = this.handleFormatChange.bind(this); 18 | this.closeSnackbar = this.closeSnackbar.bind(this); 19 | } 20 | handleFormatChange(e) { 21 | this.setState({ format: e.target.value, open: true }); 22 | this.props.handleChange(e.target.value); 23 | } 24 | closeSnackbar() { 25 | this.setState({ open: false }); 26 | } 27 | render() { 28 | const { level, changeLevel, showingAllColors, classes } = this.props; 29 | const { format } = this.state; 30 | return ( 31 |
32 |
33 | reactcolorpicker 34 |
35 | {showingAllColors && ( 36 |
37 | Level: {level} 38 |
39 | 46 |
47 |
48 | )} 49 |
50 | 55 |
56 | 62 | Format Changed To {format.toUpperCase()} 63 | 64 | } 65 | ContentProps={{ 66 | "aria-describedby": "message-id" 67 | }} 68 | onClose={this.closeSnackbar} 69 | action={[ 70 | 76 | 77 | 78 | ]} 79 | /> 80 |
81 | ); 82 | } 83 | } 84 | export default withStyles(styles)(Navbar); 85 | -------------------------------------------------------------------------------- /colors-app/src/NewPaletteForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import classNames from "classnames"; 3 | import { withStyles } from "@material-ui/core/styles"; 4 | import PaletteFormNav from "./PaletteFormNav"; 5 | import ColorPickerForm from "./ColorPickerForm"; 6 | import Drawer from "@material-ui/core/Drawer"; 7 | import Typography from "@material-ui/core/Typography"; 8 | import Divider from "@material-ui/core/Divider"; 9 | import IconButton from "@material-ui/core/IconButton"; 10 | import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; 11 | import Button from "@material-ui/core/Button"; 12 | import DraggableColorList from "./DraggableColorList"; 13 | import { arrayMove } from "react-sortable-hoc"; 14 | import styles from "./styles/NewPaletteFormStyles"; 15 | import seedColors from "./seedColors"; 16 | 17 | class NewPaletteForm extends Component { 18 | static defaultProps = { 19 | maxColors: 20 20 | }; 21 | constructor(props) { 22 | super(props); 23 | this.state = { 24 | open: true, 25 | colors: seedColors[0].colors 26 | }; 27 | this.addNewColor = this.addNewColor.bind(this); 28 | this.handleChange = this.handleChange.bind(this); 29 | this.handleSubmit = this.handleSubmit.bind(this); 30 | this.removeColor = this.removeColor.bind(this); 31 | this.clearColors = this.clearColors.bind(this); 32 | this.addRandomColor = this.addRandomColor.bind(this); 33 | } 34 | 35 | handleDrawerOpen = () => { 36 | this.setState({ open: true }); 37 | }; 38 | 39 | handleDrawerClose = () => { 40 | this.setState({ open: false }); 41 | }; 42 | 43 | addNewColor(newColor) { 44 | this.setState({ 45 | colors: [...this.state.colors, newColor], 46 | newColorName: "" 47 | }); 48 | } 49 | handleChange(evt) { 50 | this.setState({ 51 | [evt.target.name]: evt.target.value 52 | }); 53 | } 54 | clearColors() { 55 | this.setState({ colors: [] }); 56 | } 57 | addRandomColor() { 58 | const allColors = this.props.palettes.map(p => p.colors).flat(); 59 | let rand; 60 | let randomColor; 61 | let isDuplicateColor = true; 62 | while (isDuplicateColor) { 63 | rand = Math.floor(Math.random() * allColors.length); 64 | randomColor = allColors[rand]; 65 | isDuplicateColor = this.state.colors.some( 66 | color => color.name === randomColor.name 67 | ); 68 | } 69 | this.setState({ colors: [...this.state.colors, randomColor] }); 70 | } 71 | handleSubmit(newPalette) { 72 | newPalette.id = newPalette.paletteName.toLowerCase().replace(/ /g, "-"); 73 | newPalette.colors = this.state.colors; 74 | this.props.savePalette(newPalette); 75 | this.props.history.push("/"); 76 | } 77 | removeColor(colorName) { 78 | this.setState({ 79 | colors: this.state.colors.filter(color => color.name !== colorName) 80 | }); 81 | } 82 | onSortEnd = ({ oldIndex, newIndex }) => { 83 | this.setState(({ colors }) => ({ 84 | colors: arrayMove(colors, oldIndex, newIndex) 85 | })); 86 | }; 87 | 88 | render() { 89 | const { classes, maxColors, palettes } = this.props; 90 | const { open, colors } = this.state; 91 | const paletteIsFull = colors.length >= maxColors; 92 | 93 | return ( 94 |
95 | 101 | 110 |
111 | 112 | 113 | 114 |
115 | 116 |
117 | 118 | Design Your Palette 119 | 120 |
121 | 129 | 138 |
139 | 144 |
145 |
146 |
151 |
152 | 159 |
160 |
161 | ); 162 | } 163 | } 164 | export default withStyles(styles, { withTheme: true })(NewPaletteForm); 165 | -------------------------------------------------------------------------------- /colors-app/src/Page.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./styles/Page.css"; 3 | 4 | function Page({ children }) { 5 | return
{children}
; 6 | } 7 | export default Page; 8 | -------------------------------------------------------------------------------- /colors-app/src/Palette.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import ColorBox from "./ColorBox"; 3 | import Navbar from "./Navbar"; 4 | import PaletteFooter from "./PaletteFooter"; 5 | import { withStyles } from "@material-ui/styles"; 6 | import styles from "./styles/PaletteStyles"; 7 | 8 | class Palette extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { level: 500, format: "hex" }; 12 | this.changeLevel = this.changeLevel.bind(this); 13 | this.changeFormat = this.changeFormat.bind(this); 14 | } 15 | changeLevel(level) { 16 | this.setState({ level }); 17 | } 18 | changeFormat(val) { 19 | this.setState({ format: val }); 20 | } 21 | render() { 22 | const { colors, paletteName, emoji, id } = this.props.palette; 23 | const { classes } = this.props; 24 | const { level, format } = this.state; 25 | const colorBoxes = colors[level].map(color => ( 26 | 33 | )); 34 | return ( 35 |
36 | 42 |
{colorBoxes}
43 | 44 |
45 | ); 46 | } 47 | } 48 | export default withStyles(styles)(Palette); 49 | -------------------------------------------------------------------------------- /colors-app/src/PaletteFooter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withStyles } from "@material-ui/styles"; 3 | import styles from "./styles/PaletteFooterStyles"; 4 | 5 | function PaletteFooter(props) { 6 | const { paletteName, emoji, classes } = props; 7 | return ( 8 |
9 | {paletteName} 10 | {emoji} 11 |
12 | ); 13 | } 14 | export default withStyles(styles)(PaletteFooter); 15 | -------------------------------------------------------------------------------- /colors-app/src/PaletteFormNav.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { withStyles } from "@material-ui/core/styles"; 3 | import PaletteMetaForm from "./PaletteMetaForm"; 4 | import { Link } from "react-router-dom"; 5 | import classNames from "classnames"; 6 | import CssBaseline from "@material-ui/core/CssBaseline"; 7 | import AppBar from "@material-ui/core/AppBar"; 8 | import Toolbar from "@material-ui/core/Toolbar"; 9 | import Typography from "@material-ui/core/Typography"; 10 | import IconButton from "@material-ui/core/IconButton"; 11 | import AddToPhotosIcon from "@material-ui/icons/AddToPhotos"; 12 | import Button from "@material-ui/core/Button"; 13 | import styles from "./styles/PaletteFormNavStyles"; 14 | 15 | class PaletteFormNav extends Component { 16 | constructor(props) { 17 | super(props); 18 | this.state = { newPaletteName: "", formShowing: false }; 19 | this.handleChange = this.handleChange.bind(this); 20 | this.showForm = this.showForm.bind(this); 21 | this.hideForm = this.hideForm.bind(this); 22 | } 23 | 24 | handleChange(evt) { 25 | this.setState({ 26 | [evt.target.name]: evt.target.value 27 | }); 28 | } 29 | showForm() { 30 | this.setState({ formShowing: true }); 31 | } 32 | hideForm() { 33 | this.setState({ formShowing: false }); 34 | } 35 | render() { 36 | const { 37 | classes, 38 | open, 39 | palettes, 40 | handleSubmit, 41 | handleDrawerOpen 42 | } = this.props; 43 | const { formShowing } = this.state; 44 | return ( 45 |
46 | 47 | 54 | 55 | 63 | 64 | 65 | 66 | Create A Palette 67 | 68 | 69 |
70 | 71 | 78 | 79 | 87 |
88 |
89 | {formShowing && ( 90 | 95 | )} 96 |
97 | ); 98 | } 99 | } 100 | export default withStyles(styles, { withTheme: true })(PaletteFormNav); 101 | -------------------------------------------------------------------------------- /colors-app/src/PaletteList.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { CSSTransition, TransitionGroup } from "react-transition-group"; 4 | import Dialog from "@material-ui/core/Dialog"; 5 | import List from "@material-ui/core/List"; 6 | import ListItem from "@material-ui/core/ListItem"; 7 | import ListItemAvatar from "@material-ui/core/ListItemAvatar"; 8 | import Avatar from "@material-ui/core/Avatar"; 9 | import ListItemText from "@material-ui/core/ListItemText"; 10 | import CheckIcon from "@material-ui/icons/Check"; 11 | import CloseIcon from "@material-ui/icons/Close"; 12 | import DialogTitle from "@material-ui/core/DialogTitle"; 13 | import { withStyles } from "@material-ui/styles"; 14 | import MiniPalette from "./MiniPalette"; 15 | import blue from "@material-ui/core/colors/blue"; 16 | import red from "@material-ui/core/colors/red"; 17 | import styles from "./styles/PaletteListStyles"; 18 | 19 | class PaletteList extends Component { 20 | constructor(props) { 21 | super(props); 22 | this.state = { 23 | openDeleteDialog: false, 24 | deletingId: "" 25 | }; 26 | this.openDialog = this.openDialog.bind(this); 27 | this.closeDialog = this.closeDialog.bind(this); 28 | this.handleDelete = this.handleDelete.bind(this); 29 | this.goToPalette = this.goToPalette.bind(this); 30 | } 31 | openDialog(id) { 32 | this.setState({ openDeleteDialog: true, deletingId: id }); 33 | } 34 | closeDialog() { 35 | this.setState({ openDeleteDialog: false, deletingId: "" }); 36 | } 37 | goToPalette(id) { 38 | this.props.history.push(`/palette/${id}`); 39 | } 40 | handleDelete() { 41 | this.props.deletePalette(this.state.deletingId); 42 | this.closeDialog(); 43 | } 44 | render() { 45 | const { palettes, classes } = this.props; 46 | const { openDeleteDialog } = this.state; 47 | return ( 48 |
49 |
50 | 54 | 55 | {palettes.map(palette => ( 56 | 57 | 64 | 65 | ))} 66 | 67 |
68 | 73 | 74 | Delete This Palette? 75 | 76 | 77 | 78 | 79 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 |
98 | ); 99 | } 100 | } 101 | export default withStyles(styles)(PaletteList); 102 | -------------------------------------------------------------------------------- /colors-app/src/PaletteMetaForm.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Button from "@material-ui/core/Button"; 3 | import Dialog from "@material-ui/core/Dialog"; 4 | import DialogActions from "@material-ui/core/DialogActions"; 5 | import DialogContent from "@material-ui/core/DialogContent"; 6 | import DialogContentText from "@material-ui/core/DialogContentText"; 7 | import DialogTitle from "@material-ui/core/DialogTitle"; 8 | import { ValidatorForm, TextValidator } from "react-material-ui-form-validator"; 9 | import { Picker } from "emoji-mart"; 10 | import "emoji-mart/css/emoji-mart.css"; 11 | 12 | class PaletteMetaForm extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.state = { 16 | stage: "form", 17 | newPaletteName: "" 18 | }; 19 | this.handleChange = this.handleChange.bind(this); 20 | this.showEmojiPicker = this.showEmojiPicker.bind(this); 21 | this.savePalette = this.savePalette.bind(this); 22 | } 23 | componentDidMount() { 24 | ValidatorForm.addValidationRule("isPaletteNameUnique", value => 25 | this.props.palettes.every( 26 | ({ paletteName }) => paletteName.toLowerCase() !== value.toLowerCase() 27 | ) 28 | ); 29 | } 30 | handleChange(evt) { 31 | this.setState({ 32 | [evt.target.name]: evt.target.value 33 | }); 34 | } 35 | showEmojiPicker() { 36 | this.setState({ stage: "emoji" }); 37 | } 38 | savePalette(emoji) { 39 | const newPalette = { 40 | paletteName: this.state.newPaletteName, 41 | emoji: emoji.native 42 | }; 43 | this.props.handleSubmit(newPalette); 44 | this.setState({ stage: "" }); 45 | } 46 | handleClickOpen = () => { 47 | this.setState({ open: true }); 48 | }; 49 | 50 | handleClose = () => { 51 | this.setState({ open: false }); 52 | }; 53 | 54 | render() { 55 | const { newPaletteName, stage } = this.state; 56 | const { hideForm } = this.props; 57 | 58 | return ( 59 |
60 | 61 | 62 | Choose a Palette Emoji 63 | 64 | 65 | 66 | 71 | 72 | Choose a Palette Name 73 | 74 | 75 | 76 | 77 | Please enter a name for your new beautiful palette. Make sure 78 | it's unique! 79 | 80 | 81 | 91 | 92 | 93 | 96 | 99 | 100 | 101 | 102 |
103 | ); 104 | } 105 | } 106 | export default PaletteMetaForm; 107 | -------------------------------------------------------------------------------- /colors-app/src/SingleColorPalette.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { withStyles } from "@material-ui/styles"; 4 | import Navbar from "./Navbar"; 5 | import ColorBox from "./ColorBox"; 6 | import PaletteFooter from "./PaletteFooter"; 7 | import styles from "./styles/PaletteStyles"; 8 | 9 | class SingleColorPalette extends Component { 10 | constructor(props) { 11 | super(props); 12 | this._shades = this.gatherShades(this.props.palette, this.props.colorId); 13 | this.state = { format: "hex" }; 14 | this.changeFormat = this.changeFormat.bind(this); 15 | } 16 | gatherShades(palette, colorToFilterBy) { 17 | let shades = []; 18 | let allColors = palette.colors; 19 | 20 | for (let key in allColors) { 21 | shades = shades.concat( 22 | allColors[key].filter(color => color.id === colorToFilterBy) 23 | ); 24 | } 25 | return shades.slice(1); 26 | } 27 | changeFormat(val) { 28 | this.setState({ format: val }); 29 | } 30 | render() { 31 | const { format } = this.state; 32 | const { paletteName, emoji, id } = this.props.palette; 33 | const { classes } = this.props; 34 | const colorBoxes = this._shades.map(color => ( 35 | 41 | )); 42 | return ( 43 |
44 | 45 |
46 | {colorBoxes} 47 |
48 | GO BACK 49 |
50 |
51 | 52 |
53 | ); 54 | } 55 | } 56 | export default withStyles(styles)(SingleColorPalette); 57 | -------------------------------------------------------------------------------- /colors-app/src/colorHelpers.js: -------------------------------------------------------------------------------- 1 | import chroma from "chroma-js"; 2 | const levels = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; 3 | 4 | function generatePalette(starterPalette) { 5 | let newPalette = { 6 | paletteName: starterPalette.paletteName, 7 | id: starterPalette.id, 8 | emoji: starterPalette.emoji, 9 | colors: {} 10 | }; 11 | for (let level of levels) { 12 | newPalette.colors[level] = []; 13 | } 14 | for (let color of starterPalette.colors) { 15 | let scale = getScale(color.color, 10).reverse(); 16 | for (let i in scale) { 17 | newPalette.colors[levels[i]].push({ 18 | name: `${color.name} ${levels[i]}`, 19 | id: color.name.toLowerCase().replace(/ /g, "-"), 20 | hex: scale[i], 21 | rgb: chroma(scale[i]).css(), 22 | rgba: chroma(scale[i]) 23 | .css() 24 | .replace("rgb", "rgba") 25 | .replace(")", ",1.0)") 26 | }); 27 | } 28 | } 29 | return newPalette; 30 | } 31 | function getRange(hexColor) { 32 | const end = "#fff"; 33 | return [ 34 | chroma(hexColor) 35 | .darken(1.4) 36 | .hex(), 37 | hexColor, 38 | end 39 | ]; 40 | } 41 | 42 | function getScale(hexColor, numberOfColors) { 43 | return chroma 44 | .scale(getRange(hexColor)) 45 | .mode("lab") 46 | .colors(numberOfColors); 47 | } 48 | 49 | export { generatePalette }; 50 | -------------------------------------------------------------------------------- /colors-app/src/constants.js: -------------------------------------------------------------------------------- 1 | export const DRAWER_WIDTH = 400; 2 | -------------------------------------------------------------------------------- /colors-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /colors-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import { BrowserRouter } from "react-router-dom"; 4 | import "./index.css"; 5 | import App from "./App"; 6 | import * as serviceWorker from "./serviceWorker"; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById("root") 13 | ); 14 | 15 | // If you want your app to work offline and load faster, you can change 16 | // unregister() to register() below. Note this comes with some pitfalls. 17 | // Learn more about service workers: https://bit.ly/CRA-PWA 18 | serviceWorker.unregister(); 19 | -------------------------------------------------------------------------------- /colors-app/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /colors-app/src/seedColors.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | paletteName: "Material UI Colors", 4 | id: "material-ui-colors", 5 | emoji: "🎨", 6 | colors: [ 7 | { name: "red", color: "#F44336" }, 8 | { name: "pink", color: "#E91E63" }, 9 | { name: "purple", color: "#9C27B0" }, 10 | { name: "deeppurple", color: "#673AB7" }, 11 | { name: "indigo", color: "#3F51B5" }, 12 | { name: "blue", color: "#2196F3" }, 13 | { name: "lightblue", color: "#03A9F4" }, 14 | { name: "cyan", color: "#00BCD4" }, 15 | { name: "teal", color: "#009688" }, 16 | { name: "green", color: "#4CAF50" }, 17 | { name: "lightgreen", color: "#8BC34A" }, 18 | { name: "lime", color: "#CDDC39" }, 19 | { name: "yellow", color: "#FFEB3B" }, 20 | { name: "amber", color: "#FFC107" }, 21 | { name: "orange", color: "#FF9800" }, 22 | { name: "deeporange", color: "#FF5722" }, 23 | { name: "brown", color: "#795548" }, 24 | { name: "grey", color: "#9E9E9E" }, 25 | { name: "bluegrey", color: "#607D8B" } 26 | ] 27 | }, 28 | { 29 | paletteName: "Flat UI Colors v1", 30 | id: "flat-ui-colors-v1", 31 | emoji: "🤙", 32 | colors: [ 33 | { name: "Turquoise", color: "#1abc9c" }, 34 | { name: "Emerald", color: "#2ecc71" }, 35 | { name: "PeterRiver", color: "#3498db" }, 36 | { name: "Amethyst", color: "#9b59b6" }, 37 | { name: "WetAsphalt", color: "#34495e" }, 38 | { name: "GreenSea", color: "#16a085" }, 39 | { name: "Nephritis", color: "#27ae60" }, 40 | { name: "BelizeHole", color: "#2980b9" }, 41 | { name: "Wisteria", color: "#8e44ad" }, 42 | { name: "MidnightBlue", color: "#2c3e50" }, 43 | { name: "SunFlower", color: "#f1c40f" }, 44 | { name: "Carrot", color: "#e67e22" }, 45 | { name: "Alizarin", color: "#e74c3c" }, 46 | { name: "Clouds", color: "#ecf0f1" }, 47 | { name: "Concrete", color: "#95a5a6" }, 48 | { name: "Orange", color: "#f39c12" }, 49 | { name: "Pumpkin", color: "#d35400" }, 50 | { name: "Pomegranate", color: "#c0392b" }, 51 | { name: "Silver", color: "#bdc3c7" }, 52 | { name: "Asbestos", color: "#7f8c8d" } 53 | ] 54 | }, 55 | { 56 | paletteName: "Flat UI Colors Dutch", 57 | id: "flat-ui-colors-dutch", 58 | emoji: "🇳🇱", 59 | colors: [ 60 | { name: "Sunflower", color: "#FFC312" }, 61 | { name: "Energos", color: "#C4E538" }, 62 | { name: "BlueMartina", color: "#12CBC4" }, 63 | { name: "LavenderRose", color: "#FDA7DF" }, 64 | { name: "BaraRose", color: "#ED4C67" }, 65 | { name: "RadiantYellow", color: "#F79F1F" }, 66 | { name: "AndroidGreen", color: "#A3CB38" }, 67 | { name: "MediterraneanSea", color: "#1289A7" }, 68 | { name: "LavenderTea", color: "#D980FA" }, 69 | { name: "VerryBerry", color: "#B53471" }, 70 | { name: "PuffinsBill", color: "#EE5A24" }, 71 | { name: "PixelatedGrass", color: "#009432" }, 72 | { name: "MerchantMarineBlue", color: "#0652DD" }, 73 | { name: "ForgottenPurple", color: "#9980FA" }, 74 | { name: "HollyHock", color: "#833471" }, 75 | { name: "RedPigment", color: "#EA2027" }, 76 | { name: "TurkishAqua", color: "#006266" }, 77 | { name: "20000LeaguesUnderTheSea", color: "#1B1464" }, 78 | { name: "CircumorbitalRing", color: "#5758BB" }, 79 | { name: "MagentaPurple", color: "#6F1E51" } 80 | ] 81 | }, 82 | { 83 | paletteName: "Flat UI Colors American", 84 | id: "flat-ui-colors-american", 85 | emoji: "🇺🇸", 86 | colors: [ 87 | { name: "LightGreenishBlue", color: "#55efc4" }, 88 | { name: "FadedPoster", color: "#81ecec" }, 89 | { name: "GreenDarnerTail", color: "#74b9ff" }, 90 | { name: "ShyMoment", color: "#a29bfe" }, 91 | { name: "CityLights", color: "#dfe6e9" }, 92 | { name: "MintLeaf", color: "#00b894" }, 93 | { name: "RobinsEggBlue", color: "#00cec9" }, 94 | { name: "ElectronBlue", color: "#0984e3" }, 95 | { name: "ExodusFruit", color: "#6c5ce7" }, 96 | { name: "SoothingBreeze", color: "#b2bec3" }, 97 | { name: "SourLemon", color: "#ffeaa7" }, 98 | { name: "FirstDate", color: "#fab1a0" }, 99 | { name: "PinkGlamour", color: "#ff7675" }, 100 | { name: "Pico8Pink", color: "#fd79a8" }, 101 | { name: "AmericanRiver", color: "#636e72" }, 102 | { name: "BrightYarrow", color: "#fdcb6e" }, 103 | { name: "OrangeVille", color: "#e17055" }, 104 | { name: "Chi-Gong", color: "#d63031" }, 105 | { name: "PrunusAvium", color: "#e84393" }, 106 | { name: "DraculaOrchid", color: "#2d3436" } 107 | ] 108 | }, 109 | { 110 | paletteName: "Flat UI Colors Aussie", 111 | id: "flat-ui-colors-aussie", 112 | emoji: "🇦🇺", 113 | colors: [ 114 | { name: "Beekeeper", color: "#f6e58d" }, 115 | { name: "SpicedNectarine", color: "#ffbe76" }, 116 | { name: "PinkGlamour", color: "#ff7979" }, 117 | { name: "JuneBud", color: "#badc58" }, 118 | { name: "CoastalBreeze", color: "#dff9fb" }, 119 | { name: "Turbo", color: "#f9ca24" }, 120 | { name: "QuinceJelly", color: "#f0932b" }, 121 | { name: "CarminePink", color: "#eb4d4b" }, 122 | { name: "PureApple", color: "#6ab04c" }, 123 | { name: "HintOfIcePack", color: "#c7ecee" }, 124 | { name: "MiddleBlue", color: "#7ed6df" }, 125 | { name: "Heliotrope", color: "#e056fd" }, 126 | { name: "ExodusFruit", color: "#686de0" }, 127 | { name: "DeepKoamaru", color: "#30336b" }, 128 | { name: "SoaringEagle", color: "#95afc0" }, 129 | { name: "GreenlandGreen", color: "#22a6b3" }, 130 | { name: "SteelPink", color: "#be2edd" }, 131 | { name: "Blurple", color: "#4834d4" }, 132 | { name: "DeepCove", color: "#130f40" }, 133 | { name: "WizardGrey", color: "#535c68" } 134 | ] 135 | }, 136 | { 137 | paletteName: "Flat UI Colors British", 138 | id: "flat-ui-colors-british", 139 | emoji: "🇬🇧", 140 | colors: [ 141 | { name: "ProtossPylon", color: "#00a8ff" }, 142 | { name: "Periwinkle", color: "#9c88ff" }, 143 | { name: "Rise-N-Shine", color: "#fbc531" }, 144 | { name: "DownloadProgress", color: "#4cd137" }, 145 | { name: "Seabrook", color: "#487eb0" }, 146 | { name: "VanaDylBlue", color: "#0097e6" }, 147 | { name: "MattPurple", color: "#8c7ae6" }, 148 | { name: "NanohanachaGold", color: "#e1b12c" }, 149 | { name: "SkirretGreen", color: "#44bd32" }, 150 | { name: "Naval", color: "#40739e" }, 151 | { name: "NasturcianFlower", color: "#e84118" }, 152 | { name: "LynxWhite", color: "#f5f6fa" }, 153 | { name: "BlueberrySoda", color: "#7f8fa6" }, 154 | { name: "MazarineBlue", color: "#273c75" }, 155 | { name: "BlueNights", color: "#353b48" }, 156 | { name: "HarleyOrange", color: "#c23616" }, 157 | { name: "HintOfPensive", color: "#dcdde1" }, 158 | { name: "ChainGangGrey", color: "#718093" }, 159 | { name: "PicoVoid", color: "#192a56" }, 160 | { name: "ElectroMagnetic", color: "#2f3640" } 161 | ] 162 | }, 163 | { 164 | paletteName: "Flat UI Colors Spanish", 165 | id: "flat-ui-colors-spanish", 166 | emoji: "🇪🇸", 167 | colors: [ 168 | { name: "JacksonsPurple", color: "#40407a" }, 169 | { name: "C64Purple", color: "#706fd3" }, 170 | { name: "SwanWhite", color: "#f7f1e3" }, 171 | { name: "SummerSky", color: "#34ace0" }, 172 | { name: "CelestialGreen", color: "#33d9b2" }, 173 | { name: "LuckyPoint", color: "#2c2c54" }, 174 | { name: "Liberty", color: "#474787" }, 175 | { name: "HotStone", color: "#aaa69d" }, 176 | { name: "DevilBlue", color: "#227093" }, 177 | { name: "PalmSpringsSplash", color: "#218c74" }, 178 | { name: "FlourescentRed", color: "#ff5252" }, 179 | { name: "SyntheticPumpkin", color: "#ff793f" }, 180 | { name: "CrocodileTooth", color: "#d1ccc0" }, 181 | { name: "MandarinSorbet", color: "#ffb142" }, 182 | { name: "SpicedButterNut", color: "#ffda79" }, 183 | { name: "EyeOfNewt", color: "#b33939" }, 184 | { name: "ChileanFire", color: "#cd6133" }, 185 | { name: "GreyPorcelain", color: "#84817a" }, 186 | { name: "AlamedaOchre", color: "#cc8e35" }, 187 | { name: "Desert", color: "#ccae62" } 188 | ] 189 | }, 190 | { 191 | paletteName: "Flat UI Colors Indian", 192 | id: "flat-ui-colors-indian", 193 | emoji: "🇮🇳", 194 | colors: [ 195 | { name: "OrchidOrange", color: "#FEA47F" }, 196 | { name: "SpiroDiscoBall", color: "#25CCF7" }, 197 | { name: "HoneyGlow", color: "#EAB543" }, 198 | { name: "SweetGarden", color: "#55E6C1" }, 199 | { name: "FallingStar", color: "#CAD3C8" }, 200 | { name: "RichGardenia", color: "#F97F51" }, 201 | { name: "ClearChill", color: "#1B9CFC" }, 202 | { name: "WhitePepper", color: "#F8EFBA" }, 203 | { name: "Keppel", color: "#58B19F" }, 204 | { name: "ShipsOfficer", color: "#2C3A47" }, 205 | { name: "FieryFuchsia", color: "#B33771" }, 206 | { name: "BlueBell", color: "#3B3B98" }, 207 | { name: "GeorgiaPeach", color: "#FD7272" }, 208 | { name: "OasisStream", color: "#9AECDB" }, 209 | { name: "BrightUbe", color: "#D6A2E8" }, 210 | { name: "MagentaPurple", color: "#6D214F" }, 211 | { name: "EndingNavyBlue", color: "#182C61" }, 212 | { name: "SasquatchSocks", color: "#FC427B" }, 213 | { name: "PineGlade", color: "#BDC581" }, 214 | { name: "HighlighterLavender", color: "#82589F" } 215 | ] 216 | }, 217 | { 218 | paletteName: "Flat UI Colors French", 219 | id: "flat-ui-colors-french", 220 | emoji: "🇫🇷", 221 | colors: [ 222 | { name: "FlatFlesh", color: "#fad390" }, 223 | { name: "MelonMelody", color: "#f8c291" }, 224 | { name: "Livid", color: "#6a89cc" }, 225 | { name: "Spray", color: "#82ccdd" }, 226 | { name: "ParadiseGreen", color: "#b8e994" }, 227 | { name: "SquashBlossom", color: "#f6b93b" }, 228 | { name: "MandarinRed", color: "#e55039" }, 229 | { name: "AzraqBlue", color: "#4a69bd" }, 230 | { name: "Dupain", color: "#60a3bc" }, 231 | { name: "AuroraGreen", color: "#78e08f" }, 232 | { name: "IcelandPoppy", color: "#fa983a" }, 233 | { name: "TomatoRed", color: "#eb2f06" }, 234 | { name: "YueGuangBlue", color: "#1e3799" }, 235 | { name: "GoodSamaritan", color: "#3c6382" }, 236 | { name: "Waterfall", color: "#38ada9" }, 237 | { name: "CarrotOrange", color: "#e58e26" }, 238 | { name: "JalapenoRed", color: "#b71540" }, 239 | { name: "DarkSapphire", color: "#0c2461" }, 240 | { name: "ForestBlues", color: "#0a3d62" }, 241 | { name: "ReefEncounter", color: "#079992" } 242 | ] 243 | } 244 | ]; 245 | -------------------------------------------------------------------------------- /colors-app/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /colors-app/src/styles/ColorBoxStyles.js: -------------------------------------------------------------------------------- 1 | import chroma from "chroma-js"; 2 | import sizes from "./sizes"; 3 | export default { 4 | ColorBox: { 5 | width: "20%", 6 | height: props => (props.showingFullPalette ? "25%" : "50%"), 7 | margin: "0 auto", 8 | display: "inline-block", 9 | position: "relative", 10 | cursor: "pointer", 11 | marginBottom: "-3.5px", 12 | "&:hover button": { 13 | opacity: 1 14 | }, 15 | [sizes.down("lg")]: { 16 | width: "25%", 17 | height: props => (props.showingFullPalette ? "20%" : "33.3333%") 18 | }, 19 | [sizes.down("md")]: { 20 | width: "50%", 21 | height: props => (props.showingFullPalette ? "10%" : "20%") 22 | }, 23 | [sizes.down("xs")]: { 24 | width: "100%", 25 | height: props => (props.showingFullPalette ? "5%" : "10%") 26 | } 27 | }, 28 | copyText: { 29 | color: props => 30 | chroma(props.background).luminance() >= 0.7 ? "black" : "white" 31 | }, 32 | colorName: { 33 | color: props => 34 | chroma(props.background).luminance() <= 0.08 ? "white" : "black" 35 | }, 36 | seeMore: { 37 | color: props => 38 | chroma(props.background).luminance() >= 0.7 ? "rgba(0,0,0,0.6)" : "white", 39 | background: "rgba(255, 255, 255, 0.3)", 40 | position: "absolute", 41 | border: "none", 42 | right: "0px", 43 | bottom: "0px", 44 | width: "60px", 45 | height: "30px", 46 | textAlign: "center", 47 | lineHeight: "30px", 48 | textTransform: "uppercase" 49 | }, 50 | copyButton: { 51 | color: props => 52 | chroma(props.background).luminance() >= 0.7 ? "rgba(0,0,0,0.6)" : "white", 53 | width: "100px", 54 | height: "30px", 55 | position: "absolute", 56 | display: "inline-block", 57 | top: "50%", 58 | left: "50%", 59 | marginLeft: "-50px", 60 | marginTop: "-15px", 61 | textAlign: "center", 62 | outline: "none", 63 | background: "rgba(255, 255, 255, 0.3)", 64 | fontSize: "1rem", 65 | lineHeight: "30px", 66 | textTransform: "uppercase", 67 | border: "none", 68 | textDecoration: "none", 69 | opacity: 0 70 | }, 71 | boxContent: { 72 | position: "absolute", 73 | width: "100%", 74 | left: "0px", 75 | bottom: "0px", 76 | padding: "10px", 77 | color: "black", 78 | letterSpacing: "1px", 79 | textTransform: "uppercase", 80 | fontSize: "12px" 81 | }, 82 | copyOverlay: { 83 | opacity: "0", 84 | zIndex: "0", 85 | width: "100%", 86 | height: "100%", 87 | transition: "transform 0.6s ease-in-out", 88 | transform: "scale(0.1)" 89 | }, 90 | showOverlay: { 91 | opacity: "1", 92 | transform: "scale(50)", 93 | zIndex: "10", 94 | position: "absolute" 95 | }, 96 | copyMessage: { 97 | position: "fixed", 98 | left: "0", 99 | right: "0", 100 | top: "0", 101 | bottom: "0", 102 | display: "flex", 103 | alignItems: "center", 104 | justifyContent: "center", 105 | flexDirection: "column", 106 | fontSize: "4rem", 107 | transform: "scale(0.1)", 108 | opacity: "0", 109 | color: "white", 110 | "& h1": { 111 | fontWeight: "400", 112 | textShadow: "1px 2px black", 113 | background: "rgba(255, 255, 255, 0.2)", 114 | width: "100%", 115 | textAlign: "center", 116 | marginBottom: "0", 117 | padding: "1rem", 118 | textTransform: "uppercase", 119 | [sizes.down("xs")]: { 120 | fontSize: "6rem" 121 | } 122 | }, 123 | "& p": { 124 | fontSize: "2rem", 125 | fontWeight: "100" 126 | } 127 | }, 128 | showMessage: { 129 | opacity: "1", 130 | transform: "scale(1)", 131 | zIndex: "25", 132 | transition: "all 0.4s ease-in-out", 133 | transitionDelay: "0.3s" 134 | } 135 | }; 136 | -------------------------------------------------------------------------------- /colors-app/src/styles/ColorPickerFormStyles.js: -------------------------------------------------------------------------------- 1 | const styles = { 2 | picker: { 3 | width: "100% !important", 4 | marginTop: "2rem" 5 | }, 6 | addColor: { 7 | width: "100%", 8 | padding: "1rem", 9 | marginTop: "1rem", 10 | fontSize: "2rem" 11 | }, 12 | colorNameInput: { 13 | width: "100%", 14 | height: "70px" 15 | } 16 | }; 17 | 18 | export default styles; 19 | -------------------------------------------------------------------------------- /colors-app/src/styles/DraggableColorBoxStyles.js: -------------------------------------------------------------------------------- 1 | import sizes from "./sizes"; 2 | import chroma from "chroma-js"; 3 | 4 | const styles = { 5 | root: { 6 | width: "20%", 7 | height: "25%", 8 | margin: "0 auto", 9 | display: "inline-block", 10 | position: "relative", 11 | cursor: "pointer", 12 | marginBottom: "-3.5px", 13 | "&:hover svg": { 14 | color: "white", 15 | transform: "scale(1.5)" 16 | }, 17 | [sizes.down("lg")]: { 18 | width: "25%", 19 | height: "20%" 20 | }, 21 | [sizes.down("md")]: { 22 | width: "50%", 23 | height: "10%" 24 | }, 25 | [sizes.down("sm")]: { 26 | width: "100%", 27 | height: "5%" 28 | } 29 | }, 30 | boxContent: { 31 | position: "absolute", 32 | width: "100%", 33 | left: "0px", 34 | bottom: "0px", 35 | padding: "10px", 36 | color: props => 37 | chroma(props.color).luminance() <= 0.08 38 | ? "rgba(255,255,255,0.8)" 39 | : "rgba(0,0,0,0.6)", 40 | letterSpacing: "1px", 41 | textTransform: "uppercase", 42 | fontSize: "12px", 43 | display: "flex", 44 | justifyContent: "space-between" 45 | }, 46 | deleteIcon: { 47 | transition: "all 0.3s ease-in-out" 48 | } 49 | }; 50 | 51 | export default styles; 52 | -------------------------------------------------------------------------------- /colors-app/src/styles/MiniPaletteStyles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | root: { 3 | backgroundColor: "white", 4 | border: "1px solid black", 5 | borderRadius: "5px", 6 | padding: "0.5rem", 7 | position: "relative", 8 | overflow: "hidden", 9 | cursor: "pointer", 10 | "&:hover svg": { 11 | opacity: 1 12 | } 13 | }, 14 | colors: { 15 | backgroundColor: "#dae1e4", 16 | height: "150px", 17 | width: "100%", 18 | borderRadius: "5px", 19 | overflow: "hidden" 20 | }, 21 | title: { 22 | display: "flex", 23 | justifyContent: "space-between", 24 | alignItems: "center", 25 | margin: "0", 26 | color: "black", 27 | paddingTop: "0.5rem", 28 | fontSize: "1rem", 29 | position: "relative" 30 | }, 31 | emoji: { 32 | marginLeft: "0.5rem", 33 | fontSize: "1.5rem" 34 | }, 35 | miniColor: { 36 | height: "25%", 37 | width: "20%", 38 | display: "inline-block", 39 | margin: "0 auto", 40 | position: "relative", 41 | marginBottom: "-3.5px" 42 | }, 43 | deleteIcon: { 44 | color: "white", 45 | backgroundColor: "#eb3d30", 46 | width: "20px", 47 | height: "20px", 48 | position: "absolute", 49 | right: "0px", 50 | top: "0px", 51 | padding: "10px", 52 | zIndex: 10, 53 | opacity: 0 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /colors-app/src/styles/NavbarStyles.js: -------------------------------------------------------------------------------- 1 | import sizes from "./sizes"; 2 | 3 | export default { 4 | Navbar: { 5 | display: "flex", 6 | alignItems: "center", 7 | justifyContent: "flex-start", 8 | height: "6vh" 9 | }, 10 | logo: { 11 | marginRight: "15px", 12 | padding: "0 13px", 13 | fontSize: "22px", 14 | backgroundColor: "#eceff1", 15 | fontFamily: "Roboto", 16 | height: "100%", 17 | display: "flex", 18 | alignItems: "center", 19 | "& a": { 20 | textDecoration: "none", 21 | color: "black" 22 | }, 23 | [sizes.down("xs")]: { 24 | display: "none" 25 | } 26 | }, 27 | slider: { 28 | width: "340px", 29 | margin: "0 10px", 30 | display: "inline-block", 31 | "& .rc-slider-track": { 32 | backgroundColor: "transparent" 33 | }, 34 | "& .rc-slider-rail": { 35 | height: "8px" 36 | }, 37 | "& .rc-slider-handle, .rc-slider-handle:active, .rc-slider-handle:focus,.rc-slider-handle:hover": { 38 | backgroundColor: "green", 39 | outline: "none", 40 | border: "2px solid green", 41 | boxShadow: "none", 42 | width: "13px", 43 | height: "13px", 44 | marginLeft: "-7px", 45 | marginTop: "-3px" 46 | }, 47 | [sizes.down("sm")]: { 48 | width: "150px" 49 | } 50 | }, 51 | selectContainer: { 52 | marginLeft: "auto", 53 | marginRight: "1rem" 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /colors-app/src/styles/NewPaletteFormStyles.js: -------------------------------------------------------------------------------- 1 | import { DRAWER_WIDTH } from "../constants"; 2 | const drawerWidth = DRAWER_WIDTH; 3 | 4 | const styles = theme => ({ 5 | root: { 6 | display: "flex" 7 | }, 8 | drawer: { 9 | width: drawerWidth, 10 | flexShrink: 0, 11 | height: "100vh" 12 | }, 13 | drawerPaper: { 14 | width: drawerWidth, 15 | display: "flex", 16 | alignItems: "center" 17 | }, 18 | drawerHeader: { 19 | display: "flex", 20 | alignItems: "center", 21 | width: "100%", 22 | padding: "0 8px", 23 | ...theme.mixins.toolbar, 24 | justifyContent: "flex-end" 25 | }, 26 | content: { 27 | flexGrow: 1, 28 | height: "calc(100vh - 64px)", 29 | padding: 0, 30 | transition: theme.transitions.create("margin", { 31 | easing: theme.transitions.easing.sharp, 32 | duration: theme.transitions.duration.leavingScreen 33 | }), 34 | marginLeft: -drawerWidth 35 | }, 36 | contentShift: { 37 | transition: theme.transitions.create("margin", { 38 | easing: theme.transitions.easing.easeOut, 39 | duration: theme.transitions.duration.enteringScreen 40 | }), 41 | marginLeft: 0 42 | }, 43 | container: { 44 | width: "90%", 45 | height: "100%", 46 | display: "flex", 47 | flexDirection: "column", 48 | justifyContent: "center", 49 | alignItems: "center" 50 | }, 51 | buttons: { 52 | width: "100%" 53 | }, 54 | button: { 55 | width: "50%" 56 | } 57 | }); 58 | 59 | export default styles; 60 | -------------------------------------------------------------------------------- /colors-app/src/styles/Page.css: -------------------------------------------------------------------------------- 1 | .page { 2 | height: 100vh; 3 | position: fixed; 4 | width: 100%; 5 | top: 0; 6 | transition: opacity 0.5s ease-in-out; 7 | } 8 | .page-enter { 9 | opacity: 0; 10 | } 11 | .page-enter-active { 12 | opacity: 1; 13 | } 14 | .page-exit-active { 15 | opacity: 0; 16 | } 17 | -------------------------------------------------------------------------------- /colors-app/src/styles/PaletteFooterStyles.js: -------------------------------------------------------------------------------- 1 | export default { 2 | PaletteFooter: { 3 | backgroundColor: "white", 4 | height: "5vh", 5 | display: "flex", 6 | justifyContent: "flex-end", 7 | alignItems: "center", 8 | fontWeight: "bold" 9 | }, 10 | emoji: { 11 | fontSize: "1.5rem", 12 | margin: "0 1rem" 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /colors-app/src/styles/PaletteFormNavStyles.js: -------------------------------------------------------------------------------- 1 | import { DRAWER_WIDTH } from "../constants"; 2 | import sizes from "./sizes"; 3 | 4 | const drawerWidth = DRAWER_WIDTH; 5 | const styles = theme => ({ 6 | root: { 7 | display: "flex" 8 | }, 9 | hide: { 10 | display: "none" 11 | }, 12 | appBar: { 13 | transition: theme.transitions.create(["margin", "width"], { 14 | easing: theme.transitions.easing.sharp, 15 | duration: theme.transitions.duration.leavingScreen 16 | }), 17 | flexDirection: "row", 18 | justifyContent: "space-between", 19 | alignItems: "center", 20 | height: "64px" 21 | }, 22 | appBarShift: { 23 | width: `calc(100% - ${drawerWidth}px)`, 24 | marginLeft: drawerWidth, 25 | transition: theme.transitions.create(["margin", "width"], { 26 | easing: theme.transitions.easing.easeOut, 27 | duration: theme.transitions.duration.enteringScreen 28 | }) 29 | }, 30 | menuButton: { 31 | marginLeft: 12, 32 | marginRight: 20 33 | }, 34 | navBtns: { 35 | marginRight: "1rem", 36 | "& a": { 37 | textDecoration: "none" 38 | }, 39 | [sizes.down("xs")]: { 40 | marginRight: "0.5rem" 41 | } 42 | }, 43 | button: { 44 | margin: "0 0.5rem", 45 | [sizes.down("xs")]: { 46 | margin: "0 0.2rem", 47 | padding: "0.3rem" 48 | } 49 | } 50 | }); 51 | 52 | export default styles; 53 | -------------------------------------------------------------------------------- /colors-app/src/styles/PaletteListStyles.js: -------------------------------------------------------------------------------- 1 | import sizes from "./sizes"; 2 | import bg from "./bg.svg"; 3 | export default { 4 | "@global": { 5 | ".fade-exit": { 6 | opacity: 1 7 | }, 8 | ".fade-exit-active": { 9 | opacity: 0, 10 | transition: "opacity 500ms ease-out" 11 | } 12 | }, 13 | root: { 14 | height: "100vh", 15 | display: "flex", 16 | alignItems: "flex-start", 17 | justifyContent: "center", 18 | /* background by SVGBackgrounds.com */ 19 | backgroundColor: "#394bad", 20 | backgroundImage: `url(${bg})`, 21 | overflow: "scroll" 22 | }, 23 | heading: { 24 | fontSize: "2rem" 25 | }, 26 | container: { 27 | width: "50%", 28 | display: "flex", 29 | alignItems: "flex-start", 30 | flexDirection: "column", 31 | flexWrap: "wrap", 32 | [sizes.down("xl")]: { 33 | width: "80%" 34 | }, 35 | [sizes.down("xs")]: { 36 | width: "75%" 37 | } 38 | }, 39 | nav: { 40 | display: "flex", 41 | width: "100%", 42 | justifyContent: "space-between", 43 | alignItems: "center", 44 | color: "white", 45 | "& a": { 46 | color: "white" 47 | } 48 | }, 49 | palettes: { 50 | boxSizing: "border-box", 51 | width: "100%", 52 | display: "grid", 53 | gridTemplateColumns: "repeat(3, 30%)", 54 | gridGap: "2.5rem", 55 | [sizes.down("md")]: { 56 | gridTemplateColumns: "repeat(2, 50%)" 57 | }, 58 | [sizes.down("xs")]: { 59 | gridTemplateColumns: "repeat(1, 100%)", 60 | gridGap: "1.4rem" 61 | } 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /colors-app/src/styles/PaletteStyles.js: -------------------------------------------------------------------------------- 1 | import sizes from "./sizes"; 2 | export default { 3 | Palette: { 4 | height: "100vh", 5 | display: "flex", 6 | flexDirection: "column" 7 | }, 8 | colors: { 9 | height: "90%" 10 | }, 11 | goBack: { 12 | width: "20%", 13 | height: "50%", 14 | margin: "0 auto", 15 | display: "inline-block", 16 | position: "relative", 17 | cursor: "pointer", 18 | marginBottom: "-3.5px", 19 | opacity: 1, 20 | backgroundColor: "black", 21 | "& a": { 22 | color: "white", 23 | width: "100px", 24 | height: "30px", 25 | position: "absolute", 26 | display: "inline-block", 27 | top: "50%", 28 | left: "50%", 29 | marginLeft: "-50px", 30 | marginTop: "-15px", 31 | textAlign: "center", 32 | outline: "none", 33 | background: "rgba(255, 255, 255, 0.3)", 34 | fontSize: "1rem", 35 | lineHeight: "30px", 36 | textTransform: "uppercase", 37 | border: "none", 38 | textDecoration: "none" 39 | }, 40 | [sizes.down("lg")]: { 41 | width: "25%", 42 | height: "33.3333%" 43 | }, 44 | [sizes.down("md")]: { 45 | width: "50%", 46 | height: "20%" 47 | }, 48 | [sizes.down("xs")]: { 49 | width: "100%", 50 | height: "10%" 51 | } 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /colors-app/src/styles/bg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /colors-app/src/styles/sizes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | up() {}, 3 | down(size) { 4 | const sizes = { 5 | xs: "575.98px", 6 | sm: "767.98px", 7 | md: "991.98px", 8 | lg: "1199.98px", 9 | xl: "1600px" 10 | }; 11 | return `@media (max-width: ${sizes[size]})`; 12 | } 13 | }; 14 | --------------------------------------------------------------------------------