feat: Created shared TagList component with max-4 overflow, applied acr…
- "frontend/src/components/TagList.tsx" - "frontend/src/pages/Home.tsx" - "frontend/src/pages/SearchResults.tsx" - "frontend/src/pages/SubTopicPage.tsx" - "frontend/src/pages/CreatorDetail.tsx" - "frontend/src/pages/TopicsBrowse.tsx" - "frontend/src/App.css" GSD-Task: S02/T03
This commit is contained in:
parent
b01e5949b6
commit
fa1fc82d5a
8 changed files with 82 additions and 20 deletions
|
|
@ -1456,6 +1456,19 @@ a.app-footer__repo:hover {
|
|||
color: var(--color-pill-plugin-text);
|
||||
}
|
||||
|
||||
.pill--overflow {
|
||||
background: var(--color-surface-2);
|
||||
color: var(--color-text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pill--coming-soon {
|
||||
font-size: 0.65rem;
|
||||
background: var(--color-surface-2);
|
||||
color: var(--color-text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pill-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -2353,6 +2366,15 @@ a.app-footer__repo:hover {
|
|||
background: var(--color-bg-surface-hover);
|
||||
}
|
||||
|
||||
.topic-subtopic--empty {
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.topic-subtopic--empty:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.topic-subtopic + .topic-subtopic {
|
||||
border-top: 1px solid var(--color-bg-surface-hover);
|
||||
}
|
||||
|
|
|
|||
33
frontend/src/components/TagList.tsx
Normal file
33
frontend/src/components/TagList.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Shared tag list with overflow indicator.
|
||||
*
|
||||
* Renders up to `max` tag pills (default 4), plus a "+N more" pill
|
||||
* when the list exceeds the limit. Used across cards and detail pages
|
||||
* to keep tag displays compact and consistent (R027).
|
||||
*/
|
||||
|
||||
interface TagListProps {
|
||||
tags: string[];
|
||||
max?: number;
|
||||
/** Extra CSS class added to each pill (e.g. "pill--tag") */
|
||||
pillClass?: string;
|
||||
}
|
||||
|
||||
export default function TagList({ tags, max = 4, pillClass }: TagListProps) {
|
||||
const visible = tags.slice(0, max);
|
||||
const overflow = tags.length - max;
|
||||
const cls = pillClass ? `pill ${pillClass}` : "pill";
|
||||
|
||||
return (
|
||||
<>
|
||||
{visible.map((tag) => (
|
||||
<span key={tag} className={cls}>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
{overflow > 0 && (
|
||||
<span className="pill pill--overflow">+{overflow} more</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from "../api/public-client";
|
||||
import CreatorAvatar from "../components/CreatorAvatar";
|
||||
import { catSlug } from "../utils/catSlug";
|
||||
import TagList from "../components/TagList";
|
||||
|
||||
export default function CreatorDetail() {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
|
|
@ -156,11 +157,7 @@ export default function CreatorDetail() {
|
|||
</span>
|
||||
{t.topic_tags && t.topic_tags.length > 0 && (
|
||||
<span className="creator-technique-card__tags">
|
||||
{t.topic_tags.map((tag) => (
|
||||
<span key={tag} className="pill">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
<TagList tags={t.topic_tags} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { IconTopics, IconCreators } from "../components/CategoryIcons";
|
||||
import SearchAutocomplete from "../components/SearchAutocomplete";
|
||||
import TagList from "../components/TagList";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
|
|
@ -198,9 +199,9 @@ export default function Home() {
|
|||
{featured.topic_category && (
|
||||
<span className="badge badge--category">{featured.topic_category}</span>
|
||||
)}
|
||||
{featured.topic_tags && featured.topic_tags.length > 0 && featured.topic_tags.map(tag => (
|
||||
<span key={tag} className="pill pill--tag">{tag}</span>
|
||||
))}
|
||||
{featured.topic_tags && featured.topic_tags.length > 0 && (
|
||||
<TagList tags={featured.topic_tags} pillClass="pill--tag" />
|
||||
)}
|
||||
{featured.key_moment_count > 0 && (
|
||||
<span className="home-featured__moments">
|
||||
{featured.key_moment_count} moment{featured.key_moment_count !== 1 ? "s" : ""}
|
||||
|
|
@ -244,9 +245,9 @@ export default function Home() {
|
|||
<span className="badge badge--category">
|
||||
{t.topic_category}
|
||||
</span>
|
||||
{t.topic_tags && t.topic_tags.length > 0 && t.topic_tags.map(tag => (
|
||||
<span key={tag} className="pill pill--tag">{tag}</span>
|
||||
))}
|
||||
{t.topic_tags && t.topic_tags.length > 0 && (
|
||||
<TagList tags={t.topic_tags} pillClass="pill--tag" />
|
||||
)}
|
||||
{t.summary && (
|
||||
<span className="recent-card__summary">
|
||||
{t.summary.length > 150
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { Link, useSearchParams, useNavigate } from "react-router-dom";
|
|||
import { searchApi, type SearchResultItem } from "../api/public-client";
|
||||
import { catSlug } from "../utils/catSlug";
|
||||
import SearchAutocomplete from "../components/SearchAutocomplete";
|
||||
import TagList from "../components/TagList";
|
||||
|
||||
export default function SearchResults() {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
|
@ -142,11 +143,7 @@ function SearchResultCard({ item, staggerIndex }: { item: SearchResultItem; stag
|
|||
)}
|
||||
{item.topic_tags.length > 0 && (
|
||||
<span className="search-result-card__tags">
|
||||
{item.topic_tags.map((tag) => (
|
||||
<span key={tag} className="pill">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
<TagList tags={item.topic_tags} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
type TechniqueListItem,
|
||||
} from "../api/public-client";
|
||||
import { catSlug } from "../utils/catSlug";
|
||||
import TagList from "../components/TagList";
|
||||
|
||||
/** Convert a URL slug to a display name: replace hyphens with spaces, title-case. */
|
||||
function slugToDisplayName(slug: string): string {
|
||||
|
|
@ -146,9 +147,7 @@ export default function SubTopicPage() {
|
|||
<span className="subtopic-technique-card__title">{t.title}</span>
|
||||
{t.topic_tags && t.topic_tags.length > 0 && (
|
||||
<span className="subtopic-technique-card__tags">
|
||||
{t.topic_tags.map((tag) => (
|
||||
<span key={tag} className="pill">{tag}</span>
|
||||
))}
|
||||
<TagList tags={t.topic_tags} />
|
||||
</span>
|
||||
)}
|
||||
{t.summary && (
|
||||
|
|
|
|||
|
|
@ -155,6 +155,19 @@ export default function TopicsBrowse() {
|
|||
<div className="topic-subtopics">
|
||||
{cat.sub_topics.map((st) => {
|
||||
const stSlug = st.name.toLowerCase().replace(/\s+/g, "-");
|
||||
if (st.technique_count === 0) {
|
||||
return (
|
||||
<span
|
||||
key={st.name}
|
||||
className="topic-subtopic topic-subtopic--empty"
|
||||
>
|
||||
<span className="topic-subtopic__name">{st.name}</span>
|
||||
<span className="topic-subtopic__counts">
|
||||
<span className="pill pill--coming-soon">Coming soon</span>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
key={st.name}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/public-client.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/CategoryIcons.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/pages/About.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/utils/catSlug.ts"],"version":"5.6.3"}
|
||||
{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/public-client.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/CategoryIcons.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/TagList.tsx","./src/pages/About.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/utils/catSlug.ts"],"version":"5.6.3"}
|
||||
Loading…
Add table
Reference in a new issue