or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

additional-components.mdcanvas-operations.mdimage-manipulation.mdindex.mdjavascript-api.mdselection-management.mdutility-functions.md
tile.json

utility-functions.mddocs/

Utility Functions

Comprehensive set of utility functions for type checking, event handling, DOM manipulation, and mathematical operations. These utilities are re-exported from the @cropper/utils package and provide essential functionality for building cropping applications.

Capabilities

Type Checking Functions

Functions for runtime type validation and checking.

/**
 * Check if a value is a string
 * @param value - Value to check
 * @returns True if value is a string
 */
function isString(value: unknown): value is string;

/**
 * Check if a value is a number
 * @param value - Value to check  
 * @returns True if value is a number (not NaN)
 */
function isNumber(value: unknown): value is number;

/**
 * Check if a value is a positive number
 * @param value - Value to check
 * @returns True if value is a positive number
 */
function isPositiveNumber(value: unknown): value is number;

/**
 * Check if a value is undefined
 * @param value - Value to check
 * @returns True if value is undefined
 */
function isUndefined(value: unknown): value is undefined;

/**
 * Check if a value is an object
 * @param value - Value to check
 * @returns True if value is an object (not null, array, or function)
 */
function isObject(value: unknown): value is object;

/**
 * Check if a value is a plain object
 * @param value - Value to check
 * @returns True if value is a plain object (created by {} or new Object())
 */
function isPlainObject(value: unknown): value is object;

/**
 * Check if a value is a function
 * @param value - Value to check
 * @returns True if value is a function
 */
function isFunction(value: unknown): value is Function;

/**
 * Check if a node is a DOM element
 * @param node - Node to check
 * @returns True if node is an Element
 */
function isElement(node: unknown): node is Element;

/**
 * Check if a value is NaN
 * @param value - Value to check
 * @returns True if value is NaN
 */
function isNaN(value: unknown): boolean;

Usage Examples:

import { isString, isNumber, isElement, isFunction } from 'cropperjs';

// Type checking
if (isString(value)) {
  console.log('String length:', value.length);
}

if (isNumber(scale) && scale > 0) {
  image.$zoom(scale);
}

if (isElement(container)) {
  new Cropper(imageElement, { container });
}

if (isFunction(callback)) {
  callback(result);
}

// Validation examples
function validateCropperOptions(options) {
  if (!isObject(options)) {
    throw new Error('Options must be an object');
  }
  
  if (options.container && !isElement(options.container) && !isString(options.container)) {
    throw new Error('Container must be an element or selector string');
  }
}

String Transformation

Functions for converting between different string formats.

/**
 * Convert camelCase to kebab-case
 * @param value - CamelCase string to convert
 * @returns Kebab-case string
 */
function toKebabCase(value: string): string;

/**
 * Convert kebab-case to camelCase
 * @param value - Kebab-case string to convert
 * @returns CamelCase string
 */
function toCamelCase(value: string): string;

Usage Examples:

import { toKebabCase, toCamelCase } from 'cropperjs';

// Convert CSS property names
const cssProperty = toKebabCase('backgroundColor'); // 'background-color'
const jsProperty = toCamelCase('font-size'); // 'fontSize'

// Convert HTML attributes
const htmlAttribute = toKebabCase('contentEditable'); // 'content-editable'
const domProperty = toCamelCase('data-theme-color'); // 'dataThemeColor'

// Common conversions
toKebabCase('themeColor');    // 'theme-color'
toKebabCase('initialCoverage'); // 'initial-coverage'
toCamelCase('cropper-canvas'); // 'cropperCanvas'
toCamelCase('scale-step');     // 'scaleStep'

Event Handling

Comprehensive event management utilities for DOM interactions.

/**
 * Add event listener to target
 * @param target - Event target (element, document, window, etc.)
 * @param types - Event type(s) (space-separated for multiple)
 * @param listener - Event listener function or object
 * @param options - Event listener options
 */
function on(
  target: EventTarget, 
  types: string, 
  listener: EventListenerOrEventListenerObject, 
  options?: AddEventListenerOptions
): void;

/**
 * Remove event listener from target
 * @param target - Event target
 * @param types - Event type(s) (space-separated for multiple)
 * @param listener - Event listener function or object
 * @param options - Event listener options
 */
function off(
  target: EventTarget, 
  types: string, 
  listener: EventListenerOrEventListenerObject, 
  options?: EventListenerOptions
): void;

/**
 * Add one-time event listener
 * @param target - Event target
 * @param types - Event type(s) (space-separated for multiple)
 * @param listener - Event listener function or object
 * @param options - Event listener options
 */
function once(
  target: EventTarget, 
  types: string, 
  listener: EventListenerOrEventListenerObject, 
  options?: AddEventListenerOptions
): void;

/**
 * Dispatch custom event on target
 * @param target - Event target
 * @param type - Event type
 * @param detail - Event detail data
 * @param options - Event options
 * @returns True if event was not cancelled
 */
function emit(
  target: EventTarget, 
  type: string, 
  detail?: unknown, 
  options?: EventInit
): boolean;

Usage Examples:

import { on, off, once, emit } from 'cropperjs';

const canvas = document.querySelector('cropper-canvas');
const image = document.querySelector('cropper-image');

// Add single event listener
on(canvas, 'action', (event) => {
  console.log('Action:', event.detail.action);
});

// Add multiple event listeners
on(image, 'load error', (event) => {
  console.log('Image event:', event.type);
});

// Add with options
on(canvas, 'wheel', handleWheel, { passive: false });

// One-time listener
once(image, 'ready', () => {
  console.log('Image is ready');
});

// Remove listener
const handleClick = (event) => console.log('Clicked');
on(canvas, 'click', handleClick);
off(canvas, 'click', handleClick);

// Emit custom events
emit(canvas, 'customaction', { 
  action: 'crop',
  selection: { x: 50, y: 30, width: 200, height: 150 }
});

// Event delegation example
on(document, 'action', (event) => {
  if (event.target.matches('cropper-selection')) {
    console.log('Selection action:', event.detail);
  }
});

Geometry and Transform Utilities

Mathematical functions for geometry calculations and matrix transformations.

/**
 * Get element offset relative to document
 * @param element - Element to get offset for
 * @returns Object with left and top offset values
 */
function getOffset(element: Element): { left: number; top: number };

/**
 * Convert angle to radians
 * @param angle - Angle value (number in degrees, or string with unit)
 * @returns Angle in radians
 */
function toAngleInRadian(angle: number | string): number;

/**
 * Adjust sizes for aspect ratio constraints
 * @param data - Size data object
 * @param type - Adjustment type
 * @returns Adjusted width and height
 */
function getAdjustedSizes(
  data: { width: number; height: number; aspectRatio?: number }, 
  type?: string
): { width: number; height: number };

/**
 * Multiply transformation matrices
 * @param matrix - Base transformation matrix [a, b, c, d, e, f]
 * @param args - Additional matrices to multiply
 * @returns Resulting transformation matrix
 */
function multiplyMatrices(matrix: number[], ...args: number[][]): number[];

Usage Examples:

import { getOffset, toAngleInRadian, getAdjustedSizes, multiplyMatrices } from 'cropperjs';

// Get element position
const canvas = document.querySelector('cropper-canvas');
const offset = getOffset(canvas);
console.log('Canvas position:', offset.left, offset.top);

// Convert angles
const radians45 = toAngleInRadian(45);        // π/4
const radiansFromString = toAngleInRadian('90deg'); // π/2
const radiansFromTurn = toAngleInRadian('0.25turn'); // π/2

// Adjust sizes for aspect ratio
const originalSize = { width: 300, height: 200, aspectRatio: 16/9 };
const adjustedSize = getAdjustedSizes(originalSize);
console.log('Adjusted size:', adjustedSize); // { width: 300, height: 168.75 }

// Matrix multiplication for transformations
const scaleMatrix = [1.5, 0, 0, 1.5, 0, 0];      // Scale by 1.5
const rotateMatrix = [0.707, -0.707, 0.707, 0.707, 0, 0]; // Rotate 45°
const translateMatrix = [1, 0, 0, 1, 50, 30];     // Translate (50, 30)

const combinedMatrix = multiplyMatrices(scaleMatrix, rotateMatrix, translateMatrix);
console.log('Combined transform:', combinedMatrix);

// Practical usage in image transformation
function applyComplexTransform(image, scale, rotation, offsetX, offsetY) {
  const scaleMatrix = [scale, 0, 0, scale, 0, 0];
  const rotationRadians = toAngleInRadian(rotation);
  const cos = Math.cos(rotationRadians);
  const sin = Math.sin(rotationRadians);
  const rotateMatrix = [cos, -sin, sin, cos, 0, 0];
  const translateMatrix = [1, 0, 0, 1, offsetX, offsetY];
  
  const finalMatrix = multiplyMatrices(scaleMatrix, rotateMatrix, translateMatrix);
  image.$setTransform(finalMatrix);
}

DOM and Async Utilities

Utilities for DOM manipulation and asynchronous operations.

/**
 * Defer execution to next DOM update cycle
 * @param context - Optional context object
 * @param callback - Optional callback function
 * @returns Promise that resolves after next DOM update
 */
function nextTick(context?: unknown, callback?: Function): Promise<void>;

/**
 * Get root document for an element
 * @param element - Element to get root document for
 * @returns Document, DocumentFragment, or null
 */
function getRootDocument(element: Element): Document | DocumentFragment | null;

Usage Examples:

import { nextTick, getRootDocument } from 'cropperjs';

// Defer DOM updates
async function updateSelection(selection) {
  selection.$change(100, 100, 200, 150);
  
  // Wait for DOM to update before measuring
  await nextTick();
  
  const bounds = selection.getBoundingClientRect();
  console.log('Updated bounds:', bounds);
}

// With callback style
nextTick(null, () => {
  console.log('DOM has been updated');
});

// Get root document for cross-frame compatibility
const canvas = document.querySelector('cropper-canvas');
const rootDoc = getRootDocument(canvas);

if (rootDoc) {
  // Use root document for event handling
  on(rootDoc, 'keydown', handleKeydown);
}

// Shadow DOM compatibility
function getDocumentFromElement(element) {
  const rootDoc = getRootDocument(element);
  return rootDoc || document;
}

Browser Detection Constants

Runtime environment detection for cross-browser compatibility.

/**
 * Whether running in browser environment
 */
const IS_BROWSER: boolean;

/**
 * Window object reference (safe for server-side rendering)
 */
const WINDOW: any;

/**
 * Whether device supports touch events
 */
const IS_TOUCH_DEVICE: boolean;

/**
 * Whether browser supports pointer events
 */
const HAS_POINTER_EVENT: boolean;

Usage Examples:

import { IS_BROWSER, WINDOW, IS_TOUCH_DEVICE, HAS_POINTER_EVENT } from 'cropperjs';

// Check if running in browser
if (IS_BROWSER) {
  const cropper = new Cropper('#image');
} else {
  console.log('Server-side rendering detected');
}

// Safe window usage
if (WINDOW) {
  WINDOW.addEventListener('resize', handleResize);
}

// Touch vs mouse handling
if (IS_TOUCH_DEVICE) {
  canvas.scaleStep = 0.2; // Larger steps for touch
} else {
  canvas.scaleStep = 0.1; // Finer control for mouse
}

// Event type selection
const eventTypes = HAS_POINTER_EVENT ? 
  'pointerdown pointermove pointerup' : 
  'mousedown mousemove mouseup touchstart touchmove touchend';

on(canvas, eventTypes, handleInteraction);

Error Handling

Utility functions may throw errors for invalid inputs:

import { isString, toAngleInRadian, getOffset } from 'cropperjs';

// Type checking with error handling
function validateAndProcess(value) {
  if (!isString(value)) {
    throw new TypeError('Expected string value');
  }
  return value.toUpperCase();
}

// Angle conversion with validation
try {
  const radians = toAngleInRadian('invalid-angle');
} catch (error) {
  console.error('Invalid angle format:', error.message);
}

// Element operations with null checks
const element = document.querySelector('#maybe-missing');
if (element) {
  const offset = getOffset(element);
  console.log('Offset:', offset);
} else {
  console.error('Element not found');
}

Common Utility Patterns

Frequently used combinations of utility functions:

import { 
  isElement, isString, isNumber, isFunction,
  on, off, emit, nextTick,
  getOffset, toAngleInRadian, multiplyMatrices
} from 'cropperjs';

// Input validation pattern
function validateCropperInput(element, options = {}) {
  if (isString(element)) {
    element = document.querySelector(element);
  }
  
  if (!isElement(element)) {
    throw new Error('Invalid element provided');
  }
  
  if (options.container) {
    if (isString(options.container)) {
      options.container = document.querySelector(options.container);
    }
    if (!isElement(options.container)) {
      throw new Error('Invalid container provided');
    }
  }
  
  return { element, options };
}

// Event management pattern
class EventManager {
  constructor(target) {
    this.target = target;
    this.listeners = new Map();
  }
  
  add(type, listener, options) {
    on(this.target, type, listener, options);
    this.listeners.set(type, { listener, options });
  }
  
  remove(type) {
    const entry = this.listeners.get(type);
    if (entry) {
      off(this.target, type, entry.listener, entry.options);
      this.listeners.delete(type);
    }
  }
  
  removeAll() {
    for (const [type] of this.listeners) {
      this.remove(type);
    }
  }
}

// Transform calculation pattern
function calculateComplexTransform(operations) {
  let matrix = [1, 0, 0, 1, 0, 0]; // Identity matrix
  
  for (const op of operations) {
    let opMatrix;
    
    switch (op.type) {
      case 'scale':
        opMatrix = [op.x, 0, 0, op.y || op.x, 0, 0];
        break;
      case 'rotate':
        const angle = toAngleInRadian(op.angle);
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);
        opMatrix = [cos, -sin, sin, cos, 0, 0];
        break;
      case 'translate':
        opMatrix = [1, 0, 0, 1, op.x, op.y];
        break;
    }
    
    if (opMatrix) {
      matrix = multiplyMatrices(matrix, opMatrix);
    }
  }
  
  return matrix;
}