CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-cropperjs

JavaScript image cropper with extensive cropping capabilities and web component support.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

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

Install with Tessl CLI

npx tessl i tessl/npm-cropperjs

docs

additional-components.md

canvas-operations.md

image-manipulation.md

index.md

javascript-api.md

selection-management.md

utility-functions.md

tile.json