feat(front): option popup

This commit is contained in:
Hexagonn
2024-11-02 09:04:38 +00:00
committed by GitHub
parent 26ba7d280b
commit 84c7be5689
14 changed files with 738 additions and 321 deletions

View File

@@ -115,3 +115,25 @@ body {
--radius: 0.5rem;
}
}
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background: #131313;
border-radius: 6px;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #525252;
border-radius: 6px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #303030;
}

View File

@@ -4,6 +4,7 @@ import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
@@ -17,12 +18,32 @@ import useSWR from "swr";
import { getUserCount, isUserMonitored } from "@/utils/actions";
import { isSnowflake } from "@/utils/snowflake";
import Link from "next/link";
import { parameterInfo } from "@/utils/parameter";
import * as Icon from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { Input } from "@/components/ui/input";
import { filterLetters } from "@/lib/utils";
import { Button } from "@/components/ui/button";
export default function Home() {
const [userId, setUserId] = useState<null | string>(null);
const [userError, setUserError] = useState<string | JSX.Element>();
const [userData, setUserData] = useState<{ userId: string } | null>(null);
const originUrl = useMemo(() => window.location.origin, []);
const originUrl = useMemo(() => window.location?.origin, []);
const [copyState, setCopyState] = useState("Copy");
const [outputType, setOutputType] = useState<"markdown" | "html" | "url">(
"markdown",
@@ -30,6 +51,10 @@ export default function Home() {
const [isLoading, setIsLoading] = useState(true);
const [onImageLoaded, setOnImageLoaded] = useState(false);
const [option, setOption] = useState<
Array<{ name: string; value: string }>
>([]);
const userCount = useSWR("getUserCount", getUserCount);
const countRef = useRef<HTMLDivElement | null>(null);
useSmoothCount({
@@ -39,23 +64,26 @@ export default function Home() {
curve: [0, 1, 0, 1],
});
const outputText = () => {
const url = `${originUrl || "https://lanyard.cnrad.dev"}/api/${userData?.userId}${option.length > 0 ? `?${option.map((o) => `${o.name}=${o.value}`).join("&")}` : ""}`;
function outputText() {
if (outputType === "html") {
return `<a href="https://discord.com/users/${userData?.userId}"><img src="${originUrl || "https://lanyard.cnrad.dev"}/api/${userData?.userId}" /></a>`;
return `<a href="https://discord.com/users/${userData?.userId}"><img src="${url}" /></a>`;
} else if (outputType === "url") {
return `${originUrl || "https://lanyard.cnrad.dev"}/api/${userData?.userId}`;
return `${url}`;
} else {
return `[![Discord Presence](${originUrl || "https://lanyard.cnrad.dev"}/api/${userData?.userId})](https://discord.com/users/${userData?.userId})`;
return `[![Discord Presence](${url})](https://discord.com/users/${userData?.userId})`;
}
};
const copy = () => {
}
function copy() {
navigator.clipboard.writeText(outputText());
setCopyState("Copied!");
setTimeout(() => setCopyState("Copy"), 1500);
};
}
const submitDiscordId = async () => {
async function submitDiscordId() {
setIsLoading(true);
setOnImageLoaded(false);
setUserData(null);
@@ -82,7 +110,115 @@ export default function Home() {
setUserData({ userId });
setIsLoading(false);
};
}
function modifyOption(
data:
| {
type: "string";
name: string;
data: string;
event: React.ChangeEvent<HTMLInputElement>;
}
| {
type: "list";
name: string;
data: string;
}
| {
type: "boolean";
name: string;
data: string | boolean;
},
) {
if (data.type === "string") {
const filteredValue = filterLetters(
data.data,
(
parameterInfo.find(
(p) => p.type === "string" && p.parameter === data.name,
) as any
).options.omit,
);
setOption((prev) => {
if (data.data === "") {
return prev?.filter((o) => o.name !== data.name) || [];
} else {
if (prev?.find((o) => o.name === data.name)) {
return prev.map((o) => {
if (o.name === data.name) {
o.value = filteredValue;
}
return o;
});
} else {
return prev
? [
...prev,
{
name: data.name,
value: filteredValue,
},
]
: [
{
name: data.name,
value: filteredValue,
},
];
}
}
});
} else if (data.type === "list") {
setOption((prev) => {
if (prev?.find((o) => o.name === data.name)) {
return prev.map((o) => {
if (o.name === data.name) {
o.value = data.data;
}
return o;
});
} else {
return prev
? [...prev, { name: data.name, value: data.data }]
: [{ name: data.name, value: data.data }];
}
});
} else if (data.type === "boolean") {
setOption((prev) => {
if (prev?.find((o) => o.name === data.name)) {
return prev
.map((opt) => {
const options = parameterInfo.find(
(p) =>
p.type === "boolean" &&
p.parameter === data.name,
)?.options as { defaultBool?: boolean };
if (
data.data === (options?.defaultBool! || false)
) {
return null;
} else {
if (opt.name === data.name) {
opt.value = data.data.toString();
}
return opt;
}
})
.filter((opt) => opt !== null);
} else {
return prev
? [
...prev,
{ name: data.name, value: data.data.toString() },
]
: [{ name: data.name, value: data.data.toString() }];
}
});
}
}
return (
<>
@@ -169,16 +305,165 @@ export default function Home() {
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>
Are you absolutely sure?
</DialogTitle>
<DialogTitle>Option</DialogTitle>
<DialogDescription>
This action cannot be undone. This
will permanently delete your account
and remove your data from our
servers.
Select an option to enable/disable
features to your Lanyard Profile
</DialogDescription>
</DialogHeader>
<div className="flex max-h-[75dvh] flex-col gap-4 overflow-x-hidden overflow-y-scroll rounded-xl bg-black/50 p-4 px-6 text-[#cecece]">
{parameterInfo.map((item, idx) => {
return (
<div
key={item.parameter}
className="flex flex-col gap-1"
>
<div className="flex items-center gap-1">
<p>{item.title}</p>
<TooltipProvider
delayDuration={100}
>
<Tooltip>
<TooltipTrigger
onFocus={(
e,
) => {
e.preventDefault();
}}
>
<Icon.HelpCircleIcon
size={
18
}
/>
</TooltipTrigger>
<TooltipContent>
<p>
{
item.description
}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{item.type === "string" && (
<Input
placeholder={
item.options
?.placeholder ||
"..."
}
onChange={(e) =>
modifyOption({
type: "string",
name: item.parameter,
data: e
.target
.value,
event: e,
})
}
value={
option?.find(
(o) =>
o.name ===
item.parameter,
)?.value || ""
}
/>
)}
{item.type ===
"boolean" && (
<Checkbox
onCheckedChange={(
bool,
) =>
modifyOption({
type: "boolean",
name: item.parameter,
data: bool,
})
}
checked={
option?.find(
(o) =>
o.name ===
item.parameter,
)?.value ===
"true"
? true
: option?.find(
(
o,
) =>
o.name ===
item.parameter,
)
?.value ===
"false"
? false
: item
.options
?.defaultBool ||
false
}
/>
)}
{item.type === "list" && (
<Select
onValueChange={(
val,
) =>
modifyOption({
type: "list",
name: item.parameter,
data: val,
})
}
value={
option?.find(
(o) =>
o.name ===
item.parameter,
)?.value || ""
}
>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Theme" />
</SelectTrigger>
<SelectContent>
{item.options.list.map(
(
option,
) => (
<SelectItem
value={
option.value
}
key={
option.value
}
>
{
option.name
}
</SelectItem>
),
)}
</SelectContent>
</Select>
)}
</div>
);
})}
</div>
<DialogFooter>
<Button onClick={() => setOption([])}>
Reset
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
@@ -192,7 +477,7 @@ export default function Home() {
opacity: onImageLoaded ? 1 : 0,
}}
transition={{ duration: 0.5 }}
src={`/api/${userData?.userId}`}
src={url}
height={280}
width={500}
alt="Your Lanyard Banner"

View File

@@ -1,57 +1,57 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-stone-950 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:focus-visible:ring-stone-300",
{
variants: {
variant: {
default:
"bg-stone-900 text-stone-50 shadow hover:bg-stone-900/90 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50/90",
destructive:
"bg-red-500 text-stone-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-stone-50 dark:hover:bg-red-900/90",
outline:
"border border-stone-200 bg-white shadow-sm hover:bg-stone-100 hover:text-stone-900 dark:border-stone-800 dark:bg-stone-950 dark:hover:bg-stone-800 dark:hover:text-stone-50",
secondary:
"bg-stone-100 text-stone-900 shadow-sm hover:bg-stone-100/80 dark:bg-stone-800 dark:text-stone-50 dark:hover:bg-stone-800/80",
ghost: "hover:bg-stone-100 hover:text-stone-900 dark:hover:bg-stone-800 dark:hover:text-stone-50",
link: "text-stone-900 underline-offset-4 hover:underline dark:text-stone-50",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:ring-stone-300",
{
variants: {
variant: {
default:
"shadow bg-stone-50 text-stone-900 hover:bg-stone-50/90",
destructive:
"shadow-sm bg-red-900 text-stone-50 hover:bg-red-900/90",
outline:
"border shadow-sm text-stone-900 border-stone-800 bg-stone-950 hover:bg-stone-800 hover:text-stone-50",
secondary:
"shadow-sm bg-stone-800 text-stone-50 hover:bg-stone-800/80",
ghost: "hover:bg-stone-800 hover:text-stone-50",
link: "underline-offset-4 hover:underline text-stone-50",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
);
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants }
export { Button, buttonVariants };

View File

@@ -1,30 +1,30 @@
"use client"
"use client";
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { CheckIcon } from "@radix-ui/react-icons"
import * as React from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { CheckIcon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-stone-200 border-stone-900 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-stone-950 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-stone-900 data-[state=checked]:text-stone-50 dark:border-stone-800 dark:border-stone-50 dark:focus-visible:ring-stone-300 dark:data-[state=checked]:bg-stone-50 dark:data-[state=checked]:text-stone-900",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-stone-50 border-stone-800 shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-stone-300 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-stone-50 data-[state=checked]:text-stone-900",
className,
)}
{...props}
>
<CheckIcon className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<CheckIcon className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
export { Checkbox }
export { Checkbox };

View File

@@ -1,155 +0,0 @@
"use client"
import * as React from "react"
import { type DialogProps } from "@radix-ui/react-dialog"
import { MagnifyingGlassIcon } from "@radix-ui/react-icons"
import { Command as CommandPrimitive } from "cmdk"
import { cn } from "@/lib/utils"
import { Dialog, DialogContent } from "@/components/ui/dialog"
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-stone-950 dark:bg-stone-950 dark:text-stone-50",
className
)}
{...props}
/>
))
Command.displayName = CommandPrimitive.displayName
interface CommandDialogProps extends DialogProps {}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-stone-500 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5 dark:[&_[cmdk-group-heading]]:text-stone-400">
{children}
</Command>
</DialogContent>
</Dialog>
)
}
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
<CommandPrimitive.Input
ref={ref}
className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-stone-500 disabled:cursor-not-allowed disabled:opacity-50 dark:placeholder:text-stone-400",
className
)}
{...props}
/>
</div>
))
CommandInput.displayName = CommandPrimitive.Input.displayName
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
))
CommandList.displayName = CommandPrimitive.List.displayName
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
))
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
"overflow-hidden p-1 text-stone-950 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-stone-500 dark:text-stone-50 dark:[&_[cmdk-group-heading]]:text-stone-400",
className
)}
{...props}
/>
))
CommandGroup.displayName = CommandPrimitive.Group.displayName
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn("-mx-1 h-px bg-stone-200 dark:bg-stone-800", className)}
{...props}
/>
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-stone-100 data-[selected=true]:text-stone-900 data-[disabled=true]:opacity-50 dark:data-[selected=true]:bg-stone-800 dark:data-[selected=true]:text-stone-50",
className
)}
{...props}
/>
))
CommandItem.displayName = CommandPrimitive.Item.displayName
const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-stone-500 dark:text-stone-400",
className
)}
{...props}
/>
)
}
CommandShortcut.displayName = "CommandShortcut"
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
}

View File

@@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
@@ -38,13 +38,16 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-stone-800 bg-stone-950 p-6 shadow-lg duration-200 sm:rounded-lg",
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-stone-800 bg-stone-950 p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-stone-950 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-stone-300 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-stone-800 data-[state=open]:text-stone-400">
<DialogPrimitive.Close
tabIndex={1}
className="absolute right-4 top-4 rounded-sm text-stone-300 opacity-70 ring-offset-stone-950 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-stone-300 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-stone-800 data-[state=open]:text-stone-400"
>
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>

View File

@@ -0,0 +1,25 @@
import * as React from "react";
import { cn } from "@/lib/utils";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-stone-800 bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-stone-50 placeholder:text-stone-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-stone-300 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
ref={ref}
{...props}
/>
);
},
);
Input.displayName = "Input";
export { Input };

View File

@@ -1,33 +0,0 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverAnchor = PopoverPrimitive.Anchor
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border border-stone-200 bg-white p-4 text-stone-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }

View File

@@ -0,0 +1,164 @@
"use client";
import * as React from "react";
import {
CaretSortIcon,
CheckIcon,
ChevronDownIcon,
ChevronUpIcon,
} from "@radix-ui/react-icons";
import * as SelectPrimitive from "@radix-ui/react-select";
import { cn } from "@/lib/utils";
const Select = SelectPrimitive.Root;
const SelectGroup = SelectPrimitive.Group;
const SelectValue = SelectPrimitive.Value;
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-stone-800 bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-stone-950 placeholder:text-stone-400 focus:outline-none focus:ring-1 focus:ring-stone-300 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<CaretSortIcon className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<ChevronUpIcon />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className,
)}
{...props}
>
<ChevronDownIcon />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName;
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-stone-800 bg-stone-950 text-stone-50 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className,
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
{...props}
/>
));
SelectLabel.displayName = SelectPrimitive.Label.displayName;
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-stone-800 focus:text-stone-50 data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className,
)}
{...props}
>
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-stone-800", className)}
{...props}
/>
));
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
};

View File

@@ -1,32 +1,32 @@
"use client"
"use client";
import * as React from "react"
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
import * as React from "react";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const TooltipProvider = TooltipPrimitive.Provider
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-stone-900 px-3 py-1.5 text-xs text-stone-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:bg-stone-50 dark:text-stone-900",
className
)}
{...props}
/>
</TooltipPrimitive.Portal>
))
TooltipContent.displayName = TooltipPrimitive.Content.displayName
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-stone-900 px-3 py-1.5 text-xs text-stone-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
</TooltipPrimitive.Portal>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };

View File

@@ -1,6 +1,13 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
return twMerge(clsx(inputs));
}
export function filterLetters(str: string, lettersToRemove: string[] = []) {
lettersToRemove.forEach((letter) => {
str = str.replaceAll(letter, "");
});
return str;
}

View File

@@ -19,7 +19,7 @@ export type Parameters = {
idleMessage?: string;
};
export const ParameterInfo: Array<
export const parameterInfo: Array<
| {
parameter: string;
type: "boolean";
@@ -35,8 +35,8 @@ export const ParameterInfo: Array<
title: string;
description?: string;
options?: {
prefix?: string;
suffix?: string;
placeholder?: string;
omit?: string[];
};
}
| {
@@ -76,18 +76,20 @@ export const ParameterInfo: Array<
type: "string",
title: "Background Color",
description:
"This will change the background color. Must be in hex format.",
"This will change the background color. Must be in hex format. Omit the # symbol.",
options: {
prefix: "#",
placeholder: "1A1C1F",
omit: ["#"],
},
},
{
parameter: "borderRadius",
type: "string",
title: "Border Radius",
description: "This will change the border radius of the card.",
description:
"This will change the border radius of the card. Must have a size unit (px, rem, em, and more).",
options: {
suffix: "px",
placeholder: "10px",
},
},
{
@@ -106,20 +108,16 @@ export const ParameterInfo: Array<
title: "Idle Message",
description:
"If you don't want the default \"I'm not currently doing anything!\" as your idle message, this is the right option.",
options: {
placeholder: "I'm not currently doing anything!",
},
},
{
parameter: "showDisplayName",
type: "boolean",
title: "Show Display Name",
description:
"If you don't want to show your display name, this is the right option.",
},
{
parameter: "hideDecoration",
type: "boolean",
title: "Hide Avatar Decoration",
description:
"If you don't want people seeing your Avatar Decoration, this is the right option.",
"If you'd like to show your global display name as well as your username, this is the right option.",
},
{
parameter: "animatedDecoration",
@@ -131,6 +129,13 @@ export const ParameterInfo: Array<
defaultBool: true,
},
},
{
parameter: "hideDecoration",
type: "boolean",
title: "Hide Avatar Decoration",
description:
"If you don't want people seeing your Avatar Decoration, this is the right option.",
},
{
parameter: "hideStatus",
type: "boolean",
@@ -185,7 +190,10 @@ export const ParameterInfo: Array<
type: "string",
title: "Hide App by ID",
description:
"If you don't want display a specific application, this is the right option. IDs separate by `,`.",
"If you don't want display a specific application, this is the right option. IDs separate by ','",
options: {
placeholder: "1302143410907648071, 1302132259368861759",
},
},
{
parameter: "hideDiscrim",