Skip to content

Commit 2201582

Browse files
committed
Add testimonials + social media cards
1 parent 03539f0 commit 2201582

File tree

10 files changed

+341
-6
lines changed

10 files changed

+341
-6
lines changed

dist/images/boss.jpg

178 KB
Loading

dist/images/librarian.jpg

101 KB
Loading

dist/index.html

+8
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ <h2>Runic Playground</h2>
7979
<div class="runic-playground-container"></div>
8080
</article>
8181

82+
<article>
83+
<section>
84+
<h2>Testimonials</h2>
85+
<p>See what fans are saying about this site!</p>
86+
</section>
87+
<div class="testimonials-container"></div>
88+
</article>
89+
8290
<article>
8391
<section>
8492
<p>Made by Aryan Pingle.</p>
+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
.sm-card {
2+
--sm-card--color-dark: black;
3+
--sm-card--color-light: rgb(83, 100, 113);
4+
5+
color: var(--sm-card--color-dark);
6+
7+
font-size: 0.75em;
8+
padding: 0.5em 1em;
9+
background-color: white;
10+
border-radius: 1em;
11+
12+
display: grid;
13+
grid-template-columns: auto 1fr;
14+
grid-template-rows: auto 1fr auto;
15+
column-gap: 0.5em;
16+
height: 15em;
17+
max-width: 60ch;
18+
width: 100%;
19+
}
20+
21+
.sm-card__pfp {
22+
grid-row: span 3;
23+
width: 2em;
24+
height: 2em;
25+
background-color: red;
26+
border-radius: 100%;
27+
28+
overflow: hidden;
29+
}
30+
31+
.sm-card__pfp > img {
32+
max-width: 100%;
33+
max-height: 100%;
34+
}
35+
36+
.sm-card__header {
37+
}
38+
39+
.sm-card__display-name {
40+
font-weight: 700;
41+
margin-right: 0.5em;
42+
display: inline-block;
43+
}
44+
45+
.sm-card__handle {
46+
color: var(--sm-card--color-light);
47+
display: inline-block;
48+
}
49+
50+
.sm-card__content {
51+
flex: 1 0 0;
52+
overflow: hidden;
53+
}
54+
55+
.sm-card__content > svg {
56+
/* height: 100%; */
57+
}
58+
59+
.sm-card__interactions {
60+
padding-top: 0.5em;
61+
flex: 0 0 auto;
62+
color: var(--sm-card--color-light);
63+
64+
display: flex;
65+
justify-content: space-between;
66+
align-items: center;
67+
}
68+
69+
.sm-card__interactions > span {
70+
overflow: hidden;
71+
}
72+
73+
.sm-card__interactions svg {
74+
width: 1em;
75+
height: 1em;
76+
vertical-align: text-top;
77+
margin-right: 0.5em;
78+
}
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { SocialMediaCommentIcon, SocialMediaHeartIcon, SocialMediaRepostIcon, SocialMediaViewsIcon } from "components/icons";
2+
import "./index.css";
3+
4+
import { RuneSVG } from "components/RuneSVG";
5+
import { h, Component, VNode } from "preact";
6+
import { translateSentence } from "../../ipa";
7+
8+
export interface SocialMediaCardProps {
9+
pfp: VNode<HTMLImageElement>;
10+
displayName: string;
11+
handle: string;
12+
phoneticText: string;
13+
likes: number;
14+
comments: number;
15+
reposts: number;
16+
views: number;
17+
}
18+
19+
interface State {}
20+
21+
export class SocialMediaCard extends Component<SocialMediaCardProps, State> {
22+
render(props: SocialMediaCardProps, state: State) {
23+
return (
24+
<div class="sm-card">
25+
<div className="sm-card__pfp">{props.pfp}</div>
26+
<div className="sm-card__header">
27+
<span className="sm-card__display-name">
28+
{props.displayName}
29+
</span>
30+
<span className="sm-card__handle">
31+
@{props.handle} · Sep 23
32+
</span>
33+
</div>
34+
<div className="sm-card__content">
35+
<RuneSVG
36+
phoneticText={props.phoneticText}
37+
displayPhonemes={false}
38+
interactive={false}
39+
runeColor="black"
40+
/>
41+
</div>
42+
<div className="sm-card__interactions">
43+
<span>
44+
<SocialMediaCommentIcon />
45+
{props.comments.toLocaleString()}
46+
</span>
47+
<span>
48+
<SocialMediaRepostIcon />
49+
{props.reposts.toLocaleString()}
50+
</span>
51+
<span>
52+
<SocialMediaHeartIcon />
53+
{props.likes.toLocaleString()}
54+
</span>
55+
<span>
56+
<SocialMediaViewsIcon />
57+
{props.likes.toLocaleString()}
58+
</span>
59+
</div>
60+
</div>
61+
);
62+
}
63+
}

src/components/Testimonials/index.css

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
.testimonials {
2+
display: grid;
3+
width: 100%;
4+
gap: 0.5em;
5+
justify-items: center;
6+
}
7+
8+
/* Mobile - Vertical only */
9+
10+
.testimonials {
11+
grid-template-columns: 1fr;
12+
grid-auto-rows: auto;
13+
}
14+
15+
@media (min-width: 1024px) {
16+
.testimonials {
17+
grid-template-columns: 1fr 1fr;
18+
grid-auto-rows: auto;
19+
}
20+
}

src/components/Testimonials/index.tsx

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import "./index.css";
2+
import { h, Component } from "preact";
3+
import {
4+
SocialMediaCard,
5+
SocialMediaCardProps,
6+
} from "components/SocialMediaCard";
7+
8+
interface Props {}
9+
10+
interface State {}
11+
12+
const holyCrossPhonetic = `
13+
nɔɹ wɛ is is
14+
saʊ is saʊ saʊ nɔɹ
15+
saʊ wɛ nɔɹ is nɔɹ saʊ
16+
O wɛ is wɛ
17+
`.trim();
18+
19+
const testimonialsInfo: SocialMediaCardProps[] = [
20+
{
21+
pfp: <img src="./images/boss.jpg" alt="Boss Scavenger" />,
22+
displayName: "ScavQueen 🌸",
23+
handle: "boss.scavenger642",
24+
phoneticText: "ðɪs ɪz ɑsəm!\n@#runic@",
25+
comments: 300,
26+
likes: 1000,
27+
reposts: 1010,
28+
views: 50000,
29+
},
30+
{
31+
pfp: <img src="./images/librarian.jpg" alt="The Librarian" />,
32+
displayName: "Holy Cross 🐐",
33+
handle: "the.librarian",
34+
phoneticText:
35+
`bɔɹɪŋ. ɛniweɪ.\n\n${holyCrossPhonetic}\n@#runic@`,
36+
comments: 300,
37+
likes: 1000,
38+
reposts: 1010,
39+
views: 50000,
40+
},
41+
];
42+
43+
export class Testimonials extends Component<Props, State> {
44+
render(props: Props, state: State) {
45+
return (
46+
<div className="testimonials">
47+
{...testimonialsInfo.map((info) => (
48+
<SocialMediaCard {...info} />
49+
))}
50+
</div>
51+
);
52+
}
53+
}

src/components/icons.tsx

+44
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,47 @@ export const RightAlignIcon = (props: preact.JSX.HTMLAttributes) => (
116116
/>
117117
</svg>
118118
);
119+
120+
export const SocialMediaHeartIcon = (props: preact.JSX.HTMLAttributes) => (
121+
// @ts-ignore - HTML attributes are valid for SVG
122+
<svg viewBox="0 0 24 24" {...props}>
123+
<path
124+
d="M16.697 5.5c-1.222-.06-2.679.51-3.89 2.16l-.805 1.09-.806-1.09C9.984 6.01 8.526 5.44 7.304 5.5c-1.243.07-2.349.78-2.91 1.91-.552 1.12-.633 2.78.479 4.82 1.074 1.97 3.257 4.27 7.129 6.61 3.87-2.34 6.052-4.64 7.126-6.61 1.111-2.04 1.03-3.7.477-4.82-.561-1.13-1.666-1.84-2.908-1.91zm4.187 7.69c-1.351 2.48-4.001 5.12-8.379 7.67l-.503.3-.504-.3c-4.379-2.55-7.029-5.19-8.382-7.67-1.36-2.5-1.41-4.86-.514-6.67.887-1.79 2.647-2.91 4.601-3.01 1.651-.09 3.368.56 4.798 2.01 1.429-1.45 3.146-2.1 4.796-2.01 1.954.1 3.714 1.22 4.601 3.01.896 1.81.846 4.17-.514 6.67z"
125+
fill="currentColor"
126+
stroke="currentColor"
127+
></path>
128+
</svg>
129+
);
130+
131+
export const SocialMediaCommentIcon = (props: preact.JSX.HTMLAttributes) => (
132+
// @ts-ignore - HTML attributes are valid for SVG
133+
<svg viewBox="0 0 24 24" {...props}>
134+
<path
135+
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z"
136+
fill="currentColor"
137+
stroke="currentColor"
138+
></path>
139+
</svg>
140+
);
141+
142+
export const SocialMediaViewsIcon = (props: preact.JSX.HTMLAttributes) => (
143+
// @ts-ignore - HTML attributes are valid for SVG
144+
<svg viewBox="0 0 24 24" {...props}>
145+
<path
146+
d="M8.75 21V3h2v18h-2zM18 21V8.5h2V21h-2zM4 21l.004-10h2L6 21H4zm9.248 0v-7h2v7h-2z"
147+
fill="currentColor"
148+
stroke="currentColor"
149+
></path>
150+
</svg>
151+
);
152+
153+
export const SocialMediaRepostIcon = (props: preact.JSX.HTMLAttributes) => (
154+
// @ts-ignore - HTML attributes are valid for SVG
155+
<svg viewBox="0 0 24 24" {...props}>
156+
<path
157+
d="M4.5 3.88l4.432 4.14-1.364 1.46L5.5 7.55V16c0 1.1.896 2 2 2H13v2H7.5c-2.209 0-4-1.79-4-4V7.55L1.432 9.48.068 8.02 4.5 3.88zM16.5 6H11V4h5.5c2.209 0 4 1.79 4 4v8.45l2.068-1.93 1.364 1.46-4.432 4.14-4.432-4.14 1.364-1.46 2.068 1.93V8c0-1.1-.896-2-2-2z"
158+
fill="currentColor"
159+
stroke="currentColor"
160+
></path>
161+
</svg>
162+
);

src/holyCross.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// prettier-ignore
2+
const goldenPathDirections = ["South", "South", "North", "West", "East", "East", "West", "North", "East", "South", "South", "North", "West", "East", "West", "South", "North", "East"];
3+
4+
// For debugging
5+
// const goldenPathDirections = ["North", "South", "West", "East"];
6+
7+
const keyToDirection = {
8+
ArrowUp: "North",
9+
ArrowDown: "South",
10+
ArrowLeft: "West",
11+
ArrowRight: "East",
12+
};
13+
14+
let goldenPathIndex = 0;
15+
let goldenPathComplete = false;
16+
17+
function onKeyDown(event: KeyboardEvent) {
18+
const key = event.key;
19+
20+
// Check if an arrow key was pressed
21+
if (!(key in keyToDirection)) {
22+
// Arrow key was not pressed
23+
goldenPathIndex = 0;
24+
return;
25+
} else {
26+
// Arrow key was pressed
27+
28+
// Prevent multiple golden path rewards
29+
if (goldenPathComplete) return;
30+
31+
const directionPressed = keyToDirection[key];
32+
33+
// Check if the correct key was pressed
34+
if (goldenPathDirections[goldenPathIndex] !== directionPressed) {
35+
// Wrong key was pressed
36+
console.log("WRONG");
37+
goldenPathIndex = 0;
38+
return;
39+
} else {
40+
// Correct arrow key pressed
41+
++goldenPathIndex;
42+
43+
if (goldenPathIndex === goldenPathDirections.length) {
44+
// Golden path has been travelled!
45+
goldenPathComplete = true;
46+
onGoldenPathTravelled();
47+
}
48+
}
49+
}
50+
}
51+
52+
function onGoldenPathTravelled() {
53+
// TODO: Do something cool
54+
console.log("GOLDEN PATH TRAVELLED!");
55+
}
56+
57+
export function addGoldenPathListener() {
58+
document.addEventListener("keydown", onKeyDown);
59+
}

src/index.tsx

+16-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import "./index.css";
66
import { RuneReferenceTable } from "components/RuneReference";
77
import { consonantDataTable, vowelDataTable } from "./runeDataset";
88
import { RunicPlayground } from "components/RunicPlayground";
9+
import { Testimonials } from "components/Testimonials";
10+
import { addGoldenPathListener } from "./holyCross";
911

1012
async function setup() {
1113
await loadIPADict();
@@ -16,12 +18,6 @@ async function setup() {
1618
document.querySelector(".runic-editor-container"),
1719
);
1820

19-
// Runic Playground
20-
render(
21-
<RunicPlayground />,
22-
document.querySelector(".runic-playground-container"),
23-
);
24-
2521
// Vowel Table
2622
render(
2723
<RuneReferenceTable table={vowelDataTable}></RuneReferenceTable>,
@@ -33,5 +29,19 @@ async function setup() {
3329
<RuneReferenceTable table={consonantDataTable}></RuneReferenceTable>,
3430
document.querySelector("#rune-consonant-table"),
3531
);
32+
33+
// Runic Playground
34+
render(
35+
<RunicPlayground />,
36+
document.querySelector(".runic-playground-container"),
37+
);
38+
39+
// Testimonials Section
40+
render(
41+
<Testimonials />,
42+
document.querySelector(".testimonials-container")
43+
);
44+
45+
addGoldenPathListener();
3646
}
3747
setup();

0 commit comments

Comments
 (0)