Skip to content

Commit 3963391

Browse files
committed
feat: Add new hashtag option for modules and finder patter inner
1 parent 41944d3 commit 3963391

File tree

10 files changed

+77
-10
lines changed

10 files changed

+77
-10
lines changed
File renamed without changes.

.changeset/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json",
33
"changelog": ["@changesets/cli/changelog", { "repo": "LGLabGreg/react-qr-code" }],
4-
"commit": true,
4+
"commit": false,
55
"fixed": [],
66
"linked": [],
77
"access": "public",

apps/playground/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const FINDER_PATTERN_INNER_STYLES: FinderPatternInnerStyle[] = [
3939
'diamond',
4040
'heart',
4141
'star',
42+
'hashtag',
4243
]
4344

4445
export const DATA_MODULES_STYLES: DataModulesStyle[] = [
@@ -52,4 +53,5 @@ export const DATA_MODULES_STYLES: DataModulesStyle[] = [
5253
'diamond',
5354
'star',
5455
'heart',
56+
'hashtag',
5557
]

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@ describe('DataModules', () => {
3434
diamond: 'diamond',
3535
heart: 'heart',
3636
star: 'star',
37+
hashtag: 'hashtag',
3738
}
3839

3940
Object.entries(stylesToMethods).forEach(([style, method]) => {
40-
const utils = style === 'heart' || style === 'star' ? svgUtils : dataModulesUtils
41+
const utils = ['heart', 'star', 'hashtag'].includes(style)
42+
? svgUtils
43+
: dataModulesUtils
4144
const spy = vi.spyOn(utils, method as keyof typeof utils)
4245

4346
render(

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
import { isFinderPatternInnerModule } from '../utils/finder-patterns-inner'
2525
import { isFinderPatternOuterModule } from '../utils/finder-patterns-outer'
2626
import { sanitizeDataModulesSettings } from '../utils/settings'
27-
import { heart, star } from '../utils/svg'
27+
import { hashtag, heart, star } from '../utils/svg'
2828

2929
export const DataModules = ({
3030
modules,
@@ -75,6 +75,8 @@ export const DataModules = ({
7575
)
7676
} else if (style === 'heart') {
7777
ops.push(heart(xPos, yPos, size))
78+
} else if (style === 'hashtag') {
79+
ops.push(hashtag(xPos, yPos, size))
7880
} else if (style === 'rounded') {
7981
const { left, right, top, bottom, count } = getModuleNeighbours(x, y, modules)
8082

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,20 @@ describe('DataModules', () => {
130130
expect(path.getAttribute('fill')).toBe('#ff0000')
131131
})
132132
})
133+
134+
it('renders correctly with style hashtag', () => {
135+
const spy = vi.spyOn(svgUtils, 'hashtag')
136+
render(
137+
<FinderPatternsInner
138+
{...defaultProps}
139+
settings={{ style: 'hashtag', color: '#ff0000' }}
140+
/>,
141+
)
142+
const paths = screen.getAllByTestId('finder-patterns-inner')
143+
expect(paths).toHaveLength(3)
144+
paths.forEach((path) => {
145+
expect(spy).toHaveBeenCalled()
146+
expect(path.getAttribute('fill')).toBe('#ff0000')
147+
})
148+
})
133149
})

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
finderPatternsInnerLeaf,
1515
} from '../utils/finder-patterns-inner'
1616
import { sanitizeFinderPatternInnerSettings } from '../utils/settings'
17-
import { heart, star } from '../utils/svg'
17+
import { hashtag, heart, star } from '../utils/svg'
1818

1919
const testProps = {
2020
'data-testid': 'finder-patterns-inner',
@@ -155,4 +155,11 @@ export const FinderPatternsInner = ({
155155
return <path key={key(x, y)} fill={fill} d={path} {...testProps} />
156156
})
157157
}
158+
159+
if (style === 'hashtag') {
160+
return coordinates.map(({ x, y }) => {
161+
const path = hashtag(x - 0.25, y - 0.25, 3.5)
162+
return <path key={key(x, y)} fill={fill} d={path} {...testProps} />
163+
})
164+
}
158165
}

packages/react-qr-code/src/types/lib.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type DataModulesStyle =
3939
| 'diamond'
4040
| 'star'
4141
| 'heart'
42+
| 'hashtag'
4243

4344
export interface DataModulesSettings {
4445
color?: string
@@ -85,6 +86,7 @@ export type FinderPatternInnerStyle =
8586
| 'diamond'
8687
| 'star'
8788
| 'heart'
89+
| 'hashtag'
8890

8991
export interface FinderPatternInnerSettings {
9092
color?: string

packages/react-qr-code/src/utils/data-modules.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export const dataModuleCanBeRandomSize = (style: DataModulesStyle): boolean =>
66
style === 'circle' ||
77
style === 'star' ||
88
style === 'heart' ||
9-
style === 'diamond'
9+
style === 'diamond' ||
10+
style === 'hashtag'
1011

1112
export const getScaleFactor = (style: string, isRandom: boolean) => {
1213
if (style === 'square-sm') {

packages/react-qr-code/src/utils/svg.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,55 @@ export const calculateGradientVectors = (rotation: number) => {
2121
}
2222

2323
export const star = (cx: number, cy: number, size: number, spikes: number): string => {
24-
const outerRadius = size / 2 // Outer radius of the star
25-
const innerRadius = outerRadius / 2 // Inner radius for the star
26-
const step = Math.PI / spikes // Angle step for star points
24+
const outerRadius = size / 2
25+
const innerRadius = outerRadius / 2
26+
const step = Math.PI / spikes
2727
let path = ''
2828

2929
for (let i = 0; i < 2 * spikes; i++) {
30-
const angle = i * step - Math.PI / 2 // Start angle at the top
31-
const radius = i % 2 === 0 ? outerRadius : innerRadius // Alternate between outer and inner radius
30+
const angle = i * step - Math.PI / 2
31+
const radius = i % 2 === 0 ? outerRadius : innerRadius
3232
const px = cx + radius * Math.cos(angle)
3333
const py = cy + radius * Math.sin(angle)
3434
path += `${i === 0 ? 'M' : 'L'} ${px},${py} `
3535
}
3636
return path + 'Z' // Close the path
3737
}
3838

39+
export const hashtag = (x: number, y: number, size: number) => {
40+
const eigth = size / 8
41+
return `M ${x + size} ${y + eigth * 3}
42+
V ${y + eigth}
43+
h -${eigth}
44+
V ${y}
45+
H ${x + eigth * 5}
46+
v ${eigth}
47+
H ${x + eigth * 3}
48+
V ${y}
49+
H ${x + eigth}
50+
v ${eigth}
51+
H ${x}
52+
v ${eigth * 2}
53+
h ${eigth}
54+
v ${eigth * 2}
55+
H ${x}
56+
v ${eigth * 2}
57+
h ${eigth}
58+
v ${eigth}
59+
h ${eigth * 2}
60+
v -${eigth}
61+
h ${eigth * 2}
62+
v ${eigth}
63+
h ${eigth * 2}
64+
v -${eigth}
65+
h ${eigth}
66+
V ${y + eigth * 5}
67+
h -${eigth}
68+
V ${y + eigth * 3}
69+
h ${eigth}
70+
Z`
71+
}
72+
3973
export const heart = (x: number, y: number, size: number) => {
4074
let move = false
4175
let i = 0

0 commit comments

Comments
 (0)