mhsf-dev/src/components/ServerList.tsx

833 lines
29 KiB
TypeScript
Raw Normal View History

2024-07-23 18:49:21 -05:00
"use client";
import { useState } from "react";
2024-07-23 18:49:21 -05:00
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { Badge } from "./ui/badge";
import ServersList from "@/lib/list";
import { CircleUser, Network, Sun, Check, XIcon, Info } from "lucide-react";
2024-07-23 18:49:21 -05:00
import Stat from "./Stat";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import toast from "react-hot-toast";
import { allTags, allCategories } from "@/allTags";
import IconDisplay from "./IconDisplay";
import InfiniteScroll from "react-infinite-scroll-component";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Checkbox } from "@/components/ui/checkbox";
import { Spinner } from "./ui/spinner";
2024-08-03 09:51:45 -05:00
import { CommandIcon } from "lucide-react";
import { OnlineServer } from "@/lib/types/mh-server";
2024-07-23 18:49:21 -05:00
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { useEffectOnce } from "@/lib/useEffectOnce";
import ServerCard from "./ServerCard";
2024-08-03 09:51:45 -05:00
import events from "@/lib/commandEvent";
import { BorderBeam } from "@/components/effects/border-beam";
2024-08-15 23:24:15 -05:00
import { Label } from "./ui/label";
import { useRouter } from "@/lib/useRouter";
2024-07-23 18:49:21 -05:00
export default function ServerList() {
const [loading, setLoading]: any = useState(true);
const [randomText, setRandomText] = useState("");
const [motdList, setMotdList] = useState<any>({});
const allText = [""];
const getRandomText = () => {
return allText[Math.floor(Math.random() * allText.length)];
};
const [templateFilter, setTemplateFilter] = useState(false);
const [random, setRandom] = useState(false);
const [serverList, setServerList] = useState(new ServersList([]));
const [textCopied, setTextCopied] = useState(false);
const bigger = async (server: OnlineServer) =>
server.playerData.playerCount > 15;
const smaller = async (server: OnlineServer) =>
!server.staticInfo.alwaysOnline &&
server.playerData.playerCount < 15 &&
server.playerData.playerCount > 7;
const [nameFilters, setNameFilters] = useState<any>({});
const [inErrState, setErrState] = useState(false);
const [servers, setServers] = useState<Array<OnlineServer>>([]);
2024-08-15 23:24:15 -05:00
const router = useRouter();
2024-07-23 18:49:21 -05:00
const [filters, setFilters] = useState<
Array<(server: OnlineServer) => Promise<boolean>>
>([]);
const [randomData, setRandomData] = useState<OnlineServer | undefined>(
2024-08-03 09:51:45 -05:00
undefined
2024-07-23 18:49:21 -05:00
);
useEffectOnce(() => {
setRandomText(getRandomText());
serverList
.fetchDataAndFilter()
.then(() => {
serverList.moveListDown();
let stringList: Array<{ server: string; motd: string }> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
setLoading(false);
});
})
.catch(() => setErrState(true));
});
if (inErrState) {
return (
<>
<div className="flex justify-center">
<XIcon />
<br />
</div>
<div className="flex justify-center">
Hmm. Something is wrong. Reload the page.
</div>
</>
);
}
if (loading) {
return (
<>
2024-08-08 18:02:46 -05:00
<Spinner className="flex items-center" />
2024-07-23 18:49:21 -05:00
<br />
<div
2024-08-08 18:02:46 -05:00
className="flex justify-center"
2024-07-23 18:49:21 -05:00
dangerouslySetInnerHTML={{ __html: randomText }}
></div>
</>
);
}
return (
<>
2024-08-08 18:02:46 -05:00
<div className="max-lg:grid-cols-2 grid grid-cols-3 gap-4 ">
2024-07-23 18:49:21 -05:00
<Stat
title="Players online"
desc={serverList.getExtraData().total_players.toString()}
icon={CircleUser}
2024-08-15 23:24:15 -05:00
/>
2024-07-23 18:49:21 -05:00
<Stat
2024-08-07 16:37:54 -05:00
title={
<div
className={
serverList.getExtraData().total_servers >= 3200
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500"
: ""
}
>
Servers online{" "}
</div>
}
className="relative z-0"
desc={
<div className="flex items-center">
<div
className={
serverList.getExtraData().total_servers >= 3200
? "bg-clip-text text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 "
: ""
}
>
{serverList.getExtraData().total_servers.toString()}
</div>
{serverList.getExtraData().total_servers >= 3200 && (
<Tooltip>
<TooltipTrigger>
<Info size={16} className="ml-2" />
</TooltipTrigger>
<TooltipContent className="font-normal">
The server amount is over 3.2k, meaning that new servers
have to go into a queue before being able to be online.{" "}
<br />
(the server count isn't entirely accurate, so sometimes you
might not go into a queue even when the server count is at
3.2k)
</TooltipContent>
</Tooltip>
)}
</div>
}
2024-07-23 18:49:21 -05:00
icon={Network}
2024-08-07 16:37:54 -05:00
>
{serverList.getExtraData().total_servers >= 3200 && (
<BorderBeam
size={135}
duration={12}
delay={9}
colorFrom="rgb(6 182 212)"
colorTo="rgb(59 130 246)"
/>
)}
</Stat>
2024-07-23 18:49:21 -05:00
<Stat
title="Current most popular server (in filter)"
2024-08-08 18:02:46 -05:00
className="max-lg:col-span-2"
2024-07-23 18:49:21 -05:00
desc={
<>
{serverList.currentServers[0] != undefined
? serverList.currentServers[0].name
: "None"}{" "}
{serverList.currentServers[0] != undefined && (
<IconDisplay server={serverList.currentServers[0]} />
)}
</>
}
icon={Sun}
/>
</div>
<br />
<Separator />
2024-08-08 18:02:46 -05:00
<div className="mt-3 ml-3">
2024-07-23 18:49:21 -05:00
<Button
2024-08-03 09:51:45 -05:00
onClick={() => events.emit("search-request-event")}
2024-07-23 18:49:21 -05:00
variant="secondary"
2024-08-08 18:02:46 -05:00
className="max-lg:mb-3"
2024-07-23 18:49:21 -05:00
>
2024-08-03 09:51:45 -05:00
Search{" "}
<code className="ml-2 flex items-center">
2024-08-07 16:37:54 -05:00
<CommandIcon size={14} />
2024-08-03 09:51:45 -05:00
+Shift+K
</code>
2024-07-23 18:49:21 -05:00
</Button>
<Popover>
<PopoverTrigger>
2024-08-08 18:02:46 -05:00
<Button className="ml-3" variant="secondary">
2024-07-23 18:49:21 -05:00
Filter
<code className="ml-2">{filters.length}</code>
</Button>
</PopoverTrigger>
2024-08-08 18:02:46 -05:00
<PopoverContent className="w-[390px] z-3">
2024-07-23 18:49:21 -05:00
<RadioGroup
onValueChange={(v) => {
toast.promise(
new Promise((g, b) => {
if (v == "smaller") {
setTemplateFilter(true);
var filt = nameFilters;
filt["smaller-tf"] = true;
filt["bigger-tf"] = false;
setNameFilters(filt);
var filt2 = filters;
filt2.push(smaller);
if (filt2.includes(bigger)) {
filt2.splice(filt2.indexOf(bigger), 1);
}
setFilters(filt2);
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
g(undefined);
});
});
} else if (v == "bigger") {
setTemplateFilter(true);
var filt = nameFilters;
filt["smaller-tf"] = false;
filt["bigger-tf"] = true;
setNameFilters(filt);
var filt2 = filters;
filt2.push(bigger);
filt2.splice(filt2.indexOf(smaller), 1);
setFilters(filt2);
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
g(undefined);
});
});
} else {
var filt = nameFilters;
filt["smaller-tf"] = false;
filt["bigger-tf"] = false;
setNameFilters(filt);
setTemplateFilter(false);
var filt2 = filters;
filt2.splice(filt2.indexOf(smaller), 1);
filt2.splice(filt2.indexOf(bigger), 1);
setFilters(filt2);
console.log(filters, filters.includes(smaller));
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
g(undefined);
});
});
}
}),
{
error: "Error while changing filters",
loading: "Changing filters...",
success: "Changed filters!",
2024-08-03 09:51:45 -05:00
}
2024-07-23 18:49:21 -05:00
);
}}
defaultValue={(() => {
if (nameFilters["smaller-tf"]) {
return "smaller";
} else if (nameFilters["bigger-tf"]) {
return "bigger";
} else {
return "none";
}
})()}
>
2024-08-08 18:02:46 -05:00
<div className="items-top flex space-x-2">
2024-07-23 18:49:21 -05:00
<RadioGroupItem id="smaller" value="smaller" />
2024-08-08 18:02:46 -05:00
<div className="grid gap-1.5 leading-none">
2024-07-23 18:49:21 -05:00
<label
htmlFor="smaller"
2024-08-08 18:02:46 -05:00
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
2024-07-23 18:49:21 -05:00
>
Only allow smaller servers
</label>
2024-08-08 18:02:46 -05:00
<p className="text-sm text-muted-foreground">
2024-07-23 18:49:21 -05:00
Server have 15-7 players, cannot be{" "}
<Badge variant="secondary">Always Online</Badge>
</p>
</div>
</div>
2024-08-08 18:02:46 -05:00
<div className="items-top flex space-x-2">
2024-07-23 18:49:21 -05:00
<RadioGroupItem id="bigger" value="bigger" />
2024-08-08 18:02:46 -05:00
<div className="grid gap-1.5 leading-none">
2024-07-23 18:49:21 -05:00
<label
htmlFor="bigger"
2024-08-08 18:02:46 -05:00
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
2024-07-23 18:49:21 -05:00
>
Only allow bigger servers
</label>
2024-08-08 18:02:46 -05:00
<p className="text-sm text-muted-foreground">
2024-07-23 18:49:21 -05:00
Server has 16 players or more
</p>
</div>
</div>
2024-08-08 18:02:46 -05:00
<div className="items-top flex space-x-2">
2024-07-23 18:49:21 -05:00
<RadioGroupItem id="none" value="none" />
2024-08-08 18:02:46 -05:00
<div className="grid gap-1.5 leading-none">
2024-07-23 18:49:21 -05:00
<label
htmlFor="none"
2024-08-08 18:02:46 -05:00
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
2024-07-23 18:49:21 -05:00
>
Custom/no requirements
</label>
</div>
</div>
</RadioGroup>
<br />
<br />
2024-08-08 18:02:46 -05:00
<strong className="pb-2">Tags</strong>
2024-07-23 18:49:21 -05:00
<br />
{allTags.map((tag) => (
<div key={tag.docsName}>
{tag.docsName && tag.__filter == undefined && (
2024-08-08 18:02:46 -05:00
<div className="items-top flex space-x-2 pb-1">
2024-07-23 18:49:21 -05:00
<Checkbox
disabled={templateFilter && tag.__disab != undefined}
id={tag.docsName}
checked={(() => {
return nameFilters["t-" + tag.docsName];
})()}
onCheckedChange={async (b) => {
var filt = nameFilters;
filt["t-" + tag.docsName] = b;
setNameFilters(filt);
if (b) {
var filt2 = filters;
filt2.push(tag.condition);
setFilters(filt2);
} else {
var filt2 = filters;
filt2.splice(filt2.indexOf(tag.condition), 1);
setFilters(filt2);
}
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
});
});
}}
/>
2024-08-08 18:02:46 -05:00
<div className="grid gap-1.5 leading-none">
2024-07-23 18:49:21 -05:00
<label
htmlFor={tag.docsName}
2024-08-08 18:02:46 -05:00
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
2024-07-23 18:49:21 -05:00
>
2024-08-08 18:02:46 -05:00
<Badge variant={tag.role} className="mr-1">
2024-07-23 18:49:21 -05:00
{tag.docsName}
</Badge>
</label>
</div>
</div>
)}
</div>
))}
<br />
<strong>Categories</strong>
<br />
{allCategories.map((categorie) => (
<div
2024-08-08 18:02:46 -05:00
className="items-top flex space-x-2 pb-1"
2024-07-23 18:49:21 -05:00
key={categorie.name}
>
<Checkbox
id={categorie.name}
onCheckedChange={async (b) => {
var filt = nameFilters;
filt["c-" + categorie.name] = b;
setNameFilters(filt);
if (b) {
var filt2 = filters;
filt2.push(categorie.condition);
setFilters(filt2);
} else {
var filt2 = filters;
filt2.splice(filt2.indexOf(categorie.condition), 1);
setFilters(filt2);
}
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{ server: string; motd: string }> =
[];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
});
});
}}
checked={(() => {
return nameFilters["c-" + categorie.name];
})()}
/>
2024-08-08 18:02:46 -05:00
<div className="grid gap-1.5 leading-none">
2024-07-23 18:49:21 -05:00
<label
htmlFor={categorie.name}
2024-08-08 18:02:46 -05:00
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
2024-07-23 18:49:21 -05:00
>
2024-08-08 18:02:46 -05:00
<Badge variant={categorie.role} className="mr-1">
2024-07-23 18:49:21 -05:00
{categorie.name}
</Badge>
</label>
</div>
</div>
))}
</PopoverContent>
</Popover>
2024-08-15 23:24:15 -05:00
<Popover>
<PopoverTrigger>
<Button className="ml-2" variant="secondary">
Sort
</Button>
</PopoverTrigger>
<PopoverContent>
<RadioGroup
defaultValue="option-one"
onValueChange={() => router.push("/sort/favorites")}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option-one" id="option-one" />
<Label htmlFor="option-one">Online Players</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="option-two" id="option-two" />
<Label htmlFor="option-two">Favorites</Label>
</div>
</RadioGroup>
</PopoverContent>
</Popover>
2024-07-23 18:49:21 -05:00
<Button
variant="secondary"
2024-08-08 18:02:46 -05:00
className="md:ml-3 "
2024-07-23 18:49:21 -05:00
onClick={() => {
setRandomData(serverList.getRandomServer());
setRandom(true);
}}
>
Pick Random Server
</Button>
<Button
variant="secondary"
2024-08-08 18:02:46 -05:00
className="ml-3"
2024-07-23 18:49:21 -05:00
onClick={() => {
toast.promise(
new Promise((s, e) => {
setLoading(true);
serverList
.fetchDataAndFilter()
.then(() => {
serverList.moveListDown();
let stringList: Array<{ server: string; motd: string }> =
[];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
setLoading(false);
s(false);
});
})
.catch(() => {
e();
});
}),
{
success: "Succesfully refreshed servers",
loading: "Refreshing...",
error: "Error while refreshing",
2024-08-03 09:51:45 -05:00
}
2024-07-23 18:49:21 -05:00
);
}}
>
Refresh
</Button>
<Dialog open={random} onOpenChange={setRandom}>
<DialogContent>
{randomData == undefined && <>No data to randomize</>}
{randomData != undefined && (
<DialogHeader>
<DialogTitle>
<IconDisplay server={randomData} /> {randomData.name}
{randomData.author != undefined ? (
2024-08-08 18:02:46 -05:00
<div className="text-sm text-muted-foreground">
2024-07-23 18:49:21 -05:00
by {randomData.author}
</div>
) : (
<br />
)}
<TagShower server={randomData} />
</DialogTitle>
2024-08-08 18:02:46 -05:00
<DialogDescription className="float-left inline">
<span className="flex items-center">
2024-07-23 18:49:21 -05:00
{randomData.playerData.playerCount == 0 ? (
<div
2024-08-08 18:02:46 -05:00
className="items-center border"
2024-07-23 18:49:21 -05:00
style={{
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
) : (
<div
2024-08-08 18:02:46 -05:00
className="items-center"
2024-07-23 18:49:21 -05:00
style={{
backgroundColor: "#0cce6b",
width: ".5rem",
height: ".5rem",
borderRadius: "9999px",
}}
/>
)}
2024-08-08 18:02:46 -05:00
<span className="pl-1">
2024-07-23 18:49:21 -05:00
{randomData.playerData.playerCount}{" "}
{randomData.playerData.playerCount == 1
? "player"
: "players"}{" "}
currently online
</span>
</span>
<br />
<strong>Server IP</strong>
<br />
<br />
2024-08-08 18:02:46 -05:00
<code className="border p-3 rounded">
2024-08-15 23:24:15 -05:00
{randomData.name}.minehut.gg{" "}
2024-07-23 18:49:21 -05:00
<Button
size="icon"
2024-08-08 18:02:46 -05:00
className="ml-1 h-[20px]"
2024-07-23 18:49:21 -05:00
onClick={() => {
setTextCopied(true);
navigator.clipboard.writeText(
2024-08-03 09:51:45 -05:00
randomData.name + ".mshf.minehut.gg"
2024-07-23 18:49:21 -05:00
);
toast.success("Copied!");
setTimeout(() => setTextCopied(false), 1000);
}}
>
{textCopied ? (
2024-08-08 18:02:46 -05:00
<Check size={16} className="flex items-center" />
2024-07-23 18:49:21 -05:00
) : (
<p>Copy</p>
)}
</Button>
</code>
</DialogDescription>
</DialogHeader>
)}
</DialogContent>
</Dialog>
</div>
<br />
<InfiniteScroll
dataLength={serverList.currentServers.length}
hasMore={serverList.hasMore}
next={() => {
serverList.moveListDown();
let stringList: Array<{ server: string; motd: string }> = [];
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
setLoading(false);
});
}}
2024-08-08 18:02:46 -05:00
loader={<Spinner className="flex items-center" />}
2024-07-23 18:49:21 -05:00
endMessage={
<p
style={{ textAlign: "center" }}
dangerouslySetInnerHTML={{
__html: randomText + "<br /> <strong>You've seen it all</strong>",
}}
/>
}
style={{ overflow: "hidden !important", paddingLeft: 6 }}
>
2024-07-23 22:07:54 -05:00
<div className=" grid sm:grid-cols-4 gap-4">
2024-07-23 18:49:21 -05:00
{servers.map((b: any) => (
<>
<ServerCard b={b} motd={motdList[b.name]} />
</>
))}
</div>
</InfiniteScroll>
</>
);
}
2024-08-11 22:14:36 -05:00
export function TagShower(props: {
server: OnlineServer;
className?: string;
unclickable?: boolean;
}) {
2024-07-23 18:49:21 -05:00
const [loading, setLoading] = useState(true);
const [compatiableTags, setCompatiableTags] = useState<
Array<{
name: string;
docsName?: string;
tooltip: string;
htmlDocs: string;
role:
| "default"
| "destructive"
| "outline"
| "secondary"
| "red"
| "orange"
| "yellow"
| "green"
| "lime"
| "blue"
| "teal"
| "cyan"
| "violet"
| "indigo"
| "purple"
| "fuchsia"
| "pink";
}>
>([]);
useEffectOnce(() => {
if (loading) {
allTags.forEach((tag) => {
tag.condition(props.server).then((b) => {
if (b && tag.primary) {
tag.name(props.server).then((n) => {
compatiableTags.push({
name: n,
docsName: tag.docsName,
tooltip: tag.tooltipDesc,
htmlDocs: tag.htmlDocs,
role: tag.role == undefined ? "secondary" : tag.role,
});
setLoading(false);
});
}
});
});
}
});
if (loading) {
return <></>;
}
return (
<>
{compatiableTags.map((t) => (
<>
2024-08-11 22:14:36 -05:00
{props.unclickable && (
<Badge variant={t.role} className={props.className}>
{t.name}
</Badge>
)}
{!props.unclickable && (
<Dialog key={t.name}>
<DialogTrigger>
<Tooltip>
<TooltipTrigger>
<Badge variant={t.role} className={props.className}>
{t.name}
</Badge>
</TooltipTrigger>
<TooltipContent>
<div className="font-normal">
{t.tooltip}
<br />
Click the tag to learn more about it.
</div>
</TooltipContent>
</Tooltip>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
{'"'}
{t.docsName == undefined ? t.name : t.docsName}
{'"'} documentation
</DialogTitle>
<DialogDescription
dangerouslySetInnerHTML={{
__html: t.htmlDocs,
}}
/>
</DialogHeader>
</DialogContent>
</Dialog>
)}
2024-07-23 18:49:21 -05:00
</>
))}
</>
);
}