A comprehensive DOM manipulation library providing type-safe, functional utilities for elements, events, properties, and selections.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Text selection, range management, and cursor positioning utilities with cross-browser selection handling, position calculations, and comprehensive range operations.
Core selection operations for getting and setting text selections in browser windows.
/**
* Get current selection from window
* @param win - Window to get selection from
* @returns Current selection if one exists
*/
function get(win: SugarElement<Window>): Optional<SimSelection>;
/**
* Set selection in window
* @param win - Window to set selection in
* @param selection - Selection to apply
*/
function set(win: SugarElement<Window>, selection: SimSelection): void;
/**
* Clear selection in window
* @param win - Window to clear selection from
*/
function clear(win: SugarElement<Window>): void;
/**
* Get selection from element's owner window
* @param element - Element to get selection context from
* @returns Current selection if one exists
*/
function getFromElement(element: SugarElement<Node>): Optional<SimSelection>;
/**
* Set selection using element's owner window
* @param element - Element providing selection context
* @param selection - Selection to apply
*/
function setToElement(element: SugarElement<Node>, selection: SimSelection): void;Usage Examples:
import { WindowSelection, SugarElement } from "@ephox/sugar";
const win = SugarElement.fromDom(window);
// Get current selection
const currentSelection = WindowSelection.get(win);
currentSelection.each(selection => {
console.log('Found selection');
});
// Set new selection
const newSelection = SimSelection.exact(
startElement, 0,
endElement, 5
);
WindowSelection.set(win, newSelection);
// Clear selection
WindowSelection.clear(win);Simplified range representation for cross-browser compatibility.
/**
* Create simple range object
* @param start - Starting element
* @param soffset - Starting offset within element
* @param finish - Ending element
* @param foffset - Ending offset within element
* @returns Range object
*/
function create(
start: SugarElement<Node>,
soffset: number,
finish: SugarElement<Node>,
foffset: number
): SimRange;
interface SimRange {
readonly start: SugarElement<Node>;
readonly soffset: number;
readonly finish: SugarElement<Node>;
readonly foffset: number;
}Usage Examples:
import { SimRange, SugarElement } from "@ephox/sugar";
const startElement = SugarElement.fromText('Hello world');
const endElement = SugarElement.fromText('Goodbye');
// Create range from start of first text to end of second
const range = SimRange.create(startElement, 0, endElement, 7);
console.log('Range starts at:', range.start.dom.textContent);
console.log('Range starts at offset:', range.soffset);
console.log('Range ends at:', range.finish.dom.textContent);
console.log('Range ends at offset:', range.foffset);Unified selection interface handling multiple selection types with pattern matching.
/**
* Create selection from DOM Range object
* @param rng - Native DOM Range
* @returns SimSelection wrapping the range
*/
function domRange(rng: Range): SimSelection;
/**
* Create selection from relative positions
* @param startSitu - Starting position
* @param finishSitu - Ending position
* @returns SimSelection for relative positions
*/
function relative(startSitu: Situ, finishSitu: Situ): SimSelection;
/**
* Create selection from exact element positions
* @param start - Starting element
* @param soffset - Starting offset
* @param finish - Ending element
* @param foffset - Ending offset
* @returns SimSelection for exact positions
*/
function exact(
start: SugarElement<Node>,
soffset: number,
finish: SugarElement<Node>,
foffset: number
): SimSelection;
/**
* Create selection from SimRange object
* @param simRange - Range object to convert
* @returns SimSelection wrapping the range
*/
function exactFromRange(simRange: SimRange): SimSelection;
/**
* Get window associated with selection
* @param selection - Selection to get window for
* @returns Window element for the selection
*/
function getWin(selection: SimSelection): SugarElement<Window>;
interface SimSelection {
/** Pattern matching for different selection types */
fold: <U>(
domRange: (rng: Range) => U,
relative: (startSitu: Situ, finishSitu: Situ) => U,
exact: (start: SugarElement<Node>, soffset: number, finish: SugarElement<Node>, foffset: number) => U
) => U;
/** Alternative pattern matching syntax */
match: <U>(branches: {
domRange: (rng: Range) => U;
relative: (startSitu: Situ, finishSitu: Situ) => U;
exact: (start: SugarElement<Node>, soffset: number, finish: SugarElement<Node>, foffset: number) => U;
}) => U;
/** Debug logging */
log: (label: string) => void;
}Usage Examples:
import { SimSelection, SugarElement } from "@ephox/sugar";
// Create different types of selections
const domSelection = SimSelection.domRange(document.getSelection().getRangeAt(0));
const exactSelection = SimSelection.exact(startEl, 0, endEl, 5);
// Use pattern matching to handle different selection types
exactSelection.fold(
(domRange) => {
console.log('DOM Range:', domRange.toString());
return 'dom';
},
(startSitu, finishSitu) => {
console.log('Relative selection');
return 'relative';
},
(start, soffset, finish, foffset) => {
console.log(`Exact: ${start.dom.textContent}[${soffset}] to ${finish.dom.textContent}[${foffset}]`);
return 'exact';
}
);
// Alternative match syntax
const selectionType = exactSelection.match({
domRange: () => 'native-range',
relative: () => 'relative-position',
exact: () => 'exact-position'
});Position objects representing locations before, on, or after elements.
/**
* Create position before an element
* @param element - Element to position before
* @returns Position object
*/
function before(element: SugarElement<Node>): Situ;
/**
* Create position at specific offset within element
* @param element - Element containing the position
* @param offset - Offset within the element
* @returns Position object
*/
function on(element: SugarElement<Node>, offset: number): Situ;
/**
* Create position after an element
* @param element - Element to position after
* @returns Position object
*/
function after(element: SugarElement<Node>): Situ;
/**
* Pattern match on position type
* @param subject - Position to match on
* @param onBefore - Handler for before positions
* @param onOn - Handler for on positions
* @param onAfter - Handler for after positions
* @returns Result of handler function
*/
function cata<U>(
subject: Situ,
onBefore: (element: SugarElement<Node>) => U,
onOn: (element: SugarElement<Node>, offset: number) => U,
onAfter: (element: SugarElement<Node>) => U
): U;
/**
* Get element from position
* @param situ - Position to extract element from
* @returns Element referenced by position
*/
function getStart(situ: Situ): SugarElement<Node>;
interface Situ {
/** Pattern matching for position types */
fold: <U>(
before: (element: SugarElement<Node>) => U,
on: (element: SugarElement<Node>, offset: number) => U,
after: (element: SugarElement<Node>) => U
) => U;
/** Alternative pattern matching syntax */
match: <U>(branches: {
before: (element: SugarElement<Node>) => U;
on: (element: SugarElement<Node>, offset: number) => U;
after: (element: SugarElement<Node>) => U;
}) => U;
/** Debug logging */
log: (label: string) => void;
}Usage Examples:
import { Situ, SugarElement } from "@ephox/sugar";
const element = SugarElement.fromTag('p');
// Create different position types
const beforePos = Situ.before(element);
const onPos = Situ.on(element, 5);
const afterPos = Situ.after(element);
// Use pattern matching
const description = onPos.fold(
(el) => `Before ${el.dom.tagName}`,
(el, offset) => `At offset ${offset} in ${el.dom.tagName}`,
(el) => `After ${el.dom.tagName}`
);
// Extract element from position
const posElement = Situ.getStart(onPos);
console.log('Position element:', posElement.dom.tagName);
// Use in selection creation
const selection = SimSelection.relative(beforePos, afterPos);Geometric utilities for selection bounds and element positioning.
/**
* Get bounding rectangle for selection
* @param selection - Selection to get bounds for
* @returns Rectangle bounds if calculable
*/
function getBounds(selection: SimSelection): Optional<StructRect>;
/**
* Get bounding rectangle for range
* @param range - Range to get bounds for
* @returns Rectangle bounds if calculable
*/
function getRangeRect(range: SimRange): Optional<StructRect>;
/**
* Get bounding rectangle for element
* @param element - Element to get bounds for
* @returns Element's bounding rectangle
*/
function getElementRect(element: SugarElement<Element>): StructRect;
interface StructRect {
left: () => number;
top: () => number;
right: () => number;
bottom: () => number;
width: () => number;
height: () => number;
}
interface RawRect {
left: number;
top: number;
right: number;
bottom: number;
width: number;
height: number;
}
/**
* Convert structured rectangle to raw object
* @param sr - Structured rectangle
* @returns Raw rectangle object
*/
function toRaw(sr: StructRect): RawRect;Usage Examples:
import { Edge, RawRect, StructRect, SugarElement } from "@ephox/sugar";
// Get selection bounds
const selection = WindowSelection.get(window);
selection.each(sel => {
const bounds = SelectionBounds.getBounds(sel);
bounds.each(rect => {
const raw = Rect.toRaw(rect);
console.log(`Selection bounds: ${raw.width}x${raw.height} at (${raw.left}, ${raw.top})`);
});
});
// Get element bounds
const element = SugarElement.fromTag('div');
const elementRect = SelectionBounds.getElementRect(element);
console.log(`Element dimensions: ${elementRect.width()}x${elementRect.height()}`);Advanced selection operations and manipulations.
/**
* Expand selection to word boundaries
* @param selection - Selection to expand
* @returns Expanded selection
*/
function expandToWord(selection: SimSelection): SimSelection;
/**
* Collapse selection to start position
* @param selection - Selection to collapse
* @returns Collapsed selection at start
*/
function collapseToStart(selection: SimSelection): SimSelection;
/**
* Collapse selection to end position
* @param selection - Selection to collapse
* @returns Collapsed selection at end
*/
function collapseToEnd(selection: SimSelection): SimSelection;
/**
* Check if selection is collapsed (zero width)
* @param selection - Selection to check
* @returns True if selection has zero width
*/
function isCollapsed(selection: SimSelection): boolean;
/**
* Get text content of selection
* @param selection - Selection to extract text from
* @returns Text content within selection
*/
function getText(selection: SimSelection): string;
/**
* Replace selection content with text
* @param selection - Selection to replace
* @param text - Text to insert
* @returns New selection after replacement
*/
function replaceWith(selection: SimSelection, text: string): SimSelection;
/**
* Delete selection content
* @param selection - Selection to delete
* @returns New collapsed selection after deletion
*/
function deleteContents(selection: SimSelection): SimSelection;Convert between different range representations.
/**
* Convert SimSelection to native DOM Range
* @param selection - Selection to convert
* @returns Native DOM Range object
*/
function toRange(selection: SimSelection): Range;
/**
* Convert native DOM Range to SimSelection
* @param range - Native range to convert
* @returns SimSelection wrapping the range
*/
function fromRange(range: Range): SimSelection;
/**
* Convert SimRange to SimSelection
* @param simRange - Simple range to convert
* @returns SimSelection for the range
*/
function fromSimRange(simRange: SimRange): SimSelection;
/**
* Convert SimSelection to SimRange (if possible)
* @param selection - Selection to convert
* @returns Simple range if selection can be converted
*/
function toSimRange(selection: SimSelection): Optional<SimRange>;// Core selection types
interface SimRange {
readonly start: SugarElement<Node>;
readonly soffset: number;
readonly finish: SugarElement<Node>;
readonly foffset: number;
}
interface SimSelection {
fold: <U>(
domRange: (rng: Range) => U,
relative: (startSitu: Situ, finishSitu: Situ) => U,
exact: (start: SugarElement<Node>, soffset: number, finish: SugarElement<Node>, foffset: number) => U
) => U;
match: <U>(branches: {
domRange: (rng: Range) => U;
relative: (startSitu: Situ, finishSitu: Situ) => U;
exact: (start: SugarElement<Node>, soffset: number, finish: SugarElement<Node>, foffset: number) => U;
}) => U;
log: (label: string) => void;
}
interface Situ {
fold: <U>(
before: (element: SugarElement<Node>) => U,
on: (element: SugarElement<Node>, offset: number) => U,
after: (element: SugarElement<Node>) => U
) => U;
match: <U>(branches: {
before: (element: SugarElement<Node>) => U;
on: (element: SugarElement<Node>, offset: number) => U;
after: (element: SugarElement<Node>) => U;
}) => U;
log: (label: string) => void;
}
// Rectangle types for bounds calculation
interface StructRect {
left: () => number;
top: () => number;
right: () => number;
bottom: () => number;
width: () => number;
height: () => number;
}
interface RawRect {
left: number;
top: number;
right: number;
bottom: number;
width: number;
height: number;
}Install with Tessl CLI
npx tessl i tessl/npm-ephox--sugar