ShadCN setup
This commit is contained in:
@@ -10,11 +10,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/schema": "^0.68.17",
|
"@effect/schema": "^0.68.17",
|
||||||
"@emotion/react": "^11.11.4",
|
|
||||||
"@emotion/styled": "^11.11.5",
|
|
||||||
"@mui/icons-material": "^5.16.1",
|
|
||||||
"@mui/material": "^5.16.1",
|
"@mui/material": "^5.16.1",
|
||||||
|
"@radix-ui/react-checkbox": "^1.1.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"@tanstack/react-query": "4",
|
"@tanstack/react-query": "4",
|
||||||
"@tanstack/react-router": "^1.44.2",
|
"@tanstack/react-router": "^1.44.2",
|
||||||
"@thilawyn/thilalib": "^0.1.5",
|
"@thilawyn/thilalib": "^0.1.5",
|
||||||
@@ -26,10 +25,9 @@
|
|||||||
"effect": "^3.4.7",
|
"effect": "^3.4.7",
|
||||||
"framer-motion": "^11.3.6",
|
"framer-motion": "^11.3.6",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lucide-react": "^0.408.0",
|
"lucide-react": "^0.411.0",
|
||||||
"mobx": "^6.13.0",
|
"mobx": "^6.13.0",
|
||||||
"mobx-react-lite": "^4.0.7",
|
"mobx-react-lite": "^4.0.7",
|
||||||
"primereact": "^10.7.0",
|
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"remeda": "^2.6.0",
|
"remeda": "^2.6.0",
|
||||||
|
|||||||
79
packages/webui/src/components/ui/card.tsx
Normal file
79
packages/webui/src/components/ui/card.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Card.displayName = "Card"
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardHeader.displayName = "CardHeader"
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h3
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"text-2xl font-semibold leading-none tracking-tight",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardTitle.displayName = "CardTitle"
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardDescription.displayName = "CardDescription"
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||||
|
))
|
||||||
|
CardContent.displayName = "CardContent"
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex items-center p-6 pt-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
CardFooter.displayName = "CardFooter"
|
||||||
|
|
||||||
|
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
||||||
28
packages/webui/src/components/ui/checkbox.tsx
Normal file
28
packages/webui/src/components/ui/checkbox.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||||
|
import { Check } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Checkbox = React.forwardRef<
|
||||||
|
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-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<CheckboxPrimitive.Indicator
|
||||||
|
className={cn("flex items-center justify-center text-current")}
|
||||||
|
>
|
||||||
|
<Check className="h-4 w-4" />
|
||||||
|
</CheckboxPrimitive.Indicator>
|
||||||
|
</CheckboxPrimitive.Root>
|
||||||
|
))
|
||||||
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Checkbox }
|
||||||
15
packages/webui/src/components/ui/skeleton.tsx
Normal file
15
packages/webui/src/components/ui/skeleton.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Skeleton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("rounded-md animate-pulse bg-muted", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Skeleton }
|
||||||
28
packages/webui/src/components/ui/tooltip.tsx
Normal file
28
packages/webui/src/components/ui/tooltip.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const TooltipProvider = TooltipPrimitive.Provider
|
||||||
|
|
||||||
|
const Tooltip = TooltipPrimitive.Root
|
||||||
|
|
||||||
|
const TooltipTrigger = TooltipPrimitive.Trigger
|
||||||
|
|
||||||
|
const TooltipContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
|
<TooltipPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md 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}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||||
13
packages/webui/src/lib/Passthrough.tsx
Normal file
13
packages/webui/src/lib/Passthrough.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import type { ReactNode } from "react"
|
||||||
|
|
||||||
|
|
||||||
|
export module Passthrough {
|
||||||
|
export interface Props {
|
||||||
|
children?: ReactNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function Passthrough({ children }: Passthrough.Props) {
|
||||||
|
return children
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import { RouterProvider, createRouter } from "@tanstack/react-router"
|
import { RouterProvider, createRouter } from "@tanstack/react-router"
|
||||||
import { PrimeReactProvider } from "primereact/api"
|
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import ReactDOM from "react-dom/client"
|
import ReactDOM from "react-dom/client"
|
||||||
import { routeTree } from "./routeTree.gen"
|
import { routeTree } from "./routeTree.gen"
|
||||||
import { TRPCClientProvider } from "./trpc/TRPCClientProvider"
|
import { TRPCClientProvider } from "./trpc/TRPCClientProvider"
|
||||||
|
|
||||||
import "primereact/resources/themes/lara-light-cyan/theme.css"
|
|
||||||
import "./index.css"
|
import "./index.css"
|
||||||
|
|
||||||
|
|
||||||
@@ -20,10 +18,8 @@ declare module "@tanstack/react-router" {
|
|||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<PrimeReactProvider>
|
<TRPCClientProvider>
|
||||||
<TRPCClientProvider>
|
<RouterProvider router={router} />
|
||||||
<RouterProvider router={router} />
|
</TRPCClientProvider>
|
||||||
</TRPCClientProvider>
|
|
||||||
</PrimeReactProvider>
|
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { Card, CardContent } from "@/components/ui/card"
|
||||||
|
import { Skeleton as UISkeleton } from "@/components/ui/skeleton"
|
||||||
|
import { Tooltip, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||||
|
import { Passthrough } from "@/lib/Passthrough"
|
||||||
import { Schema as S } from "@effect/schema"
|
import { Schema as S } from "@effect/schema"
|
||||||
import { ArrowDownward, ArrowUpward, Delete } from "@mui/icons-material"
|
import { TooltipContent } from "@radix-ui/react-tooltip"
|
||||||
|
import { ChevronDown, ChevronUp, X } from "lucide-react"
|
||||||
import { observer } from "mobx-react-lite"
|
import { observer } from "mobx-react-lite"
|
||||||
import { Button } from "primereact/button"
|
|
||||||
import { JsonifiableTodo, Todo } from "../data"
|
import { JsonifiableTodo, Todo } from "../data"
|
||||||
import { trpc } from "../trpc/trpc"
|
import { trpc } from "../trpc/trpc"
|
||||||
|
|
||||||
@@ -21,53 +26,88 @@ export const VTodo = observer(({ todo }: VTodo.Props) => {
|
|||||||
const updateTodo = trpc.todo.update.useMutation()
|
const updateTodo = trpc.todo.update.useMutation()
|
||||||
const removeTodo = trpc.todo.remove.useMutation()
|
const removeTodo = trpc.todo.remove.useMutation()
|
||||||
|
|
||||||
|
const Skeleton = updateTodo.isLoading || removeTodo.isLoading
|
||||||
|
? UISkeleton
|
||||||
|
: Passthrough
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row justify-between content-center p-4 rounded-lg border-2 border-black">
|
<Skeleton>
|
||||||
<p>{todo.content}</p>
|
<Card>
|
||||||
|
<CardContent className="flex flex-row justify-between content-center">
|
||||||
|
<p>{todo.content}</p>
|
||||||
|
|
||||||
<div className="flex flex-row gap-1 content-center">
|
<div className="flex flex-row gap-1 content-center">
|
||||||
<Button
|
<TooltipProvider>
|
||||||
severity="secondary"
|
<Tooltip>
|
||||||
rounded
|
<TooltipTrigger>
|
||||||
size="small"
|
<Button
|
||||||
icon={<ArrowDownward />}
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
|
||||||
loading={updateTodo.isLoading}
|
onClick={() => updateTodo.mutate(encodeTodo(
|
||||||
onClick={() => updateTodo.mutate(encodeTodo(
|
new Todo({
|
||||||
new Todo({
|
...todo,
|
||||||
...todo,
|
order: todo.order + 2,
|
||||||
order: todo.order + 2,
|
}, { disableValidation: true })
|
||||||
}, { disableValidation: true })
|
))}
|
||||||
))}
|
>
|
||||||
/>
|
<ChevronDown />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
|
||||||
<Button
|
<TooltipContent>
|
||||||
severity="secondary"
|
<p>Move down</p>
|
||||||
rounded
|
</TooltipContent>
|
||||||
size="small"
|
</Tooltip>
|
||||||
icon={<ArrowUpward />}
|
</TooltipProvider>
|
||||||
|
|
||||||
loading={updateTodo.isLoading}
|
<TooltipProvider>
|
||||||
onClick={() => updateTodo.mutate(encodeTodo(
|
<Tooltip>
|
||||||
new Todo({
|
<TooltipTrigger>
|
||||||
...todo,
|
<Button
|
||||||
order: todo.order - 2,
|
variant="outline"
|
||||||
}, { disableValidation: true })
|
size="icon"
|
||||||
))}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
onClick={() => updateTodo.mutate(encodeTodo(
|
||||||
severity="secondary"
|
new Todo({
|
||||||
rounded
|
...todo,
|
||||||
size="small"
|
order: todo.order - 2,
|
||||||
icon={<Delete />}
|
}, { disableValidation: true })
|
||||||
|
))}
|
||||||
|
>
|
||||||
|
<ChevronUp />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
|
||||||
loading={removeTodo.isLoading}
|
<TooltipContent>
|
||||||
onClick={() => removeTodo.mutate(encodeTodo(todo))}
|
<p>Move up</p>
|
||||||
/>
|
</TooltipContent>
|
||||||
</div>
|
</Tooltip>
|
||||||
</div>
|
</TooltipProvider>
|
||||||
|
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
|
||||||
|
onClick={() => removeTodo.mutate(encodeTodo(todo))}
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
|
||||||
|
<TooltipContent>
|
||||||
|
<p>Delete</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Skeleton>
|
||||||
)
|
)
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user