├── .gitignore ├── .travis.yml ├── README.md ├── jest.config.js ├── package.json ├── src ├── Palette.tsx ├── __mocks__ │ └── node-vibrant.ts ├── __tests__ │ ├── Palette.tsx │ ├── __snapshots__ │ │ └── getPalette.ts.snap │ └── getPalette.ts ├── getPalette.ts ├── index.ts └── usePalette.tsx ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | .rpt2_cache 5 | .rts2_cache_cjs 6 | .rts2_cache_es 7 | .rts2_cache_umd 8 | yarn-error.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | script: 5 | - 'npm run test:coverage' 6 | after_script: 7 | - 'cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js' 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # REACT PALETTE 2 | 3 | Extract prominent colors from an image 4 | 5 | [![Build Status](https://travis-ci.org/leonardokl/react-palette.svg?branch=master)](https://travis-ci.org/leonardokl/react-palette) 6 | [![Coverage Status](https://coveralls.io/repos/github/leonardokl/react-palette/badge.svg?branch=master)](https://coveralls.io/github/leonardokl/react-palette?branch=master) 7 | 8 | ## Install 9 | ``` 10 | npm i -S react-palette 11 | ``` 12 | 13 | ## Usage 14 | ```jsx 15 | import Palette from 'react-palette'; 16 | // In your render... 17 | 18 | {({ data, loading, error }) => ( 19 |
20 | Text with the vibrant color 21 |
22 | )} 23 |
24 | ``` 25 | 26 | ```jsx 27 | import { usePalette } from 'react-palette' 28 | 29 | const { data, loading, error } = usePalette(IMAGE_URL) 30 | 31 |
32 | Text with the vibrant color 33 |
34 | ``` 35 | 36 | ## Palette callback example 37 | ```js 38 | { 39 | darkMuted: "#2a324b" 40 | darkVibrant: "#0e7a4b" 41 | lightMuted: "#9cceb7" 42 | lightVibrant: "#a4d4bc" 43 | muted: "#64aa8a" 44 | vibrant: "#b4d43c" 45 | } 46 | ``` 47 | 48 | ## Notes 49 | 50 | That library was created using `node-vibrant` to extract the prominent colors. 51 | 52 | [https://github.com/akfish/node-vibrant](https://github.com/akfish/node-vibrant) -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ["/src"], 3 | transform: { 4 | "^.+\\.tsx?$": "ts-jest" 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-palette", 3 | "version": "1.0.2", 4 | "description": "", 5 | "main": "dist/react-palette.js", 6 | "umd:main": "dist/react-palette.umd.js", 7 | "module": "dist/react-palette.mjs", 8 | "browser": "dist/react-palette.umd.js", 9 | "types": "dist/index.d.ts", 10 | "scripts": { 11 | "test": "jest", 12 | "test:watch": "npm test -- --watch", 13 | "test:coverage": "npm test -- --coverage", 14 | "build": "microbundle", 15 | "preversion": "npm test && npm run build", 16 | "postversion": "git push && git push --tags && npm publish" 17 | }, 18 | "keywords": [ 19 | "react", 20 | "palette", 21 | "dominant color" 22 | ], 23 | "author": "Leonardo Luiz ", 24 | "license": "ISC", 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/leonardokl/react-palette.git" 28 | }, 29 | "files": [ 30 | "dist", 31 | "src" 32 | ], 33 | "peerDependencies": { 34 | "react": "^16.8.6", 35 | "react-dom": "^16.8.6" 36 | }, 37 | "dependencies": { 38 | "lodash.camelcase": "^4.3.0", 39 | "lodash.invoke": "^4.5.2", 40 | "node-vibrant": "^3.1.3" 41 | }, 42 | "devDependencies": { 43 | "@testing-library/react": "^8.0.1", 44 | "@types/jest": "^24.0.15", 45 | "@types/react": "^16.8.20", 46 | "coveralls": "^3.0.4", 47 | "jest": "^24.8.0", 48 | "microbundle": "^0.11.0", 49 | "react": "^16.8.6", 50 | "react-dom": "^16.8.6", 51 | "ts-jest": "^24.0.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Palette.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from "react"; 2 | import { PaletteState, usePalette } from "./usePalette"; 3 | 4 | export type PaletteProps = { 5 | src: string; 6 | children(palette: PaletteState): ReactNode; 7 | }; 8 | 9 | export const Palette: React.FC = ({ 10 | src, 11 | children 12 | }: PaletteProps) => { 13 | const palette = usePalette(src); 14 | 15 | return <>{children(palette)}; 16 | }; 17 | -------------------------------------------------------------------------------- /src/__mocks__/node-vibrant.ts: -------------------------------------------------------------------------------- 1 | const Swatch = (hex) => ({ 2 | getHex: () => hex 3 | }) 4 | 5 | const palette = { 6 | DarkMuted: Swatch('#2a324b'), 7 | DarkVibrant: Swatch("#0e7a4b"), 8 | LightMuted: Swatch("#9cceb7"), 9 | LightVibrant: Swatch("#a4d4bc"), 10 | Muted: Swatch("#64aa8a"), 11 | Vibrant: Swatch("#b4d43c") 12 | } 13 | 14 | export default { 15 | from: () => ({ 16 | getPalette: async () => palette 17 | }) 18 | } -------------------------------------------------------------------------------- /src/__tests__/Palette.tsx: -------------------------------------------------------------------------------- 1 | import { render, wait } from "@testing-library/react"; 2 | import React from "react"; 3 | import { Palette, getPalette } from "../"; 4 | 5 | test("execute children with palette", async () => { 6 | const children = jest.fn(() => null); 7 | const src = "test"; 8 | const palette = await getPalette(src); 9 | 10 | render(); 11 | 12 | expect(children).toHaveBeenCalledWith({ 13 | loading: true, 14 | error: undefined, 15 | data: {} 16 | }); 17 | 18 | await wait(); 19 | 20 | expect(children).toHaveBeenCalledWith({ 21 | loading: false, 22 | error: undefined, 23 | data: palette 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/getPalette.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`return camelCased values 1`] = ` 4 | Object { 5 | "darkMuted": "#2a324b", 6 | "darkVibrant": "#0e7a4b", 7 | "lightMuted": "#9cceb7", 8 | "lightVibrant": "#a4d4bc", 9 | "muted": "#64aa8a", 10 | "vibrant": "#b4d43c", 11 | } 12 | `; 13 | -------------------------------------------------------------------------------- /src/__tests__/getPalette.ts: -------------------------------------------------------------------------------- 1 | import { getPalette } from "../"; 2 | 3 | test("return camelCased values", async () => { 4 | const actual = await getPalette("test"); 5 | 6 | expect(actual).toMatchSnapshot(); 7 | }); 8 | -------------------------------------------------------------------------------- /src/getPalette.ts: -------------------------------------------------------------------------------- 1 | import Vibrant from "node-vibrant"; 2 | import camelCase from "lodash.camelcase"; 3 | import invoke from 'lodash.invoke'; 4 | 5 | export type PaletteColors = { 6 | vibrant?: string; 7 | muted?: string; 8 | darkVibrant?: string; 9 | darkMuted?: string; 10 | lightVibrant?: string; 11 | lightMuted?: string; 12 | [name: string]: string | undefined; 13 | }; 14 | 15 | export async function getPalette(src: string) { 16 | const palette = await Vibrant.from(src).getPalette(); 17 | const setPaletteColor = (acc, paletteName) => ({ 18 | ...acc, 19 | [camelCase(paletteName)]: invoke(palette, [paletteName, 'getHex']) 20 | }); 21 | 22 | return Object.keys(palette).reduce(setPaletteColor, {}); 23 | } 24 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./getPalette"; 2 | export * from "./Palette"; 3 | export { Palette as default } from "./Palette"; 4 | export * from "./usePalette"; 5 | -------------------------------------------------------------------------------- /src/usePalette.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getPalette, PaletteColors } from "./getPalette"; 3 | 4 | export type PaletteState = { 5 | loading: boolean; 6 | error?: Error; 7 | data: PaletteColors; 8 | }; 9 | 10 | const initialState: PaletteState = { 11 | loading: true, 12 | data: {}, 13 | error: undefined, 14 | }; 15 | 16 | function reducer(state: PaletteState, action): PaletteState { 17 | switch (action.type) { 18 | case "getPalette": 19 | return initialState; 20 | case "resolvePalette": 21 | return { ...state, data: action.payload, loading: false }; 22 | case "rejectPalette": 23 | return { ...state, error: action.payload, loading: false }; 24 | } 25 | } 26 | 27 | export function usePalette(src: string) { 28 | const [state, dispatch] = React.useReducer(reducer, initialState); 29 | 30 | React.useEffect(() => { 31 | dispatch({ type: "getPalette" }); 32 | 33 | getPalette(src) 34 | .then((palette) => { 35 | dispatch({ type: "resolvePalette", payload: palette }); 36 | }) 37 | .catch((ex) => { 38 | dispatch({ type: "rejectPalette", payload: ex }); 39 | }); 40 | }, [src]); 41 | 42 | return state; 43 | } 44 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "jsx": "react", 5 | "target": "es5", 6 | "esModuleInterop": true 7 | }, 8 | "exclude": ["src/__mocks__", "src/__tests__"] 9 | } 10 | --------------------------------------------------------------------------------