CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-udecode--plate-link

Link plugin for Plate rich text editor providing hyperlink functionality with URL validation, keyboard shortcuts, and UI components

Pending
Overview
Eval results
Files

floating-interface.mddocs/

Floating Link Interface

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.

Capabilities

Floating Link Triggers

triggerFloatingLink

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);
}

triggerFloatingLinkInsert

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");
  }
}

triggerFloatingLinkEdit

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);
}

Floating Insert Interface

useFloatingLinkInsert

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>
  );
}

Floating Edit Interface

useFloatingLinkEdit

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>
  );
}

Virtual Floating Positioning

useVirtualFloatingLink

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>
  );
}

Keyboard Handling

useFloatingLinkEscape

Handles escape key behavior for closing floating link interfaces.

/**
 * Handle escape key behavior for floating link UI
 */
function useFloatingLinkEscape(): void;

useFloatingLinkEnter

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>
  );
}

Advanced Floating Patterns

Custom Floating UI with Full Control

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>
  );
}

Contextual Floating Behavior

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

docs

core-plugin.md

floating-interface.md

index.md

react-components.md

react-integration.md

transforms.md

url-utils.md

tile.json