├── .dockerignore
├── .env.example
├── .gitignore
├── Dockerfile
├── README.md
├── docker-compose.yml
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── server.js
├── src
├── App.test.tsx
├── App.tsx
├── components
│ ├── CheckboxField.tsx
│ ├── ImageUploadField.tsx
│ ├── InputField.tsx
│ ├── SelectField.tsx
│ └── TextArea.tsx
├── index.css
├── index.js
├── react-app-env.d.ts
├── reportWebVitals.js
└── setupTests.js
└── tsconfig.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | Dockerfile
2 | .dockerignore
3 | .git
4 | .gitignore
5 | .gitattributes
6 | README.md
7 | .npmrc
8 | .prettierrc
9 | .eslintrc.cjs
10 | .graphqlrc
11 | .editorconfig
12 | .svelte-kit
13 | .vscode
14 | node_modules
15 | build
16 | package
17 | cli
18 | **/.env
19 | .env.example
20 | k8s
21 | .github
22 | .yarn
23 | .env
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | PORT=3000
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | .env
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ###################
2 | # DEPENDENCIES
3 | ###################
4 |
5 | FROM node:16-alpine As dependencies
6 |
7 | WORKDIR /usr/src/app
8 |
9 | COPY --chown=node:node package*.json ./
10 |
11 | RUN apk add --update --no-cache \
12 | make \
13 | g++ \
14 | jpeg-dev \
15 | cairo-dev \
16 | giflib-dev \
17 | pango-dev \
18 | libtool \
19 | autoconf \
20 | automake
21 |
22 | RUN npm install
23 |
24 | USER node
25 |
26 | ###################
27 | # BUILD
28 | ###################
29 |
30 | FROM node:16-alpine As build
31 |
32 | WORKDIR /usr/src/app
33 |
34 | COPY --chown=node:node --from=dependencies /usr/src/app/node_modules ./node_modules
35 | COPY --chown=node:node package*.json ./
36 |
37 | COPY --chown=node:node src ./src
38 | COPY --chown=node:node public ./public
39 | COPY --chown=node:node tsconfig.json ./tsconfig.json
40 |
41 | RUN apk add --update --no-cache \
42 | make \
43 | g++ \
44 | jpeg-dev \
45 | cairo-dev \
46 | giflib-dev \
47 | pango-dev \
48 | libtool \
49 | autoconf \
50 | automake
51 |
52 | RUN npm run build
53 | RUN npm install --omit=dev
54 |
55 | USER node
56 |
57 | ###################
58 | # PRODUCTION
59 | ###################
60 |
61 | FROM node:16-alpine As production
62 |
63 | WORKDIR /usr/src/app
64 |
65 | COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules
66 | COPY --chown=node:node --from=build /usr/src/app/build ./build
67 | COPY --chown=node:node server.js ./server.js
68 |
69 | RUN apk add --no-cache bash
70 |
71 | CMD ["node", "server.js"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QRCodeCustomizer
2 |
3 | Example project for react-qrcode-logo package.
4 |
5 | Built using create-react-app.
6 |
7 | ## running the example
8 |
9 | In the root folder of the project run
10 |
11 | `npm install --legacy-peer-deps`
12 |
13 | `npm start`
14 |
15 | The project will be reacheable in your browser at localhost:3000
16 |
17 | ## online demo
18 |
19 | Try it [here](https://gcoro.github.io/QRCodeCustomizer/)
20 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 | networks:
3 | qrcode-customizer-network:
4 | name: qrcode-customizer-network
5 | ipam:
6 | driver: default
7 | services:
8 | qrcode-customizer:
9 | container_name: qrcode-customizer
10 | image: qrcode-customizer
11 | build: .
12 | env_file: .env
13 | ports:
14 | - '${PORT:-3000}:${PORT:-3000}'
15 | networks:
16 | - qrcode-customizer-network
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "homepage": "https://gcoro.github.io/qrcode-customizer",
3 | "name": "qrcode-customizer",
4 | "version": "1.0.0",
5 | "private": true,
6 | "dependencies": {
7 | "@testing-library/jest-dom": "5.16.2",
8 | "@testing-library/react": "12.1.3",
9 | "@testing-library/user-event": "13.5.0",
10 | "@types/jest": "27.4.1",
11 | "@types/node": "17.0.21",
12 | "@types/react": "18.3.1",
13 | "@types/react-dom": "18.3.0",
14 | "react": "18.3.1",
15 | "react-dom": "18.3.1",
16 | "react-json-view": "1.21.3",
17 | "react-qrcode-logo": "3.0.0",
18 | "react-scripts": "5.0.0",
19 | "typescript": "5.4.5",
20 | "web-vitals": "4.0.1"
21 | },
22 | "scripts": {
23 | "start": "react-scripts start",
24 | "build": "react-scripts build",
25 | "test": "react-scripts test",
26 | "eject": "react-scripts eject",
27 | "predeploy": "npm run build",
28 | "deploy": "gh-pages -d build"
29 | },
30 | "eslintConfig": {
31 | "extends": [
32 | "react-app",
33 | "react-app/jest"
34 | ]
35 | },
36 | "browserslist": {
37 | "production": [
38 | ">0.2%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | },
48 | "devDependencies": {
49 | "gh-pages": "^6.1.1"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gcoro/QRCodeCustomizer/287e781868df0e46b93a64eb82aa0921e101143d/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | QR Code Customizer - a react-qrcode-logo example project
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gcoro/QRCodeCustomizer/287e781868df0e46b93a64eb82aa0921e101143d/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gcoro/QRCodeCustomizer/287e781868df0e46b93a64eb82aa0921e101143d/public/logo512.png
--------------------------------------------------------------------------------
/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 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | //simple express server to run frontend production build;
2 | const express = require("express");
3 | const path = require("path");
4 | const dotenv = require('dotenv');
5 |
6 | const app = express();
7 | const envFound = dotenv.config();
8 | const port = process.env.PORT || 3000;
9 |
10 | app.use(express.static(path.join(__dirname, "build")));
11 |
12 | app.get("/*", function (req, res) {
13 | res.sendFile(path.join(__dirname, "build", "index.html"));
14 | });
15 |
16 | app.listen(port, () => {
17 | console.log(`
18 | ################################################
19 | 🛡️ Server listening on port: ${port} 🛡️
20 | ################################################
21 | `)
22 | });
--------------------------------------------------------------------------------
/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import { render, screen } from '@testing-library/react';
5 |
6 | it('renders without crashing', () => {
7 | const div = document.createElement('div');
8 | ReactDOM.render(, div);
9 | ReactDOM.unmountComponentAtNode(div);
10 | });
11 |
12 | test('renders learn react link', () => {
13 | render();
14 | const linkElement = screen.getByText(/learn react/i);
15 | expect(linkElement).toBeInTheDocument();
16 | });
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { MutableRefObject, useRef, useState } from 'react';
2 | import { InputField } from './components/InputField';
3 | import { QRCode } from 'react-qrcode-logo';
4 | import { SelectField } from './components/SelectField';
5 | import { TextArea } from './components/TextArea';
6 | import { ImageUploadField } from './components/ImageUploadField';
7 | import { CheckboxField } from './components/CheckboxField';
8 | import ReactJson from 'react-json-view';
9 |
10 | const App: React.FC = () => {
11 | const [state, setState] = useState<{ [key: string]: any }>({});
12 | const ref = useRef()
13 |
14 | const handleChange = ({ target }: any) => {
15 | setState(prevState => ({ ...prevState, [target.name]: target.value }))
16 | }
17 |
18 | const handleDownload = () => {
19 | ref.current?.download()
20 | }
21 |
22 | const buildEyeRadiusInput = (id: string) => {
23 | return
32 | };
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
44 |
49 |
53 |
60 |
67 |
68 |
74 |
80 |
81 |
82 |
83 |
87 |
94 |
101 |
110 |
115 |
119 |
128 |
133 |
134 |
135 |
136 |
eyeRadius
137 |
138 |
139 |
Top left eye
140 |
Outer
141 | {buildEyeRadiusInput('eyeradius_0_outer_0')}
142 | {buildEyeRadiusInput('eyeradius_0_outer_1')}
143 | {buildEyeRadiusInput('eyeradius_0_outer_2')}
144 | {buildEyeRadiusInput('eyeradius_0_outer_3')}
145 |
Inner
146 | {buildEyeRadiusInput('eyeradius_0_inner_0')}
147 | {buildEyeRadiusInput('eyeradius_0_inner_1')}
148 | {buildEyeRadiusInput('eyeradius_0_inner_2')}
149 | {buildEyeRadiusInput('eyeradius_0_inner_3')}
150 |
151 |
152 |
Top right eye
153 |
Outer
154 | {buildEyeRadiusInput('eyeradius_1_outer_0')}
155 | {buildEyeRadiusInput('eyeradius_1_outer_1')}
156 | {buildEyeRadiusInput('eyeradius_1_outer_2')}
157 | {buildEyeRadiusInput('eyeradius_1_outer_3')}
158 |
Inner
159 | {buildEyeRadiusInput('eyeradius_1_inner_0')}
160 | {buildEyeRadiusInput('eyeradius_1_inner_1')}
161 | {buildEyeRadiusInput('eyeradius_1_inner_2')}
162 | {buildEyeRadiusInput('eyeradius_1_inner_3')}
163 |
164 |
165 |
Bottom left eye
166 |
Outer
167 | {buildEyeRadiusInput('eyeradius_2_outer_0')}
168 | {buildEyeRadiusInput('eyeradius_2_outer_1')}
169 | {buildEyeRadiusInput('eyeradius_2_outer_2')}
170 | {buildEyeRadiusInput('eyeradius_2_outer_3')}
171 |
Inner
172 | {buildEyeRadiusInput('eyeradius_2_inner_0')}
173 | {buildEyeRadiusInput('eyeradius_2_inner_1')}
174 | {buildEyeRadiusInput('eyeradius_2_inner_2')}
175 | {buildEyeRadiusInput('eyeradius_2_inner_3')}
176 |
177 |
178 |
179 |
180 |
eyeColor
181 |
182 |
183 |
Top left eye
184 |
Outer
185 |
192 |
Inner
193 |
200 |
201 |
202 |
Top right eye
203 |
Outer
204 |
211 |
Inner
212 |
219 |
220 |
221 |
Bottom left eye
222 |
Outer
223 |
230 |
Inner
231 |
238 |
239 |
240 |
241 |
242 |
253 | }
255 | logoOnLoad={(e) => console.log('logo loaded', e)}
256 | {...{
257 | ...state,
258 | eyeRadius: [ // build eyeRadius manually
259 | {
260 | outer: [state.eyeradius_0_outer_0, state.eyeradius_0_outer_1, state.eyeradius_0_outer_2, state.eyeradius_0_outer_3],
261 | inner: [state.eyeradius_0_inner_0, state.eyeradius_0_inner_1, state.eyeradius_0_inner_2, state.eyeradius_0_inner_3],
262 | },
263 | {
264 | outer: [state.eyeradius_1_outer_0, state.eyeradius_1_outer_1, state.eyeradius_1_outer_2, state.eyeradius_1_outer_3],
265 | inner: [state.eyeradius_1_inner_0, state.eyeradius_1_inner_1, state.eyeradius_1_inner_2, state.eyeradius_1_inner_3],
266 | },
267 | {
268 | outer: [state.eyeradius_2_outer_0, state.eyeradius_2_outer_1, state.eyeradius_2_outer_2, state.eyeradius_2_outer_3],
269 | inner: [state.eyeradius_2_inner_0, state.eyeradius_2_inner_1, state.eyeradius_2_inner_2, state.eyeradius_2_inner_3],
270 | }
271 | ],
272 | eyeColor: [ // build eyeColor manually
273 | {
274 | outer: state.eyecolor_0_outer ?? state.fgColor ?? '#000000',
275 | inner: state.eyecolor_0_inner ?? state.fgColor ?? '#000000'
276 | },
277 | {
278 | outer: state.eyecolor_1_outer ?? state.fgColor ?? '#000000',
279 | inner: state.eyecolor_1_inner ?? state.fgColor ?? '#000000'
280 | },
281 | {
282 | outer: state.eyecolor_2_outer ?? state.fgColor ?? '#000000',
283 | inner: state.eyecolor_2_inner ?? state.fgColor ?? '#000000'
284 | },
285 | ]
286 | }}
287 | />
288 |
291 |
292 |
293 |
306 |
307 |
308 | );
309 | }
310 |
311 | export default App;
312 |
--------------------------------------------------------------------------------
/src/components/CheckboxField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | interface ICheckboxFieldProps {
4 | name: string;
5 | handleChange: (target: any) => void;
6 | }
7 |
8 | export const CheckboxField = ({ name, handleChange }: ICheckboxFieldProps) => {
9 |
10 | const handleCheckboxToggle = (e: any) => {
11 | const target = {
12 | name,
13 | value: e.target.checked
14 | }
15 | handleChange({ target });
16 | };
17 |
18 | return (
19 |
20 |
25 |
26 |
27 | );
28 | }
--------------------------------------------------------------------------------
/src/components/ImageUploadField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | interface IImageUploadFieldProps {
4 | name: string;
5 | handleChange: (target: any) => void;
6 | }
7 |
8 | export const ImageUploadField = ({ name, handleChange }: IImageUploadFieldProps) => {
9 |
10 | const retrievePathFile = (files: any) => {
11 | const file = files[0];
12 | if (file.type !== 'image/png' && file.type !== 'image/jpeg') {
13 | console.error('Only png and jpg/jpeg allowed.')
14 | } else {
15 | const target: any = {};
16 | const reader = new FileReader();
17 | reader.readAsDataURL(file);
18 | reader.onloadend = e => {
19 | target.name = name;
20 | target.value = reader.result;
21 | target.logoName = file.name;
22 | handleChange({ target });
23 | };
24 | }
25 | }
26 |
27 | return (
28 |
29 |
30 | retrievePathFile(e.target.files)} />
35 |
36 | );
37 | }
--------------------------------------------------------------------------------
/src/components/InputField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | interface IInputFieldProps {
4 | name: string;
5 | type: 'color' | 'range' | 'text';
6 | min?: number;
7 | max?: number;
8 | step?: number;
9 | defaultValue?: string | number;
10 | handleChange: (target: any) => void;
11 | hideLabel?: boolean;
12 | value?: string | number;
13 | }
14 |
15 | export const InputField = ({ name, type, handleChange, min, max, step, defaultValue, hideLabel, value }: IInputFieldProps) => {
16 | return (
17 |
18 | {!hideLabel && }
19 |
30 |
31 | );
32 | }
--------------------------------------------------------------------------------
/src/components/SelectField.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | interface ISelectFieldProps {
4 | name: string;
5 | options: string[];
6 | handleChange: (target: any) => void;
7 | }
8 |
9 | export const SelectField = ({ name, options, handleChange }: ISelectFieldProps) => {
10 | return (
11 |
12 |
13 |
18 |
19 | );
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/src/components/TextArea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | interface ITextAreaProps {
4 | name: string;
5 | rows?: number;
6 | cols?: number;
7 | role?: string;
8 | defaultValue?: string | number;
9 | handleChange: (target: any) => void;
10 | hideLabel?: boolean;
11 | value?: string | number;
12 | }
13 |
14 | export const TextArea = ({ name, handleChange, role, rows, cols, defaultValue, hideLabel, value }: ITextAreaProps) => {
15 | return (
16 |
17 | {!hideLabel && }
18 |
28 |
29 | );
30 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOMClient from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | const rootElement = document.getElementById("root");
8 | const root = ReactDOMClient.createRoot(rootElement);
9 |
10 | root.render(
11 |
12 |
13 | ,
14 | );
15 |
16 | // If you want to start measuring performance in your app, pass a function
17 | // to log results (for example: reportWebVitals(console.log))
18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
19 | reportWebVitals();
20 |
--------------------------------------------------------------------------------
/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------