diff --git a/web/src/components/badge-cloud.tsx b/web/src/components/badge-cloud.tsx new file mode 100644 index 000000000..566071a63 --- /dev/null +++ b/web/src/components/badge-cloud.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +interface BadgeItem { + key: number; + value: string; + tooltip?: string; + href?: string; +} + +interface BadgeCloudProps { + items: readonly BadgeItem[]; +} + +const BadgeElement = ({ item }: { item: BadgeItem }) => { + const badge = {item.value}; + + return item.href ? ( + + {badge} + + ) : ( + badge + ); +}; + +export function BadgeCloud({ items }: BadgeCloudProps) { + const hasTooltips = items.some((item) => item.tooltip || item.href); + + const badges = items.map((item) => + item.tooltip || item.href ? ( + + + + + +

{item.tooltip ?? `View ${item.value}'s homepage`}

+
+
+ ) : ( + + ), + ); + + return ( +
+ {hasTooltips ? {badges} : badges} +
+ ); +} diff --git a/web/src/components/filterlist-table/columns.tsx b/web/src/components/filterlist-table/columns.tsx index 7fb61d2b0..472e28e72 100644 --- a/web/src/components/filterlist-table/columns.tsx +++ b/web/src/components/filterlist-table/columns.tsx @@ -1,9 +1,9 @@ "use client"; -import { FilterList } from "@/services/get-filterlists"; import { createColumnHelper } from "@tanstack/react-table"; import { FilterListsTableMeta } from "."; -import { LanguageBadges } from "./language-badges"; +import { BadgeCloud } from "@/components/badge-cloud"; +import { FilterList } from "@/services/get-filterlists"; const columnHelper = createColumnHelper(); @@ -19,11 +19,19 @@ export const columns = [ cell: (info) => { const meta = info.table.options.meta as FilterListsTableMeta; const languages = - info - .getValue() - ?.flatMap((id) => meta.languages.find((l) => l.id === id) || []) ?? - []; - return ; + info.getValue()?.flatMap((id) => { + const language = meta.languages.find((l) => l.id === id); + return language + ? [ + { + key: language.id, + value: language.iso6391, + tooltip: language.name, + }, + ] + : []; + }) ?? []; + return ; }, }), columnHelper.accessor((row) => row.licenseId, { @@ -31,7 +39,17 @@ export const columns = [ cell: (info) => { const meta = info.table.options.meta as FilterListsTableMeta; const license = meta.licenses.find((l) => l.id === info.getValue()); - return license?.name; + return license ? ( + + ) : null; }, }), ]; diff --git a/web/src/components/filterlist-table/index.tsx b/web/src/components/filterlist-table/index.tsx index 3faca8c3c..d10b9ee6c 100644 --- a/web/src/components/filterlist-table/index.tsx +++ b/web/src/components/filterlist-table/index.tsx @@ -28,19 +28,19 @@ import { Syntax } from "@/services/get-syntaxes"; import { Tag } from "@/services/get-tags"; export interface FilterListsTableMeta extends TableMeta { - languages: Language[]; - licenses: License[]; + languages: readonly Language[]; + licenses: readonly License[]; } interface FilterListTableProps { columns: typeof columns; initialFilterLists: FilterList[]; - languages: Language[]; - licenses: License[]; - maintainers: Maintainer[]; - software: Software[]; - syntaxes: Syntax[]; - tags: Tag[]; + languages: readonly Language[]; + licenses: readonly License[]; + maintainers: readonly Maintainer[]; + software: readonly Software[]; + syntaxes: readonly Syntax[]; + tags: readonly Tag[]; } export function FilterListTable({ diff --git a/web/src/components/filterlist-table/language-badges.tsx b/web/src/components/filterlist-table/language-badges.tsx deleted file mode 100644 index 93d1c72cc..000000000 --- a/web/src/components/filterlist-table/language-badges.tsx +++ /dev/null @@ -1,33 +0,0 @@ -"use client"; - -import { Badge } from "@/components/ui/badge"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { Language } from "@/services/get-languages"; - -interface LanguageBadgesProps { - languages: Language[]; -} - -export function LanguageBadges({ languages }: LanguageBadgesProps) { - return ( -
- - {languages.map((language) => ( - - - {language.iso6391} - - -

{language.name}

-
-
- ))} -
-
- ); -} diff --git a/web/src/services/get-filterlist-details.ts b/web/src/services/get-filterlist-details.ts index b5795a1b0..32dc7b5bf 100644 --- a/web/src/services/get-filterlist-details.ts +++ b/web/src/services/get-filterlist-details.ts @@ -11,10 +11,10 @@ export type FilterListDetails = { name: string; description: string | null; licenseId: number; - syntaxIds: number[] | null; - languageIds: number[] | null; - tagIds: number[] | null; - viewUrls: ViewUrl[] | null; + syntaxIds: readonly number[] | null; + languageIds: readonly number[] | null; + tagIds: readonly number[] | null; + viewUrls: readonly ViewUrl[] | null; homeUrl: string | null; onionUrl: string | null; policyUrl: string | null; @@ -24,13 +24,13 @@ export type FilterListDetails = { chatUrl: string | null; emailAddress: string | null; donateUrl: string | null; - maintainerIds: number[] | null; - upstreamFilterListIds: number[] | null; - forkFilterListIds: number[] | null; - includedInFilterListIds: number[] | null; - includesFilterListIds: number[] | null; - dependencyFilterListIds: number[] | null; - dependentFilterListIds: number[] | null; + maintainerIds: readonly number[] | null; + upstreamFilterListIds: readonly number[] | null; + forkFilterListIds: readonly number[] | null; + includedInFilterListIds: readonly number[] | null; + includesFilterListIds: readonly number[] | null; + dependencyFilterListIds: readonly number[] | null; + dependentFilterListIds: readonly number[] | null; }; export async function getFilterListDetails( diff --git a/web/src/services/get-filterlists.ts b/web/src/services/get-filterlists.ts index e98fc9bbd..27dbf0f62 100644 --- a/web/src/services/get-filterlists.ts +++ b/web/src/services/get-filterlists.ts @@ -5,11 +5,11 @@ export type FilterList = { name: string; description: string | null; licenseId: number; - syntaxIds: number[]; - languageIds: number[]; - tagIds: number[]; + syntaxIds: readonly number[]; + languageIds: readonly number[]; + tagIds: readonly number[]; primaryViewUrl: string | null; - maintainerIds: number[]; + maintainerIds: readonly number[]; }; export async function getFilterLists(): Promise { diff --git a/web/src/services/get-languages.ts b/web/src/services/get-languages.ts index 25a6d2a6f..b3d570027 100644 --- a/web/src/services/get-languages.ts +++ b/web/src/services/get-languages.ts @@ -4,7 +4,7 @@ export type Language = { id: number; iso6391: string; name: string; - filterListIds: number[]; // TODO: are these needed, or can we remove them from the API? + filterListIds: readonly number[]; // TODO: are these needed, or can we remove them from the API? }; export async function getLanguages(): Promise { diff --git a/web/src/services/get-licenses.ts b/web/src/services/get-licenses.ts index 765d12cab..bb91df2e4 100644 --- a/web/src/services/get-licenses.ts +++ b/web/src/services/get-licenses.ts @@ -2,12 +2,12 @@ import { API_BASE_URL, DEFAULT_REVALIDATE_SECS } from "./constants"; export type License = { id: number; - name: string | null; + name: string; url: string | null; permitsModification: boolean; permitsDistribution: boolean; permitsCommercialUse: boolean; - filterListIds: number[] | null; + filterListIds: readonly number[] | null; // TODO: are these needed, or can we remove them from the API? }; export async function getLicenses(): Promise { diff --git a/web/src/services/get-maintainers.ts b/web/src/services/get-maintainers.ts index 2b392d44b..3a6fe3bbf 100644 --- a/web/src/services/get-maintainers.ts +++ b/web/src/services/get-maintainers.ts @@ -6,7 +6,7 @@ export type Maintainer = { url: string | null; emailAddress: string | null; twitterHandle: string | null; - filterListIds: number[] | null; + filterListIds: readonly number[] | null; }; export async function getMaintainers(): Promise { diff --git a/web/src/services/get-software.ts b/web/src/services/get-software.ts index ce21b7e9c..9cdb775c1 100644 --- a/web/src/services/get-software.ts +++ b/web/src/services/get-software.ts @@ -7,7 +7,7 @@ export type Software = { homeUrl: string | null; downloadUrl: string | null; supportsAbpUrlScheme: boolean; - syntaxIds: number[] | null; + syntaxIds: readonly number[] | null; }; export async function getSoftware(): Promise { diff --git a/web/src/services/get-syntaxes.ts b/web/src/services/get-syntaxes.ts index 35d8b5a39..8daaa558b 100644 --- a/web/src/services/get-syntaxes.ts +++ b/web/src/services/get-syntaxes.ts @@ -5,8 +5,8 @@ export type Syntax = { name: string | null; description: string | null; url: string | null; - filterListIds: number[] | null; - softwareIds: number[] | null; + filterListIds: readonly number[] | null; + softwareIds: readonly number[] | null; }; export async function getSyntaxes(): Promise { diff --git a/web/src/services/get-tags.ts b/web/src/services/get-tags.ts index a2be5d5da..e8a17348f 100644 --- a/web/src/services/get-tags.ts +++ b/web/src/services/get-tags.ts @@ -4,7 +4,7 @@ export type Tag = { id: number; name: string | null; description: string | null; - filterListIds: number[] | null; + filterListIds: readonly number[] | null; }; export async function getTags(): Promise {