mirror of
https://github.com/collinbarrett/FilterLists.git
synced 2026-03-11 09:04:27 +00:00
refactor(web): wrap Licenses in Badge
This commit is contained in:
parent
4de9282310
commit
cd4aa3b59c
12 changed files with 119 additions and 72 deletions
62
web/src/components/badge-cloud.tsx
Normal file
62
web/src/components/badge-cloud.tsx
Normal file
|
|
@ -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 = <Badge variant="secondary">{item.value}</Badge>;
|
||||
|
||||
return item.href ? (
|
||||
<a
|
||||
href={item.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label={item.tooltip ?? `View ${item.value}'s homepage`}
|
||||
>
|
||||
{badge}
|
||||
</a>
|
||||
) : (
|
||||
badge
|
||||
);
|
||||
};
|
||||
|
||||
export function BadgeCloud({ items }: BadgeCloudProps) {
|
||||
const hasTooltips = items.some((item) => item.tooltip || item.href);
|
||||
|
||||
const badges = items.map((item) =>
|
||||
item.tooltip || item.href ? (
|
||||
<Tooltip key={item.key}>
|
||||
<TooltipTrigger>
|
||||
<BadgeElement item={item} />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{item.tooltip ?? `View ${item.value}'s homepage`}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<BadgeElement key={item.key} item={item} />
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{hasTooltips ? <TooltipProvider>{badges}</TooltipProvider> : badges}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -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<FilterList>();
|
||||
|
||||
|
|
@ -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 <LanguageBadges languages={languages} />;
|
||||
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 <BadgeCloud items={languages} />;
|
||||
},
|
||||
}),
|
||||
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 ? (
|
||||
<BadgeCloud
|
||||
items={[
|
||||
{
|
||||
key: license.id,
|
||||
value: license.name,
|
||||
href: license.url ?? undefined,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null;
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -28,19 +28,19 @@ import { Syntax } from "@/services/get-syntaxes";
|
|||
import { Tag } from "@/services/get-tags";
|
||||
|
||||
export interface FilterListsTableMeta extends TableMeta<FilterList> {
|
||||
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({
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
<TooltipProvider>
|
||||
{languages.map((language) => (
|
||||
<Tooltip key={language.id}>
|
||||
<TooltipTrigger>
|
||||
<Badge variant="secondary">{language.iso6391}</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{language.name}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
))}
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<FilterList[]> {
|
||||
|
|
|
|||
|
|
@ -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<Language[]> {
|
||||
|
|
|
|||
|
|
@ -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<License[]> {
|
||||
|
|
|
|||
|
|
@ -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<Maintainer[]> {
|
||||
|
|
|
|||
|
|
@ -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<Software[]> {
|
||||
|
|
|
|||
|
|
@ -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<Syntax[]> {
|
||||
|
|
|
|||
|
|
@ -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<Tag[]> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue