├── .appcast.xml ├── .babelrc ├── .circleci └── config.yml ├── .env.example ├── .eslintrc ├── .github ├── CODEOWNERS └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .releaserc.json ├── .sketchpacks.json ├── .vscode └── settings.json ├── README.md ├── __mocks__ ├── fileMock.js └── styleMock.js ├── assets └── icon.png ├── constants.ts ├── enums ├── color-type.enum.ts └── layer-type.enum.ts ├── jest.config.js ├── package-lock.json ├── package.json ├── prepareRelease.sh ├── resources ├── app │ ├── Global.styles.ts │ ├── ListContext.tsx │ ├── assets │ │ ├── Futura-Bold.otf │ │ ├── Rectangle.svg │ │ ├── SFProDisplay-Bold.otf │ │ ├── SFProDisplay-Heavy.otf │ │ ├── SFProDisplay-Regular.otf │ │ ├── arrowActive.svg │ │ ├── arrowGrey.svg │ │ ├── arrowInactive.svg │ │ ├── artboard.svg │ │ ├── checkered_small.svg │ │ ├── colormateLogo.svg │ │ ├── minimilistBubbles.svg │ │ ├── replaceBtn.svg │ │ ├── replaceBtnHover.svg │ │ ├── text1Loader.svg │ │ ├── text2Loader.svg │ │ ├── textIcon.svg │ │ └── washingTransparent.gif │ ├── components │ │ ├── App.tsx │ │ ├── Banner.tsx │ │ ├── Button.test.tsx │ │ ├── Button.tsx │ │ ├── ColorPicker.tsx │ │ ├── Footer.test.tsx │ │ ├── Footer.tsx │ │ ├── Header.test.tsx │ │ ├── Header.tsx │ │ ├── List │ │ │ ├── List.tsx │ │ │ ├── ListItem.styles.tsx │ │ │ ├── ListItem.tsx │ │ │ ├── ListItemTree.tsx │ │ │ └── Tree │ │ │ │ ├── LayerNode.styles.tsx │ │ │ │ └── LayerNode.tsx │ │ ├── Loader.test.tsx │ │ ├── Loader.tsx │ │ ├── NoColorsFound.test.tsx │ │ ├── NoColorsFound.tsx │ │ └── __snapshots__ │ │ │ ├── Footer.test.tsx.snap │ │ │ ├── Header.test.tsx.snap │ │ │ ├── Loader.test.tsx.snap │ │ │ └── NoColorsFound.test.tsx.snap │ ├── helpers │ │ ├── calculations.test.js │ │ ├── calculations.ts │ │ ├── replace-color.test.ts │ │ ├── replace-color.ts │ │ ├── transform-sketch-colormap.test.ts │ │ ├── transform-sketch-colormap.ts │ │ ├── window.test.ts │ │ └── window.ts │ ├── hooks │ │ ├── useHover.test.tsx │ │ └── useHover.ts │ ├── index.tsx │ └── models │ │ ├── color-with-layers.model.ts │ │ └── sketch-color-map.model.ts ├── style.css ├── webview.html └── webview.js ├── setupTest.ts ├── src ├── __mocks__ │ ├── MockLayer.json │ ├── MockSketchDocument.json │ ├── MockTextLayer.json │ └── getColorsResult.json ├── get-colors.test.ts ├── get-colors.ts ├── helpers │ ├── environment.ts │ ├── get-colors.test.ts │ ├── get-colors.ts │ ├── get-page.test.ts │ ├── get-page.ts │ ├── replace-color-in-layers.test.ts │ ├── replace-color-in-layers.ts │ ├── traverse.test.ts │ └── traverse.ts ├── manifest.json ├── models │ └── layer.model.ts └── my-command.js ├── tsconfig.json ├── tsconfig.test.json ├── typings ├── assets.d.ts ├── sketch.d.ts └── window.d.ts ├── updateAppcast.sh └── webpack.skpm.config.js /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Colormate 5 | http://sparkle-project.org/files/sparkletestcast.xml 6 | Colormate is a kickass sketch plugin that will help you figure out how in the hell you ended up with 457 different greys, instead of the 1 grey Mandy gave you in the handover 7 | en 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Version v1.2.0 16 | 17 | 18 | 19 | Version v1.3.0 20 | 21 | 22 | 23 | Version v1.3.1 24 | 25 | 26 | 27 | Version v1.4.4 28 | 29 | 30 | 31 | Version v1.4.5 32 | 33 | 34 | 35 | Version v1.4.6 36 | 37 | 38 | 39 | Version v1.4.6 40 | 41 | 42 | 43 | Version v1.4.7 44 | 45 | 46 | 47 | Version v1.4.8 48 | 49 | 50 | 51 | Version v1.4.9 52 | 53 | 54 | 55 | Version v1.4.9 56 | 57 | 58 | 59 | Version v1.4.10 60 | 61 | 62 | 63 | Version v1.4.11 64 | 65 | 66 | 67 | Version vv1.4.12 68 | 69 | 70 | 71 | Version v1.4.13 72 | 73 | 74 | 75 | Version v1.4.14 76 | 77 | 78 | 79 | Version v1.4.14 80 | 81 | 82 | 83 | Version v1.4.16 84 | 85 | 86 | 87 | Version v1.4.17 88 | 89 | 90 | 91 | Version v1.4.18 92 | 93 | 94 | 95 | Version v1.4.22 96 | 97 | 98 | 99 | Version v1.4.24 100 | 101 | 102 | 103 | Version v1.4.24 104 | 105 | 106 | 107 | Version v1.4.24 108 | 109 | 110 | 111 | Version v1.4.25 112 | 113 | 114 | 115 | Version v1.4.26 116 | 117 | 118 | 119 | Version v1.4.27 120 | 121 | 122 | 123 | Version v1.5.0 124 | 125 | 126 | 127 | Version v1.6.0 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | working_directory: ~/repo 3 | docker: 4 | - image: nikolaik/python-nodejs:python3.7-nodejs11 5 | 6 | version: 2 7 | jobs: 8 | install: 9 | <<: *defaults 10 | steps: 11 | - checkout 12 | - restore_cache: 13 | keys: 14 | - v1-dep-{{ checksum "package.json" }} 15 | - v1-dep- 16 | - run: 17 | name: Run install 18 | command: npm install 19 | - save_cache: 20 | paths: 21 | - node_modules 22 | key: v1-dep-{{ checksum "package.json" }} 23 | 24 | testing: 25 | <<: *defaults 26 | steps: 27 | - checkout 28 | - restore_cache: 29 | keys: 30 | - v1-dep-{{ checksum "package.json" }} 31 | - v1-dep- 32 | - run: 33 | name: Run tests 34 | command: npm run test 35 | 36 | linting: 37 | <<: *defaults 38 | steps: 39 | - checkout 40 | - restore_cache: 41 | keys: 42 | - v1-dep-{{ checksum "package.json" }} 43 | - v1-dep- 44 | - run: 45 | name: Run ESLint 46 | command: npm run lint 47 | 48 | pre-build-staging: 49 | <<: *defaults 50 | steps: 51 | - checkout 52 | - restore_cache: 53 | keys: 54 | - v1-dep-{{ checksum "package.json" }} 55 | - v1-dep- 56 | 57 | - run: apt-get update 58 | - run: apt-get -y install jq zip moreutils 59 | 60 | - run: 61 | name: Run semantic-release 62 | command: | 63 | npm i 64 | npm run semantic-release -- --branch ${CIRCLE_BRANCH} --dry-run 65 | 66 | - run: 67 | name: update env file 68 | command: | 69 | echo REACT_APP_TYPEFORM_URL=$TYPEFORM_URL >> .env 70 | 71 | - run: mkdir -p workspace 72 | 73 | - run: | 74 | cat .env > workspace/.env 75 | cat package.json > workspace/package.json 76 | cat src/manifest.json > workspace/manifest.json 77 | - persist_to_workspace: 78 | root: workspace 79 | paths: 80 | - .env 81 | - package.json 82 | - manifest.json 83 | 84 | build-staging: 85 | <<: *defaults 86 | steps: 87 | - checkout 88 | - attach_workspace: 89 | at: workspace 90 | - restore_cache: 91 | keys: 92 | - v1-dep-{{ checksum "package.json" }} 93 | - v1-dep- 94 | 95 | - run: apt-get update 96 | - run: apt-get -y install jq zip moreutils 97 | 98 | - run: cat workspace/.env >> $BASH_ENV 99 | - run: cat workspace/.env > .env 100 | - run: cat workspace/package.json > package.json 101 | - run: cat workspace/manifest.json > src/manifest.json 102 | 103 | - run: echo "REACT_APP_VERSION:" $REACT_APP_VERSION 104 | - run: 105 | name: Build project 106 | command: npm run build 107 | 108 | - run: mkdir -p workspace/build 109 | - run: cp -R colormate.sketchplugin workspace/build 110 | 111 | - persist_to_workspace: 112 | root: workspace 113 | paths: 114 | - .env 115 | - build/ 116 | 117 | zip-build: 118 | <<: *defaults 119 | steps: 120 | - attach_workspace: 121 | at: workspace 122 | - run: cat workspace/.env >> $BASH_ENV 123 | - run: apt-get update 124 | - run: apt-get -y install zip 125 | 126 | - run: 127 | name: Zipping project 128 | command: | 129 | echo "creating zip file:" colormate.zip 130 | cd workspace/build && zip -r ../../colormate.zip * 131 | - run: cp colormate.zip workspace/colormate.zip 132 | 133 | - persist_to_workspace: 134 | root: workspace 135 | paths: 136 | - . 137 | 138 | deploy-staging: 139 | <<: *defaults 140 | steps: 141 | - attach_workspace: 142 | at: workspace 143 | - run: cat workspace/.env >> $BASH_ENV 144 | 145 | - run: apt-get update 146 | - run: apt-get -y install jq zip moreutils 147 | - run: pip install awscli 148 | 149 | - run: 150 | name: Deploy to S3 151 | command: aws s3 cp workspace/colormate.zip s3://colormate-testing/staging/ 152 | 153 | - run: 154 | name: Notify slack channel 155 | command: | 156 | curl --header "Content-Type: application/json" --request POST --data \ 157 | '{"text": "<'"$CIRCLE_BUILD_URL"'|#'"$CIRCLE_BUILD_NUM"'> New testing version deployed you can download it here: <'"$AWS_S3_URL_STAGING"/colormate.zip'|'colormate_"$REACT_APP_VERSION".zip'> "}' \ 158 | $SLACK_WEBHOOK_URL 159 | pre-build-production: 160 | <<: *defaults 161 | steps: 162 | - checkout 163 | - restore_cache: 164 | keys: 165 | - v1-dep-{{ checksum "package.json" }} 166 | - v1-dep- 167 | 168 | - run: apt-get update 169 | - run: apt-get -y install jq moreutils 170 | 171 | - run: 172 | name: Set next version in .env by using prepareRelease.sh 173 | command: | 174 | npm run semantic-release -- --dry-run 175 | - run: 176 | name: update env file 177 | command: | 178 | echo REACT_APP_TYPEFORM_URL=$TYPEFORM_URL >> .env 179 | - run: 180 | name: Prepare workspace 181 | command: | 182 | mkdir -p workspace 183 | - run: 184 | name: Save files to workspace 185 | command: | 186 | cat .env > workspace/.env 187 | cat package.json > workspace/package.json 188 | cat src/manifest.json > workspace/manifest.json 189 | - persist_to_workspace: 190 | root: workspace 191 | paths: 192 | - . 193 | 194 | build-production: 195 | <<: *defaults 196 | steps: 197 | - checkout 198 | - attach_workspace: 199 | at: workspace 200 | - restore_cache: 201 | keys: 202 | - v1-dep-{{ checksum "package.json" }} 203 | - v1-dep- 204 | 205 | - run: apt-get update 206 | - run: apt-get -y install xmlstarlet 207 | 208 | - run: cat workspace/.env >> $BASH_ENV 209 | - run: cat workspace/.env > .env 210 | - run: cat workspace/package.json > package.json 211 | - run: cat workspace/manifest.json > src/manifest.json 212 | 213 | - run: 214 | name: Build project for version $REACT_APP_VERSION 215 | command: npm run build 216 | 217 | - run: 218 | name: Generate new appcast item 219 | command: bash ./updateAppcast.sh $REACT_APP_VERSION 220 | 221 | - run: mkdir -p workspace/build 222 | - run: 223 | name: Save build files to workspace 224 | command: | 225 | cat .appcast.xml > workspace/.appcast.xml 226 | cp -R colormate.sketchplugin workspace/build 227 | - persist_to_workspace: 228 | root: workspace 229 | paths: 230 | - . 231 | 232 | deploy-production: 233 | <<: *defaults 234 | steps: 235 | - checkout 236 | - attach_workspace: 237 | at: workspace 238 | - restore_cache: 239 | keys: 240 | - v1-dep-{{ checksum "package.json" }} 241 | - v1-dep- 242 | 243 | - run: apt-get update 244 | - run: apt-get -y install jq moreutils 245 | 246 | - run: 247 | name: Setup Environment Variables 248 | command: | 249 | cat workspace/.env >> $BASH_ENV 250 | source $BASH_ENV 251 | - add_ssh_keys: 252 | fingerprints: 253 | - "14:80:8a:23:90:81:f4:28:16:ee:47:ef:14:ec:72:27" 254 | 255 | - run: 256 | name: Update .appcast.xml 257 | command: cat workspace/.appcast.xml > .appcast.xml 258 | 259 | - run: 260 | name: Update package.json 261 | command: cat workspace/package.json > package.json 262 | 263 | - run: 264 | name: Update manifest.json 265 | command: cat workspace/manifest.json > src/manifest.json 266 | 267 | - run: 268 | name: Commit files 269 | command: | 270 | echo """Host * 271 | StrictHostKeyChecking no""" >> ~/.ssh/config 272 | git config --global user.email "ci@circleci.com" 273 | git config --global user.name "Ci Server" 274 | git add .appcast.xml package.json src/manifest.json 275 | git commit -m "New version released ${REACT_APP_VERSION} [ci skip]" 276 | git push 277 | 278 | - run: 279 | name: Tag and release 280 | command: | 281 | npm i 282 | mkdir ~/.ssh/ && echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config 283 | npm run semantic-release 284 | - run: 285 | name: Notify slack channel 286 | command: | 287 | curl --header "Content-Type: application/json" --request POST --data \ 288 | '{"text": "<'"$CIRCLE_BUILD_URL"'|#'"$CIRCLE_BUILD_NUM"'> :rocket: Colormate *v'"$REACT_APP_VERSION"'* deployed :rocket: \n Download it here <'https://github.com/themainingredient/colormate/releases/download/v"$REACT_APP_VERSION"/colormate.zip'> "}' \ 289 | $SLACK_WEBHOOK_URL 290 | workflows: 291 | version: 2 292 | untagged-build: 293 | jobs: 294 | - install 295 | - testing 296 | - linting 297 | tagged-build: 298 | jobs: 299 | - install: 300 | filters: 301 | branches: 302 | only: 303 | - master 304 | - testing: 305 | filters: 306 | branches: 307 | only: 308 | - master 309 | - linting: 310 | filters: 311 | branches: 312 | only: 313 | - master 314 | - pre-build-production: 315 | requires: 316 | - install 317 | - testing 318 | - linting 319 | filters: 320 | branches: 321 | only: 322 | - master 323 | - build-production: 324 | requires: 325 | - pre-build-production 326 | - zip-build: 327 | requires: 328 | - build-production 329 | - deploy-production: 330 | requires: 331 | - zip-build 332 | 333 | deploy-staging: 334 | jobs: 335 | - install: 336 | filters: 337 | branches: 338 | only: 339 | - /release/.*/ 340 | - testing: 341 | filters: 342 | branches: 343 | only: 344 | - /release/.*/ 345 | - linting: 346 | filters: 347 | branches: 348 | only: 349 | - /release/.*/ 350 | - pre-build-staging: 351 | requires: 352 | - install 353 | - testing 354 | - linting 355 | filters: 356 | branches: 357 | only: 358 | - /release/.*/ 359 | - build-staging: 360 | requires: 361 | - pre-build-staging 362 | - zip-build: 363 | requires: 364 | - build-staging 365 | - deploy-staging: 366 | requires: 367 | - zip-build 368 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Example env file: rename to .env and adjust to your needs 2 | # default values are for the develop environment 3 | 4 | REACT_APP_ENV=develop # develop || staging || production 5 | REACT_APP_VERSION=x.y.z # x.y.z for dev || 1.2.0-beta for staging || 1.2.0 for production 6 | REACT_APP_IS_BETA=true # true for dev/staging || false for production 7 | REACT_APP_TYPEFORM_URL= 8 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["jest"], 5 | "rules": { 6 | "arrow-body-style": "off", 7 | "class-methods-use-this": "off", 8 | "consistent-return": 1, 9 | "function-paren-newline": "off", 10 | "import/no-extraneous-dependencies": [ 11 | "error", 12 | { 13 | "devDependencies": true 14 | } 15 | ], 16 | "import/prefer-default-export": "off", 17 | "import/no-named-as-default": "off", 18 | "max-len": [ 19 | 1, 20 | 120 21 | ], 22 | "no-else-return": 1, 23 | "no-param-reassign": 0, 24 | "no-underscore-dangle": "off", 25 | "no-unused-expressions": "off", 26 | "no-unused-vars": "off", 27 | "jsx-quotes": [ 28 | "error", 29 | "prefer-single" 30 | ], 31 | "react/forbid-prop-types": "off", 32 | "react/jsx-curly-brace-presence": { 33 | "props": "always", 34 | "children": "off" 35 | }, 36 | "react/jsx-filename-extension": [1, { "extensions": [".tsx"] }], 37 | "react/no-array-index-key": "off", 38 | "react/prop-types": 1, 39 | "react/jsx-one-expression-per-line": "off", 40 | "react/no-unescaped-entities": "off", 41 | "react/destructuring-assignment": 1, 42 | "import/first": 1, 43 | "jsx-a11y/no-noninteractive-element-interactions": "off", 44 | "jsx-a11y/click-events-have-key-events": "off", 45 | "jsx-a11y/label-has-for": "off", 46 | "jsx-a11y/anchor-is-valid": [ 47 | "error", 48 | { 49 | "components": [ 50 | "Link" 51 | ], 52 | "specialLink": [ 53 | "hrefLeft", 54 | "hrefRight" 55 | ], 56 | "aspects": [ 57 | "invalidHref", 58 | "preferButton" 59 | ] 60 | } 61 | ], 62 | "jsx-a11y/accessible-emoji": "off" 63 | }, 64 | "overrides": [ 65 | { 66 | "files": [ 67 | "src/containers/**/*Container.jsx" 68 | ], 69 | "rules": { 70 | "react/prop-types": false 71 | } 72 | } 73 | ], 74 | "globals": { 75 | "Headers": true, 76 | "document": true, 77 | "window": true, 78 | "navigator": true, 79 | "fetch": true 80 | }, 81 | "env": { 82 | "jest/globals": true 83 | }, 84 | "parserOptions": { 85 | "sourceType": "module" 86 | }, 87 | "settings": { 88 | "import/resolver": { 89 | "node": { 90 | "extensions": [ 91 | ".js", 92 | ".jsx", 93 | ".ts", 94 | ".tsx" 95 | ], 96 | "paths": [ 97 | "typings" 98 | ] 99 | } 100 | } 101 | }, 102 | } 103 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lines starting with '#' are comments. 2 | # Each line is a file pattern followed by one or more owners. 3 | # The last matching pattern has the most precedence. 4 | 5 | * @DiogoBatista @jeroenknol @Iar0 6 | *.md @RomyHartendorp -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS version: [e.g. 10.14.6] 28 | - Sketch version [e.g. 55] 29 | - Colormate version [e.g. 1.2] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artefacts 2 | colormate.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | coverage 13 | 14 | .env 15 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branch": "master", 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | [ 7 | "@semantic-release/github", 8 | { 9 | "assets": [ 10 | {"path": "workspace/colormate.zip", "label": "To install: download this file, unzip and double click on the .sketchplugin"} 11 | ] 12 | } 13 | ], 14 | [ 15 | "@semantic-release/exec", 16 | { 17 | "verifyReleaseCmd": "bash ./prepareRelease.sh ${nextRelease.version} ${options.branch}" 18 | } 19 | ] 20 | ] 21 | } -------------------------------------------------------------------------------- /.sketchpacks.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema_version": "1.0.0", 3 | "manifest_path": "src/manifest.json", 4 | "appcast_path": ".appcast.xml" 5 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.eslintIntegration": true 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Install PLUGIN NAME with Sketchpacks](http://sketchpacks-com.s3.amazonaws.com/assets/badges/sketchpacks-badge-install.png "Install Colormate with Sketchpacks")](https://sketchpacks.com/themainingredient/colormate/install) 2 | 3 | # Colormate Sketch Plugin 🌈 4 | 5 | This free plugin gives you an impression of _all the colours_ in your Sketch file and _how many times_ a certain colour is being used. 6 | 7 | ## Why this Plugin? 🤔 8 | 9 | During designing we often found ourselves ending up with fifty different shades of grey (and of course other colours). Sometimes we would just lose ourselves in designing and don't keep track of all the colours that we used. But this is not ideal when you have to hand it over to the development team. 10 | 11 | That's why we've created this plugin to help designers keep track of the colours in the file, _without_ having to add the colours to the document colours first! 12 | 13 | Did we peak your interest already? 🧐 14 | 15 | ## Installation ⚙️ 16 | 17 | 1. Download the latest [Colormate release](https://api.sketchpacks.com/v1/plugins/com.colormate.plugin/download) 18 | 2. Unzip the file 19 | 3. Double-click `colormate.sketchplugin` to install 20 | 21 | ## Getting started 💪 22 | 23 | Did you already download the plugin? Good. Let's get started then! 24 | 25 | Open your Sketch file and run the plugin by using the shortcut "CMD+Shift+8" on Mac or "CTRL+Shift+8" on Windows. You're also able to run the plugin by going to "Plugins" in the top nav bar and then select the Colormate plugin. 26 | 27 | The plugin starts with scanning every layer in the file and gathering all the colours that are being used in text, symbols & objects. This may take some seconds if you have a really big file (but we provided you with a nice gif to look at while waiting 😎). 28 | 29 | When the plugin is done scanning your file, you will get an overview of all the colours you've used in the document. You will be able to see the colour number, how many times this colour has been used (and where!) and if this colour has an opacity value. Now you're able to see that there are two types of red that you're using: #CC0000 has been used 117 times, but #C50909 has only been used 3 times. 30 | 31 | 32 | ## Features 33 | - Get colors overview on the entire Sketch file, seeing where each color is being used 34 | - Get colors overview on a specific selection 35 | - Replace all the occurences of a color 36 | - Replace colors of a single layer 37 | 38 | ### Future plans 🚀 39 | 40 | We're not going to sit back and let the plugin work its magic. We're continously working on developing new features. 41 | 42 | If you can think of any other functionalities, please let us know! 🤩 43 | 44 | Made with ❤️ by [The Main Ingredient](https://themainingredient.co) 45 | 46 | ## Join the community! 47 | 48 | Helps us improve by joining our community! 49 | 50 | Click below to join our Slack. 51 | 52 | [Join slack community](https://themainingredient.typeform.com/to/X65bqg) 53 | -------------------------------------------------------------------------------- /__mocks__/fileMock.js: -------------------------------------------------------------------------------- 1 | module.exports = 'test-file-stub'; 2 | -------------------------------------------------------------------------------- /__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themainingredient/colormate/bb64fe40b364121e329ce0203992d02eedf25fa1/assets/icon.png -------------------------------------------------------------------------------- /constants.ts: -------------------------------------------------------------------------------- 1 | export const tmiUrl = 'https://www.themainingredient.co'; 2 | export const browserWindowSize = { 3 | height: 674, 4 | width: 370, 5 | } 6 | -------------------------------------------------------------------------------- /enums/color-type.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ColorType { 2 | fill = 'fill', 3 | border = 'border', 4 | text = 'text', 5 | } -------------------------------------------------------------------------------- /enums/layer-type.enum.ts: -------------------------------------------------------------------------------- 1 | export enum LayerType { 2 | page = 'Page', 3 | artboard = 'Artboard', 4 | group = 'Group', 5 | shapePath = 'ShapePath', 6 | text = 'Text', 7 | shape = 'Shape', 8 | document = 'Document', 9 | } -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | // automock: false, 7 | // Stop running tests after `n` failures 8 | // bail: 0, 9 | 10 | // Respect "browser" field in package.json when resolving modules 11 | // browser: false, 12 | 13 | // The directory where Jest should store its cached dependency information 14 | // cacheDirectory: "/private/var/folders/p9/c21c_pbx1hz4gm173xmpkjjw0000gn/T/jest_dx", 15 | 16 | // Automatically clear mock calls and instances between every test 17 | clearMocks: true, 18 | 19 | // Indicates whether the coverage information should be collected while executing the test 20 | // collectCoverage: false, 21 | 22 | // An array of glob patterns indicating a set of files for which coverage information should be collected 23 | // collectCoverageFrom: null, 24 | 25 | // The directory where Jest should output its coverage files 26 | coverageDirectory: 'coverage', 27 | 28 | // An array of regexp pattern strings used to skip coverage collection 29 | 30 | // A list of reporter names that Jest uses when writing coverage reports 31 | // coverageReporters: [ 32 | // "json", 33 | // "text", 34 | // "lcov", 35 | // "clover" 36 | // ], 37 | 38 | // An object that configures minimum threshold enforcement for coverage results 39 | // coverageThreshold: null, 40 | 41 | // A path to a custom dependency extractor 42 | // dependencyExtractor: null, 43 | 44 | // Make calling deprecated APIs throw helpful error messages 45 | // errorOnDeprecated: false, 46 | 47 | // Force coverage collection from ignored files using an array of glob patterns 48 | // forceCoverageMatch: [], 49 | 50 | // A path to a module which exports an async function that is triggered once before all test suites 51 | // globalSetup: null, 52 | 53 | // A path to a module which exports an async function that is triggered once after all test suites 54 | // globalTeardown: null, 55 | 56 | // A set of global variables that need to be available in all test environments 57 | globals: { 58 | 'ts-jest': { 59 | tsConfig: 'tsconfig.test.json', 60 | }, 61 | }, 62 | 63 | // An array of directory names to be searched recursively up from the requiring module's location 64 | moduleDirectories: [ 65 | 'node_modules', 66 | ], 67 | 68 | // An array of file extensions your modules use 69 | moduleFileExtensions: [ 70 | 'ts', 71 | 'tsx', 72 | 'js', 73 | 'jsx', 74 | 'json', 75 | 'node', 76 | ], 77 | 78 | // A map from regular expressions to module names that allow to stub out resources with a single module 79 | moduleNameMapper: { 80 | '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/__mocks__/fileMock.js', 81 | '\\.(css|less)$': '/__mocks__/styleMock.js', 82 | }, 83 | 84 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 85 | modulePathIgnorePatterns: [ 86 | 'colormate.sketchplugin', 87 | ], 88 | 89 | // Activates notifications for test results 90 | // notify: false, 91 | 92 | // An enum that specifies notification mode. Requires { notify: true } 93 | // notifyMode: "failure-change", 94 | 95 | // A preset that is used as a base for Jest's configuration 96 | // preset: null, 97 | 98 | // Run tests from one or more projects 99 | // projects: null, 100 | 101 | // Use this configuration option to add custom reporters to Jest 102 | // reporters: undefined, 103 | 104 | // Automatically reset mock state between every test 105 | // resetMocks: false, 106 | 107 | // Reset the module registry before running each individual test 108 | // resetModules: false, 109 | 110 | // A path to a custom resolver 111 | // resolver: null, 112 | 113 | // Automatically restore mock state between every test 114 | // restoreMocks: false, 115 | 116 | // The root directory that Jest should scan for tests and modules within 117 | // rootDir: null, 118 | 119 | // A list of paths to directories that Jest should use to search for files in 120 | // roots: [], 121 | 122 | // Allows you to use a custom runner instead of Jest's default test runner 123 | // runner: "jest-runner", 124 | 125 | // The paths to modules that run some code to configure or set up the testing environment before each test 126 | // setupFiles: [], 127 | 128 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 129 | setupFilesAfterEnv: [ 130 | './setupTest.ts', 131 | ], 132 | 133 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 134 | // snapshotSerializers: [], 135 | 136 | // The test environment that will be used for testing 137 | testEnvironment: 'jsdom', 138 | 139 | // Options that will be passed to the testEnvironment 140 | // testEnvironmentOptions: {}, 141 | 142 | // Adds a location field to test results 143 | // testLocationInResults: false, 144 | 145 | // The glob patterns Jest uses to detect test files 146 | // testMatch: [ 147 | // "**/__tests__/**/*.[jt]s?(x)", 148 | // "**/?(*.)+(spec|test).[tj]s?(x)" 149 | // ], 150 | 151 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 152 | testPathIgnorePatterns: [ 153 | '/node_modules/', 154 | '/colormate.sketchplugin/', 155 | ], 156 | 157 | // The regexp pattern or array of patterns that Jest uses to detect test files 158 | // testRegex: [], 159 | 160 | // This option allows the use of a custom results processor 161 | // testResultsProcessor: null, 162 | 163 | // This option allows use of a custom test runner 164 | // testRunner: "jasmine2", 165 | 166 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 167 | // testURL: "http://localhost", 168 | 169 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 170 | // timers: "real", 171 | 172 | // A map from regular expressions to paths to transformers 173 | transform: { 174 | '^.+\\.tsx?$': 'ts-jest', 175 | '^.+\\.jsx?$': 'babel-jest', 176 | '^.+\\.svg$': 'jest-svg-transformer', 177 | }, 178 | 179 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 180 | // transformIgnorePatterns: [ 181 | // "/node_modules/" 182 | // ], 183 | 184 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 185 | // unmockedModulePathPatterns: undefined, 186 | 187 | // Indicates whether each individual test should be reported during the run 188 | // verbose: null, 189 | 190 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 191 | // watchPathIgnorePatterns: [], 192 | 193 | // Whether to use watchman for file crawling 194 | // watchman: true, 195 | }; 196 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colormate", 3 | "version": "1.6.0", 4 | "repository": { 5 | "type": "git", 6 | "url": "git+https://github.com/themainingredient/colormate.git" 7 | }, 8 | "description": "Colormate is a kickass sketch plugin that will help you figure out how in the hell you ended up with 457 different greys, instead of the 1 grey Mandy gave you in the handover", 9 | "engines": { 10 | "sketch": ">=3.0" 11 | }, 12 | "skpm": { 13 | "name": "Colormate", 14 | "manifest": "src/manifest.json", 15 | "identifier": "com.colormate.plugin", 16 | "main": "colormate.sketchplugin", 17 | "assets": [ 18 | "assets/**/*", 19 | "resources/app/assets/**/*" 20 | ] 21 | }, 22 | "scripts": { 23 | "publish": "skpm publish", 24 | "build": "skpm-build", 25 | "watch": "skpm-build --watch", 26 | "start": "skpm-build --watch", 27 | "postinstall": "npm run build && skpm-link", 28 | "test": "jest", 29 | "test:coverage": "jest --coverage && open coverage/lcov-report/index.html", 30 | "test:watch": "jest --watch", 31 | "lint": "./node_modules/.bin/eslint ./src/ ./resources/ --ext .ts --ext .tsx", 32 | "log": "skpm log -f", 33 | "semantic-release": "semantic-release" 34 | }, 35 | "devDependencies": { 36 | "@babel/preset-env": "^7.4.2", 37 | "@babel/preset-react": "^7.0.0", 38 | "@semantic-release/exec": "^3.3.3", 39 | "@semantic-release/github": "^5.3.2", 40 | "@skpm/builder": "^0.5.11", 41 | "@skpm/extract-loader": "^2.0.2", 42 | "@testing-library/jest-dom": "^4.1.0", 43 | "@types/jest": "^24.0.11", 44 | "@types/prop-types": "^15.7.1", 45 | "@types/react-color": "^3.0.0", 46 | "@typescript-eslint/parser": "^1.11.0", 47 | "babel-eslint": "^10.0.1", 48 | "babel-jest": "^24.7.1", 49 | "babel-loader": "^8.0.5", 50 | "css-loader": "^1.0.0", 51 | "cz-conventional-changelog": "^2.1.0", 52 | "dotenv": "^7.0.0", 53 | "eslint": "^5.15.3", 54 | "eslint-config-airbnb": "^17.1.0", 55 | "eslint-plugin-import": "^2.16.0", 56 | "eslint-plugin-jest": "^22.4.1", 57 | "eslint-plugin-jsx-a11y": "^6.2.1", 58 | "eslint-plugin-react": "^7.12.4", 59 | "file-loader": "^3.0.1", 60 | "html-loader": "^0.5.1", 61 | "jest": "^24.5.0", 62 | "jest-styled-components": "^6.3.1", 63 | "jest-svg-transformer": "^1.0.0", 64 | "react-hooks-testing-library": "^0.4.0", 65 | "react-svg-loader": "^3.0.3", 66 | "react-testing-library": "^6.1.2", 67 | "semantic-release": "^15.13.24", 68 | "source-map-loader": "^0.2.4", 69 | "ts-jest": "^24.0.2", 70 | "ts-loader": "^5.3.3", 71 | "typescript": "^3.4.3" 72 | }, 73 | "resources": [ 74 | "resources/**/*.js" 75 | ], 76 | "dependencies": { 77 | "@types/lodash": "^4.14.123", 78 | "@types/react": "^16.8.13", 79 | "@types/react-dom": "^16.8.4", 80 | "@types/styled-components": "^4.1.14", 81 | "lodash": "^4.17.15", 82 | "prop-types": "^15.7.2", 83 | "react": "^16.8.5", 84 | "react-color": "^2.17.3", 85 | "react-dom": "^16.8.5", 86 | "sketch-module-web-view": "^2.1.5", 87 | "styled-components": "^4.2.0" 88 | }, 89 | "author": "The Main Ingredient", 90 | "config": { 91 | "commitizen": { 92 | "path": "./node_modules/cz-conventional-changelog" 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /prepareRelease.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 'Building APP with version: ' $1 4 | echo 'Building app for branch: ' $2 5 | 6 | jq --arg h "$1" '.version=$h' package.json | sponge package.json 7 | jq --arg h "$1" '.version=$h' src/manifest.json | sponge src/manifest.json 8 | 9 | touch .env 10 | echo REACT_APP_VERSION=$1 >> .env 11 | 12 | if [[ "$2" =~ release/* ]] 13 | then 14 | echo REACT_APP_ENV=staging >> .env 15 | echo REACT_APP_IS_BETA=true >> .env 16 | fi 17 | -------------------------------------------------------------------------------- /resources/app/Global.styles.ts: -------------------------------------------------------------------------------- 1 | import { css, createGlobalStyle } from 'styled-components'; 2 | 3 | import SFProRegular from './assets/SFProDisplay-Regular.otf'; 4 | import SFProBold from './assets/SFProDisplay-Bold.otf'; 5 | import SFProHeavy from './assets/SFProDisplay-Heavy.otf'; 6 | import FuturaBold from './assets/Futura-Bold.otf'; 7 | 8 | export const GlobalFonts = createGlobalStyle` 9 | @font-face { 10 | font-family: 'SFProRegular'; 11 | src: url(${SFProRegular}); 12 | } 13 | 14 | @font-face { 15 | font-family: 'SFProBold'; 16 | src: url(${SFProBold}); 17 | } 18 | 19 | @font-face { 20 | font-family: 'SFProHeavy'; 21 | src: url(${SFProHeavy}); 22 | } 23 | 24 | @font-face { 25 | font-family: 'FuturaBold'; 26 | src: url(${FuturaBold}); 27 | } 28 | `; 29 | 30 | export default { 31 | colors: { 32 | White: '#FFFFFF', 33 | TMIBlueLight: '#EDECFF', 34 | TMIBlue: '#4E41FF', 35 | TMIBlueDark: '#3B2EEB', 36 | LightGrey: '#E8E9EF', 37 | MediumGrey: '#B5B8C6', 38 | DarkGrey: '#4d4f59', 39 | Black25: '#00000040', 40 | Cyan: '#00FFFF', 41 | CyanDark: '#00DDDD', 42 | Navy: '#0D0166', 43 | }, 44 | fonts: { 45 | SFPro: { 46 | reg: 'SFProRegular', 47 | bold: 'SFProBold', 48 | heavy: 'SFProHeavy', 49 | }, 50 | Futura: { 51 | bold: 'FuturaBold', 52 | }, 53 | }, 54 | }; 55 | 56 | export const flexCenter = css` 57 | display: flex; 58 | justify-content: center; 59 | align-items: center; 60 | `; 61 | -------------------------------------------------------------------------------- /resources/app/ListContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, Dispatch } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ListContext = React.createContext({ 5 | selectedColor: '', 6 | setSelectedColor: '' as unknown as Dispatch, 7 | selectedLayer: '', 8 | setSelectedLayer: '' as unknown as Dispatch, 9 | colors: {}, 10 | setColors: '' as unknown as Dispatch, 11 | }); 12 | 13 | export const ListProvider = ({ children }) => { 14 | const [selectedColor, setSelectedColor] = useState(); 15 | const [selectedLayer, setSelectedLayer] = useState(); 16 | const [colors, setColors] = useState(); 17 | 18 | return ( 19 | 29 | {children} 30 | 31 | ); 32 | }; 33 | 34 | ListProvider.propTypes = { 35 | children: PropTypes.element.isRequired, 36 | }; 37 | 38 | export default ListContext; 39 | -------------------------------------------------------------------------------- /resources/app/assets/Futura-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themainingredient/colormate/bb64fe40b364121e329ce0203992d02eedf25fa1/resources/app/assets/Futura-Bold.otf -------------------------------------------------------------------------------- /resources/app/assets/Rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rectangle 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/app/assets/SFProDisplay-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themainingredient/colormate/bb64fe40b364121e329ce0203992d02eedf25fa1/resources/app/assets/SFProDisplay-Bold.otf -------------------------------------------------------------------------------- /resources/app/assets/SFProDisplay-Heavy.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themainingredient/colormate/bb64fe40b364121e329ce0203992d02eedf25fa1/resources/app/assets/SFProDisplay-Heavy.otf -------------------------------------------------------------------------------- /resources/app/assets/SFProDisplay-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themainingredient/colormate/bb64fe40b364121e329ce0203992d02eedf25fa1/resources/app/assets/SFProDisplay-Regular.otf -------------------------------------------------------------------------------- /resources/app/assets/arrowActive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/app/assets/arrowGrey.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/app/assets/arrowInactive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Path Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /resources/app/assets/artboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tv 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /resources/app/assets/checkered_small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom Preset 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /resources/app/assets/minimilistBubbles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BD05930F-448E-41CB-951B-8B3A476E31B4@1.00x 5 | Created with sketchtool. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /resources/app/assets/replaceBtn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | replaceBtn 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /resources/app/assets/replaceBtnHover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | replaceBtnHover 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /resources/app/assets/text1Loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | E8D7D3A8-6192-44E0-950A-67C72503054D@1.00x 5 | Created with sketchtool. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /resources/app/assets/textIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | textIcon 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /resources/app/assets/washingTransparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/themainingredient/colormate/bb64fe40b364121e329ce0203992d02eedf25fa1/resources/app/assets/washingTransparent.gif -------------------------------------------------------------------------------- /resources/app/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useContext } from 'react'; 2 | 3 | import styled from 'styled-components'; 4 | 5 | import { GlobalFonts } from '../Global.styles'; 6 | 7 | import ListContext from '../ListContext'; 8 | import Header from './Header'; 9 | import List from './List/List'; 10 | import Footer from './Footer'; 11 | import Loader from './Loader'; 12 | import NoColorsFound from './NoColorsFound'; 13 | import { browserWindowSize } from '../../../constants'; 14 | import { Banner } from './Banner'; 15 | 16 | const PluginWrapper = styled.div` 17 | height: ${browserWindowSize.height}px; 18 | display: flex; 19 | flex-direction: column; 20 | justify-content: space-between; 21 | `; 22 | 23 | export default function () { 24 | const [isLoading, setIsLoading] = useState(true); 25 | const { selectedLayer, colors = {}, setColors } = useContext(ListContext); 26 | 27 | useEffect(() => { 28 | window.sendUsedColors = (incomingColors) => { 29 | setIsLoading(false); 30 | setColors(incomingColors); 31 | }; 32 | 33 | // Call function to get all used colors 34 | window.postMessage('getColors', 'Loading all colors'); 35 | }, []); 36 | 37 | useEffect(() => { 38 | window.replaceColor = (args) => { 39 | const { colorToReplace, targetColor, layerIds } = args; 40 | const newState = Object.entries(colors).reduce((acc, keyValue) => { 41 | const colorKey = keyValue[0]; 42 | const instances = keyValue[1] as any[]; 43 | 44 | if (keyValue[0] !== colorToReplace) { 45 | acc[colorKey] = instances; 46 | return acc; 47 | } 48 | 49 | const changedLayers = instances.filter(layer => layerIds.includes(layer.id)); 50 | const untouchedLayers = instances.filter(layer => !layerIds.includes(layer.id)); 51 | 52 | acc[targetColor] = changedLayers; 53 | 54 | if (untouchedLayers.length !== 0) { 55 | acc[colorKey] = untouchedLayers; 56 | } 57 | return acc; 58 | }, {}); 59 | 60 | setColors(newState); 61 | }; 62 | }, [colors]); 63 | 64 | const content = Object.keys(colors).length !== 0 ? ( 65 | <> 66 |
67 | 68 | 69 |