├── .babelrc ├── .browserslistrc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .huskyrc ├── .lintstagedrc ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .stylelintrc ├── .travis.yml ├── LICENSE.md ├── README.md ├── examples └── import-export │ ├── cat.txt │ └── json-cat.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── _redirects ├── index.dev.html └── index.html ├── screenshots ├── animation-cat.gif ├── screenshot-cat.png ├── screenshot-potion.png └── tree-pixelartcss.png ├── src ├── assets │ ├── apple-touch-icon.png │ ├── bmac-icon.png │ ├── coindrop-img.png │ ├── favicon.ico │ └── regular-icon.png ├── components │ ├── Animation.jsx │ ├── App.jsx │ ├── Bucket.jsx │ ├── CellSize.jsx │ ├── CellsInfo.jsx │ ├── Checkbox.jsx │ ├── ColorPicker.jsx │ ├── Cookies.jsx │ ├── CopyCSS.jsx │ ├── CssDisplay.jsx │ ├── Dimensions.jsx │ ├── DownloadDrawing.jsx │ ├── Duration.jsx │ ├── Eraser.jsx │ ├── Eyedropper.jsx │ ├── Frame.jsx │ ├── FramesHandler.jsx │ ├── GridWrapper.jsx │ ├── KeyBindings.jsx │ ├── KeyBindingsLegend.jsx │ ├── LoadDrawing.jsx │ ├── Modal.jsx │ ├── Move.jsx │ ├── NewProject.jsx │ ├── NotFound.jsx │ ├── Output.jsx │ ├── PaletteColor.jsx │ ├── PaletteGrid.jsx │ ├── Picker.jsx │ ├── PixelCanvas.jsx │ ├── PixelCell.jsx │ ├── PixelGrid.jsx │ ├── Preview.jsx │ ├── PreviewBox.jsx │ ├── RadioSelector.jsx │ ├── Reset.jsx │ ├── Root.jsx │ ├── SaveDrawing.jsx │ ├── SimpleNotification.jsx │ ├── SimpleSpinner.jsx │ ├── UndoRedo.jsx │ ├── UsefulData.jsx │ ├── common │ │ └── Button.jsx │ └── loadFromFile │ │ ├── ImageDimensions.jsx │ │ ├── ImageSetup.jsx │ │ ├── ImageSizeDisplay.jsx │ │ ├── ValidationMessage.jsx │ │ └── index.jsx ├── css │ ├── _base.css │ ├── _normalize.css │ ├── _utils.css │ ├── _variables.css │ ├── components │ │ ├── _App.css │ │ ├── _Bucket.css │ │ ├── _CellInfo.css │ │ ├── _CellSize.css │ │ ├── _Checkbox.css │ │ ├── _ColorPicker.css │ │ ├── _CopyCss.css │ │ ├── _CssDisplay.css │ │ ├── _Dimensions.css │ │ ├── _DownloadDrawing.css │ │ ├── _Duration.css │ │ ├── _Eraser.css │ │ ├── _EyeDropper.css │ │ ├── _Frame.css │ │ ├── _FramesHandler.css │ │ ├── _LoadDrawing.css │ │ ├── _Modal.css │ │ ├── _Move.css │ │ ├── _NewProject.css │ │ ├── _Output.css │ │ ├── _PaletteColor.css │ │ ├── _PaletteGrid.css │ │ ├── _Picker.css │ │ ├── _PixelGrid.css │ │ ├── _PreviewBox.css │ │ ├── _RadioSelector.css │ │ ├── _Reset.css │ │ ├── _SaveDrawing.css │ │ ├── _SimpleNotification.css │ │ ├── _SimpleSpinner.css │ │ ├── _UndoRedo.css │ │ └── _UsefulData.css │ ├── fonts │ │ ├── _fonts.css │ │ └── files │ │ │ ├── minecraftia-regular-webfont.eot │ │ │ ├── minecraftia-regular-webfont.svg │ │ │ ├── minecraftia-regular-webfont.ttf │ │ │ ├── minecraftia-regular-webfont.woff │ │ │ ├── minecraftia-regular-webfont.woff2 │ │ │ ├── webfont-icons.eot │ │ │ ├── webfont-icons.svg │ │ │ ├── webfont-icons.ttf │ │ │ └── webfont-icons.woff │ ├── imports.css │ ├── input │ │ ├── _button.css │ │ └── _inputText.css │ ├── layout │ │ ├── _flex.css │ │ ├── _grid.css │ │ ├── _header.css │ │ └── _queries.css │ └── views │ │ ├── _cookies.css │ │ └── _notFound.css ├── index.jsx ├── store │ ├── actions │ │ ├── actionCreators.js │ │ └── actionTypes.js │ ├── configureStore.js │ └── reducers │ │ ├── activeFrameReducer.js │ │ ├── drawingToolReducer.js │ │ ├── drawingToolStates.js │ │ ├── framesReducer.js │ │ ├── paletteReducer.js │ │ └── reducer.js └── utils │ ├── ImageToCanvas.js │ ├── breakpoints.js │ ├── canvasGIF.js │ ├── color.js │ ├── cssParse.js │ ├── drawHandlersProvider.js │ ├── intervals.js │ ├── loadFromCanvas.js │ ├── outputParse.js │ ├── polyfills.js │ ├── random.js │ ├── startup.js │ ├── storage.js │ └── throttle.js ├── test ├── actions.test.js ├── activeFrameReducer.test.js ├── drawingToolReducer.test.js ├── framesReducer.test.js ├── paletteReducer.test.js ├── reducer.test.js └── utils │ ├── color.test.js │ ├── intervals.test.js │ ├── loadFromCanvas.test.js │ └── outputParse.test.js ├── webpack.config.js └── webpack.production.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | last 2 versions 2 | IE > 8 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | deploy 5 | *config.js 6 | images 7 | screenshots 8 | examples 9 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "eslint-config-prettier"], 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "jest": true 7 | }, 8 | "parser": "babel-eslint", 9 | "parserOptions": { 10 | "ecmaFeatures": { 11 | "forOf": true, 12 | "jsx": true, 13 | "es6": true 14 | } 15 | }, 16 | "plugins": ["react-hooks"], 17 | "rules": { 18 | "comma-dangle": 0, 19 | "indent": [2, 2, { "SwitchCase": 1 }], 20 | "react/prop-types": 0, 21 | "func-names": 0, 22 | "arrow-body-style": [2, "as-needed"], 23 | "no-underscore-dangle": ["error", { "allow": ["__INITIAL_STATE__"] }], 24 | "new-cap": ["error", { "capIsNewExceptions": ["Map", "List"] }], 25 | "react/prefer-es6-class": 1, 26 | "no-restricted-syntax": 0, 27 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], 28 | "import/no-extraneous-dependencies": ["error", { "devDependencies": true }], 29 | "quote-props": ["error", "consistent"], 30 | "no-console": 0, 31 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 32 | "react/jsx-props-no-spreading": [0, { "custom": "ignore" }], 33 | "jsx-a11y/no-static-element-interactions": 0, 34 | "jsx-a11y/label-has-for": [ 35 | 2, 36 | { "required": { "some": ["nesting", "id"] } } 37 | ], 38 | "react-hooks/rules-of-hooks": "error", 39 | "react-hooks/exhaustive-deps": "warn" 40 | }, 41 | "globals": { 42 | "$": true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/bundle.js 3 | npm-debug.log 4 | deploy 5 | config.json 6 | routes 7 | .env 8 | npm-debug.log 9 | .directory 10 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "pre-commit": "lint-staged" 4 | } 5 | } -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "**/*.{js,jsx}": "eslint", 3 | "src/**/*.css": "stylelint", 4 | "**/*.{js,jsx,json,yml,yaml,css,md}": [ 5 | "prettier --write", 6 | "git add" 7 | ] 8 | } -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | dist 4 | deploy 5 | *config.js 6 | images 7 | screenshots 8 | examples -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "htmlWhitespaceSensitivity": "css", 5 | "insertPragma": false, 6 | "jsxBracketSameLine": false, 7 | "jsxSingleQuote": false, 8 | "printWidth": 80, 9 | "proseWrap": "preserve", 10 | "requirePragma": false, 11 | "semi": true, 12 | "singleQuote": true, 13 | "tabWidth": 2, 14 | "trailingComma": "none", 15 | "useTabs": false 16 | } -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "stylelint-config-recommended", 4 | "stylelint-config-lost" 5 | ], 6 | "rules": { 7 | "at-rule-no-unknown": [ 8 | true, 9 | { 10 | "ignoreAtRules": ["mixin", "if", "extend", "/^define[a-z]*/"] 11 | } 12 | ], 13 | "no-extra-semicolons": null, 14 | "font-family-no-missing-generic-family-keyword": null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '16' 4 | before_install: 5 | - npm i -g npm@7.24.1 6 | script: 7 | - npm run lint 8 | - npm run csslint 9 | - npm test 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Javier Valencia Romero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

⚠️ Temporary Limited Interaction Notice ⚠️

3 | (Please check Contributing) 4 |
5 |
6 |
7 |

8 | 9 |

10 |

Pixel Art to CSS

11 |

12 |

13 | Animate pixel art and get CSS 14 |

15 |

16 |

17 | 18 | travis ci 19 |

20 | 21 | ## Did you know that you can create pixel art using CSS? 22 | 23 | **Pixel Art to CSS** is an online editor that helps you with that task. 24 | 25 | Combining the power of both **box-shadow** and **keyframes** CSS properties, you will get CSS code ready to use in your site. 26 | 27 | Furthermore, you can download your work in different formats such as a static image, animated GIF or sprite like image. 28 | 29 | :pencil2: [Try it out](https://www.pixelartcss.com/) 30 | 31 |

32 | 33 |

34 | 35 | **Pixel Art to CSS** aims to be an intuitive tool by its simplicity, however it is equipped with a wide range of features: customize your color palette, go back and forth in time, modify animation settings, save or load your projects, among others. 36 | 37 | ## Example 38 | 39 | By default, you will find the following project within the LOAD section: 40 | 41 | ![Cat animation example](screenshots/animation-cat.gif) 42 | 43 | See it live at [pixelartcss](https://www.pixelartcss.com/) 44 | 45 | You can also import it directly submitting [this](examples/import-export/cat.txt) code. 46 | 47 | ## Technical overview 48 | 49 | This application has been built with the following technologies: 50 | 51 | - [React](https://facebook.github.io/react/): Library to build the UI. 52 | - [Redux](http://redux.js.org/): Implements a Flux like architecture. 53 | - [ImmutableJS](https://facebook.github.io/immutable-js/) Helps to keep the data immutable aiming to avoid side effects. 54 | - [PostCSS](https://github.com/postcss/postcss) Handle the app CSS. 55 | 56 | ## Installation 57 | 58 | ```bash 59 | npm install 60 | ``` 61 | 62 | ## Development 63 | 64 | ```bash 65 | npm run development 66 | ``` 67 | 68 | ## Deploy 69 | 70 | Create the production build. 71 | 72 | ```bash 73 | npm run deploy 74 | ``` 75 | 76 | ## Lint 77 | 78 | There are several libraries used in the project that help us to keep our codebase healthy: 79 | 80 | - [ESlint](https://eslint.org/) 81 | - [Stylelint](https://stylelint.io/) 82 | - [Prettier](https://prettier.io/) 83 | 84 | Every time we commit something it will execute the linters and format the staged files if needed. 85 | 86 | If you want to check them individually you could execute the following scripts: 87 | 88 | ```bash 89 | npm run lint 90 | npm run csslint 91 | npm run format 92 | ``` 93 | 94 | ## Testing 95 | 96 | We are using [Jest](https://jestjs.io/) as the testing platform. 97 | 98 | ```bash 99 | npm run test 100 | ``` 101 | 102 | ## Contributing 103 | 104 |
105 |

106 | ⚠️ Please Note: This repository is currently in a temporary idle state due to a refactor and tech upgrade. I am not accepting new Pull Requests or Issues at the moment. Sorry for the inconveniences. 107 |

108 |
109 | 110 | ~~#### Help me to improve it :seedling:~~ 111 | 112 | ~~Create a GitHub issue if there is something wrong or to be improved.~~ 113 | 114 | ~~Pull requests are also welcome, please take the following requirements as a checklist before opening one:~~ 115 | 116 | ~~- [x] The PR does fix a problem or adds a new feature, not just cosmetic or syntactic sugar changes.~~ 117 | ~~- [x] It would be great to open an issue in advance.~~ 118 | ~~- [x] The PR should be issued to the **develop** branch.~~ 119 | ~~- [x] The PR should have a explanation or be related to an issue.~~ 120 | 121 | ~~Thank you!~~ 122 | 123 | ## License 124 | 125 | [MIT](https://opensource.org/licenses/mit-license.php) 126 | Copyright © 2016 Javier Valencia Romero (@jvalen) 127 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pixel-art-react", 3 | "version": "3.10.0", 4 | "description": "Draw and animate Pixel Art, then export the results to CSS or download them, powered by React", 5 | "author": "Javier Valencia Romero", 6 | "scripts": { 7 | "lint": "eslint **/*.{js,jsx}", 8 | "csslint": "stylelint src/**/*.{css,js,jsx}", 9 | "prettier": "prettier **/*.{js,jsx,json,yml,yaml,css,md}", 10 | "format": "npm run prettier -- --write --list-different", 11 | "test": "jest", 12 | "test:watch": "npm run test -- --watch", 13 | "validate": "npm run lint && npm run csslint && npm run prettier -- --list-different && npm run test", 14 | "development": "webpack-dev-server --hot", 15 | "deploy": "NODE_ENV=production webpack --mode production --config webpack.production.config.js", 16 | "postinstall": "NODE_ENV=production webpack --mode production --config webpack.production.config.js" 17 | }, 18 | "main": "index.js", 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/jvalen/pixel-art-react" 22 | }, 23 | "keywords": [ 24 | "pixel", 25 | "art", 26 | "drawing", 27 | "react", 28 | "redux", 29 | "express" 30 | ], 31 | "license": "MIT", 32 | "jest": { 33 | "setupFiles": [ 34 | "jest-canvas-mock" 35 | ] 36 | }, 37 | "dependencies": { 38 | "assert": "^2.1.0", 39 | "autoprefixer": "^9.7.2", 40 | "blob-stream": "^0.1.3", 41 | "body-scroll-lock": "^3.0.2", 42 | "box-shadow-pixels": "0.0.4", 43 | "buffer": "^6.0.3", 44 | "canvas-toBlob": "^1.0.0", 45 | "css-loader": "^3.2.0", 46 | "dotenv-webpack": "^8.0.1", 47 | "file-loader": "^6.2.0", 48 | "file-saver": "^2.0.2", 49 | "gif-encoder": "^0.7.2", 50 | "gm": "^1.23.1", 51 | "immutable": "^3.8.2", 52 | "lost": "^8.3.1", 53 | "oauth": "^0.9.15", 54 | "postcss-import": "^12.0.1", 55 | "postcss-loader": "^3.0.0", 56 | "postcss-reporter": "^6.0.1", 57 | "precss": "^2.0.0", 58 | "prop-types": "^15.7.2", 59 | "pug": "^2.0.4", 60 | "react": "^16.8.6", 61 | "react-addons-pure-render-mixin": "^15.6.2", 62 | "react-addons-shallow-compare": "^15.6.2", 63 | "react-beautiful-dnd": "^12.1.1", 64 | "react-color": "^2.18.1", 65 | "react-cookie-consent": "^5.2.0", 66 | "react-custom-scrollbars": "^4.2.1", 67 | "react-dom": "^16.8.6", 68 | "react-ga": "^3.3.1", 69 | "react-modal": "^3.9.1", 70 | "react-redux": "^7.1.0", 71 | "react-router-dom": "^6.16.0", 72 | "react-transform-hmr": "^1.0.4", 73 | "react-transition-group": "^2.3.1", 74 | "redux": "^4.0.4", 75 | "redux-undo": "^1.0.0-beta9", 76 | "shortid": "^2.2.14", 77 | "stream-browserify": "^3.0.0", 78 | "style-loader": "^0.23.1", 79 | "styled-components": "^5.2.0", 80 | "styled-theming": "^2.2.0", 81 | "temp": "^0.9.0", 82 | "tinykeys": "^1.1.0", 83 | "twitter": "^1.7.1", 84 | "url-loader": "^4.1.1", 85 | "util": "^0.12.5", 86 | "whatwg-fetch": "^2.0.4" 87 | }, 88 | "engines": { 89 | "node": "16.x.x", 90 | "npm": "8.x.x" 91 | }, 92 | "devDependencies": { 93 | "@babel/core": "^7.5.4", 94 | "@babel/preset-env": "^7.5.4", 95 | "@babel/preset-react": "^7.0.0", 96 | "@babel/runtime-corejs2": "^7.5.4", 97 | "babel-core": "^7.0.0-bridge.0", 98 | "babel-eslint": "^10.0.2", 99 | "babel-jest": "^24.8.0", 100 | "babel-loader": "^8.0.6", 101 | "babel-plugin-react-transform": "^3.0.0", 102 | "copy-webpack-plugin": "^11.0.0", 103 | "eslint": "^6.0.1", 104 | "eslint-config-airbnb": "^18.0.1", 105 | "eslint-config-prettier": "^6.7.0", 106 | "eslint-plugin-import": "^2.18.2", 107 | "eslint-plugin-jsx-a11y": "^6.2.3", 108 | "eslint-plugin-react": "^7.16.0", 109 | "eslint-plugin-react-hooks": "^2.3.0", 110 | "html-webpack-plugin": "^5.5.3", 111 | "husky": "^3.0.0", 112 | "jest": "^24.8.0", 113 | "jest-canvas-mock": "^2.3.1", 114 | "lint-staged": "^9.2.0", 115 | "mini-css-extract-plugin": "^2.7.6", 116 | "prettier": "^1.18.2", 117 | "stylelint": "^10.1.0", 118 | "stylelint-config-lost": "0.0.3", 119 | "stylelint-config-recommended": "^2.2.0", 120 | "webpack": "^5.88.2", 121 | "webpack-cli": "^5.1.4", 122 | "webpack-dev-middleware": "^6.1.1", 123 | "webpack-dev-server": "^4.15.1", 124 | "webpack-hot-middleware": "^2.25.4" 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer'), 4 | require('postcss-import'), 5 | require('precss'), 6 | require('lost'), 7 | require('postcss-reporter'), 8 | ] 9 | } -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/index.dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 |
13 |
14 |
15 |

PIXEL ART TO CSS

16 |
17 |
18 | 40 |
41 |
42 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pixel Art to CSS 7 | 11 | 15 | 16 | 17 | 18 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |
36 |

PIXEL ART TO CSS

37 |
38 |
39 | 60 |
61 |
62 |
63 |
64 | 65 | 66 | -------------------------------------------------------------------------------- /screenshots/animation-cat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/screenshots/animation-cat.gif -------------------------------------------------------------------------------- /screenshots/screenshot-cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/screenshots/screenshot-cat.png -------------------------------------------------------------------------------- /screenshots/screenshot-potion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/screenshots/screenshot-potion.png -------------------------------------------------------------------------------- /screenshots/tree-pixelartcss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/screenshots/tree-pixelartcss.png -------------------------------------------------------------------------------- /src/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/src/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/bmac-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/src/assets/bmac-icon.png -------------------------------------------------------------------------------- /src/assets/coindrop-img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/src/assets/coindrop-img.png -------------------------------------------------------------------------------- /src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/src/assets/favicon.ico -------------------------------------------------------------------------------- /src/assets/regular-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sealdeck/pixel-art-react/6d617d1268aa9e2098164748581f7c4d9497fba7/src/assets/regular-icon.png -------------------------------------------------------------------------------- /src/components/Animation.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { keyframes } from 'styled-components'; 3 | import randomString from '../utils/random'; 4 | 5 | const Animation = props => { 6 | const { boxShadow, duration, name } = props; 7 | const keyframeName = name !== undefined ? name : randomString(); 8 | const keyframeRules = keyframes`${boxShadow}`.rules; 9 | const style = { 10 | position: 'absolute', 11 | animation: `x ${duration}s infinite`, 12 | animationName: keyframeName, 13 | left: '-5px', 14 | top: '-5px' 15 | }; 16 | const animString = `@keyframes ${keyframeName} {${keyframeRules}}`; 17 | return ( 18 |
19 |
20 | 21 |
22 | ); 23 | }; 24 | 25 | export default Animation; 26 | -------------------------------------------------------------------------------- /src/components/Bucket.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { switchTool } from '../store/actions/actionCreators'; 4 | import { BUCKET } from '../store/reducers/drawingToolStates'; 5 | 6 | const Bucket = ({ bucketOn, switchBucket }) => ( 7 | 30 | ); 31 | }; 32 | 33 | export default DownloadDrawing; 34 | -------------------------------------------------------------------------------- /src/components/Duration.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators } from 'redux'; 4 | import * as actionCreators from '../store/actions/actionCreators'; 5 | 6 | const Duration = ({ actions, duration }) => { 7 | const handleChange = event => { 8 | actions.setDuration(event.target.value); 9 | }; 10 | return ( 11 |
12 | 23 |
24 | ); 25 | }; 26 | 27 | const mapStateToProps = state => ({ 28 | duration: state.present.get('duration') 29 | }); 30 | 31 | const mapDispatchToProps = dispatch => ({ 32 | actions: bindActionCreators(actionCreators, dispatch) 33 | }); 34 | 35 | const DurationContainer = connect( 36 | mapStateToProps, 37 | mapDispatchToProps 38 | )(Duration); 39 | export default DurationContainer; 40 | -------------------------------------------------------------------------------- /src/components/Eraser.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { switchTool } from '../store/actions/actionCreators'; 4 | import { ERASER } from '../store/reducers/drawingToolStates'; 5 | 6 | const Eraser = ({ eraserOn, switchEraser }) => ( 7 |
91 | )} 92 | 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/components/FramesHandler.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators } from 'redux'; 4 | import { DragDropContext, Droppable } from 'react-beautiful-dnd'; 5 | import { Scrollbars } from 'react-custom-scrollbars'; 6 | import * as actionCreators from '../store/actions/actionCreators'; 7 | import Frame from './Frame'; 8 | 9 | class FramesHandler extends React.Component { 10 | constructor(props) { 11 | super(props); 12 | this.state = { newFrame: false }; 13 | this.onDragEnd = this.onDragEnd.bind(this); 14 | } 15 | 16 | handleClick() { 17 | const { actions } = this.props; 18 | actions.createNewFrame(); 19 | this.setState({ newFrame: true }); 20 | } 21 | 22 | onDragEnd(result) { 23 | const { destination, source } = result; 24 | const { actions } = this.props; 25 | 26 | if (!destination) { 27 | return; 28 | } 29 | 30 | if ( 31 | destination.droppableId === source.droppableId && 32 | destination.index === source.index 33 | ) { 34 | return; 35 | } 36 | 37 | actions.reorderFrame(source.index, destination.index); 38 | } 39 | 40 | onScrollbarUpdate() { 41 | const { newFrame } = this.state; 42 | if (newFrame) { 43 | this.setState({ newFrame: false }); 44 | this.scrollbars.scrollToRight(); 45 | } 46 | } 47 | 48 | getFrames() { 49 | const { list, columns, rows, activeIndex, actions } = this.props; 50 | return list.map((frameData, index) => ( 51 | 66 | )); 67 | } 68 | 69 | render() { 70 | return ( 71 |
72 | 81 |
82 | { 85 | this.scrollbars = c; 86 | }} 87 | universal 88 | onUpdate={() => { 89 | this.onScrollbarUpdate(); 90 | }} 91 | > 92 | 93 | 94 | {provided => ( 95 |
100 | {this.getFrames()} 101 | {provided.placeholder} 102 |
103 | )} 104 |
105 |
106 |
107 |
108 |
109 | ); 110 | } 111 | } 112 | 113 | const mapStateToProps = state => state.present.get('frames').toObject(); 114 | 115 | const mapDispatchToProps = dispatch => ({ 116 | actions: bindActionCreators(actionCreators, dispatch) 117 | }); 118 | 119 | const FramesHandlerContainer = connect( 120 | mapStateToProps, 121 | mapDispatchToProps 122 | )(FramesHandler); 123 | export default FramesHandlerContainer; 124 | -------------------------------------------------------------------------------- /src/components/GridWrapper.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PixelGrid from './PixelGrid'; 3 | 4 | export default class GridWrapper extends React.Component { 5 | shouldComponentUpdate(newProps) { 6 | const { cells } = this.props; 7 | return newProps.cells !== cells; 8 | } 9 | 10 | onMouseOver(ev) { 11 | const { activeTool, drawHandlers } = this.props; 12 | if (activeTool === 'MOVE') { 13 | drawHandlers.onMoveMouseOver(ev); 14 | } 15 | } 16 | 17 | onMouseDown(ev) { 18 | const { activeTool, drawHandlers } = this.props; 19 | if (activeTool === 'MOVE') { 20 | drawHandlers.onMoveMouseDown(ev); 21 | } 22 | } 23 | 24 | onTouchStart(ev) { 25 | const { activeTool, drawHandlers } = this.props; 26 | if (activeTool === 'MOVE') { 27 | drawHandlers.onMoveTouchStart(ev); 28 | } 29 | } 30 | 31 | onTouchMove(ev) { 32 | const { activeTool, drawHandlers } = this.props; 33 | if (activeTool === 'MOVE') { 34 | drawHandlers.onMoveTouchMove(ev); 35 | } 36 | } 37 | 38 | render() { 39 | const { props } = this; 40 | return ( 41 |
this.onMouseOver(ev)} 43 | onFocus={ev => this.onMouseOver(ev)} 44 | onMouseDown={ev => this.onMouseDown(ev)} 45 | onTouchStart={ev => this.onTouchStart(ev)} 46 | onTouchMove={ev => this.onTouchMove(ev)} 47 | > 48 | 55 |
56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/components/KeyBindings.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import { useDispatch } from 'react-redux'; 3 | import tinykeys from 'tinykeys'; 4 | import { 5 | undo, 6 | redo, 7 | switchTool, 8 | changeDimensions 9 | } from '../store/actions/actionCreators'; 10 | import { 11 | MOVE, 12 | ERASER, 13 | BUCKET, 14 | EYEDROPPER, 15 | COLOR_PICKER 16 | } from '../store/reducers/drawingToolStates'; 17 | 18 | const KeyBindings = ({ onClick }) => { 19 | const dispatch = useDispatch(); 20 | useEffect(() => { 21 | const keyCombinations = { 22 | '$mod+KeyZ': event => { 23 | event.preventDefault(); 24 | dispatch(undo()); 25 | }, 26 | '$mod+KeyY': event => { 27 | event.preventDefault(); 28 | dispatch(redo()); 29 | }, 30 | // prettier-ignore 31 | 'KeyM': event => { 32 | event.preventDefault(); 33 | dispatch(switchTool(MOVE)); 34 | }, 35 | // prettier-ignore 36 | 'KeyE': event => { 37 | event.preventDefault(); 38 | dispatch(switchTool(ERASER)); 39 | }, 40 | // prettier-ignore 41 | 'KeyB': event => { 42 | event.preventDefault(); 43 | dispatch(switchTool(BUCKET)); 44 | }, 45 | // prettier-ignore 46 | 'KeyO': event => { 47 | event.preventDefault(); 48 | dispatch(switchTool(EYEDROPPER)); 49 | }, 50 | // prettier-ignore 51 | 'KeyP': event => { 52 | event.preventDefault(); 53 | dispatch(switchTool(COLOR_PICKER)); 54 | }, 55 | '$mod+ArrowRight': event => { 56 | event.preventDefault(); 57 | dispatch(changeDimensions('columns', 1)); 58 | }, 59 | '$mod+ArrowLeft': event => { 60 | event.preventDefault(); 61 | dispatch(changeDimensions('columns', -1)); 62 | }, 63 | '$mod+ArrowDown': event => { 64 | event.preventDefault(); 65 | dispatch(changeDimensions('rows', 1)); 66 | }, 67 | '$mod+ArrowUp': event => { 68 | event.preventDefault(); 69 | dispatch(changeDimensions('rows', -1)); 70 | } 71 | }; 72 | const unsubscribe = tinykeys(window, keyCombinations); 73 | return () => { 74 | unsubscribe(); 75 | }; 76 | }); 77 | return ( 78 | 21 | 22 | ); 23 | }; 24 | 25 | const mapDispatchToProps = dispatch => ({ 26 | actions: bindActionCreators(actionCreators, dispatch) 27 | }); 28 | 29 | const NewProjectContainer = connect( 30 | null, 31 | mapDispatchToProps 32 | )(NewProject); 33 | export default NewProjectContainer; 34 | -------------------------------------------------------------------------------- /src/components/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NotFound = () => ( 4 |
5 |

Oops! No pixels to draw here

6 |
7 |
8 |
9 | Go back to the editor 10 |
11 | ); 12 | 13 | export default NotFound; 14 | -------------------------------------------------------------------------------- /src/components/Output.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from 'react'; 2 | 3 | const Output = ({ 4 | copyClipboardData = {}, 5 | readOnly = true, 6 | outputText, 7 | preFormatted = false 8 | }) => { 9 | const { showButton, textButton, successMessage } = copyClipboardData; 10 | const [copySuccess, setCopySuccess] = useState(''); 11 | const textAreaRef = useRef(null); 12 | const copyToClipboard = e => { 13 | textAreaRef.current.select(); 14 | document.execCommand('copy'); 15 | e.target.focus(); 16 | setCopySuccess(successMessage || 'Copied!'); 17 | }; 18 | return ( 19 |
20 | {showButton && document.queryCommandSupported('copy') && ( 21 |
22 | 29 | {copySuccess} 30 |
31 | )} 32 |