Skip to content

Commit 21d33b9

Browse files
committed
client: add generate.ts and PageSeven
1 parent d40451a commit 21d33b9

File tree

6 files changed

+283
-1
lines changed

6 files changed

+283
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import FormWrapper from "../ui/FormWrapper";
2+
import { useCardUrl } from "../../hooks/useCardUrl";
3+
import { useMultistepContext } from "../../hooks/useMultistepContext";
4+
5+
const PageSeven = () => {
6+
const { card } = useMultistepContext();
7+
const url = useCardUrl();
8+
9+
return (
10+
<FormWrapper title="Your Card" className="items-center">
11+
<img
12+
className="select-none"
13+
width={`${card.width}px`}
14+
src={url}
15+
alt={card.title}
16+
/>
17+
</FormWrapper>
18+
);
19+
};
20+
21+
export default PageSeven;

client/src/components/lines/BadgeItem.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const BadgeItem = ({ badge, removeBadge, lineNumber }: Props) => {
3737
return (
3838
<button
3939
draggable
40+
type="button"
4041
onDragStart={handleDragStart}
4142
onDragEnd={() => {
4243
setDragged(false);

client/src/components/lines/NewBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const NewBadge = ({ addBadge }: Props) => {
8686
value={color}
8787
onChange={(e) => setColor(e.target.value)}
8888
setColor={(c) => setColor(c)}
89-
placeholder="#58a6ff"
89+
placeholder={file === null ? "#58a6ff" : ""}
9090
/>
9191
</InputWrapper>
9292

client/src/context/MultistepContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import PageTwo from "../components/form/PageTwo";
88
import { INITIAL_CARD } from "../const";
99
import { useMultistepForm } from "../hooks/useMultistepForm";
1010
import { Badge, BadgeDataTransfer, Card, Line } from "../types";
11+
import PageSeven from "../components/form/PageSeven";
1112

1213
export interface MultistepContextType {
1314
isFirstPage: boolean;
@@ -59,6 +60,7 @@ export const MultistepProvider: FC<MultistepProviderProps> = ({ children }) => {
5960
<PageFour />,
6061
<PageFive />,
6162
<PageSix />,
63+
<PageSeven />,
6264
]);
6365

6466
/**

client/src/hooks/useCardUrl.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { useMemo } from "react";
2+
import { generateLink } from "../utils/generate";
3+
import { useMultistepContext } from "./useMultistepContext";
4+
5+
export const useCardUrl = () => {
6+
const { card } = useMultistepContext();
7+
return useMemo(() => generateLink(card), [JSON.stringify(card)]);
8+
};

client/src/utils/generate.ts

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
import { Card, Line } from "../types";
2+
import { INITIAL_CARD, HEX_COLOR_REGEX } from "../const";
3+
4+
class CardBuilder {
5+
private url: string;
6+
7+
public constructor() {
8+
this.url = "https://github-readme-tech-stack.vercel.app/api/cards?";
9+
}
10+
11+
public title(title: string) {
12+
if (title === INITIAL_CARD.title) {
13+
return this;
14+
}
15+
16+
this.url += `title=${encodeURI(title)}&`;
17+
return this;
18+
}
19+
20+
public theme(theme: string) {
21+
if (theme === INITIAL_CARD.theme) {
22+
return this;
23+
}
24+
25+
this.url += `theme=${theme}&`;
26+
return this;
27+
}
28+
29+
public showBorder(showBorder: boolean) {
30+
if (showBorder === INITIAL_CARD.showBorder) {
31+
return this;
32+
}
33+
34+
this.url += `showBorder=${showBorder}&`;
35+
return this;
36+
}
37+
38+
public hideBg(hideBg: boolean) {
39+
if (hideBg === INITIAL_CARD.hideBg) {
40+
return this;
41+
}
42+
43+
this.url += `hideBg=${hideBg}&`;
44+
return this;
45+
}
46+
47+
public hideTitle(hideTitle: boolean) {
48+
if (hideTitle === INITIAL_CARD.hideTitle) {
49+
return this;
50+
}
51+
52+
this.url += `hideTitle=${hideTitle}&`;
53+
return this;
54+
}
55+
56+
public align(align: string) {
57+
if (align === INITIAL_CARD.align) {
58+
return this;
59+
}
60+
61+
this.url += `align=${align}&`;
62+
return this;
63+
}
64+
65+
public titleAlign(titleAlign: string) {
66+
if (titleAlign === INITIAL_CARD.titleAlign) {
67+
return this;
68+
}
69+
70+
this.url += `titleAlign=${titleAlign}&`;
71+
return this;
72+
}
73+
74+
public borderRadius(borderRadius: number) {
75+
if (borderRadius === INITIAL_CARD.borderRadius) {
76+
return this;
77+
}
78+
79+
this.url += `borderRadius=${borderRadius}&`;
80+
return this;
81+
}
82+
83+
public gap(gap: number) {
84+
if (gap === INITIAL_CARD.gap) {
85+
return this;
86+
}
87+
88+
this.url += `gap=${gap}&`;
89+
return this;
90+
}
91+
92+
public lineHeight(lineHeight: number) {
93+
if (lineHeight === INITIAL_CARD.lineHeight) {
94+
return this;
95+
}
96+
97+
this.url += `lineHeight=${lineHeight}&`;
98+
return this;
99+
}
100+
101+
public fontSize(fontSize: number) {
102+
if (fontSize === INITIAL_CARD.fontSize) {
103+
return this;
104+
}
105+
106+
this.url += `fontSize=${fontSize}&`;
107+
return this;
108+
}
109+
110+
public fontWeight(fontWeight: string) {
111+
if (fontWeight === INITIAL_CARD.fontWeight) {
112+
return this;
113+
}
114+
115+
this.url += `fontWeight=${fontWeight}&`;
116+
return this;
117+
}
118+
119+
public fontFamily(fontFamily: string) {
120+
if (fontFamily === INITIAL_CARD.fontFamily) {
121+
return this;
122+
}
123+
124+
this.url += `fontFamily=${encodeURI(fontFamily)}&`;
125+
return this;
126+
}
127+
128+
public lineCount(lc: number) {
129+
this.url += `lineCount=${lc}&`;
130+
return this;
131+
}
132+
133+
public width(width: number) {
134+
if (width === INITIAL_CARD.width) {
135+
return this;
136+
}
137+
138+
this.url += `width=${width}&`;
139+
return this;
140+
}
141+
142+
public backgroundColor(backgroundColor: string | undefined = "") {
143+
if (HEX_COLOR_REGEX.test(backgroundColor)) {
144+
backgroundColor = backgroundColor.replace("#", "%23");
145+
this.url += `bg=${backgroundColor}&`;
146+
}
147+
148+
return this;
149+
}
150+
151+
public badgeColor(badgeColor: string | undefined = "") {
152+
if (HEX_COLOR_REGEX.test(badgeColor)) {
153+
badgeColor = badgeColor.replace("#", "%23");
154+
this.url += `badge=${badgeColor}&`;
155+
}
156+
157+
return this;
158+
}
159+
160+
public borderColor(borderColor: string | undefined = "") {
161+
if (HEX_COLOR_REGEX.test(borderColor)) {
162+
borderColor = borderColor.replace("#", "%23");
163+
this.url += `border=${borderColor}&`;
164+
}
165+
166+
return this;
167+
}
168+
169+
public titleColor(titleColor: string | undefined = "") {
170+
if (HEX_COLOR_REGEX.test(titleColor)) {
171+
titleColor = titleColor.replace("#", "%23");
172+
this.url += `titleColor=${titleColor}&`;
173+
}
174+
175+
return this;
176+
}
177+
178+
public lines(lines: Line[]) {
179+
for (const l of lines) {
180+
// if the line doesn't contain badges
181+
if (l.badges.length < 1) {
182+
continue;
183+
}
184+
185+
let line = `line${l.lineNumber}=`;
186+
for (const b of l.badges) {
187+
const color = b.color.replace("#", "");
188+
line += `${encodeURI(b.icon)},${encodeURI(b.label)},${color};`;
189+
}
190+
191+
this.url += line;
192+
}
193+
194+
return this;
195+
}
196+
197+
public build() {
198+
const lastChar = this.url.at(-1);
199+
if (["?", "&"].includes(lastChar ?? "")) {
200+
this.url = this.url.slice(0, -1);
201+
}
202+
203+
return this.url;
204+
}
205+
}
206+
207+
export const generateLink = ({
208+
title,
209+
theme,
210+
align,
211+
lines,
212+
showBorder,
213+
borderRadius,
214+
fontWeight,
215+
fontSize,
216+
fontFamily,
217+
lineHeight,
218+
gap,
219+
hideBg,
220+
hideTitle,
221+
titleAlign,
222+
width,
223+
backgroundColor,
224+
badgeColor,
225+
borderColor,
226+
titleColor,
227+
}: Card): string => {
228+
return new CardBuilder()
229+
.title(title)
230+
.align(align)
231+
.titleAlign(titleAlign)
232+
.borderRadius(borderRadius)
233+
.fontFamily(fontFamily)
234+
.fontSize(fontSize)
235+
.fontWeight(fontWeight)
236+
.showBorder(showBorder)
237+
.lineHeight(lineHeight)
238+
.lineCount(lines.length)
239+
.theme(theme)
240+
.gap(gap)
241+
.width(width)
242+
.hideBg(hideBg)
243+
.hideTitle(hideTitle)
244+
.backgroundColor(backgroundColor)
245+
.badgeColor(badgeColor)
246+
.borderColor(borderColor)
247+
.titleColor(titleColor)
248+
.lines(lines)
249+
.build();
250+
};

0 commit comments

Comments
 (0)