diff --git a/src/config.ts b/src/config.ts
index dafdfa3..5019fa4 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -4,6 +4,12 @@ export type BodyShape = "square" | "circle";
export interface Config {
length: number;
+ value: string;
+ logo?: {
+ url: string;
+ height: number;
+ width: number;
+ };
shapes: {
eyeFrame: EyeFrameShape;
body: BodyShape;
@@ -26,24 +32,36 @@ export interface Config {
}
export const defaultConfig: Config = {
- length: 400,
+ length: 200,
+ value: "https://intosoft.com",
+ logo: {
+ url: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjIuMSAxOTQuNyI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiM0MmM5ODU7fS5jbHMtMntmaWxsOiM2NmJmODM7fTwvc3R5bGU+PC9kZWZzPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNOTQuMzYsNzUuNTljMTguMzksMTkuNzUsMTguNzYsNDkuMjguODIsNjZsLTUuMjQsNC44Ny03LjQ5LDctMy4xNSwzLjEyTDQwLjQ3LDE5Mi44N2E2Ljc3LDYuNzcsMCwwLDEtOS41Ny0uMzFMMCwxNTkuNTVsMTguNi0xNy40MWEyMi43OCwyMi43OCwwLDAsMSwxLjU5LTEuNjhsLjE1LS4xNCw4LjE3LTcuNjIsOS4wOS04LjQ3LjkyLS44NywxMS4yNy0xMC41MWExOSwxOSwwLDAsMCwxMi40NSwzLjYzLDE5LjE2LDE5LjE2LDAsMSwwLTE3LjQ0LTlMMzEuMTQsMTIwLjI1QTEuODEsMS44MSwwLDAsMSwyOC42LDEyMGwtLjg2LS45MkM5LjM1LDk5LjM3LDksNjkuODQsMjYuOTIsNTMuMTZMMzkuNjUsNDEuMzFsMzgtMzUuNzZMNzIsMTAuODhsOS42NC05LjA2YTYuNzMsNi43MywwLDAsMSw5LjUyLjMxbDMwLjkyLDMzTDEwMy41LDUyLjU3YTIwLjg1LDIwLjg1LDAsMCwxLTEuNTksMS42OGwtLjE1LjE0TDkzLjYsNjIsODcuMDcsNjguMWw2LjE1LDYuMzJaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNOTQuMzYsNzUuNTlsLTEuMTQtMS4xN2EyLjI2LDIuMjYsMCwwLDEsLjI5LjI1WiIvPjwvZz48L2c+PC9zdmc+",
+ height: 70,
+ width: 105,
+ },
shapes: {
eyeFrame: "circle",
- body: "square",
+ body: "circle",
eyeball: "circle",
},
colors: {
background: "white",
- body: "black",
+ body: "linear-gradient(90deg, rgba(255,31,234,1) 4%, RGBA(225,147,129,1) 35%, rgba(0,212,255,1) 100%)",
eyeFrame: {
- topLeft: "black",
- topRight: "black",
- bottomLeft: "black",
+ topLeft:
+ "linear-gradient(90deg, RGBA(66, 58, 187, 1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)",
+ topRight:
+ "linear-gradient(90deg, RGBA(66, 58, 187, 1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)",
+ bottomLeft:
+ "linear-gradient(90deg, RGBA(66, 58, 187, 1) 0%, rgba(9,9,121,1) 35%, rgba(0,212,255,1) 100%)",
},
eyeball: {
- topLeft: "black",
- topRight: "black",
- bottomLeft: "black",
+ topLeft:
+ "linear-gradient(90deg, rgba(244,209,74,1) 0%, RGB(5, 5, 5) 35%, rgba(0,212,255,1) 100%)",
+ topRight:
+ "linear-gradient(90deg, rgba(244,209,74,1) 0%, RGB(5, 5, 5) 35%, rgba(0,212,255,1) 100%)",
+ bottomLeft:
+ "linear-gradient(90deg, rgba(244,209,74,1) 0%, RGB(5, 5, 5) 35%, rgba(0,212,255,1) 100%)",
},
},
};
diff --git a/src/index.ts b/src/index.ts
index 1a48499..6c7bfc1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,7 +15,7 @@ import {
squareEyeFrame,
} from "./eyeframes";
import { generatePath } from "./path";
-import { generateMatrix } from "./utils";
+import { generateMatrix, renderLogoFromConfig } from "./utils";
const quietZone = 0;
@@ -35,7 +35,7 @@ const eyeballFunction: {
};
export const generateSVGString = (config: Config) => {
- const matrix = generateMatrix("https://intosoft.com", "L");
+ const matrix = generateMatrix(config.value || "https://intosoft.com", "L");
const path = generatePath({ matrix, size: config.length, config });
@@ -48,16 +48,16 @@ export const generateSVGString = (config: Config) => {
config.length
}" xmlns="http://www.w3.org/2000/svg">
- ${generateLinearGradientByConfig(config)}
+ ${generateLinearGradientByConfig(config)}
-
-
-
-
+
+ ${renderLogoFromConfig(config)}
${generateEyeFrameSVGFromConfig(config, matrix.length)}
${generateEyeballSVGFromConfig(config, matrix.length)}
-
+
`;
return svg;
diff --git a/src/utils.ts b/src/utils.ts
index 6db731d..cf45b26 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,3 +1,4 @@
+import { Config } from "./config";
import QRCode, { QRCodeErrorCorrectionLevel } from "qrcode";
export const generateMatrix = (
@@ -135,3 +136,14 @@ export const getPositionForEyes = ({
},
};
};
+
+export const renderLogoFromConfig = (config: Config) => {
+ if (config.logo?.url) {
+ const centerX = (config.length - config.logo.width) / 2;
+ const centerY = (config.length - config.logo.height) / 2;
+ return ``;
+ }
+
+ return "";
+};
diff --git a/web/package.json b/web/package.json
index 986ff43..58d383b 100644
--- a/web/package.json
+++ b/web/package.json
@@ -11,6 +11,7 @@
"@types/react": "^18.2.58",
"@types/react-dom": "^18.2.19",
"copy-to-clipboard": "^3.3.3",
+ "lodash": "^4.17.21",
"rc-slider": "^10.5.0",
"react": "^18.2.0",
"react-best-gradient-color-picker": "^3.0.7",
@@ -49,6 +50,7 @@
},
"devDependencies": {
"@craco/craco": "^7.1.0",
+ "@types/lodash": "^4.14.202",
"@types/react-color": "^3.0.12",
"@types/react-syntax-highlighter": "^15.5.11",
"customize-cra": "^1.0.0",
diff --git a/web/src/pages/Home/Code.tsx b/web/src/pages/Home/Code.tsx
index ae89b88..cc14265 100644
--- a/web/src/pages/Home/Code.tsx
+++ b/web/src/pages/Home/Code.tsx
@@ -21,28 +21,29 @@ type Platform =
| "vanilla";
const CODES: { [key in Platform]: string } = {
- react: `
-import QRCode from "@intosoft/qrcode/react";
+ react: `import { generateSVGString } from '@intosoft/qrcode';
-export const QRCodeRenderer = () => {
- return
-}
+ const svgString = generateSVGString(config);
+ export const RenderQR = () => {
+ return (
);
+ };
`,
- "react-native": `
-// Prerequisite
+ "react-native": `// Prerequisite
// npm i react-native-svg | yarn add react-native-svg
-import QRCode from '@intosoft/qrcode/native';
+import { SvgFromXml } from "react-native-svg";
+import { generateSVGString } from '@intosoft/qrcode';
-export const QRCodeRenderer = () => {
- return
-}
+const svgString = generateSVGString(config);
+
+export const RenderQR = () => {
+ return ();
+};
`,
node: ``,
angular: ``,
vue: ``,
- vanilla: `
-
+ vanilla: `
@@ -83,6 +84,8 @@ const PlatformCode = ({ id }: { id: Platform }) => {
style={materialDark}
customStyle={{
width: "100%",
+ fontSize: 14,
+ borderRadius: 4,
}}
>
{CODES[id]}
@@ -146,6 +149,8 @@ export const CodeBlock = ({ config }: CodeBlockProps) => {
style={materialDark}
customStyle={{
width: "100%",
+ fontSize: 14,
+ borderRadius: 4,
}}
>
{codeString}
diff --git a/web/src/pages/Home/customization/Colors.tsx b/web/src/pages/Home/customization/Colors.tsx
index 9e227d6..4205bbb 100644
--- a/web/src/pages/Home/customization/Colors.tsx
+++ b/web/src/pages/Home/customization/Colors.tsx
@@ -72,60 +72,60 @@ export const Colors = ({
label: "Background",
value: qrConfig.colors.background,
onChange: (value: string) => {
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
colors: {
- ...prev.colors,
+ ...qrConfig.colors,
background: value,
},
- }));
+ });
},
},
{
label: "Body",
value: qrConfig.colors.body,
onChange: (value: string) => {
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
colors: {
- ...prev.colors,
+ ...qrConfig.colors,
body: value,
},
- }));
+ });
},
},
{
label: "EyeFrame",
value: qrConfig.colors.eyeFrame.topLeft,
onChange: (value: string) => {
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
colors: {
- ...prev.colors,
+ ...qrConfig.colors,
eyeFrame: {
topLeft: value,
topRight: value,
bottomLeft: value,
},
},
- }));
+ });
},
},
{
label: "Eyeball",
value: qrConfig.colors.eyeball.topLeft,
onChange: (value: string) => {
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
colors: {
- ...prev.colors,
+ ...qrConfig.colors,
eyeball: {
topLeft: value,
topRight: value,
bottomLeft: value,
},
},
- }));
+ });
},
},
];
diff --git a/web/src/pages/Home/customization/Content.tsx b/web/src/pages/Home/customization/Content.tsx
new file mode 100644
index 0000000..598304d
--- /dev/null
+++ b/web/src/pages/Home/customization/Content.tsx
@@ -0,0 +1,40 @@
+import styled from "styled-components";
+
+import { CustomizationSectionProps } from "./type";
+
+const Input = styled.input`
+ height: 40px;
+ width: 100%;
+ border: 1px solid #d0d7df;
+ border-radius: 4px;
+ padding: 4px 10px;
+ outline: none;
+
+ &:focus {
+ border: 1px solid #a2e344;
+ }
+`;
+
+const Label = styled.p`
+ margin: 4px 2px;
+`;
+
+export const Content = ({
+ setQrConfig,
+ qrConfig,
+}: CustomizationSectionProps) => {
+ return (
+ <>
+
+
+ setQrConfig({
+ ...qrConfig,
+ value,
+ })
+ }
+ />
+ >
+ );
+};
diff --git a/web/src/pages/Home/customization/Logo.tsx b/web/src/pages/Home/customization/Logo.tsx
index 9b3a03f..9bc010b 100644
--- a/web/src/pages/Home/customization/Logo.tsx
+++ b/web/src/pages/Home/customization/Logo.tsx
@@ -1,5 +1,119 @@
+import styled from "styled-components";
+
import { CustomizationSectionProps } from "./type";
+import { fileToBase64 } from "../../../utils/file";
+
+const Input = styled.input`
+ height: 40px;
+ width: 100%;
+ border: 1px solid #d0d7df;
+ border-radius: 4px;
+ padding: 4px 10px;
+ outline: none;
+
+ &:focus {
+ border: 1px solid #a2e344;
+ }
+`;
+
+const Label = styled.p`
+ margin: 4px 2px;
+`;
+
+const Upload = styled.div`
+ height: 80px;
+ width: 100px;
+ border: 1px solid #d0d7df;
+ border-radius: 4px;
+ cursor: pointer;
+ background-color: white;
+`;
export const Logo = ({ setQrConfig, qrConfig }: CustomizationSectionProps) => {
- return <>>;
+ return (
+ <>
+
+
+ setQrConfig({
+ ...qrConfig,
+ logo: {
+ ...(qrConfig.logo || { url: "", height: 0, width: 0 }),
+ url: value,
+ },
+ })
+ }
+ />
+
+
+ OR
+
+
+
+ {
+ if (files?.[0]) {
+ const base64 = await fileToBase64(files[0]);
+ setQrConfig({
+ ...qrConfig,
+ logo: {
+ ...(qrConfig.logo || { url: "", height: 0, width: 0 }),
+ url: base64,
+ },
+ });
+ }
+ }}
+ />
+
+
+ Dimensions
+
+
+
+ setQrConfig({
+ ...qrConfig,
+ logo: {
+ ...(qrConfig.logo || { url: "", height: 0, width: 0 }),
+ height: parseInt(value),
+ },
+ })
+ }
+ />
+
+
+ setQrConfig({
+ ...qrConfig,
+ logo: {
+ ...(qrConfig.logo || { url: "", height: 0, width: 0 }),
+ width: parseInt(value),
+ },
+ })
+ }
+ />
+ >
+ );
};
diff --git a/web/src/pages/Home/customization/Shape.tsx b/web/src/pages/Home/customization/Shape.tsx
index 4abf656..cb6deb5 100644
--- a/web/src/pages/Home/customization/Shape.tsx
+++ b/web/src/pages/Home/customization/Shape.tsx
@@ -39,13 +39,13 @@ export const Shape = ({ setQrConfig, qrConfig }: CustomizationSectionProps) => {
key={item[0]}
$active={item[0] === qrConfig.shapes.body}
onClick={() =>
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
shapes: {
- ...prev.shapes,
+ ...qrConfig.shapes,
body: item[0],
},
- }))
+ })
}
>
@@ -59,13 +59,13 @@ export const Shape = ({ setQrConfig, qrConfig }: CustomizationSectionProps) => {
key={item[0]}
$active={item[0] === qrConfig.shapes.eyeball}
onClick={() =>
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
shapes: {
- ...prev.shapes,
+ ...qrConfig.shapes,
eyeball: item[0],
},
- }))
+ })
}
>
@@ -79,13 +79,13 @@ export const Shape = ({ setQrConfig, qrConfig }: CustomizationSectionProps) => {
key={item[0]}
$active={item[0] === qrConfig.shapes.eyeFrame}
onClick={() =>
- setQrConfig((prev) => ({
- ...prev,
+ setQrConfig({
+ ...qrConfig,
shapes: {
- ...prev.shapes,
+ ...qrConfig.shapes,
eyeFrame: item[0],
},
- }))
+ })
}
>
diff --git a/web/src/pages/Home/customization/type.ts b/web/src/pages/Home/customization/type.ts
index 08d4c61..f3c3441 100644
--- a/web/src/pages/Home/customization/type.ts
+++ b/web/src/pages/Home/customization/type.ts
@@ -1,7 +1,6 @@
-import { Dispatch, SetStateAction } from "react";
import { Config } from "../../../../../src/config";
export interface CustomizationSectionProps {
qrConfig: Config;
- setQrConfig: Dispatch>;
+ setQrConfig: (config: Config) => void;
}
diff --git a/web/src/pages/Home/index.tsx b/web/src/pages/Home/index.tsx
index 1798791..aba07d4 100644
--- a/web/src/pages/Home/index.tsx
+++ b/web/src/pages/Home/index.tsx
@@ -1,5 +1,5 @@
import styled from "styled-components";
-
+import packageJSON from "../../../package.json";
import { SVG } from "../../components/SVG";
import { useEffect, useState } from "react";
import { generateSVGString } from "../../../../src/index";
@@ -15,6 +15,7 @@ import { Colors } from "./customization/Colors";
import { Logo } from "./customization/Logo";
import { Config, defaultConfig } from "../../../../src/config";
import { CodeBlock } from "./Code";
+import { Content as ContentTab } from "./customization/Content";
const Container = styled.div`
background-color: #f7f8fa;
@@ -102,6 +103,10 @@ export const HomePage = () => {
useState(1);
const TABS = [
+ {
+ title: "Content",
+ Component: ContentTab,
+ },
{
title: "Shape",
Component: Shape,
@@ -125,87 +130,144 @@ export const HomePage = () => {
);
}, [qrConfig]);
+ const handleSetConfig = (config: Config) => {
+ localStorage.setItem(
+ "qr-config",
+ JSON.stringify({
+ config: {
+ ...config,
+ length: 200,
+ },
+ version: packageJSON.version,
+ })
+ );
+
+ setQrConfig(config);
+ };
+
+ useEffect(() => {
+ const _config = localStorage.getItem("qr-config");
+ if (_config) {
+ try {
+ const { config, version } = JSON.parse(_config);
+ if (version === packageJSON.version) {
+ setQrConfig(config);
+ console.log("set from storage");
+ }
+ } catch (err) {
+ console.log("Err", err);
+ }
+ }
+ }, []);
+
return (
-
-
- setSelectedCustomizationTabIndex(index)}
- >
-
- {TABS.map(({ title }) => (
- {title}
- ))}
-
-
+ setSelectedCustomizationTabIndex(index)}
+ style={{
+ width: "100%",
+ }}
+ >
+
+ {TABS.map(({ title }) => (
+ {title}
+ ))}
+
+
+
{TABS.map(({ title, Component }) => (
- {Component({ qrConfig, setQrConfig })}
+
))}
-
-
-
-
+
-
-
- Low
-
-
- {imageSize} x {imageSize} px
-
+
+
+
+
+
+ Low
+
+
+ {imageSize} x {imageSize} px
+
+
+ High
+
+
+ setImageSize(val as number)}
+ value={imageSize}
+ />
- Hight
+ Download
-
- setImageSize(val as number)}
- value={imageSize}
- />
-
- Download
-
-
-
- downloadSVG({ svgString, downloadType: "png" })}
- >
- PNG
-
- downloadSVG({ svgString, downloadType: "jpeg" })}
- >
- JPEG
-
- downloadSVG({ svgString, downloadType: "svg" })}
- >
- SVG
-
-
-
-
+
+
+
+ downloadSVG({
+ config: qrConfig,
+ imageSize,
+ downloadType: "png",
+ })
+ }
+ >
+ PNG
+
+
+ downloadSVG({
+ config: qrConfig,
+ imageSize,
+ downloadType: "jpeg",
+ })
+ }
+ >
+ JPEG
+
+
+ downloadSVG({
+ config: qrConfig,
+ imageSize,
+ downloadType: "svg",
+ })
+ }
+ >
+ SVG
+
+
+
+
+
diff --git a/web/src/utils/file.ts b/web/src/utils/file.ts
index 8a138ad..f003624 100644
--- a/web/src/utils/file.ts
+++ b/web/src/utils/file.ts
@@ -1,10 +1,30 @@
+import { generateSVGString } from "../../../src";
+import { Config } from "../../../src/config";
+import { cloneDeep } from "lodash";
+
interface DownloadSVGParams {
- svgString: string;
downloadType: "svg" | "png" | "jpeg";
+ imageSize: number;
+ config: Config;
}
const FILE_NAME = "intosoft-qrcode";
-export const downloadSVG = ({ svgString, downloadType }: DownloadSVGParams) => {
+export const downloadSVG = ({
+ config,
+ imageSize,
+ downloadType,
+}: DownloadSVGParams) => {
+ const sizeConfig = cloneDeep(config);
+ if (sizeConfig.logo?.url) {
+ sizeConfig.logo.height =
+ (sizeConfig.logo.height / sizeConfig.length) * imageSize;
+ sizeConfig.logo.width =
+ (sizeConfig.logo.width / sizeConfig.length) * imageSize;
+ }
+ const svgString = generateSVGString({
+ ...sizeConfig,
+ length: imageSize,
+ });
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
@@ -43,3 +63,21 @@ const downloadFile = (url: string, type: string, filename: string) => {
document.body.appendChild(link);
link.click();
};
+
+export const fileToBase64 = (file: File): Promise => {
+ var reader = new FileReader();
+
+ reader.readAsDataURL(file);
+ return new Promise((resolve, reject) => {
+ reader.onloadend = function () {
+ if (typeof reader.result === "string") {
+ resolve(reader.result);
+ } else {
+ reject("result is not string");
+ }
+ };
+ reader.onerror = () => {
+ reject("Couldn't process file");
+ };
+ });
+};
diff --git a/web/yarn.lock b/web/yarn.lock
index 857651c..cad7341 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -2173,6 +2173,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
+"@types/lodash@^4.14.202":
+ version "4.14.202"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
+ integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
+
"@types/mime@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45"