Skip to content

Commit 128748a

Browse files
authored
Merge pull request #27 from LGLabGreg/feat/gradient
Feat/gradient
2 parents c734c17 + 2251692 commit 128748a

16 files changed

Lines changed: 243 additions & 94 deletions

File tree

.changeset/orange-trainers-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@lglab/react-qr-code': minor
3+
---
4+
5+
Added gradient support for background and qr code data

apps/playground/src/App.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@ function App() {
3131
value: 'https://github.com/LGLabGreg/react-qr-code.git',
3232
size: 500,
3333
marginSize: 3,
34-
bgColor: '#f1f1f1',
34+
background: '#f9f9f9',
35+
gradient: {
36+
type: 'linear',
37+
rotation: 0,
38+
stops: [
39+
{ offset: '0%', color: '#4568DC' },
40+
{ offset: '100%', color: '#B06AB3' },
41+
],
42+
},
3543
dataModulesSettings: {
3644
color: '#560bad',
3745
style: dataModulesStyle,
@@ -56,7 +64,7 @@ function App() {
5664
}
5765

5866
const download = ({ format = 'svg', size = 400 }: DownloadOptions) => {
59-
ref.current?.download({ format, size, name: 'demo-qr-code' })
67+
ref.current?.download({ format, size })
6068
}
6169

6270
return (

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "git",
66
"url": "https://github.com/LGLabGreg/react-qr-code.git"
77
},
8-
"packageManager": "pnpm@9.15.4",
8+
"packageManager": "pnpm@10.2.0",
99
"type": "module",
1010
"scripts": {
1111
"dev": "pnpm concurrently --names packages,apps \"pnpm watch:packages\" \"pnpm watch:apps\"",
@@ -38,7 +38,7 @@
3838
"react": "^19.0.0",
3939
"react-dom": "^19.0.0",
4040
"typescript": "~5.7.3",
41-
"typescript-eslint": "^8.22.0",
41+
"typescript-eslint": "^8.23.0",
4242
"vite": "^6.0.5"
4343
}
4444
}

packages/react-qr-code/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"react": "^19.0.0",
3939
"react-dom": "^19.0.0",
4040
"typescript": "~5.7.3",
41-
"typescript-eslint": "^8.22.0",
41+
"typescript-eslint": "^8.23.0",
4242
"vite": "^6.0.5",
4343
"vite-plugin-dts": "^4.5.0"
4444
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { BG_GRADIENT_ID, DEFAULT_BGCOLOR } from '../constants'
2+
import type { BackgroundSettings } from '../types/lib'
3+
import { calculateGradientVectors } from '../utils/svg'
4+
5+
interface BackgroundProps {
6+
background?: BackgroundSettings
7+
numCells: number
8+
}
9+
10+
export const Background = ({ background, numCells }: BackgroundProps) => {
11+
if (!background) {
12+
return <path fill={DEFAULT_BGCOLOR} d={`M0,0 h${numCells}v${numCells}H0z`} />
13+
}
14+
15+
if (typeof background === 'string') {
16+
return <path fill={background} d={`M0,0 h${numCells}v${numCells}H0z`} />
17+
}
18+
19+
const vectors = calculateGradientVectors(background?.rotation || 0)
20+
21+
return (
22+
<>
23+
<defs>
24+
{background.type === 'linear' ? (
25+
<linearGradient id={BG_GRADIENT_ID} gradientUnits='userSpaceOnUse' {...vectors}>
26+
{background.stops?.map((stop, index) => (
27+
<stop key={index} offset={stop.offset} stopColor={stop.color} />
28+
))}
29+
</linearGradient>
30+
) : (
31+
<radialGradient
32+
id={BG_GRADIENT_ID}
33+
gradientUnits='userSpaceOnUse'
34+
cx='50%'
35+
cy='50%'
36+
r='50%'
37+
>
38+
{background.stops?.map((stop, index) => (
39+
<stop key={index} offset={stop.offset} stopColor={stop.color} />
40+
))}
41+
</radialGradient>
42+
)}
43+
</defs>
44+
<path fill={`url(#${BG_GRADIENT_ID})`} d={`M0,0 h${numCells}v${numCells}H0z`} />
45+
</>
46+
)
47+
}

packages/react-qr-code/src/components/data-modules.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type ReactNode, useCallback, useMemo } from 'react'
22

3-
import { DEFAULT_NUM_STAR_POINTS } from '../constants'
3+
import { DEFAULT_NUM_STAR_POINTS, GRADIENT_ID } from '../constants'
44
import type { DataModulesProps } from '../types/utils'
55
import {
66
bottomRounded,
@@ -29,6 +29,7 @@ export const DataModules = ({
2929
modules,
3030
margin,
3131
settings,
32+
gradient,
3233
}: DataModulesProps): ReactNode => {
3334
const { color, style, randomSize } = useMemo(
3435
() => sanitizeDataModulesSettings(settings),
@@ -148,7 +149,7 @@ export const DataModules = ({
148149
})
149150
return (
150151
<path
151-
fill={color}
152+
fill={gradient ? `url(#${GRADIENT_ID})` : color}
152153
d={ops.join('')}
153154
shapeRendering={style === 'square' ? 'crispEdges' : 'geometricPrecision'}
154155
/>

packages/react-qr-code/src/components/finder-patterns-inner.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
FINDER_PATTERN_INNER_SIZE,
77
FINDER_PATTERN_OUTER_ROTATIONS,
88
FINDER_PATTERN_SIZE,
9+
GRADIENT_ID,
910
} from '../constants'
1011
import type { FinderPatternsInnerProps } from '../types/utils'
1112
import {
@@ -19,11 +20,13 @@ export const FinderPatternsInner = ({
1920
modules,
2021
margin,
2122
settings,
23+
gradient,
2224
}: FinderPatternsInnerProps): ReactNode => {
2325
const { color, style } = useMemo(
2426
() => sanitizeFinderPatternInnerSettings(settings),
2527
[settings],
2628
)
29+
const fill = gradient ? `url(#${GRADIENT_ID})` : color
2730

2831
const coordinates = useMemo(
2932
() => [
@@ -52,7 +55,7 @@ export const FinderPatternsInner = ({
5255
y={y}
5356
width={FINDER_PATTERN_INNER_SIZE}
5457
height={FINDER_PATTERN_INNER_SIZE}
55-
fill={color}
58+
fill={fill}
5659
rx={FINDER_PATTERN_INNER_RADIUSES[style]}
5760
/>
5861
)
@@ -72,7 +75,7 @@ export const FinderPatternsInner = ({
7275
y={y + posDiff / 2}
7376
width={size}
7477
height={size}
75-
fill={color}
78+
fill={fill}
7679
style={{
7780
transform: `rotate(${45}deg)`,
7881
transformOrigin: 'center',
@@ -112,7 +115,7 @@ export const FinderPatternsInner = ({
112115
return (
113116
<path
114117
key={key(x, y)}
115-
fill={color}
118+
fill={fill}
116119
d={path}
117120
style={{
118121
transform: `rotate(${rotation}deg)`,
@@ -127,7 +130,7 @@ export const FinderPatternsInner = ({
127130
if (style === 'heart') {
128131
return coordinates.map(({ x, y }) => {
129132
return (
130-
<path key={key(x, y)} fill={color} d={heart(x, y, FINDER_PATTERN_INNER_SIZE)} />
133+
<path key={key(x, y)} fill={fill} d={heart(x, y, FINDER_PATTERN_INNER_SIZE)} />
131134
)
132135
})
133136
}
@@ -137,7 +140,7 @@ export const FinderPatternsInner = ({
137140
const cx = x + FINDER_PATTERN_INNER_SIZE / 2
138141
const cy = y + FINDER_PATTERN_INNER_SIZE / 2
139142
const path = star(cx, cy, FINDER_PATTERN_INNER_SIZE * 1.2, DEFAULT_NUM_STAR_POINTS)
140-
return <path key={key(x, y)} fill={color} d={path} />
143+
return <path key={key(x, y)} fill={fill} d={path} />
141144
})
142145
}
143146

packages/react-qr-code/src/components/finder-patterns-outer.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
FINDER_PATTERN_OUTER_RADIUSES,
55
FINDER_PATTERN_OUTER_ROTATIONS,
66
FINDER_PATTERN_SIZE,
7+
GRADIENT_ID,
78
} from '../constants'
89
import type { FinderPatternsOuterProps } from '../types/utils'
910
import {
@@ -17,11 +18,13 @@ export const FinderPatternsOuter = ({
1718
modules,
1819
margin,
1920
settings,
21+
gradient,
2022
}: FinderPatternsOuterProps): ReactNode => {
2123
const { style, color } = useMemo(
2224
() => sanitizeFinderPatternOuterSettings(settings),
2325
[settings],
2426
)
27+
const fill = gradient ? `url(#${GRADIENT_ID})` : color
2528

2629
const ops: Array<string> = []
2730

@@ -34,9 +37,6 @@ export const FinderPatternsOuter = ({
3437
[margin, modules.length],
3538
)
3639

37-
console.log('modules.length', modules.length)
38-
console.log('coordinates', coordinates)
39-
4040
if (['rounded-sm', 'rounded', 'rounded-lg', 'circle', 'square'].includes(style)) {
4141
for (const coordinate of coordinates) {
4242
const { x, y } = coordinate
@@ -74,7 +74,7 @@ export const FinderPatternsOuter = ({
7474
)
7575
}
7676
}
77-
return <path fill={color} d={ops.join('')} />
77+
return <path fill={fill} d={ops.join('')} />
7878
}
7979

8080
if (
@@ -106,7 +106,7 @@ export const FinderPatternsOuter = ({
106106
return (
107107
<path
108108
key={`finder-patterns-outer-${style}-${x}-${y}`}
109-
fill={color}
109+
fill={fill}
110110
d={path}
111111
style={{
112112
transform: `rotate(${rotation}deg)`,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { GRADIENT_ID } from '../constants'
2+
import type { GradientSettings } from '../types/lib'
3+
import { calculateGradientVectors } from '../utils/svg'
4+
5+
interface GradientProps {
6+
gradient?: GradientSettings
7+
}
8+
9+
export const Gradient = ({ gradient }: GradientProps) => {
10+
if (!gradient) {
11+
return null
12+
}
13+
14+
const vectors = calculateGradientVectors(gradient?.rotation || 0)
15+
16+
return (
17+
<defs>
18+
{gradient.type === 'linear' ? (
19+
<linearGradient id={GRADIENT_ID} gradientUnits='userSpaceOnUse' {...vectors}>
20+
{gradient.stops?.map((stop, index) => (
21+
<stop key={index} offset={stop.offset} stopColor={stop.color} />
22+
))}
23+
</linearGradient>
24+
) : (
25+
<radialGradient
26+
id={GRADIENT_ID}
27+
gradientUnits='userSpaceOnUse'
28+
cx='50%'
29+
cy='50%'
30+
r='50%'
31+
>
32+
{gradient.stops?.map((stop, index) => (
33+
<stop key={index} offset={stop.offset} stopColor={stop.color} />
34+
))}
35+
</radialGradient>
36+
)}
37+
</defs>
38+
)
39+
}

packages/react-qr-code/src/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ export const DEFAULT_FINDER_PATTERN_OUTER_STYLE: FinderPatternOuterStyle = 'squa
3333
export const DEFAULT_FINDER_PATTERN_INNER_STYLE: FinderPatternInnerStyle = 'square'
3434
export const DEFAULT_DATA_MODULES_STYLE: DataModulesStyle = 'square'
3535

36+
export const DEFAULT_FILENAME = 'react-qr-code'
37+
38+
export const GRADIENT_ID = 'react-qr-code-gradient'
39+
export const BG_GRADIENT_ID = 'react-qr-code-bg-gradient'
40+
3641
// This is *very* rough estimate of max amount of QRCode allowed to be covered.
3742
// It is "wrong" in a lot of ways (area is a terrible way to estimate, it
3843
// really should be number of modules covered), but if for some reason we don't

0 commit comments

Comments
 (0)