@ntrs/meme 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +92 -0
- package/dist/index.js +278 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
type MemeCardState = 'idle' | 'selected' | 'winner' | 'loser';
|
|
4
|
+
interface MemeCardProps {
|
|
5
|
+
title: string;
|
|
6
|
+
imageUrl: string;
|
|
7
|
+
rating?: number;
|
|
8
|
+
record?: {
|
|
9
|
+
wins: number;
|
|
10
|
+
losses: number;
|
|
11
|
+
};
|
|
12
|
+
/** rating change shown as a badge in the "winner"/"loser" states */
|
|
13
|
+
delta?: number;
|
|
14
|
+
state?: MemeCardState;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function MemeCard({ title, imageUrl, rating, record, delta, state, onClick, className, }: MemeCardProps): react.JSX.Element;
|
|
19
|
+
|
|
20
|
+
interface VoteButtonProps {
|
|
21
|
+
side: 'left' | 'right';
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
onVote?: () => void;
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
declare function VoteButton({ side, disabled, onVote, className }: VoteButtonProps): react.JSX.Element;
|
|
27
|
+
|
|
28
|
+
interface RatingDeltaProps {
|
|
29
|
+
delta: number;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function RatingDelta({ delta, className }: RatingDeltaProps): react.JSX.Element;
|
|
33
|
+
|
|
34
|
+
interface BattleMeme {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
imageUrl: string;
|
|
38
|
+
rating: number;
|
|
39
|
+
record?: {
|
|
40
|
+
wins: number;
|
|
41
|
+
losses: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
interface BattleResult {
|
|
45
|
+
winnerId: string;
|
|
46
|
+
delta: number;
|
|
47
|
+
}
|
|
48
|
+
interface BattleStageProps {
|
|
49
|
+
memeA: BattleMeme;
|
|
50
|
+
memeB: BattleMeme;
|
|
51
|
+
/** ready → cards clickable · voting → optimistic selection shown · revealed → winner/loser + deltas */
|
|
52
|
+
phase: 'ready' | 'voting' | 'revealed';
|
|
53
|
+
/** the meme the user clicked (voting phase) */
|
|
54
|
+
selectedId?: string;
|
|
55
|
+
result?: BattleResult;
|
|
56
|
+
onVote?: (memeId: string) => void;
|
|
57
|
+
className?: string;
|
|
58
|
+
}
|
|
59
|
+
declare function BattleStage(props: BattleStageProps): react.JSX.Element;
|
|
60
|
+
|
|
61
|
+
interface LeaderboardRow {
|
|
62
|
+
rank: number;
|
|
63
|
+
id: string;
|
|
64
|
+
title: string;
|
|
65
|
+
imageUrl: string;
|
|
66
|
+
rating: number;
|
|
67
|
+
wins: number;
|
|
68
|
+
losses: number;
|
|
69
|
+
uploader: string;
|
|
70
|
+
}
|
|
71
|
+
interface LeaderboardTableProps {
|
|
72
|
+
rows: LeaderboardRow[];
|
|
73
|
+
isLoading?: boolean;
|
|
74
|
+
/** meme ids to highlight (e.g. the current user's uploads) */
|
|
75
|
+
highlightIds?: string[];
|
|
76
|
+
className?: string;
|
|
77
|
+
}
|
|
78
|
+
declare function LeaderboardTable({ rows, isLoading, highlightIds, className }: LeaderboardTableProps): react.JSX.Element;
|
|
79
|
+
|
|
80
|
+
interface UploadDropzoneProps {
|
|
81
|
+
/** visual state — parent owns the actual file/drag logic */
|
|
82
|
+
state?: 'idle' | 'dragover' | 'preview' | 'error';
|
|
83
|
+
previewUrl?: string;
|
|
84
|
+
error?: string;
|
|
85
|
+
accept?: string;
|
|
86
|
+
maxSizeMb?: number;
|
|
87
|
+
onFile?: (file: File) => void;
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
declare function UploadDropzone({ state, previewUrl, error, accept, maxSizeMb, onFile, className, }: UploadDropzoneProps): react.JSX.Element;
|
|
91
|
+
|
|
92
|
+
export { type BattleMeme, type BattleResult, BattleStage, type BattleStageProps, type LeaderboardRow, LeaderboardTable, type LeaderboardTableProps, MemeCard, type MemeCardProps, type MemeCardState, RatingDelta, type RatingDeltaProps, UploadDropzone, type UploadDropzoneProps, VoteButton, type VoteButtonProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/meme-card/index.tsx
|
|
4
|
+
import { cn as cn2 } from "@ntrs/core";
|
|
5
|
+
|
|
6
|
+
// src/rating-delta/index.tsx
|
|
7
|
+
import { cn } from "@ntrs/core";
|
|
8
|
+
import { jsxs } from "react/jsx-runtime";
|
|
9
|
+
function RatingDelta({ delta, className }) {
|
|
10
|
+
const sign = delta > 0 ? "+" : delta < 0 ? "\u2212" : "\xB1";
|
|
11
|
+
const arrow = delta > 0 ? "\u25B2" : delta < 0 ? "\u25BC" : "";
|
|
12
|
+
return /* @__PURE__ */ jsxs(
|
|
13
|
+
"span",
|
|
14
|
+
{
|
|
15
|
+
className: cn(
|
|
16
|
+
"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold tabular-nums",
|
|
17
|
+
delta > 0 && "bg-success/15 text-success",
|
|
18
|
+
delta < 0 && "bg-destructive/15 text-destructive",
|
|
19
|
+
delta === 0 && "bg-muted text-muted-foreground",
|
|
20
|
+
className
|
|
21
|
+
),
|
|
22
|
+
children: [
|
|
23
|
+
sign,
|
|
24
|
+
Math.abs(delta),
|
|
25
|
+
" ",
|
|
26
|
+
arrow
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/meme-card/index.tsx
|
|
33
|
+
import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
34
|
+
function MemeCard({
|
|
35
|
+
title,
|
|
36
|
+
imageUrl,
|
|
37
|
+
rating,
|
|
38
|
+
record,
|
|
39
|
+
delta,
|
|
40
|
+
state = "idle",
|
|
41
|
+
onClick,
|
|
42
|
+
className
|
|
43
|
+
}) {
|
|
44
|
+
const clickable = state === "idle" && !!onClick;
|
|
45
|
+
const Tag = clickable ? "button" : "div";
|
|
46
|
+
return /* @__PURE__ */ jsxs2(
|
|
47
|
+
Tag,
|
|
48
|
+
{
|
|
49
|
+
type: clickable ? "button" : void 0,
|
|
50
|
+
onClick: clickable ? onClick : void 0,
|
|
51
|
+
"aria-label": clickable ? `Vote for ${title}` : void 0,
|
|
52
|
+
className: cn2(
|
|
53
|
+
"color-card relative block w-full overflow-hidden rounded-xl border border-border text-left",
|
|
54
|
+
"transition-all duration-300",
|
|
55
|
+
clickable && "cursor-pointer hover:-translate-y-1 hover:shadow-lg hover:border-primary/50",
|
|
56
|
+
state === "selected" && "ring-2 ring-primary",
|
|
57
|
+
state === "winner" && "ring-2 ring-success shadow-lg",
|
|
58
|
+
state === "loser" && "opacity-50 grayscale",
|
|
59
|
+
className
|
|
60
|
+
),
|
|
61
|
+
children: [
|
|
62
|
+
state === "winner" && /* @__PURE__ */ jsx("span", { className: "absolute left-3 top-3 z-10 rounded-full bg-success px-2.5 py-1 text-xs font-bold text-success-foreground shadow", children: "\u{1F3C6} Winner" }),
|
|
63
|
+
/* @__PURE__ */ jsx("div", { className: "aspect-[4/3] w-full bg-muted", children: /* @__PURE__ */ jsx("img", { src: imageUrl, alt: title, className: "h-full w-full object-cover" }) }),
|
|
64
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-3 p-4", children: [
|
|
65
|
+
/* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
|
|
66
|
+
/* @__PURE__ */ jsx("p", { className: "truncate font-semibold", children: title }),
|
|
67
|
+
record && /* @__PURE__ */ jsxs2("p", { className: "text-xs text-muted-foreground tabular-nums", children: [
|
|
68
|
+
record.wins,
|
|
69
|
+
"W \u2013 ",
|
|
70
|
+
record.losses,
|
|
71
|
+
"L"
|
|
72
|
+
] })
|
|
73
|
+
] }),
|
|
74
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
75
|
+
(state === "winner" || state === "loser") && delta !== void 0 && /* @__PURE__ */ jsx(RatingDelta, { delta: state === "winner" ? Math.abs(delta) : -Math.abs(delta) }),
|
|
76
|
+
rating !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-lg bg-muted px-2 py-1 text-sm font-bold tabular-nums", children: rating })
|
|
77
|
+
] })
|
|
78
|
+
] })
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/vote-button/index.tsx
|
|
85
|
+
import { cn as cn3 } from "@ntrs/core";
|
|
86
|
+
import { jsx as jsx2, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
87
|
+
function VoteButton({ side, disabled, onVote, className }) {
|
|
88
|
+
return /* @__PURE__ */ jsxs3(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
type: "button",
|
|
92
|
+
disabled,
|
|
93
|
+
onClick: onVote,
|
|
94
|
+
"aria-label": `Vote ${side}`,
|
|
95
|
+
className: cn3(
|
|
96
|
+
"inline-flex h-12 items-center gap-2 rounded-full bg-primary px-6 font-semibold text-primary-foreground",
|
|
97
|
+
"transition-transform hover:scale-105 active:scale-95",
|
|
98
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
99
|
+
"disabled:pointer-events-none disabled:opacity-40",
|
|
100
|
+
className
|
|
101
|
+
),
|
|
102
|
+
children: [
|
|
103
|
+
side === "left" && /* @__PURE__ */ jsx2("span", { "aria-hidden": true, children: "\u2190" }),
|
|
104
|
+
"Vote",
|
|
105
|
+
side === "right" && /* @__PURE__ */ jsx2("span", { "aria-hidden": true, children: "\u2192" })
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/battle-stage/index.tsx
|
|
112
|
+
import { cn as cn4 } from "@ntrs/core";
|
|
113
|
+
import { jsx as jsx3, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
114
|
+
function cardState(memeId, { phase, selectedId, result }) {
|
|
115
|
+
if (phase === "voting") return memeId === selectedId ? "selected" : "idle";
|
|
116
|
+
if (phase === "revealed" && result) return memeId === result.winnerId ? "winner" : "loser";
|
|
117
|
+
return "idle";
|
|
118
|
+
}
|
|
119
|
+
function BattleStage(props) {
|
|
120
|
+
const { memeA, memeB, phase, result, onVote, className } = props;
|
|
121
|
+
return /* @__PURE__ */ jsxs4("div", { className: cn4("grid grid-cols-1 items-center gap-6 md:grid-cols-[1fr_auto_1fr]", className), children: [
|
|
122
|
+
[memeA, memeB].map((meme, i) => /* @__PURE__ */ jsx3("div", { className: cn4(i === 1 && "md:order-3"), children: /* @__PURE__ */ jsx3(
|
|
123
|
+
MemeCard,
|
|
124
|
+
{
|
|
125
|
+
title: meme.title,
|
|
126
|
+
imageUrl: meme.imageUrl,
|
|
127
|
+
rating: meme.rating,
|
|
128
|
+
record: meme.record,
|
|
129
|
+
state: cardState(meme.id, props),
|
|
130
|
+
delta: result?.delta,
|
|
131
|
+
onClick: phase === "ready" && onVote ? () => onVote(meme.id) : void 0
|
|
132
|
+
}
|
|
133
|
+
) }, meme.id)),
|
|
134
|
+
/* @__PURE__ */ jsx3("div", { className: "justify-self-center md:order-2", "aria-hidden": true, children: /* @__PURE__ */ jsx3(
|
|
135
|
+
"span",
|
|
136
|
+
{
|
|
137
|
+
className: cn4(
|
|
138
|
+
"flex h-14 w-14 items-center justify-center rounded-full border-2 border-border bg-card text-lg font-black text-muted-foreground",
|
|
139
|
+
phase === "voting" && "animate-pulse border-primary text-primary"
|
|
140
|
+
),
|
|
141
|
+
children: "VS"
|
|
142
|
+
}
|
|
143
|
+
) }),
|
|
144
|
+
/* @__PURE__ */ jsxs4("p", { className: "text-center text-sm text-muted-foreground md:order-4 md:col-span-3", children: [
|
|
145
|
+
phase === "ready" && "Click the funnier meme \u2014 or press \u2190 / \u2192",
|
|
146
|
+
phase === "voting" && "Counting your vote\u2026",
|
|
147
|
+
phase === "revealed" && result && `Ratings updated: \xB1${result.delta} points`
|
|
148
|
+
] })
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/leaderboard-table/index.tsx
|
|
153
|
+
import { cn as cn5 } from "@ntrs/core";
|
|
154
|
+
import { jsx as jsx4, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
155
|
+
var MEDALS = { 1: "\u{1F947}", 2: "\u{1F948}", 3: "\u{1F949}" };
|
|
156
|
+
function LeaderboardTable({ rows, isLoading, highlightIds = [], className }) {
|
|
157
|
+
if (isLoading) {
|
|
158
|
+
return /* @__PURE__ */ jsx4("div", { className: cn5("space-y-2", className), "aria-busy": "true", "aria-label": "Loading leaderboard", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx4("div", { className: "h-16 animate-pulse rounded-xl bg-muted" }, i)) });
|
|
159
|
+
}
|
|
160
|
+
if (rows.length === 0) {
|
|
161
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn5("color-card rounded-xl border border-border p-12 text-center", className), children: [
|
|
162
|
+
/* @__PURE__ */ jsx4("p", { className: "text-4xl", children: "\u{1F997}" }),
|
|
163
|
+
/* @__PURE__ */ jsx4("p", { className: "mt-3 font-semibold", children: "No champions yet" }),
|
|
164
|
+
/* @__PURE__ */ jsx4("p", { className: "mt-1 text-sm text-muted-foreground", children: "Upload a meme and start voting to crown the first one." })
|
|
165
|
+
] });
|
|
166
|
+
}
|
|
167
|
+
return /* @__PURE__ */ jsx4("div", { className: cn5("color-card overflow-x-auto rounded-xl border border-border", className), children: /* @__PURE__ */ jsxs5("table", { className: "w-full text-sm", children: [
|
|
168
|
+
/* @__PURE__ */ jsx4("thead", { children: /* @__PURE__ */ jsxs5("tr", { className: "border-b border-border text-left text-xs uppercase tracking-wide text-muted-foreground", children: [
|
|
169
|
+
/* @__PURE__ */ jsx4("th", { scope: "col", className: "px-4 py-3 w-14", children: "#" }),
|
|
170
|
+
/* @__PURE__ */ jsx4("th", { scope: "col", className: "px-4 py-3", children: "Meme" }),
|
|
171
|
+
/* @__PURE__ */ jsx4("th", { scope: "col", className: "px-4 py-3 text-right", children: "Record" }),
|
|
172
|
+
/* @__PURE__ */ jsx4("th", { scope: "col", className: "px-4 py-3 text-right", children: "Rating" })
|
|
173
|
+
] }) }),
|
|
174
|
+
/* @__PURE__ */ jsx4("tbody", { children: rows.map((row) => {
|
|
175
|
+
const podium = row.rank <= 3;
|
|
176
|
+
return /* @__PURE__ */ jsxs5(
|
|
177
|
+
"tr",
|
|
178
|
+
{
|
|
179
|
+
className: cn5(
|
|
180
|
+
"border-b border-border last:border-0 transition-colors hover:bg-muted/50",
|
|
181
|
+
highlightIds.includes(row.id) && "bg-primary/5"
|
|
182
|
+
),
|
|
183
|
+
children: [
|
|
184
|
+
/* @__PURE__ */ jsx4("td", { className: cn5("px-4 py-3 tabular-nums", podium ? "text-lg" : "text-muted-foreground"), children: MEDALS[row.rank] ?? row.rank }),
|
|
185
|
+
/* @__PURE__ */ jsx4("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
|
|
186
|
+
/* @__PURE__ */ jsx4("img", { src: row.imageUrl, alt: "", className: "h-10 w-10 rounded-lg object-cover" }),
|
|
187
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
|
|
188
|
+
/* @__PURE__ */ jsx4("p", { className: cn5("truncate", podium ? "font-bold" : "font-medium"), children: row.title }),
|
|
189
|
+
/* @__PURE__ */ jsxs5("p", { className: "truncate text-xs text-muted-foreground", children: [
|
|
190
|
+
"by ",
|
|
191
|
+
row.uploader
|
|
192
|
+
] })
|
|
193
|
+
] })
|
|
194
|
+
] }) }),
|
|
195
|
+
/* @__PURE__ */ jsxs5("td", { className: "px-4 py-3 text-right tabular-nums text-muted-foreground", children: [
|
|
196
|
+
/* @__PURE__ */ jsxs5("span", { className: "text-success font-medium", children: [
|
|
197
|
+
row.wins,
|
|
198
|
+
"W"
|
|
199
|
+
] }),
|
|
200
|
+
" \u2013 ",
|
|
201
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
202
|
+
row.losses,
|
|
203
|
+
"L"
|
|
204
|
+
] })
|
|
205
|
+
] }),
|
|
206
|
+
/* @__PURE__ */ jsx4("td", { className: cn5("px-4 py-3 text-right tabular-nums font-bold", row.rank === 1 && "text-gold"), children: row.rating })
|
|
207
|
+
]
|
|
208
|
+
},
|
|
209
|
+
row.id
|
|
210
|
+
);
|
|
211
|
+
}) })
|
|
212
|
+
] }) });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/upload-dropzone/index.tsx
|
|
216
|
+
import { useRef } from "react";
|
|
217
|
+
import { cn as cn6 } from "@ntrs/core";
|
|
218
|
+
import { Fragment, jsx as jsx5, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
219
|
+
function UploadDropzone({
|
|
220
|
+
state = "idle",
|
|
221
|
+
previewUrl,
|
|
222
|
+
error,
|
|
223
|
+
accept = "image/jpeg,image/png,image/webp,image/gif",
|
|
224
|
+
maxSizeMb = 5,
|
|
225
|
+
onFile,
|
|
226
|
+
className
|
|
227
|
+
}) {
|
|
228
|
+
const inputRef = useRef(null);
|
|
229
|
+
return /* @__PURE__ */ jsxs6("div", { className, children: [
|
|
230
|
+
/* @__PURE__ */ jsx5(
|
|
231
|
+
"button",
|
|
232
|
+
{
|
|
233
|
+
type: "button",
|
|
234
|
+
onClick: () => inputRef.current?.click(),
|
|
235
|
+
"aria-label": "Choose an image to upload",
|
|
236
|
+
className: cn6(
|
|
237
|
+
"flex min-h-56 w-full flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-6 transition-colors",
|
|
238
|
+
state === "idle" && "border-border bg-muted/30 hover:border-primary/60 hover:bg-muted/60",
|
|
239
|
+
state === "dragover" && "border-primary bg-primary/5",
|
|
240
|
+
state === "preview" && "border-border bg-card p-3",
|
|
241
|
+
state === "error" && "border-destructive bg-destructive/5"
|
|
242
|
+
),
|
|
243
|
+
children: state === "preview" && previewUrl ? /* @__PURE__ */ jsx5("img", { src: previewUrl, alt: "Upload preview", className: "max-h-72 w-full rounded-lg object-contain" }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
244
|
+
/* @__PURE__ */ jsx5("span", { className: "text-4xl", "aria-hidden": true, children: state === "error" ? "\u{1F6AB}" : state === "dragover" ? "\u{1F4E5}" : "\u{1F5BC}\uFE0F" }),
|
|
245
|
+
/* @__PURE__ */ jsx5("span", { className: "font-medium", children: state === "dragover" ? "Drop it!" : "Drag a meme here, or click to browse" }),
|
|
246
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-xs text-muted-foreground", children: [
|
|
247
|
+
"JPEG \xB7 PNG \xB7 WebP \xB7 GIF \u2014 up to ",
|
|
248
|
+
maxSizeMb,
|
|
249
|
+
" MB"
|
|
250
|
+
] })
|
|
251
|
+
] })
|
|
252
|
+
}
|
|
253
|
+
),
|
|
254
|
+
state === "error" && error && /* @__PURE__ */ jsx5("p", { role: "alert", className: "mt-2 text-sm font-medium text-destructive", children: error }),
|
|
255
|
+
/* @__PURE__ */ jsx5(
|
|
256
|
+
"input",
|
|
257
|
+
{
|
|
258
|
+
ref: inputRef,
|
|
259
|
+
type: "file",
|
|
260
|
+
accept,
|
|
261
|
+
className: "hidden",
|
|
262
|
+
onChange: (e) => {
|
|
263
|
+
const file = e.target.files?.[0];
|
|
264
|
+
if (file) onFile?.(file);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
] });
|
|
269
|
+
}
|
|
270
|
+
export {
|
|
271
|
+
BattleStage,
|
|
272
|
+
LeaderboardTable,
|
|
273
|
+
MemeCard,
|
|
274
|
+
RatingDelta,
|
|
275
|
+
UploadDropzone,
|
|
276
|
+
VoteButton
|
|
277
|
+
};
|
|
278
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/meme-card/index.tsx","../src/rating-delta/index.tsx","../src/vote-button/index.tsx","../src/battle-stage/index.tsx","../src/leaderboard-table/index.tsx","../src/upload-dropzone/index.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@ntrs/core';\nimport { RatingDelta } from '../rating-delta';\n\nexport type MemeCardState = 'idle' | 'selected' | 'winner' | 'loser';\n\nexport interface MemeCardProps {\n title: string;\n imageUrl: string;\n rating?: number;\n record?: { wins: number; losses: number };\n /** rating change shown as a badge in the \"winner\"/\"loser\" states */\n delta?: number;\n state?: MemeCardState;\n onClick?: () => void;\n className?: string;\n}\n\nexport function MemeCard({\n title,\n imageUrl,\n rating,\n record,\n delta,\n state = 'idle',\n onClick,\n className,\n}: MemeCardProps) {\n const clickable = state === 'idle' && !!onClick;\n const Tag = clickable ? 'button' : 'div';\n\n return (\n <Tag\n type={clickable ? 'button' : undefined}\n onClick={clickable ? onClick : undefined}\n aria-label={clickable ? `Vote for ${title}` : undefined}\n className={cn(\n 'color-card relative block w-full overflow-hidden rounded-xl border border-border text-left',\n 'transition-all duration-300',\n clickable && 'cursor-pointer hover:-translate-y-1 hover:shadow-lg hover:border-primary/50',\n state === 'selected' && 'ring-2 ring-primary',\n state === 'winner' && 'ring-2 ring-success shadow-lg',\n state === 'loser' && 'opacity-50 grayscale',\n className,\n )}\n >\n {state === 'winner' && (\n <span className=\"absolute left-3 top-3 z-10 rounded-full bg-success px-2.5 py-1 text-xs font-bold text-success-foreground shadow\">\n 🏆 Winner\n </span>\n )}\n <div className=\"aspect-[4/3] w-full bg-muted\">\n <img src={imageUrl} alt={title} className=\"h-full w-full object-cover\" />\n </div>\n <div className=\"flex items-center justify-between gap-3 p-4\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-semibold\">{title}</p>\n {record && (\n <p className=\"text-xs text-muted-foreground tabular-nums\">\n {record.wins}W – {record.losses}L\n </p>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {(state === 'winner' || state === 'loser') && delta !== undefined && (\n <RatingDelta delta={state === 'winner' ? Math.abs(delta) : -Math.abs(delta)} />\n )}\n {rating !== undefined && (\n <span className=\"rounded-lg bg-muted px-2 py-1 text-sm font-bold tabular-nums\">{rating}</span>\n )}\n </div>\n </div>\n </Tag>\n );\n}\n","import { cn } from '@ntrs/core';\n\nexport interface RatingDeltaProps {\n delta: number;\n className?: string;\n}\n\nexport function RatingDelta({ delta, className }: RatingDeltaProps) {\n const sign = delta > 0 ? '+' : delta < 0 ? '−' : '±';\n const arrow = delta > 0 ? '▲' : delta < 0 ? '▼' : '';\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-semibold tabular-nums',\n delta > 0 && 'bg-success/15 text-success',\n delta < 0 && 'bg-destructive/15 text-destructive',\n delta === 0 && 'bg-muted text-muted-foreground',\n className,\n )}\n >\n {sign}\n {Math.abs(delta)} {arrow}\n </span>\n );\n}\n","'use client';\n\nimport { cn } from '@ntrs/core';\n\nexport interface VoteButtonProps {\n side: 'left' | 'right';\n disabled?: boolean;\n onVote?: () => void;\n className?: string;\n}\n\nexport function VoteButton({ side, disabled, onVote, className }: VoteButtonProps) {\n return (\n <button\n type=\"button\"\n disabled={disabled}\n onClick={onVote}\n aria-label={`Vote ${side}`}\n className={cn(\n 'inline-flex h-12 items-center gap-2 rounded-full bg-primary px-6 font-semibold text-primary-foreground',\n 'transition-transform hover:scale-105 active:scale-95',\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary',\n 'disabled:pointer-events-none disabled:opacity-40',\n className,\n )}\n >\n {side === 'left' && <span aria-hidden>←</span>}\n Vote\n {side === 'right' && <span aria-hidden>→</span>}\n </button>\n );\n}\n","'use client';\n\nimport { cn } from '@ntrs/core';\nimport { MemeCard, type MemeCardState } from '../meme-card';\n\nexport interface BattleMeme {\n id: string;\n title: string;\n imageUrl: string;\n rating: number;\n record?: { wins: number; losses: number };\n}\n\nexport interface BattleResult {\n winnerId: string;\n delta: number;\n}\n\nexport interface BattleStageProps {\n memeA: BattleMeme;\n memeB: BattleMeme;\n /** ready → cards clickable · voting → optimistic selection shown · revealed → winner/loser + deltas */\n phase: 'ready' | 'voting' | 'revealed';\n /** the meme the user clicked (voting phase) */\n selectedId?: string;\n result?: BattleResult;\n onVote?: (memeId: string) => void;\n className?: string;\n}\n\nfunction cardState(memeId: string, { phase, selectedId, result }: BattleStageProps): MemeCardState {\n if (phase === 'voting') return memeId === selectedId ? 'selected' : 'idle';\n if (phase === 'revealed' && result) return memeId === result.winnerId ? 'winner' : 'loser';\n return 'idle';\n}\n\nexport function BattleStage(props: BattleStageProps) {\n const { memeA, memeB, phase, result, onVote, className } = props;\n\n return (\n <div className={cn('grid grid-cols-1 items-center gap-6 md:grid-cols-[1fr_auto_1fr]', className)}>\n {[memeA, memeB].map((meme, i) => (\n <div key={meme.id} className={cn(i === 1 && 'md:order-3')}>\n <MemeCard\n title={meme.title}\n imageUrl={meme.imageUrl}\n rating={meme.rating}\n record={meme.record}\n state={cardState(meme.id, props)}\n delta={result?.delta}\n onClick={phase === 'ready' && onVote ? () => onVote(meme.id) : undefined}\n />\n </div>\n ))}\n <div className=\"justify-self-center md:order-2\" aria-hidden>\n <span\n className={cn(\n 'flex h-14 w-14 items-center justify-center rounded-full border-2 border-border bg-card text-lg font-black text-muted-foreground',\n phase === 'voting' && 'animate-pulse border-primary text-primary',\n )}\n >\n VS\n </span>\n </div>\n <p className=\"text-center text-sm text-muted-foreground md:order-4 md:col-span-3\">\n {phase === 'ready' && 'Click the funnier meme — or press ← / →'}\n {phase === 'voting' && 'Counting your vote…'}\n {phase === 'revealed' && result && `Ratings updated: ±${result.delta} points`}\n </p>\n </div>\n );\n}\n","import { cn } from '@ntrs/core';\n\nexport interface LeaderboardRow {\n rank: number;\n id: string;\n title: string;\n imageUrl: string;\n rating: number;\n wins: number;\n losses: number;\n uploader: string;\n}\n\nexport interface LeaderboardTableProps {\n rows: LeaderboardRow[];\n isLoading?: boolean;\n /** meme ids to highlight (e.g. the current user's uploads) */\n highlightIds?: string[];\n className?: string;\n}\n\nconst MEDALS: Record<number, string> = { 1: '🥇', 2: '🥈', 3: '🥉' };\n\nexport function LeaderboardTable({ rows, isLoading, highlightIds = [], className }: LeaderboardTableProps) {\n if (isLoading) {\n return (\n <div className={cn('space-y-2', className)} aria-busy=\"true\" aria-label=\"Loading leaderboard\">\n {Array.from({ length: 5 }).map((_, i) => (\n <div key={i} className=\"h-16 animate-pulse rounded-xl bg-muted\" />\n ))}\n </div>\n );\n }\n\n if (rows.length === 0) {\n return (\n <div className={cn('color-card rounded-xl border border-border p-12 text-center', className)}>\n <p className=\"text-4xl\">🦗</p>\n <p className=\"mt-3 font-semibold\">No champions yet</p>\n <p className=\"mt-1 text-sm text-muted-foreground\">Upload a meme and start voting to crown the first one.</p>\n </div>\n );\n }\n\n return (\n <div className={cn('color-card overflow-x-auto rounded-xl border border-border', className)}>\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border text-left text-xs uppercase tracking-wide text-muted-foreground\">\n <th scope=\"col\" className=\"px-4 py-3 w-14\">#</th>\n <th scope=\"col\" className=\"px-4 py-3\">Meme</th>\n <th scope=\"col\" className=\"px-4 py-3 text-right\">Record</th>\n <th scope=\"col\" className=\"px-4 py-3 text-right\">Rating</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const podium = row.rank <= 3;\n return (\n <tr\n key={row.id}\n className={cn(\n 'border-b border-border last:border-0 transition-colors hover:bg-muted/50',\n highlightIds.includes(row.id) && 'bg-primary/5',\n )}\n >\n <td className={cn('px-4 py-3 tabular-nums', podium ? 'text-lg' : 'text-muted-foreground')}>\n {MEDALS[row.rank] ?? row.rank}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <img src={row.imageUrl} alt=\"\" className=\"h-10 w-10 rounded-lg object-cover\" />\n <div className=\"min-w-0\">\n <p className={cn('truncate', podium ? 'font-bold' : 'font-medium')}>{row.title}</p>\n <p className=\"truncate text-xs text-muted-foreground\">by {row.uploader}</p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3 text-right tabular-nums text-muted-foreground\">\n <span className=\"text-success font-medium\">{row.wins}W</span>\n {' – '}\n <span>{row.losses}L</span>\n </td>\n <td className={cn('px-4 py-3 text-right tabular-nums font-bold', row.rank === 1 && 'text-gold')}>\n {row.rating}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n );\n}\n","'use client';\n\nimport { useRef } from 'react';\nimport { cn } from '@ntrs/core';\n\nexport interface UploadDropzoneProps {\n /** visual state — parent owns the actual file/drag logic */\n state?: 'idle' | 'dragover' | 'preview' | 'error';\n previewUrl?: string;\n error?: string;\n accept?: string;\n maxSizeMb?: number;\n onFile?: (file: File) => void;\n className?: string;\n}\n\nexport function UploadDropzone({\n state = 'idle',\n previewUrl,\n error,\n accept = 'image/jpeg,image/png,image/webp,image/gif',\n maxSizeMb = 5,\n onFile,\n className,\n}: UploadDropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null);\n\n return (\n <div className={className}>\n <button\n type=\"button\"\n onClick={() => inputRef.current?.click()}\n aria-label=\"Choose an image to upload\"\n className={cn(\n 'flex min-h-56 w-full flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-6 transition-colors',\n state === 'idle' && 'border-border bg-muted/30 hover:border-primary/60 hover:bg-muted/60',\n state === 'dragover' && 'border-primary bg-primary/5',\n state === 'preview' && 'border-border bg-card p-3',\n state === 'error' && 'border-destructive bg-destructive/5',\n )}\n >\n {state === 'preview' && previewUrl ? (\n <img src={previewUrl} alt=\"Upload preview\" className=\"max-h-72 w-full rounded-lg object-contain\" />\n ) : (\n <>\n <span className=\"text-4xl\" aria-hidden>\n {state === 'error' ? '🚫' : state === 'dragover' ? '📥' : '🖼️'}\n </span>\n <span className=\"font-medium\">\n {state === 'dragover' ? 'Drop it!' : 'Drag a meme here, or click to browse'}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n JPEG · PNG · WebP · GIF — up to {maxSizeMb} MB\n </span>\n </>\n )}\n </button>\n {state === 'error' && error && (\n <p role=\"alert\" className=\"mt-2 text-sm font-medium text-destructive\">\n {error}\n </p>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0];\n if (file) onFile?.(file);\n }}\n />\n </div>\n );\n}\n"],"mappings":"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","names":["cn","jsxs","cn","cn","jsx","jsxs","cn","jsx","jsxs","cn","cn","jsx","jsxs","cn","jsx","jsxs"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ntrs/meme",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "NTRS design system — Meme Battle Arena components",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": ["dist"],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@ntrs/core": "^0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": "^19.0.0",
|
|
23
|
+
"react-dom": "^19.0.0"
|
|
24
|
+
}
|
|
25
|
+
}
|