User list view
This commit is contained in:
@@ -59,7 +59,7 @@ export const handle = implement(api, {
|
||||
|
||||
let query = db
|
||||
.selectFrom("User")
|
||||
.leftJoin("UserRole", "User.userId", "UserRole.role")
|
||||
.leftJoin("UserRole", "User.userId", "UserRole.userId")
|
||||
.select("User.userId")
|
||||
.groupBy("User.userId")
|
||||
.orderBy("displayName")
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"@radix-ui/react-dialog": "catalog:",
|
||||
"@radix-ui/react-dropdown-menu": "catalog:",
|
||||
"@radix-ui/react-label": "catalog:",
|
||||
"@radix-ui/react-select": "catalog:",
|
||||
"@radix-ui/react-slot": "catalog:",
|
||||
"clsx": "catalog:",
|
||||
"common": "workspace:^",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Admin } from "@/routes/Admin";
|
||||
import { Home } from "@/routes/Home";
|
||||
import { Login } from "@/routes/Login";
|
||||
import { Piece } from "@/routes/Piece";
|
||||
@@ -61,6 +62,10 @@ const router = createBrowserRouter([
|
||||
path: "settings",
|
||||
Component: Settings,
|
||||
},
|
||||
{
|
||||
path: "admin",
|
||||
Component: Admin,
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
156
packages/frontend/src/components/ui/select.tsx
Normal file
156
packages/frontend/src/components/ui/select.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import * as React from "react";
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
||||
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-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDown 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}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
</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}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</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-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground 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 origin-[--radix-select-content-transform-origin]",
|
||||
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-accent focus:text-accent-foreground 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>
|
||||
<Check 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-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
};
|
||||
183
packages/frontend/src/routes/Admin.tsx
Normal file
183
packages/frontend/src/routes/Admin.tsx
Normal file
@@ -0,0 +1,183 @@
|
||||
import { userCache } from "@/cache";
|
||||
import { client } from "@/client";
|
||||
import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { useBreakpoint } from "@/hooks/useBreakpoint";
|
||||
import { useCache } from "@/hooks/useCache";
|
||||
import { useLoading } from "@/hooks/useLoading";
|
||||
import { DEBOUNCE } from "@/snippets";
|
||||
import { UserId } from "common";
|
||||
import { Role } from "common/the_api";
|
||||
import { Array, Effect, HashSet, Match, Option, pipe } from "effect";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
export function Admin() {
|
||||
|
||||
const [displayName, setDisplayName] = useState("");
|
||||
const [role, setRole] = useState<Role | "">("");
|
||||
|
||||
const debounce = useRef(Effect.void);
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
const { isLoading, error, data: userIds } = useLoading(Effect.gen(function* () {
|
||||
yield* debounce.current;
|
||||
const data = yield* client.queryUsers({
|
||||
displayName: displayName !== "" ? Option.some(displayName) : Option.none(),
|
||||
role: role !== "" ? Option.some(role) : Option.none(),
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
});
|
||||
return data;
|
||||
}), [displayName, role]);
|
||||
|
||||
return (
|
||||
<div className="p-4 overflow-y-auto flex flex-col gap-4">
|
||||
<div className="flex flex-row flex-wrap items-baseline gap-4">
|
||||
<Input
|
||||
className="w-full md:w-[32ch]"
|
||||
type="text"
|
||||
placeholder="Nazwa wyświetlana"
|
||||
value={displayName}
|
||||
onChange={(e) => {
|
||||
setDisplayName(e.target.value);
|
||||
debounce.current = DEBOUNCE;
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
value={role}
|
||||
onValueChange={(role) => setRole(role === "None" ? "" : role as Role)}
|
||||
>
|
||||
<SelectTrigger className="w-full md:w-[24ch]">
|
||||
<SelectValue placeholder="Rola" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectLabel>Role</SelectLabel>
|
||||
<SelectItem value="None"><em>Dowolna</em></SelectItem>
|
||||
<SelectItem value={Role.Viewer}>Przeglądanie</SelectItem>
|
||||
<SelectItem value={Role.Editor}>Edytor</SelectItem>
|
||||
<SelectItem value={Role.Admin}>Administrator</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<Table>
|
||||
{breakpoint && (
|
||||
<TableHeader className="bg-white sticky top-0">
|
||||
<TableRow>
|
||||
<TableHead className="w-10" />
|
||||
<TableHead>Nazwa wyświetlana</TableHead>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>Role</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
)}
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} >
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="animate-spin" />
|
||||
Ładowanie…
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : error !== null ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} >
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
Wystąpił błąd: {Match.value(error).pipe(
|
||||
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
|
||||
Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
|
||||
Match.exhaustive,
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
userIds.map((userId) => <UserRow key={userId} userId={userId} />)
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
namespace UserRow {
|
||||
export interface Props {
|
||||
readonly userId: UserId;
|
||||
}
|
||||
}
|
||||
|
||||
function UserRow(props: UserRow.Props) {
|
||||
|
||||
const { isLoading, error, data: user } = useCache(userCache, props.userId);
|
||||
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns}>Ładowanie…</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
if (error !== null) {
|
||||
return (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns}>
|
||||
Wystąpił błąd: {Match.value(error).pipe(
|
||||
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
|
||||
Match.tag("NotFound", () => "Użytkownik nie istnieje"),
|
||||
Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
|
||||
Match.exhaustive,
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
const rolesText = pipe(
|
||||
user.roles,
|
||||
HashSet.values,
|
||||
Array.join(", "),
|
||||
);
|
||||
|
||||
return (
|
||||
<TableRow>
|
||||
{breakpoint ? (<>
|
||||
<TableCell>
|
||||
<Avatar className="shadow">
|
||||
<AvatarImage src={Option.getOrUndefined(user.avatarUrl)} />
|
||||
</Avatar>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{Option.getOrUndefined(user.displayName)}
|
||||
</TableCell>
|
||||
<TableCell className="font-mono">
|
||||
{user.userId}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{rolesText}
|
||||
</TableCell>
|
||||
</>) : (
|
||||
<TableCell className="flex flex-col">
|
||||
<div className="flex flex-row items-center gap-2 mb-2">
|
||||
<Avatar className="shadow">
|
||||
<AvatarImage src={Option.getOrUndefined(user.avatarUrl)} />
|
||||
</Avatar>
|
||||
<div>{Option.getOrUndefined(user.displayName)}</div>
|
||||
</div>
|
||||
<div className="text-xs font-mono">{user.userId}</div>
|
||||
<div className="text-xs">{rolesText}</div>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export function Pieces() {
|
||||
}), [name, author]);
|
||||
|
||||
return (
|
||||
<div className="p-4 overflow-y-auto flex flex-col items-start gap-4">
|
||||
<div className="p-4 overflow-y-auto flex flex-col gap-4">
|
||||
<div className="flex flex-row flex-wrap gap-4">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
|
||||
@@ -34,7 +34,7 @@ export function Repertoires() {
|
||||
}), [name]);
|
||||
|
||||
return (
|
||||
<div className="p-4 overflow-y-auto flex flex-col items-start gap-4">
|
||||
<div className="p-4 overflow-y-auto flex flex-col gap-4">
|
||||
<div className="flex flex-row flex-wrap gap-4">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
|
||||
@@ -3,8 +3,9 @@ import { Avatar, AvatarImage } from "@/components/ui/avatar";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
||||
import { setUser, useStore } from "@/hooks/useStore";
|
||||
import { Effect, Option, pipe } from "effect";
|
||||
import { LogOut, Settings } from "lucide-react";
|
||||
import { Role } from "common/the_api";
|
||||
import { Effect, HashSet, Option, pipe } from "effect";
|
||||
import { LogOut, Settings, Users } from "lucide-react";
|
||||
import { useEffect } from "react";
|
||||
import { Link, Outlet, useNavigate } from "react-router-dom";
|
||||
|
||||
@@ -63,6 +64,13 @@ export function Root() {
|
||||
<Settings />Ustawienia
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{HashSet.has(user.roles, Role.Admin) && (
|
||||
<DropdownMenuItem asChild>
|
||||
<Link to="/admin">
|
||||
<Users />Administracja
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem onClick={onLogoutClick}>
|
||||
<LogOut />Wyloguj się
|
||||
</DropdownMenuItem>
|
||||
|
||||
90
pnpm-lock.yaml
generated
90
pnpm-lock.yaml
generated
@@ -24,6 +24,9 @@ catalogs:
|
||||
'@radix-ui/react-label':
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.7
|
||||
'@radix-ui/react-select':
|
||||
specifier: ^2.2.6
|
||||
version: 2.2.6
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
@@ -184,6 +187,9 @@ importers:
|
||||
'@radix-ui/react-label':
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.7(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-select':
|
||||
specifier: 'catalog:'
|
||||
version: 2.2.6(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-slot':
|
||||
specifier: 'catalog:'
|
||||
version: 1.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
@@ -708,6 +714,9 @@ packages:
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@radix-ui/number@1.1.1':
|
||||
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
||||
|
||||
'@radix-ui/primitive@1.1.3':
|
||||
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
||||
|
||||
@@ -938,6 +947,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-select@2.2.6':
|
||||
resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-slot@1.2.3':
|
||||
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
|
||||
peerDependencies:
|
||||
@@ -1001,6 +1023,15 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.1':
|
||||
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-rect@1.1.1':
|
||||
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
|
||||
peerDependencies:
|
||||
@@ -1019,6 +1050,19 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-visually-hidden@1.2.3':
|
||||
resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/rect@1.1.1':
|
||||
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
|
||||
|
||||
@@ -3108,6 +3152,8 @@ snapshots:
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/number@1.1.1': {}
|
||||
|
||||
'@radix-ui/primitive@1.1.3': {}
|
||||
|
||||
'@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
@@ -3335,6 +3381,35 @@ snapshots:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.1(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-select@2.2.6(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/number': 1.1.1
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.2.0)
|
||||
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
aria-hidden: 1.2.6
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.1(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
|
||||
@@ -3383,6 +3458,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/rect': 1.1.1
|
||||
@@ -3397,6 +3478,15 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
|
||||
'@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.2
|
||||
'@types/react-dom': 19.2.1(@types/react@19.2.2)
|
||||
|
||||
'@radix-ui/rect@1.1.1': {}
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.27': {}
|
||||
|
||||
@@ -16,6 +16,7 @@ catalog:
|
||||
'@radix-ui/react-dialog': '^1.1.15'
|
||||
'@radix-ui/react-dropdown-menu': '^2.1.16'
|
||||
'@radix-ui/react-label': '^2.1.7'
|
||||
'@radix-ui/react-select': '^2.2.6'
|
||||
'@radix-ui/react-slot': '^1.2.3'
|
||||
'@stylistic/eslint-plugin': '^4.4.1'
|
||||
'@tailwindcss/vite': '^4.1.14'
|
||||
|
||||
Reference in New Issue
Block a user