or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

conversion-utilities.mddata-models.mdeditor-components.mdentity-system.mdindex.mdkey-bindings-utilities.mdtext-modification.md
tile.json

key-bindings-utilities.mddocs/

Key Bindings and Utilities

Utilities for key binding management, selection handling, and miscellaneous helper functions that support the Draft.js editor.

Capabilities

KeyBindingUtil

Utility functions for detecting keyboard modifiers and command keys across different platforms.

/**
 * Key binding utilities for cross-platform keyboard handling
 */
const KeyBindingUtil: {
  /**
   * Check if event has Ctrl key (or Cmd on Mac)
   * @param e - Keyboard event
   * @returns True if Ctrl/Cmd is pressed
   */
  isCtrlKeyCommand(e: SyntheticKeyboardEvent): boolean;
  
  /**
   * Check if event has Option key (Mac only)
   * @param e - Keyboard event
   * @returns True if Option is pressed on Mac
   */
  isOptionKeyCommand(e: SyntheticKeyboardEvent): boolean;
  
  /**
   * Check if event has the platform's command modifier
   * @param e - Keyboard event
   * @returns True if command modifier is pressed
   */
  hasCommandModifier(e: SyntheticKeyboardEvent): boolean;
};

Usage Examples:

import { KeyBindingUtil } from "draft-js";

// Custom key binding function
function myKeyBindingFn(e) {
  // Handle Ctrl+S / Cmd+S for save
  if (e.keyCode === 83 && KeyBindingUtil.hasCommandModifier(e)) {
    return 'save-content';
  }
  
  // Handle Ctrl+K / Cmd+K for link
  if (e.keyCode === 75 && KeyBindingUtil.hasCommandModifier(e)) {
    return 'add-link';
  }
  
  // Handle Alt+Arrow keys for word navigation
  if (KeyBindingUtil.isOptionKeyCommand(e)) {
    if (e.keyCode === 37) return 'move-word-left';  // Alt+Left
    if (e.keyCode === 39) return 'move-word-right'; // Alt+Right
  }
  
  // Fall back to default key bindings
  return getDefaultKeyBinding(e);
}

// Custom key command handler
function handleKeyCommand(command, editorState) {
  switch (command) {
    case 'save-content':
      saveContent(editorState);
      return 'handled';
      
    case 'add-link':
      addLink(editorState);
      return 'handled';
      
    case 'move-word-left':
      // Custom word navigation logic
      return 'handled';
      
    default:
      return RichUtils.handleKeyCommand(editorState, command);
  }
}

getDefaultKeyBinding

Default key binding function that maps keyboard events to Draft.js commands.

/**
 * Default key binding function for Draft.js editor
 * @param e - Keyboard event
 * @returns Command string or null
 */
function getDefaultKeyBinding(e: SyntheticKeyboardEvent): ?string;

Usage Examples:

import { getDefaultKeyBinding } from "draft-js";

// Extend default key bindings
function customKeyBindingFn(e) {
  // Add custom bindings
  if (e.keyCode === 9) { // Tab key
    return 'indent';
  }
  
  if (e.keyCode === 13 && e.shiftKey) { // Shift+Enter
    return 'soft-newline';
  }
  
  // Fall back to default bindings for standard commands
  return getDefaultKeyBinding(e);
}

// Use in editor
<Editor
  editorState={editorState}
  onChange={setEditorState}
  keyBindingFn={customKeyBindingFn}
  handleKeyCommand={handleKeyCommand}
/>

genKey (exported as genKey)

Utility function for generating unique keys for blocks and entities.

/**
 * Generate unique key for blocks and entities
 * @returns Random key string
 */
function genKey(): string;

Usage Examples:

import { genKey, ContentBlock } from "draft-js";

// Create new content block with unique key
function createNewBlock(text, type = 'paragraph') {
  return new ContentBlock({
    key: genKey(),
    type: type,
    text: text,
    characterList: List(Repeat(CharacterMetadata.EMPTY, text.length))
  });
}

// Generate keys for custom entities
function createCustomEntity(type, mutability, data) {
  const entityKey = genKey();
  // Store entity with custom key...
  return entityKey;
}

getVisibleSelectionRect

Utility for getting the visible selection rectangle in the viewport.

/**
 * Get visible selection rectangle
 * @param window - Window object
 * @returns ClientRect object or null if no visible selection
 */
function getVisibleSelectionRect(window: DOMWindow): ?ClientRect;

Usage Examples:

import { getVisibleSelectionRect } from "draft-js";

// Position popup relative to selection
function showSelectionPopup() {
  const selectionRect = getVisibleSelectionRect(window);
  
  if (selectionRect) {
    const popup = document.getElementById('selection-popup');
    popup.style.display = 'block';
    popup.style.top = `${selectionRect.top - 40}px`;
    popup.style.left = `${selectionRect.left}px`;
  }
}

// Hide popup when selection changes
function handleSelectionChange(editorState) {
  const selection = editorState.getSelection();
  
  if (selection.isCollapsed()) {
    const popup = document.getElementById('selection-popup');
    popup.style.display = 'none';
  } else {
    // Small delay to ensure DOM is updated
    setTimeout(showSelectionPopup, 10);
  }
}

BlockMapBuilder

Utility for building BlockMap structures from arrays of ContentBlocks.

/**
 * Utilities for building BlockMap structures
 */
const BlockMapBuilder: {
  /**
   * Create BlockMap from array of ContentBlocks
   * @param blocks - Array of ContentBlock instances
   * @returns OrderedMap of blocks keyed by block key
   */
  createFromArray(blocks: Array<ContentBlock>): BlockMap;
};

Usage Examples:

import { BlockMapBuilder, ContentBlock, CharacterMetadata } from "draft-js";

// Create blocks and build map
function createContentFromBlocks(blockData) {
  const blocks = blockData.map(({ text, type = 'paragraph' }) => {
    return new ContentBlock({
      key: genKey(),
      type: type,
      text: text,
      characterList: List(Repeat(CharacterMetadata.EMPTY, text.length))
    });
  });
  
  return BlockMapBuilder.createFromArray(blocks);
}

// Use with ContentState
function createContentState(blockData) {
  const blockMap = createContentFromBlocks(blockData);
  
  return ContentState.createFromBlockArray(
    blockMap.toArray(),
    {} // entity map
  );
}

// Example usage
const content = createContentState([
  { text: 'Welcome to Draft.js', type: 'header-one' },
  { text: 'This is a paragraph with some text.' },
  { text: 'This is another paragraph.' }
]);

Advanced Utility Functions

Additional utility functions for working with Draft.js content and editor state.

/**
 * Advanced utility functions for Draft.js
 */

// Get all text content with formatting preserved
function getFormattedText(contentState: ContentState): Array<{
  text: string,
  style: DraftInlineStyle,
  entityKey: ?string,
  blockType: DraftBlockType
}> {
  const result = [];
  const blocks = contentState.getBlocksAsArray();
  
  blocks.forEach(block => {
    const text = block.getText();
    const characterList = block.getCharacterList();
    const blockType = block.getType();
    
    for (let i = 0; i < text.length; i++) {
      const char = text[i];
      const metadata = characterList.get(i);
      
      result.push({
        text: char,
        style: metadata.getStyle(),
        entityKey: metadata.getEntity(),
        blockType: blockType
      });
    }
  });
  
  return result;
}

// Find text occurrences with position info
function findText(contentState: ContentState, searchText: string): Array<{
  blockKey: string,
  start: number,
  end: number,
  text: string
}> {
  const results = [];
  const blocks = contentState.getBlocksAsArray();
  
  blocks.forEach(block => {
    const text = block.getText();
    const blockKey = block.getKey();
    let index = 0;
    
    while ((index = text.indexOf(searchText, index)) !== -1) {
      results.push({
        blockKey,
        start: index,
        end: index + searchText.length,
        text: searchText
      });
      index += searchText.length;
    }
  });
  
  return results;
}

// Get word boundaries for selection
function getWordBoundaries(contentState: ContentState, blockKey: string, offset: number): {
  start: number,
  end: number
} {
  const block = contentState.getBlockForKey(blockKey);
  const text = block.getText();
  
  // Find word start
  let start = offset;
  while (start > 0 && /\w/.test(text[start - 1])) {
    start--;
  }
  
  // Find word end
  let end = offset;
  while (end < text.length && /\w/.test(text[end])) {
    end++;
  }
  
  return { start, end };
}

// Select word at position
function selectWordAt(editorState: EditorState, blockKey: string, offset: number): EditorState {
  const contentState = editorState.getCurrentContent();
  const { start, end } = getWordBoundaries(contentState, blockKey, offset);
  
  const selection = SelectionState.createEmpty(blockKey).merge({
    anchorOffset: start,
    focusOffset: end
  });
  
  return EditorState.forceSelection(editorState, selection);
}

Complete Utility Example:

import React, { useState, useRef } from "react";
import { 
  Editor, 
  EditorState, 
  KeyBindingUtil, 
  getDefaultKeyBinding,
  getVisibleSelectionRect,
  RichUtils 
} from "draft-js";

function AdvancedEditor() {
  const [editorState, setEditorState] = useState(EditorState.createEmpty());
  const [showPopup, setShowPopup] = useState(false);
  const editorRef = useRef(null);
  
  // Custom key binding with utilities
  const keyBindingFn = (e) => {
    // Save with Ctrl+S / Cmd+S
    if (e.keyCode === 83 && KeyBindingUtil.hasCommandModifier(e)) {
      return 'save';
    }
    
    // Show popup with Ctrl+/ / Cmd+/
    if (e.keyCode === 191 && KeyBindingUtil.hasCommandModifier(e)) {
      return 'show-popup';
    }
    
    return getDefaultKeyBinding(e);
  };
  
  // Handle key commands
  const handleKeyCommand = (command, editorState) => {
    if (command === 'save') {
      console.log('Saving content...');
      return 'handled';
    }
    
    if (command === 'show-popup') {
      const rect = getVisibleSelectionRect(window);
      if (rect) {
        setShowPopup(true);
      }
      return 'handled';
    }
    
    const newState = RichUtils.handleKeyCommand(editorState, command);
    if (newState) {
      setEditorState(newState);
      return 'handled';
    }
    
    return 'not-handled';
  };
  
  // Handle selection change
  const handleChange = (newEditorState) => {
    setEditorState(newEditorState);
    
    // Hide popup when selection changes
    const selection = newEditorState.getSelection();
    if (selection.isCollapsed()) {
      setShowPopup(false);
    }
  };
  
  return (
    <div style={{ position: 'relative' }}>
      <Editor
        ref={editorRef}
        editorState={editorState}
        onChange={handleChange}
        keyBindingFn={keyBindingFn}
        handleKeyCommand={handleKeyCommand}
        placeholder="Type here... (Ctrl+S to save, Ctrl+/ for popup)"
      />
      
      {showPopup && (
        <div 
          style={{
            position: 'absolute',
            backgroundColor: 'black',
            color: 'white',
            padding: '8px',
            borderRadius: '4px',
            fontSize: '12px',
            zIndex: 1000
          }}
        >
          Selection popup!
        </div>
      )}
    </div>
  );
}

Types

Utility-Related Types

// Keyboard event type
type SyntheticKeyboardEvent = Event & {
  keyCode: number,
  which: number,
  key: string,
  ctrlKey: boolean,
  metaKey: boolean,
  altKey: boolean,
  shiftKey: boolean,
  preventDefault: () => void,
  stopPropagation: () => void
};

// DOM Window type
type DOMWindow = Window;

// Client rectangle
interface ClientRect {
  bottom: number;
  height: number;
  left: number;
  right: number;
  top: number;
  width: number;
}

// Key binding function type
type KeyBindingFunction = (e: SyntheticKeyboardEvent) => ?string;

// Block map type
type BlockMap = OrderedMap<string, ContentBlock>;

// Search result type
interface SearchResult {
  blockKey: string;
  start: number;
  end: number;
  text: string;
}

// Word boundary type
interface WordBoundary {
  start: number;
  end: number;
}