Link plugin for Plate rich text editor providing hyperlink functionality with URL validation, keyboard shortcuts, and UI components
—
Complete floating UI system for inserting and editing links with keyboard shortcuts, positioning, and form management. Provides modal-style interfaces that appear contextually based on user interactions.
Main entry point for showing floating link interface based on current mode and state.
/**
* Trigger floating link interface based on current mode
* @param editor - Slate editor instance
* @param options - Optional focus behavior
*/
function triggerFloatingLink(
editor: SlateEditor,
options?: { focused?: boolean }
): void;Usage Examples:
import { triggerFloatingLink } from "@udecode/plate-link/react";
// Trigger from keyboard shortcut
function handleKeyboardShortcut(editor: SlateEditor) {
triggerFloatingLink(editor, { focused: true });
}
// Trigger from toolbar button
function handleToolbarClick(editor: SlateEditor) {
triggerFloatingLink(editor);
}Specifically triggers the floating interface in insert mode for creating new links.
/**
* Trigger floating link interface in insert mode
* @param editor - Slate editor instance
* @param options - Optional focus behavior
* @returns Whether the trigger was successful
*/
function triggerFloatingLinkInsert(
editor: SlateEditor,
options?: { focused?: boolean }
): boolean | undefined;Usage Examples:
import { triggerFloatingLinkInsert } from "@udecode/plate-link/react";
// Trigger insert mode
function handleAddLink(editor: SlateEditor) {
const success = triggerFloatingLinkInsert(editor, { focused: true });
if (!success) {
console.log("Cannot insert link in current context");
}
}
// Check selection before triggering
function handleToolbarInsert(editor: SlateEditor) {
if (editor.selection) {
triggerFloatingLinkInsert(editor);
} else {
alert("Please select text to create a link");
}
}Triggers the floating interface in edit mode for modifying existing links.
/**
* Trigger floating link interface in edit mode for existing links
* @param editor - Slate editor instance
* @returns Whether edit mode was successfully triggered
*/
function triggerFloatingLinkEdit(
editor: SlateEditor
): boolean | undefined;Usage Examples:
import { triggerFloatingLinkEdit } from "@udecode/plate-link/react";
// Trigger edit mode
function handleEditLink(editor: SlateEditor) {
const success = triggerFloatingLinkEdit(editor);
if (!success) {
console.log("No link found at current selection");
}
}
// Context menu integration
function handleLinkContextMenu(editor: SlateEditor, event: MouseEvent) {
event.preventDefault();
triggerFloatingLinkEdit(editor);
}Complete hook system for managing floating link insertion UI with positioning and form handling.
/**
* Get state for floating link insert interface
* @param options - Optional floating toolbar configuration
* @returns State object with floating UI data
*/
function useFloatingLinkInsertState(options?: LinkFloatingToolbarState): {
floating: ReturnType<typeof useVirtualFloating>;
focused: boolean;
isOpen: boolean;
readOnly: boolean;
triggerFloatingLinkHotkeys: string;
};
/**
* Get props and behavior for floating link insert interface
* @param state - State from useFloatingLinkInsertState
* @returns Interface props, refs, and input handling
*/
function useFloatingLinkInsert(
state: ReturnType<typeof useFloatingLinkInsertState>
): {
hidden: boolean;
props: object;
ref: RefObject<HTMLElement>;
textInputProps: object;
};
interface LinkFloatingToolbarState {
floatingOptions?: UseVirtualFloatingOptions;
}Usage Examples:
import React from 'react';
import {
useFloatingLinkInsertState,
useFloatingLinkInsert,
FloatingLinkUrlInput
} from "@udecode/plate-link/react";
function FloatingLinkInsertPopover() {
const state = useFloatingLinkInsertState({
floatingOptions: {
placement: 'bottom-start',
offset: 4
}
});
const { hidden, props, ref, textInputProps } = useFloatingLinkInsert(state);
if (hidden) return null;
return (
<div
{...props}
ref={ref}
className="floating-link-popover"
>
<div className="popover-content">
<FloatingLinkUrlInput />
<div className="popover-actions">
<button type="submit">Add Link</button>
<button type="button" onClick={() => /* close */}>
Cancel
</button>
</div>
</div>
</div>
);
}Complete hook system for managing floating link editing UI with existing link data population.
/**
* Get state for floating link edit interface
* @param options - Optional floating toolbar configuration
* @returns Comprehensive state with editor and floating data
*/
function useFloatingLinkEditState(options?: LinkFloatingToolbarState): {
editor: SlateEditor;
floating: ReturnType<typeof useVirtualFloating>;
isEditing: boolean;
isOpen: boolean;
readOnly: boolean;
triggerFloatingLinkHotkeys: string;
versionEditor: number;
};
/**
* Get props and behavior for floating link edit interface
* @param state - State from useFloatingLinkEditState
* @returns Edit interface props and button handlers
*/
function useFloatingLinkEdit(
state: ReturnType<typeof useFloatingLinkEditState>
): {
editButtonProps: object;
props: object;
ref: RefObject<HTMLElement>;
unlinkButtonProps: object;
};Usage Examples:
import React from 'react';
import {
useFloatingLinkEditState,
useFloatingLinkEdit,
FloatingLinkUrlInput,
FloatingLinkNewTabInput
} from "@udecode/plate-link/react";
function FloatingLinkEditPopover() {
const state = useFloatingLinkEditState();
const {
editButtonProps,
props,
ref,
unlinkButtonProps
} = useFloatingLinkEdit(state);
if (!state.isOpen) return null;
return (
<div
{...props}
ref={ref}
className="floating-link-edit-popover"
>
<div className="popover-content">
<FloatingLinkUrlInput />
<FloatingLinkNewTabInput />
<div className="popover-actions">
<button {...editButtonProps}>
Save Changes
</button>
<button {...unlinkButtonProps} className="btn-danger">
Remove Link
</button>
</div>
</div>
</div>
);
}Manages virtual floating positioning for link UI elements relative to editor selection.
/**
* Manage virtual floating positioning for link UI
* @param config - Editor ID and floating options
* @returns Virtual floating positioning data
*/
function useVirtualFloatingLink({
editorId,
...floatingOptions
}: {
editorId: string;
} & UseVirtualFloatingOptions): ReturnType<typeof useVirtualFloating>;Usage Examples:
import React from 'react';
import { useVirtualFloatingLink } from "@udecode/plate-link/react";
import { useEditorPlugin } from '@udecode/plate/react';
import { LinkPlugin } from '@udecode/plate-link/react';
function CustomFloatingLink() {
const { editor } = useEditorPlugin(LinkPlugin);
const floating = useVirtualFloatingLink({
editorId: editor.id,
placement: 'bottom',
offset: 8,
autoUpdate: true
});
return (
<div
ref={floating.refs.setFloating}
style={{
position: floating.strategy,
top: floating.y ?? 0,
left: floating.x ?? 0,
zIndex: 1000
}}
className="custom-floating-link"
>
{/* Floating UI content */}
</div>
);
}Handles escape key behavior for closing floating link interfaces.
/**
* Handle escape key behavior for floating link UI
*/
function useFloatingLinkEscape(): void;Handles enter key submission for floating link forms.
/**
* Handle enter key submission for floating link forms
*/
function useFloatingLinkEnter(): void;Usage Examples:
import React from 'react';
import {
useFloatingLinkEscape,
useFloatingLinkEnter
} from "@udecode/plate-link/react";
function FloatingLinkForm() {
// Enable keyboard handling
useFloatingLinkEscape();
useFloatingLinkEnter();
return (
<form className="floating-link-form">
{/* Form fields */}
<input placeholder="Enter URL..." />
<div className="form-hint">
Press Enter to save, Escape to cancel
</div>
</form>
);
}import React from 'react';
import {
useFloatingLinkInsertState,
useFloatingLinkInsert,
useFloatingLinkEscape,
useFloatingLinkEnter,
submitFloatingLink
} from "@udecode/plate-link/react";
import { useEditorPlugin } from '@udecode/plate/react';
import { LinkPlugin } from '@udecode/plate-link/react';
function AdvancedFloatingLink() {
const { editor } = useEditorPlugin(LinkPlugin);
const state = useFloatingLinkInsertState({
floatingOptions: {
placement: 'bottom-start',
middleware: [
{ name: 'offset', options: { mainAxis: 8 } },
{ name: 'flip' },
{ name: 'shift', options: { padding: 8 } }
]
}
});
const { hidden, props, ref } = useFloatingLinkInsert(state);
// Enable keyboard shortcuts
useFloatingLinkEscape();
useFloatingLinkEnter();
const handleSubmit = () => {
const success = submitFloatingLink(editor);
if (success) {
editor.api.floatingLink.hide();
}
};
if (hidden) return null;
return (
<div
{...props}
ref={ref}
className="advanced-floating-link"
>
<div className="floating-header">
<h3>Add Link</h3>
<button
onClick={() => editor.api.floatingLink.hide()}
className="close-button"
>
×
</button>
</div>
<form onSubmit={handleSubmit}>
<div className="form-fields">
<input placeholder="https://example.com" />
<label>
<input type="checkbox" />
Open in new tab
</label>
</div>
<div className="form-actions">
<button type="submit">Add Link</button>
<button type="button" onClick={() => editor.api.floatingLink.hide()}>
Cancel
</button>
</div>
</form>
</div>
);
}import {
triggerFloatingLinkInsert,
triggerFloatingLinkEdit
} from "@udecode/plate-link/react";
function handleFloatingLinkTrigger(editor: SlateEditor) {
// Check if cursor is in existing link
const linkEntry = editor.api.above({
match: { type: 'a' }
});
if (linkEntry) {
// Edit existing link
triggerFloatingLinkEdit(editor);
} else {
// Insert new link
triggerFloatingLinkInsert(editor, { focused: true });
}
}
// Keyboard shortcut handler
function setupFloatingLinkShortcut(editor: SlateEditor) {
const handleKeyDown = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
event.preventDefault();
handleFloatingLinkTrigger(editor);
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}Install with Tessl CLI
npx tessl i tessl/npm-udecode--plate-link