Move to TanStack Start from Next.js (#184)

This commit is contained in:
Aslam
2024-10-07 16:44:17 +07:00
committed by GitHub
parent 3a89a1c07f
commit 950ebc3dad
514 changed files with 20021 additions and 15508 deletions

View File

@@ -0,0 +1,125 @@
import * as React from "react"
import type { Editor } from "@tiptap/react"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { cn } from "@/lib/utils"
import { storeImageFn } from "@shared/actions"
import { ZodError } from "zod"
interface ImageEditBlockProps extends React.HTMLAttributes<HTMLDivElement> {
editor: Editor
close: () => void
}
const ImageEditBlock = ({
editor,
className,
close,
...props
}: ImageEditBlockProps) => {
const fileInputRef = React.useRef<HTMLInputElement>(null)
const [link, setLink] = React.useState<string>("")
const [isUploading, setIsUploading] = React.useState<boolean>(false)
const [error, setError] = React.useState<string | null>(null)
const handleClick = (e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
fileInputRef.current?.click()
}
const handleLink = () => {
editor.chain().focus().setImage({ src: link }).run()
close()
}
const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files
if (!files || files.length === 0) return
setIsUploading(true)
setError(null)
const formData = new FormData()
formData.append("file", files[0])
try {
const response = await storeImageFn(formData)
editor
.chain()
.setImage({ src: response.fileModel.content.src })
.focus()
.run()
close()
} catch (error) {
if (error instanceof Error) {
try {
const errors = JSON.parse(error.message)
if (errors.body.name === "ZodError") {
setError(
(errors.body as ZodError).issues
.map((issue) => issue.message)
.join(", "),
)
} else {
setError(error.message)
}
} catch (parseError) {
setError(error.message)
}
} else {
setError("An unknown error occurred")
}
} finally {
setIsUploading(false)
}
}
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
e.stopPropagation()
handleLink()
}
return (
<form onSubmit={handleSubmit}>
<div className={cn("space-y-6", className)} {...props}>
<div className="space-y-1">
<Label>Attach an image link</Label>
<div className="flex">
<Input
type="url"
required
placeholder="https://example.com"
value={link}
className="grow"
onChange={(e) => setLink(e.target.value)}
/>
<Button type="submit" className="ml-2 inline-block">
Submit
</Button>
</div>
</div>
<Button className="w-full" onClick={handleClick} disabled={isUploading}>
{isUploading ? "Uploading..." : "Upload from your computer"}
</Button>
<input
type="file"
accept="image/jpeg,image/png,image/gif,image/webp"
ref={fileInputRef}
className="hidden"
onChange={handleFile}
/>
{error && (
<div className="text-destructive text-sm bg-destructive/10 p-2 rounded">
{error}
</div>
)}
</div>
</form>
)
}
export { ImageEditBlock }

View File

@@ -0,0 +1,50 @@
import * as React from "react"
import type { Editor } from "@tiptap/react"
import type { VariantProps } from "class-variance-authority"
import type { toggleVariants } from "@/components/ui/toggle"
import { ImageIcon } from "@radix-ui/react-icons"
import { ToolbarButton } from "../toolbar-button"
import {
Dialog,
DialogContent,
DialogHeader,
DialogDescription,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { ImageEditBlock } from "./image-edit-block"
interface ImageEditDialogProps extends VariantProps<typeof toggleVariants> {
editor: Editor
}
const ImageEditDialog = ({ editor, size, variant }: ImageEditDialogProps) => {
const [open, setOpen] = React.useState(false)
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<ToolbarButton
isActive={editor.isActive("image")}
tooltip="Image"
aria-label="Image"
size={size}
variant={variant}
>
<ImageIcon className="size-5" />
</ToolbarButton>
</DialogTrigger>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>Select image</DialogTitle>
<DialogDescription className="sr-only">
Upload an image from your computer
</DialogDescription>
</DialogHeader>
<ImageEditBlock editor={editor} close={() => setOpen(false)} />
</DialogContent>
</Dialog>
)
}
export { ImageEditDialog }

View File

@@ -0,0 +1,25 @@
import { ToolbarButton } from "../toolbar-button"
import { TrashIcon } from "@radix-ui/react-icons"
const ImagePopoverBlock = ({
onRemove,
}: {
onRemove: (e: React.MouseEvent<HTMLButtonElement>) => void
}) => {
const handleRemove = (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
onRemove(e)
}
return (
<div className="flex h-10 overflow-hidden rounded bg-background p-2 shadow-lg">
<div className="inline-flex items-center gap-1">
<ToolbarButton tooltip="Remove" onClick={handleRemove}>
<TrashIcon className="size-4" />
</ToolbarButton>
</div>
</div>
)
}
export { ImagePopoverBlock }