From cc89beca31b554ec5e216f68b4cac0d1d7548235 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Wed, 24 Jun 2026 14:34:05 +0200 Subject: [PATCH] fix: workshop UI/UX polish and custom widget table layout Workshop: fix vote color semantics (primary accent for active state, contextual score colors), conditional badge positioning, inline delete confirm, shorter sign-in CTA, comment count on cards, visual anchor for imageless cards, always-visible gallery nav, footer density polish, Mantine bg prop for type dot, ghost button back-link. Custom widgets table: fixed layout with truncation, responsive columns that hide URL/display on smaller screens to keep name visible. --- apps/docs/src/components/store/DetailPage.tsx | 65 ++++++---- apps/docs/src/components/store/StoreApp.tsx | 113 ++++++++++++------ .../custom-widgets/_custom-widget-list.tsx | 18 +-- .../store-install/store-browser-modal.tsx | 3 +- 4 files changed, 134 insertions(+), 65 deletions(-) diff --git a/apps/docs/src/components/store/DetailPage.tsx b/apps/docs/src/components/store/DetailPage.tsx index 0801d8fb1..018c4b047 100644 --- a/apps/docs/src/components/store/DetailPage.tsx +++ b/apps/docs/src/components/store/DetailPage.tsx @@ -39,7 +39,7 @@ const copyState = [ { Icon: IconCopy, label: "Copy", iconClass: "" }, { Icon: IconCheck, label: "Copied!", iconClass: "text-green-500" }, ] as const; -const scoreClassBySign = { positive: "text-destructive", negative: "text-destructive", neutral: "" } as const; +const scoreClassBySign = { positive: "text-foreground", negative: "text-destructive", neutral: "" } as const; const dotClass = ["bg-white/40", "bg-white"]; const commentAuthorName = (c: StoreComment) => c.expand?.author?.name || c.expand?.author?.username || "unknown"; @@ -108,14 +108,14 @@ const ScreenshotGallery = ({ urls, title }: { urls: string[]; title: string }) = {urls.length > 1 && ( <> + ); +}; + const MarketplaceDetail = ({ storeUrl }: { storeUrl: string }) => { const location = useLocation(); const submissionId = parseSubmissionId(location.pathname); @@ -492,9 +520,11 @@ const MarketplaceDetail = ({ storeUrl }: { storeUrl: string }) => {

Submission not found

This listing may have been removed or the link is invalid.

- - Back to Workshop - +
); } @@ -504,9 +534,11 @@ const MarketplaceDetail = ({ storeUrl }: { storeUrl: string }) => { return (
- - Back to Workshop - + {error && (
@@ -540,7 +572,7 @@ const MarketplaceDetail = ({ storeUrl }: { storeUrl: string }) => { aria-label="Upvote" className={cn( "flex items-center justify-center rounded-[5px] p-1.5 transition-colors hover:bg-accent", - userVote?.value === 1 && "bg-destructive/15 text-destructive", + userVote?.value === 1 && "bg-primary/15 text-primary", )} > @@ -553,7 +585,7 @@ const MarketplaceDetail = ({ storeUrl }: { storeUrl: string }) => { aria-label="Downvote" className={cn( "flex items-center justify-center rounded-[5px] p-1.5 transition-colors hover:bg-accent", - userVote?.value === -1 && "bg-destructive/15 text-destructive", + userVote?.value === -1 && "bg-primary/15 text-primary", )} > @@ -583,16 +615,7 @@ const MarketplaceDetail = ({ storeUrl }: { storeUrl: string }) => { )} {user?.id === submission.author && ( - + )}
diff --git a/apps/docs/src/components/store/StoreApp.tsx b/apps/docs/src/components/store/StoreApp.tsx index 31f8a0185..fe119d4b8 100644 --- a/apps/docs/src/components/store/StoreApp.tsx +++ b/apps/docs/src/components/store/StoreApp.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import { IconArrowBigDown, IconArrowBigUp, + IconBrandCss3, IconBrandGithub, IconCheck, IconChevronLeft, @@ -10,8 +11,10 @@ import { IconDownload, IconFlag, IconLogout, + IconMessage, IconPackage, IconPlus, + IconPuzzle, IconSearch, IconTrash, IconX, @@ -39,6 +42,8 @@ import { useStore } from "./useStore"; const typeDotColors: Record = { css: "bg-blue-500", widget: "bg-yellow-500" }; const typeLabels: Record = { css: "CSS", widget: "Widget" }; +const typeIcons: Record> = { css: IconBrandCss3, widget: IconPuzzle }; +const typeBgColors: Record = { css: "bg-blue-500/5", widget: "bg-yellow-500/5" }; const filterActiveClass = "bg-background text-foreground shadow-sm"; const filterInactiveClass = "text-muted-foreground hover:text-foreground"; const copyState = [ @@ -127,9 +132,12 @@ export const StoreApp = ({ storeUrl }: { storeUrl: string }) => { ) : ( - +
+ +

Vote and share your creations

+
)}
@@ -250,6 +258,8 @@ const SubmissionCard = ({ }: SubmissionCardProps) => { const isOwner = currentUserId === submission.author; const [copied, setCopied] = useState(false); + const [confirmDelete, setConfirmDelete] = useState(false); + const confirmTimer = useRef>(null); const copyTimer = useRef>(null); const score = submission.upvotes - submission.downvotes; const { Icon: CopyIcon, label: copyLabel, iconClass: copyIconClass } = copyState[Number(copied)]; @@ -261,6 +271,7 @@ const SubmissionCard = ({ useEffect( () => () => { if (copyTimer.current) clearTimeout(copyTimer.current); + if (confirmTimer.current) clearTimeout(confirmTimer.current); }, [], ); @@ -279,29 +290,53 @@ const SubmissionCard = ({ } }; - const handleReport = () => { - const reason = window.prompt("Why are you reporting this submission?"); - if (reason && reason.trim().length >= 3) void onReport(submission.id, reason.trim()); + const handleDelete = () => { + if (!confirmDelete) { + setConfirmDelete(true); + if (confirmTimer.current) clearTimeout(confirmTimer.current); + confirmTimer.current = setTimeout(() => setConfirmDelete(false), 3000); + return; + } + setConfirmDelete(false); + void onDelete(submission.id); }; + const hasScreenshots = screenshotUrls.length > 0; + const TypeIcon = typeIcons[submission.type]; + return ( - - - {typeLabels[submission.type]} - - - {screenshotUrls.length > 0 && } + {hasScreenshots ? ( +
+ + + {typeLabels[submission.type]} + + +
+ ) : ( +
+ +
+ )}
- - {submission.title} - +
+ + {submission.title} + + {!hasScreenshots && ( + + + {typeLabels[submission.type]} + + )} +
{submission.authorName} · v{submission.version} · {relativeTime(submission.created)} @@ -312,7 +347,7 @@ const SubmissionCard = ({ aria-label="Upvote" className={cn( "flex items-center justify-center rounded-[5px] p-1 transition-colors hover:bg-accent", - userVote === 1 && "bg-destructive/15 text-destructive", + userVote === 1 && "bg-primary/15 text-primary", )} > @@ -320,7 +355,7 @@ const SubmissionCard = ({ 0 && "text-destructive", + score > 0 && "text-foreground", score < 0 && "text-destructive", )} > @@ -331,7 +366,7 @@ const SubmissionCard = ({ aria-label="Downvote" className={cn( "flex items-center justify-center rounded-[5px] p-1 transition-colors hover:bg-accent", - userVote === -1 && "bg-destructive/15 text-destructive", + userVote === -1 && "bg-primary/15 text-primary", )} > @@ -356,11 +391,11 @@ const SubmissionCard = ({ > {copyFailed ? ( <> - Failed + Failed ) : ( <> - {copyLabel} + {copyLabel} )} @@ -371,27 +406,37 @@ const SubmissionCard = ({ onClick={() => downloadJson(submission)} className="text-muted-foreground hover:text-foreground" > - Download + Download )} + {submission.commentCount > 0 && ( + + {submission.commentCount} + + )}
{isOwner && ( )}
@@ -443,14 +488,14 @@ const ScreenshotGallery = ({ urls, title }: { urls: string[]; title: string }) = {urls.length > 1 && ( <>