npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@storepress/utils

v0.13.0

Published

StorePress Utility Functions

Readme

@storepress/utils

A powerful JavaScript utility library for building modular, reusable plugins with a consistent API pattern. Designed for creating DOM-based plugins with automatic instance management, event handling, lifecycle control. and configuration parsing.

npm version License: GPL-3.0+

Features

  • Plugin Base Class — Abstract class for building consistent, reusable plugins
  • Automatic Instance Management — Track and retrieve plugin instances globally
  • Data Attribute Configuration — Parse settings from HTML data attributes (JSON or individual attributes)
  • Event System — Built-in custom event dispatching with triggerEvent
  • DOM Utilities — Helper functions for common DOM operations
  • Global Plugin Registry — Access plugins and instances from anywhere via StorePress.Utils
  • Lifecycle Hooksinit, destroy, setup, clear methods for complete control
  • WordPress Compatible — Works seamlessly with WordPress and WooCommerce projects

Installation

Install the module:

npm install @storepress/utils --save

This package assumes that your code will run in an ES2015+ environment. If you're using an environment that has limited or no support for such language features and APIs, you should include the polyfill shipped in @wordpress/babel-preset-default in your code.

Core Concepts

The library provides utilities for:

  • Case conversion - Transform strings between naming conventions
  • DOM selection - Flexible element selection with consistent output
  • Configuration parsing - Extract options from data attributes with type conversion
  • Event management - Namespaced event handling with automatic cleanup
  • Plugin creation - Full lifecycle management for DOM-based plugins

Usages

Creating a Plugin (Plugin.js):


// Plugin.js



// Define your plugin logic
function Plugin(element, options) {
  
    const reset = () => {
        element.textContent = ''
    }
    
    // Expose to public.
    const expose = () => ({
        reset,
        handleNext
    })
    
    const init = () => {
        element.textContent = options.message || 'Hello!'
        return expose()
    }
    
    return init()
}


// index.js

import { createStorePressPlugin } from '@storepress/utils'

// Create and setup the plugin
const MyPluginManager = createStorePressPlugin({
    selector: '[data-my-plugin]',
    plugin: MyPlugin,
    namespace: 'my-plugin',
    options: { message: 'Default message' },
})

MyPluginManager.setup()

export default MyPluginManager

Using the Plugin (script.js):

import MyPluginManager from './my-plugin';

document.addEventListener( 'DOMContentLoaded', () => {
    MyPluginManager.init();
} );

HTML:

<div data-my-plugin>Loading...</div>

Example Plugin Plugin.js

/**
 * External dependencies
 */
import { getOptionsFromAttribute } from '@storepress/utils';

function Plugin(element, options) {
  // Default Settings
  const DEFAULTS = {
    item: 3,
    size: 20,
  }

  // Collecting settings from html attribute
  const ATTRIBUTE = 'slider-settings' // data-slider-settings

  const handleInput = (event) => {
    window.console.log(event.target.value)
  }

  const addEvents = () => {
    this.$element.querySelectorAll('input').forEach(($el) => {
      $el.addEventListener('input', handleInput, { passive: true, signal: this.controller.signal })
    })
  }

  const addClass = () => {
    this.$element.querySelectorAll('input').forEach(($el) => {
      $el.classList.add('example-input-class')
    })
  }

  const removeClass = () => {
    this.$element.querySelectorAll('input').forEach(($el) => {
      $el.classList.remove('example-input-class')
    })
  }

  const removeEvents = () => {
    this.controller.abort()
  }

  const reset = () => {
    removeEvents()
    removeClass()
  }
  
  const handleNext = () =>{
    
  }

  // Expose to public.
  const expose = () => ({
    reset,
    handleNext
  })

  // Do what you need and return expose fn.
  const init = () => {
    this.$element = element
    this.settings = {
      ...DEFAULTS,
      ...options,
      ...getOptionsFromAttribute( this.$element, ATTRIBUTE) // Remember that all number like string and yes|no|true|false string will be converted.
    };

    this.controller = new AbortController()

    addClass()

    addEvents()

    return expose()
  }

  return init()
}

export { Plugin };

Create Plugin System slider.js or index.js

/**
 * External dependencies
 */
import { createStorePressPlugin } from '@storepress/utils'
import { Plugin } from 'Plugin'

const StorePressSlider = createStorePressPlugin({
  selector: '[data-slider-settings]',
  options: { size: 50},
  plugin: Plugin,
  namespace: 'slider',
})

// Setup the event listeners
StorePressSlider.setup()

export default StorePressSlider

Usages example scripts.js


import { StorePressSlider } from 'slider'

domReady(function () {
  StorePressSlider.init() // Setup element events to interact by user.

  StorePressSlider.destroy() // Destroy attached events from default elements.
  StorePressSlider.reload() // Destroy events and reattach for default elements.

  StorePressSlider.clear() // Completely remove plugin system events and element events.
  StorePressSlider.setup() // Setup plugin system events to init, destroy and reload.
  // NOTE: Must use StorePressSlider.init() after every StorePressSlider.setup()
})

Example markup

<div id="container">
    <div class="slider-wrapper inp" data-slider-settings="{'size': 40}">
        <a>1</a>
        <form>
            <input type="text" placeholder="Location X">
        </form>
    </div>

    <div class="slider-wrapper inp" data-slider-settings--size="30">
        <a>2</a>
        <form>
            <input type="text" placeholder="Location Y">
        </form>
    </div>
</div>

API Reference

Event Manager Documentation

createEventManager provides a structured way to manage DOM event listeners with:

  • Namespacing - Isolate events by component/feature
  • Automatic Cleanup - Remove all listeners with a single call using AbortController
  • Event Tracking - Know exactly which events are attached to which elements
  • Custom Events - Trigger both native and custom events seamlessly
  • Memory Safety - Prevent memory leaks from orphaned event listeners

The Problem It Solves

Problem 1: Memory Leaks from Orphaned Listeners

Without Event Manager:

class Slider {
    constructor(element) {
        this.element = element;
        this.handleClick = this.handleClick.bind(this);
        this.handleResize = this.handleResize.bind(this);
        
        // Adding listeners
        this.element.addEventListener('click', this.handleClick);
        window.addEventListener('resize', this.handleResize);
    }
    
    handleClick() { /* ... */ }
    handleResize() { /* ... */ }
    
    destroy() {
        // Must manually track and remove each listener
        // Easy to forget one, causing memory leaks
        this.element.removeEventListener('click', this.handleClick);
        window.removeEventListener('resize', this.handleResize);
        // What if we added more listeners? Easy to miss them!
    }
}

With Event Manager:

class Slider {
    constructor(element) {
        this.element = element;
        this.events = createEventManager('slider');
        
        // Adding listeners - automatically tracked
        this.events.add(this.element, 'click', () => this.handleClick());
        this.events.add(window, 'resize', () => this.handleResize());
    }
    
    handleClick() { /* ... */ }
    handleResize() { /* ... */ }
    
    destroy() {
        // Single call removes ALL listeners - no memory leaks
        this.events.removeAll();
    }
}
Problem 2: Tracking Which Events Are Attached

Without Event Manager:

// No way to know what's attached to an element
element.addEventListener('click', handler1);
element.addEventListener('click', handler2);
element.addEventListener('mouseenter', handler3);

// How many listeners? Which types? No built-in way to check!

With Event Manager:

const events = createEventManager('my-component');

events.add(element, 'click', handler1);
events.add(element, 'click', handler2);
events.add(element, 'mouseenter', handler3);

// Get all events for an element
const info = events.get(element);
console.log(info);
// [{
//     $element: element,
//     $events: [
//         { eventType: 'storepress:my_component:click', isNative: true, nativeType: 'click' },
//         { eventType: 'storepress:my_component:mouseenter', isNative: true, nativeType: 'mouseenter' }
//     ]
// }]

// Get all events in namespace
const allEvents = events.getAll();
Problem 3: Component Isolation

Without Event Manager:

// Component A
document.addEventListener('click', handleClickA);

// Component B
document.addEventListener('click', handleClickB);

// Removing Component A's listener might accidentally affect Component B
// if handlers get mixed up or references are lost

With Event Manager:

// Component A - isolated namespace
const eventsA = createEventManager('component-a');
eventsA.add(document, 'click', handleClickA);

// Component B - isolated namespace
const eventsB = createEventManager('component-b');
eventsB.add(document, 'click', handleClickB);

// Removing Component A's events doesn't affect Component B
eventsA.removeAll(); // Only removes component-a events
Problem 4: Triggering Events Consistently

Without Event Manager:

// Native event
element.dispatchEvent(new Event('click', { bubbles: true }));

// Custom event with data
element.dispatchEvent(new CustomEvent('slideChange', {
    bubbles: true,
    cancelable: true,
    detail: { index: 3 }
}));

// Different syntax for different event types - inconsistent!

With Event Manager:

const events = createEventManager('slider');

// Both native and custom events use the same API
events.trigger(element, 'click');
events.trigger(element, 'slideChange', { index: 3 });

API Reference

createEventManager(namespace, options)

Creates a new event manager instance.

Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | namespace | string | Required | Unique identifier for this event group | | options.prefix | string | 'storepress' | Prefix for event names | | options.separator | string | ':' | Separator between namespace parts |

Returns: EventManager object with methods: add, remove, removeAll, trigger, get, getAll

// Default options
const events = createEventManager('slider');
// Event names: storepress:slider:click

// Custom prefix
const events = createEventManager('slider', { prefix: 'myapp' });
// Event names: myapp:slider:click

// Custom separator
const events = createEventManager('slider', { prefix: 'app', separator: '.' });
// Event names: app.slider.click

// No prefix (global events)
const events = createEventManager('slider', { prefix: '' });
// Event names: $global:slider:click

events.add(targets, eventType, handler, options)

Attaches event listeners to elements.

Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | targets | string \| Element \| NodeList \| Array | Target element(s) - CSS selector or element reference | | eventType | string | Event type (e.g., 'click', 'mouseenter', 'customEvent') | | handler | Function | Event handler function | | options | Object | Standard addEventListener options (passive, once, capture) |

// CSS selector
events.add('#button', 'click', handleClick);

// Element reference
events.add(buttonElement, 'click', handleClick);

// Multiple elements
events.add('.buttons', 'click', handleClick);

// With options
events.add('#button', 'click', handleClick, { passive: true, once: true });

// Document-level events
events.add(document, 'keydown', handleKeydown);

// Window events
events.add(window, 'resize', handleResize);

events.remove(targets, eventType)

Removes event listeners from elements.

Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | targets | string \| Element \| NodeList \| Array | Target element(s) | | eventType | string \| null | Event type to remove, or null to remove all events from element |

// Remove specific event type
events.remove('#button', 'click');

// Remove all events from element
events.remove('#button', null);

// Remove from multiple elements
events.remove('.buttons', 'click');

events.removeAll()

Removes all event listeners in this namespace from all elements.

events.removeAll();

events.trigger(targets, eventType, details, options)

Dispatches events on elements.

Parameters:

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | targets | string \| Element \| NodeList \| Array | Required | Target element(s) | | eventType | string \| null | null | Event type to trigger, or null for all events | | details | Object | {} | Data to include in event.detail | | options | Object | {} | CustomEvent options (bubbles, cancelable, composed) |

// Trigger native event
events.trigger('#button', 'click');

// Trigger custom event with data
events.trigger('#slider', 'slideChange', { 
    currentIndex: 3,
    previousIndex: 2 
});

// Trigger with bubbling
events.trigger('#button', 'customEvent', { data: 'test' }, { bubbles: true });

// Trigger all events on element
events.trigger('#button', null);

events.get(targets)

Gets information about events attached to specific elements.

Parameters:

| Parameter | Type | Description | |-----------|------|-------------| | targets | string \| Element \| NodeList \| Array | Target element(s) |

Returns: Array<{ $element: Element, $events: Array<EventInfo> }>

const info = events.get('#button');
// [{
//     $element: <button>,
//     $events: [
//         { eventType: 'storepress:my_component:click', isNative: true, nativeType: 'click' },
//         { eventType: 'storepress:my_component:focus', isNative: true, nativeType: 'focus' }
//     ]
// }]

events.getAll()

Gets information about all events in this namespace.

Returns: Array<{ $element: Element, $events: Array<EventInfo> }>

const allEvents = events.getAll();
// [
//     { $element: <button>, $events: [...] },
//     { $element: <div>, $events: [...] },
//     ...
// ]

Basic Usage

Simple Component Example
import { createEventManager } from '@storepress/utils';

function createCounter(element) {
    const events = createEventManager('counter');
    let count = 0;
    
    const $display = element.querySelector('.count');
    const $increment = element.querySelector('.increment');
    const $decrement = element.querySelector('.decrement');
    
    const updateDisplay = () => {
        $display.textContent = count;
        events.trigger(element, 'countChange', { count });
    };
    
    const increment = () => {
        count++;
        updateDisplay();
    };
    
    const decrement = () => {
        count--;
        updateDisplay();
    };
    
    // Setup events
    events.add($increment, 'click', increment);
    events.add($decrement, 'click', decrement);
    
    // Cleanup function
    const destroy = () => {
        events.removeAll();
    };
    
    return { increment, decrement, destroy };
}

// Usage
const counter = createCounter(document.querySelector('.counter'));

// Listen for custom events
document.querySelector('.counter').addEventListener('storepress:counter:countChange', (e) => {
    console.log('Count changed:', e.detail.count);
});

// Cleanup when done
counter.destroy();

Use Cases

Use Case 1: Modal Dialog

A modal component with keyboard handling, overlay clicks, and focus management.

import { createEventManager, getElement } from '@storepress/utils';

function createModal(selector) {
    const element = getElement(selector);
    const events = createEventManager('modal');
    
    let isOpen = false;
    let previouslyFocused = null;
    
    const $overlay = element.querySelector('.modal-overlay');
    const $dialog = element.querySelector('.modal-dialog');
    const $closeBtn = element.querySelector('.modal-close');
    const $triggers = document.querySelectorAll(`[data-modal-trigger="${element.id}"]`);
    
    const open = () => {
        if (isOpen) return;
        
        previouslyFocused = document.activeElement;
        isOpen = true;
        element.hidden = false;
        element.classList.add('is-open');
        
        // Focus first focusable element
        const focusable = $dialog.querySelector('button, input, [tabindex="0"]');
        focusable?.focus();
        
        // Add active-state events
        events.add(document, 'keydown', handleKeydown);
        events.add($overlay, 'click', close);
        
        events.trigger(element, 'open', { modal: element });
    };
    
    const close = () => {
        if (!isOpen) return;
        
        isOpen = false;
        element.classList.remove('is-open');
        
        setTimeout(() => {
            element.hidden = true;
            previouslyFocused?.focus();
        }, 300);
        
        // Remove active-state events
        events.remove(document, 'keydown');
        events.remove($overlay, 'click');
        
        events.trigger(element, 'close', { modal: element });
    };
    
    const handleKeydown = (e) => {
        if (e.key === 'Escape') {
            close();
        }
        
        // Focus trap
        if (e.key === 'Tab') {
            const focusables = $dialog.querySelectorAll(
                'button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
            );
            const first = focusables[0];
            const last = focusables[focusables.length - 1];
            
            if (e.shiftKey && document.activeElement === first) {
                e.preventDefault();
                last.focus();
            } else if (!e.shiftKey && document.activeElement === last) {
                e.preventDefault();
                first.focus();
            }
        }
    };
    
    // Setup permanent events
    events.add($closeBtn, 'click', close);
    $triggers.forEach($trigger => {
        events.add($trigger, 'click', open);
    });
    
    const destroy = () => {
        close();
        events.removeAll();
    };
    
    return { open, close, destroy, isOpen: () => isOpen };
}

// Usage
const modal = createModal('#my-modal');

// Listen for modal events
document.querySelector('#my-modal').addEventListener('storepress:modal:open', () => {
    console.log('Modal opened');
});

document.querySelector('#my-modal').addEventListener('storepress:modal:close', () => {
    console.log('Modal closed');
});

Best Practices

1. Always Clean Up

// ✅ Good - cleanup on destroy
function createComponent(element) {
    const events = createEventManager('component');
    
    events.add(element, 'click', handleClick);
    
    return {
        destroy() {
            events.removeAll(); // Clean up all events
        }
    };
}

// ❌ Bad - no cleanup
function createComponent(element) {
    const events = createEventManager('component');
    
    events.add(element, 'click', handleClick);
    
    return {}; // Memory leak!
}

2. Use Descriptive Namespaces

// ✅ Good - descriptive namespace
const modalEvents = createEventManager('product-modal');
const sliderEvents = createEventManager('hero-slider');

// ❌ Bad - generic namespace
const events1 = createEventManager('events');
const events2 = createEventManager('stuff');

3. Conditional Event Attachment

// ✅ Good - add/remove events based on state
const open = () => {
    isOpen = true;
    events.add(document, 'keydown', handleEscape);
    events.add($overlay, 'click', close);
};

const close = () => {
    isOpen = false;
    events.remove(document, 'keydown');
    events.remove($overlay, 'click');
};

// ❌ Bad - always listening even when not needed
events.add(document, 'keydown', (e) => {
    if (isOpen && e.key === 'Escape') {
        close();
    }
});

4. Use Passive Listeners for Scroll/Touch

// ✅ Good - passive for performance
events.add(window, 'scroll', handleScroll, { passive: true });
events.add(element, 'touchstart', handleTouch, { passive: true });

// ❌ Bad - blocking scroll events
events.add(window, 'scroll', handleScroll);

5. Separate Concerns with Multiple Managers

// ✅ Good - separate managers for different concerns
function createComponent(element) {
    const uiEvents = createEventManager('component-ui');
    const dataEvents = createEventManager('component-data');
    
    // UI interactions
    uiEvents.add(element, 'click', handleClick);
    uiEvents.add(element, 'mouseenter', handleHover);
    
    // Data events
    dataEvents.add(element, 'dataLoaded', handleDataLoaded);
    dataEvents.add(element, 'dataError', handleDataError);
    
    return {
        destroy() {
            uiEvents.removeAll();
            dataEvents.removeAll();
        }
    };
}

Common Patterns

Pattern 1: Event Delegation
const events = createEventManager('list');

// Instead of attaching to each item
events.add(listContainer, 'click', (e) => {
    const $item = e.target.closest('.list-item');
    if ($item) {
        handleItemClick($item);
    }
    
    const $deleteBtn = e.target.closest('.delete-btn');
    if ($deleteBtn) {
        handleDelete($deleteBtn.closest('.list-item'));
    }
});
Pattern 2: One-Time Events
const events = createEventManager('animation');

// Remove after first trigger
events.add(element, 'transitionend', (e) => {
    handleTransitionEnd(e);
    events.remove(element, 'transitionend');
}, { once: true });
Pattern 3: Debounced Events
const events = createEventManager('search');

let debounceTimer;

events.add(input, 'input', (e) => {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
        performSearch(e.target.value);
    }, 300);
});
Pattern 4: Event Chaining
const events = createEventManager('wizard');

// Step completion triggers next step
events.add(step1, 'complete', () => {
    events.trigger(step2, 'activate');
});

events.add(step2, 'complete', () => {
    events.trigger(step3, 'activate');
});

events.add(step3, 'complete', () => {
    events.trigger(wizard, 'finished');
});

Debugging

List All Events
const events = createEventManager('my-component');

// Setup some events
events.add('#btn1', 'click', handler1);
events.add('#btn2', 'click', handler2);
events.add(document, 'keydown', handler3);

// Debug: see all events
console.table(events.getAll().flatMap(({ $element, $events }) => 
    $events.map(e => ({
        element: $element.tagName + ($element.id ? `#${$element.id}` : ''),
        eventType: e.eventType,
        isNative: e.isNative,
        nativeType: e.nativeType
    }))
));
Check Specific Element
const info = events.get('#my-button');
console.log('Events on button:', info[0]?.$events || 'None');
Global Event Store Access
// Access the global event store (for debugging only)
console.log(window.StorePress.$Events);

Summary

createEventManager solves common event handling problems:

| Problem | Solution | |---------|----------| | Memory leaks | removeAll() cleans everything | | Lost references | Events tracked automatically | | No visibility | get() and getAll() for inspection | | Inconsistent API | Same API for native and custom events | | Component isolation | Namespaced event groups | | Cleanup complexity | Single call removes all listeners |

Use it whenever you need reliable, trackable, cleanable event management in your DOM-based components.


Case Conversion Functions

toKebabCase(string)

Converts strings to kebab-case format.

import { toKebabCase } from '@storepress/utils';

toKebabCase('firstName');        // 'first-name'
toKebabCase('UserProfile');      // 'user-profile'
toKebabCase('api_endpoint');     // 'api-endpoint'
toKebabCase('hello world');      // 'hello-world'

toSnakeCase(string)

Converts strings to snake_case format.

import { toSnakeCase } from '@storepress/utils';

toSnakeCase('firstName');        // 'first_name'
toSnakeCase('UserProfile');      // 'user_profile'
toSnakeCase('api-endpoint');     // 'api_endpoint'

toConstantCase(string)

Converts strings to CONSTANT_CASE format.

import { toConstantCase } from '@storepress/utils';

toConstantCase('apiKey');        // 'API_KEY'
toConstantCase('maxRetries');    // 'MAX_RETRIES'
toConstantCase('database-url');  // 'DATABASE_URL'

toCamelCase(string)

Converts strings to camelCase format.

import { toCamelCase } from '@storepress/utils';

toCamelCase('user-profile');     // 'userProfile'
toCamelCase('api_endpoint');     // 'apiEndpoint'
toCamelCase('hello world');      // 'helloWorld'

toUpperCamelCase(string)

Converts strings to PascalCase format.

import { toUpperCamelCase } from '@storepress/utils';

toUpperCamelCase('user-profile');    // 'UserProfile'
toUpperCamelCase('api_endpoint');    // 'ApiEndpoint'
toUpperCamelCase('hello world');     // 'HelloWorld'

DOM Selection

getElement(selector)

Returns a single HTMLElement or null.

import { getElement } from '@storepress/utils';

// CSS selector
const button = getElement('#submit-btn');

// Existing element (passed through)
const element = document.querySelector('.card');
const same = getElement(element); // Returns same reference

// Null handling
getElement(null);      // null
getElement();          // null

getElements(selectors)

Returns a collection of HTMLElements.

import { getElements } from '@storepress/utils';

// CSS selector - returns NodeList
const buttons = getElements('.btn');

// Single element - wrapped in array
const element = document.querySelector('.card');
const wrapped = getElements(element); // [element]

// Array passed through
const elements = [el1, el2, el3];
const same = getElements(elements); // Same reference

// Empty handling
getElements([]);       // []
getElements();         // []

Configuration from Data Attributes

getOptionsFromAttribute(element, attributeName, features)

Parses configuration from HTML data attributes with automatic type conversion and nested structure support.

<div id="slider"
     data-slider='{"autoplay": true, "speed": 500}'
     data-slider--animation--duration="800"
     data-slider--animation--easing="ease-out"
     data-slider-loop="yes"
     data-slider-count="5">
</div>
import { getOptionsFromAttribute } from '@storepress/utils';

const element = document.getElementById('slider');
const options = getOptionsFromAttribute(element, 'slider');

console.log(options);
// {
//   autoplay: true,
//   speed: 500,
//   animation: {
//     duration: 800,        // Parsed as number
//     easing: 'ease-out'
//   },
//   loop: true,             // 'yes' parsed as boolean
//   count: 5                // Parsed as number
// }

Type Conversion Features:

| Input | Output | Feature | |-------|--------|---------| | "true", "yes" | true | parseBoolean | | "false", "no" | false | parseBoolean | | "42", "3.14" | 42, 3.14 | parseNumber | | "/pattern/gi" | /pattern/gi | parseRegex |

Custom Features:

const options = getOptionsFromAttribute(element, 'config', {
    parseNumber: false,      // Keep numbers as strings
    parseBoolean: true,
    truthyStrings: ['yes', 'true', 'on', '1'],
    falsyStrings: ['no', 'false', 'off', '0'],
    parseRegex: true
});

Deep Merge

deepMerge(...sources)

Recursively merges objects. Arrays are replaced, not concatenated.

import { deepMerge } from '@storepress/utils';

const defaults = {
    theme: { color: 'blue', size: 'medium' },
    features: ['search']
};

const custom = {
    theme: { color: 'red' },
    features: ['filter']
};

const merged = deepMerge(defaults, custom);
// {
//   theme: { color: 'red', size: 'medium' },
//   features: ['filter']  // Array replaced, not merged
// }

triggerEvent(targets, eventType, details, options)

Dispatches custom events on elements.

import { triggerEvent } from '@storepress/utils';

// Basic dispatch
triggerEvent(button, 'customClick', { userId: 123 });

// With options
triggerEvent(form, 'beforeSubmit', { data: formData }, {
    bubbles: true,
    cancelable: true
});

// Listen for custom event
button.addEventListener('customClick', (e) => {
    console.log(e.detail.userId); // 123
});

Swipe Detection

swipeEvent(target, callback, options)

Attaches swipe gesture detection supporting both touch and pointer events.

import { swipeEvent } from '@storepress/utils';

const cleanup = swipeEvent(carousel, (event) => {
    const { x, y, left, right, top, bottom, moving, done } = event.detail;

    if (moving) {
        // Live tracking during swipe
        carousel.style.transform = `translateX(${x}px)`;
    }

    if (done) {
        if (left) nextSlide();
        if (right) prevSlide();
        carousel.style.transform = '';
    }
}, {
    offset: 10,        // Minimum pixels for direction detection
    touchOnly: false   // Set true to ignore mouse/pointer events
});

// Cleanup when done
cleanup();

Object Path Access

findObjectValue(obj, path, defaultValue, notation)

Safely access nested object properties using dot/dash/underscore notation.

import { findObjectValue } from '@storepress/utils';

const config = {
    database: {
        connection: {
            host: 'localhost',
            port: 5432
        }
    }
};

findObjectValue(config, 'database.connection.host');           // 'localhost'
findObjectValue(config, 'database-connection-port', 3000);     // 5432
findObjectValue(config, 'database.missing', 'default');        // 'default'

// Custom notation
findObjectValue(config, 'database/connection/host', null, ['/']);

Regex Escaping

escapeRegex(string)

Escapes special regex characters for safe pattern creation.

import { escapeRegex } from '@storepress/utils';

const userInput = 'Price: $50.99 (sale!)';
const escaped = escapeRegex(userInput);
// 'Price: \\$50\\.99 \\(sale!\\)'

const regex = new RegExp(escaped, 'gi');
console.log(regex.test('Price: $50.99 (sale!)')); // true

Getting Slider Instance


import {
  getPluginInstance,
  getStorePressPlugin,
  triggerEvent
} from '@storepress/utils';

// Method 1: Get specific plugin instance
const plugin = getPluginInstance('.element', 'slider');

// Method 2: Get via plugin manager
const plugin = getStorePressPlugin('slider').get('.element');

// Method 3: Get all plugin instances
const allSliders = getStorePressPlugin('slider').get();


// OR

// Method 1: Get specific plugin instance
const plugin = StorePress.Utils.getPluginInstance('.element', 'slider');

// Method 2: Get via plugin manager
const plugin = StorePress.Utils.getStorePressPlugin('slider').get('.element');

// Method 3: Get all plugin instances
const allSliders = StorePress.Utils.getStorePressPlugin('slider').get();


allSliders.forEach(instance => {
  instance.handleNext(); // Access plugin exposed functions.
});

Links