"use client"; import { flip, offset, type UseVirtualFloatingOptions } from "@platejs/floating"; import { getLinkAttributes } from "@platejs/link"; import { FloatingLinkUrlInput, type LinkFloatingToolbarState, useFloatingLinkEdit, useFloatingLinkEditState, useFloatingLinkInsert, useFloatingLinkInsertState, } from "@platejs/link/react"; import { cva } from "class-variance-authority"; import { ExternalLink, Link, Text, Unlink } from "lucide-react"; import type { TLinkElement } from "platejs"; import { KEYS } from "platejs"; import { useEditorRef, useEditorSelection, useFormInputProps, usePluginOption, } from "platejs/react"; import * as React from "react"; import { buttonVariants } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; const popoverVariants = cva( "z-50 w-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-hidden" ); const inputVariants = cva( "flex h-[28px] w-full rounded-md border-none bg-transparent px-1.5 py-1 text-base placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-transparent md:text-sm" ); export function LinkFloatingToolbar({ state }: { state?: LinkFloatingToolbarState }) { const activeCommentId = usePluginOption({ key: KEYS.comment }, "activeId"); const activeSuggestionId = usePluginOption({ key: KEYS.suggestion }, "activeId"); const floatingOptions: UseVirtualFloatingOptions = React.useMemo( () => ({ middleware: [ offset(8), flip({ fallbackPlacements: ["bottom-end", "top-start", "top-end"], padding: 12, }), ], placement: activeSuggestionId || activeCommentId ? "top-start" : "bottom-start", }), [activeCommentId, activeSuggestionId] ); const insertState = useFloatingLinkInsertState({ ...state, floatingOptions: { ...floatingOptions, ...state?.floatingOptions, }, }); const { hidden, props: insertProps, ref: insertRef, textInputProps, } = useFloatingLinkInsert(insertState); const editState = useFloatingLinkEditState({ ...state, floatingOptions: { ...floatingOptions, ...state?.floatingOptions, }, }); const { editButtonProps, props: editProps, ref: editRef, unlinkButtonProps, } = useFloatingLinkEdit(editState); const inputProps = useFormInputProps({ preventDefaultOnEnterKeydown: true, }); if (hidden) return null; const input = (
); const editContent = editState.isEditing ? ( input ) : (
); return ( <>
{input}
{editContent}
); } function LinkOpenButton() { const editor = useEditorRef(); const selection = useEditorSelection(); const attributes = React.useMemo( () => { const entry = editor.api.node({ match: { type: editor.getType(KEYS.link) }, }); if (!entry) { return {}; } const [element] = entry; return getLinkAttributes(editor, element); }, // eslint-disable-next-line react-hooks/exhaustive-deps [editor, selection] ); return ( { e.stopPropagation(); }} aria-label="Open link in a new tab" target="_blank" > ); }