diff --git a/.vscode/settings.json b/.vscode/settings.json index 98712889..0c936e31 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,6 +29,7 @@ "cSpell.words": [ "articifial", "compat", + "Embla", "ianvs", "jiti", "jullerino", diff --git a/apps/nextjs/src/app/_components/posts.tsx b/apps/nextjs/src/app/_components/posts.tsx deleted file mode 100644 index a7d2b997..00000000 --- a/apps/nextjs/src/app/_components/posts.tsx +++ /dev/null @@ -1,168 +0,0 @@ -"use client"; - -import type { RouterOutputs } from "@battle-stadium/api"; -import { CreatePostSchema } from "@battle-stadium/db/schema"; -import { cn } from "@battle-stadium/ui"; -import { Button } from "@battle-stadium/ui/button"; -import { - Form, - FormControl, - FormField, - FormItem, - FormMessage, - useForm, -} from "@battle-stadium/ui/form"; -import { Input } from "@battle-stadium/ui/input"; -import { toast } from "@battle-stadium/ui/toast"; - -import { api } from "~/trpc/react"; - -export function CreatePostForm() { - const form = useForm({ - schema: CreatePostSchema, - defaultValues: { - content: "", - title: "", - }, - }); - - const utils = api.useUtils(); - const createPost = api.post.create.useMutation({ - onSuccess: async () => { - form.reset(); - await utils.post.invalidate(); - }, - onError: (err) => { - toast.error( - err.data?.code === "UNAUTHORIZED" - ? "You must be logged in to post" - : "Failed to create post", - ); - }, - }); - - return ( -
- { - createPost.mutate(data); - })} - > - ( - - - - - - - )} - /> - ( - - - - - - - )} - /> - - - - ); -} - -export function PostList() { - const [posts] = api.post.all.useSuspenseQuery(); - - if (posts.length === 0) { - return ( -
- - - - -
-

No posts yet

-
-
- ); - } - - return ( -
- {posts.map((p) => { - return ; - })} -
- ); -} - -export function PostCard(props: { - post: RouterOutputs["post"]["all"][number]; -}) { - const utils = api.useUtils(); - const deletePost = api.post.delete.useMutation({ - onSuccess: async () => { - await utils.post.invalidate(); - }, - onError: (err) => { - toast.error( - err.data?.code === "UNAUTHORIZED" - ? "You must be logged in to delete a post" - : "Failed to delete post", - ); - }, - }); - - return ( -
-
-

{props.post.title}

-

{props.post.content}

-
-
- -
-
- ); -} - -export function PostCardSkeleton(props: { pulse?: boolean }) { - const { pulse = true } = props; - return ( -
-
-

-   -

-

-   -

-
-
- ); -} diff --git a/apps/nextjs/src/app/layout.tsx b/apps/nextjs/src/app/layout.tsx index 156903a3..2a5152a6 100644 --- a/apps/nextjs/src/app/layout.tsx +++ b/apps/nextjs/src/app/layout.tsx @@ -4,7 +4,6 @@ import { GeistSans } from "geist/font/sans"; import { cn } from "@battle-stadium/ui"; import { ThemeProvider, ThemeToggle } from "@battle-stadium/ui/theme"; -import { Toaster } from "@battle-stadium/ui/toast"; import { TRPCReactProvider } from "~/trpc/react"; @@ -40,24 +39,23 @@ export const viewport: Viewport = { ], }; -export default function RootLayout ( +export default function RootLayout( props: Readonly<{ children: React.ReactNode }>, ) { return ( - { props.children } + {props.children}
-
diff --git a/packages/ui/components.json b/packages/ui/components.json index b64bbd17..0f787e1e 100644 --- a/packages/ui/components.json +++ b/packages/ui/components.json @@ -5,7 +5,7 @@ "tsx": true, "tailwind": { "config": "../../tooling/tailwind/web.ts", - "css": "unused.css", + "css": "./src/globals.css", "baseColor": "zinc", "cssVariables": true }, diff --git a/packages/ui/package.json b/packages/ui/package.json index d653c31e..7a99acef 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -29,16 +29,49 @@ }, "prettier": "@battle-stadium/prettier-config", "dependencies": { - "@hookform/resolvers": "^3.9.0", + "@hookform/resolvers": "^3.9.1", + "@radix-ui/react-accordion": "^1.2.1", + "@radix-ui/react-alert-dialog": "^1.1.2", + "@radix-ui/react-aspect-ratio": "^1.1.0", + "@radix-ui/react-avatar": "^1.1.1", + "@radix-ui/react-checkbox": "^1.1.2", + "@radix-ui/react-collapsible": "^1.1.1", + "@radix-ui/react-context-menu": "^2.2.2", + "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-hover-card": "^1.1.2", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-menubar": "^1.1.2", + "@radix-ui/react-navigation-menu": "^1.2.1", + "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-radio-group": "^1.2.1", + "@radix-ui/react-scroll-area": "^1.2.0", + "@radix-ui/react-select": "^2.1.2", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slider": "^1.2.1", "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.1", + "@radix-ui/react-tabs": "^1.1.1", + "@radix-ui/react-toast": "^1.2.2", + "@radix-ui/react-toggle": "^1.1.0", + "@radix-ui/react-toggle-group": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.3", "class-variance-authority": "^0.7.0", + "cmdk": "1.0.0", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.3.1", + "input-otp": "^1.4.1", + "lucide-react": "^0.454.0", "next-themes": "^0.3.0", - "react-hook-form": "^7.53.0", - "sonner": "^1.5.0", - "tailwind-merge": "^2.5.4" + "react-day-picker": "8.10.1", + "react-hook-form": "^7.53.1", + "react-resizable-panels": "^2.1.6", + "recharts": "^2.13.3", + "sonner": "^1.7.0", + "tailwind-merge": "^2.5.4", + "vaul": "^1.1.1" }, "devDependencies": { "@battle-stadium/eslint-config": "workspace:*", diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx index 8f1b066e..f8e39654 100644 --- a/packages/ui/src/button.tsx +++ b/packages/ui/src/button.tsx @@ -3,14 +3,14 @@ import * as React from "react"; import { Slot } from "@radix-ui/react-slot"; import { cva } from "class-variance-authority"; -import { cn } from "@battle-stadium/ui"; +import { cn } from "."; const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + "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-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", { variants: { variant: { - primary: + default: "bg-primary text-primary-foreground shadow hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", @@ -22,20 +22,20 @@ const buttonVariants = cva( link: "text-primary underline-offset-4 hover:underline", }, size: { + default: "h-9 px-4 py-2", sm: "h-8 rounded-md px-3 text-xs", - md: "h-9 px-4 py-2", lg: "h-10 rounded-md px-8", - icon: "size-9", + icon: "h-9 w-9", }, }, defaultVariants: { - variant: "primary", - size: "md", + variant: "default", + size: "default", }, }, ); -interface ButtonProps +export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; diff --git a/packages/ui/src/dropdown-menu.tsx b/packages/ui/src/dropdown-menu.tsx index 62ceab3b..6a8ed95b 100644 --- a/packages/ui/src/dropdown-menu.tsx +++ b/packages/ui/src/dropdown-menu.tsx @@ -8,13 +8,18 @@ import { DotFilledIcon, } from "@radix-ui/react-icons"; -import { cn } from "@battle-stadium/ui"; +import { cn } from "."; const DropdownMenu = DropdownMenuPrimitive.Root; + const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + const DropdownMenuGroup = DropdownMenuPrimitive.Group; + const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + const DropdownMenuSub = DropdownMenuPrimitive.Sub; + const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< @@ -26,14 +31,14 @@ const DropdownMenuSubTrigger = React.forwardRef< {children} - + )); DropdownMenuSubTrigger.displayName = @@ -83,7 +88,7 @@ const DropdownMenuItem = React.forwardRef< svg]:size-4 [&>svg]:shrink-0", inset && "pl-8", className, )} @@ -105,9 +110,9 @@ const DropdownMenuCheckboxItem = React.forwardRef< checked={checked} {...props} > - + - + {children} @@ -128,9 +133,9 @@ const DropdownMenuRadioItem = React.forwardRef< )} {...props} > - + - + {children} diff --git a/packages/ui/src/form.tsx b/packages/ui/src/form.tsx deleted file mode 100644 index 5ff9d73b..00000000 --- a/packages/ui/src/form.tsx +++ /dev/null @@ -1,205 +0,0 @@ -"use client"; - -import type * as LabelPrimitive from "@radix-ui/react-label"; -import type { - ControllerProps, - FieldPath, - FieldValues, - UseFormProps, -} from "react-hook-form"; -import type { ZodType, ZodTypeDef } from "zod"; -import * as React from "react"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { Slot } from "@radix-ui/react-slot"; -import { - useForm as __useForm, - Controller, - FormProvider, - useFormContext, -} from "react-hook-form"; - -import { cn } from "@battle-stadium/ui"; - -import { Label } from "./label"; - -const useForm = < - TOut extends FieldValues, - TDef extends ZodTypeDef, - TIn extends FieldValues, ->( - props: Omit, "resolver"> & { - schema: ZodType; - }, -) => { - const form = __useForm({ - ...props, - resolver: zodResolver(props.schema, undefined), - }); - - return form; -}; - -const Form = FormProvider; - -interface FormFieldContextValue< - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, -> { - name: TName; -} - -const FormFieldContext = React.createContext( - null, -); - -const FormField = < - TFieldValues extends FieldValues = FieldValues, - TName extends FieldPath = FieldPath, ->({ - ...props -}: ControllerProps) => { - return ( - - - - ); -}; - -const useFormField = () => { - const fieldContext = React.useContext(FormFieldContext); - const itemContext = React.useContext(FormItemContext); - const { getFieldState, formState } = useFormContext(); - - if (!fieldContext) { - throw new Error("useFormField should be used within "); - } - const fieldState = getFieldState(fieldContext.name, formState); - - const { id } = itemContext; - - return { - id, - name: fieldContext.name, - formItemId: `${id}-form-item`, - formDescriptionId: `${id}-form-item-description`, - formMessageId: `${id}-form-item-message`, - ...fieldState, - }; -}; - -interface FormItemContextValue { - id: string; -} - -const FormItemContext = React.createContext( - {} as FormItemContextValue, -); - -const FormItem = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => { - const id = React.useId(); - - return ( - -
- - ); -}); -FormItem.displayName = "FormItem"; - -const FormLabel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { error, formItemId } = useFormField(); - - return ( -