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,88 @@
import * as React from "react"
import { Button } from "@/components/ui/button"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { Input } from "@/components/ui/input"
import { cn } from "@/lib/utils"
export interface LinkEditorProps extends React.HTMLAttributes<HTMLDivElement> {
defaultUrl?: string
defaultText?: string
defaultIsNewTab?: boolean
onSave: (url: string, text?: string, isNewTab?: boolean) => void
}
export const LinkEditBlock = React.forwardRef<HTMLDivElement, LinkEditorProps>(
({ onSave, defaultIsNewTab, defaultUrl, defaultText, className }, ref) => {
const formRef = React.useRef<HTMLDivElement>(null)
const [url, setUrl] = React.useState(defaultUrl || "")
const [text, setText] = React.useState(defaultText || "")
const [isNewTab, setIsNewTab] = React.useState(defaultIsNewTab || false)
const handleSave = React.useCallback(
(e: React.FormEvent) => {
e.preventDefault()
if (formRef.current) {
const isValid = Array.from(
formRef.current.querySelectorAll("input"),
).every((input) => input.checkValidity())
if (isValid) {
onSave(url, text, isNewTab)
} else {
formRef.current.querySelectorAll("input").forEach((input) => {
if (!input.checkValidity()) {
input.reportValidity()
}
})
}
}
},
[onSave, url, text, isNewTab],
)
React.useImperativeHandle(ref, () => formRef.current as HTMLDivElement)
return (
<div ref={formRef}>
<div className={cn("space-y-4", className)}>
<div className="space-y-1">
<Label>URL</Label>
<Input
type="url"
required
placeholder="Enter URL"
value={url}
onChange={(e) => setUrl(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label>Display Text (optional)</Label>
<Input
type="text"
placeholder="Enter display text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
</div>
<div className="flex items-center space-x-2">
<Label>Open in New Tab</Label>
<Switch checked={isNewTab} onCheckedChange={setIsNewTab} />
</div>
<div className="flex justify-end space-x-2">
<Button type="button" onClick={handleSave}>
Save
</Button>
</div>
</div>
</div>
)
},
)
LinkEditBlock.displayName = "LinkEditBlock"
export default LinkEditBlock

View File

@@ -0,0 +1,72 @@
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 {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
import { Link2Icon } from "@radix-ui/react-icons"
import { ToolbarButton } from "../toolbar-button"
import { LinkEditBlock } from "./link-edit-block"
interface LinkEditPopoverProps extends VariantProps<typeof toggleVariants> {
editor: Editor
}
const LinkEditPopover = ({ editor, size, variant }: LinkEditPopoverProps) => {
const [open, setOpen] = React.useState(false)
const { from, to } = editor.state.selection
const text = editor.state.doc.textBetween(from, to, " ")
const onSetLink = React.useCallback(
(url: string, text?: string, openInNewTab?: boolean) => {
editor
.chain()
.focus()
.extendMarkRange("link")
.insertContent({
type: "text",
text: text || url,
marks: [
{
type: "link",
attrs: {
href: url,
target: openInNewTab ? "_blank" : "",
},
},
],
})
.setLink({ href: url })
.run()
editor.commands.enter()
},
[editor],
)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<ToolbarButton
isActive={editor.isActive("link")}
tooltip="Link"
aria-label="Insert link"
disabled={editor.isActive("codeBlock")}
size={size}
variant={variant}
>
<Link2Icon className="size-5" />
</ToolbarButton>
</PopoverTrigger>
<PopoverContent className="w-full min-w-80" align="start" side="bottom">
<LinkEditBlock onSave={onSetLink} defaultText={text} />
</PopoverContent>
</Popover>
)
}
export { LinkEditPopover }

View File

@@ -0,0 +1,77 @@
import * as React from "react"
import { Separator } from "@/components/ui/separator"
import { ToolbarButton } from "../toolbar-button"
import {
CopyIcon,
ExternalLinkIcon,
LinkBreak2Icon,
} from "@radix-ui/react-icons"
interface LinkPopoverBlockProps {
url: string
onClear: () => void
onEdit: (e: React.MouseEvent<HTMLButtonElement>) => void
}
export const LinkPopoverBlock: React.FC<LinkPopoverBlockProps> = ({
url,
onClear,
onEdit,
}) => {
const [copyTitle, setCopyTitle] = React.useState<string>("Copy")
const handleCopy = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
navigator.clipboard
.writeText(url)
.then(() => {
setCopyTitle("Copied!")
setTimeout(() => setCopyTitle("Copy"), 1000)
})
.catch(console.error)
},
[url],
)
const handleOpenLink = React.useCallback(() => {
window.open(url, "_blank", "noopener,noreferrer")
}, [url])
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="Edit link"
onClick={onEdit}
className="w-auto px-2"
>
Edit link
</ToolbarButton>
<Separator orientation="vertical" />
<ToolbarButton
tooltip="Open link in a new tab"
onClick={handleOpenLink}
>
<ExternalLinkIcon className="size-4" />
</ToolbarButton>
<Separator orientation="vertical" />
<ToolbarButton tooltip="Clear link" onClick={onClear}>
<LinkBreak2Icon className="size-4" />
</ToolbarButton>
<Separator orientation="vertical" />
<ToolbarButton
tooltip={copyTitle}
onClick={handleCopy}
tooltipOptions={{
onPointerDownOutside: (e) => {
if (e.target === e.currentTarget) e.preventDefault()
},
}}
>
<CopyIcon className="size-4" />
</ToolbarButton>
</div>
</div>
)
}