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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@psc-44/module-js

v1.0.0

Published

A framework designed to facilitate the management and initialization of visible components. It provides a foundation for creating modular components with event handling capabilities.

Downloads

8

Readme

module-js

A framework designed to facilitate the management and initialization of visible components. It provides a foundation for creating modular components with event handling capabilities.

🚧 This package is not stable and might change at any time 🚧

Table of contents

Why

  • Modular application structure.
  • Centralized module management system to easily initialize, update, and destroy modules within specified contexts.
  • Encourages code reusability and extensibility.
  • Set module-scoped events.
  • Select module-scoped DOM elements.
  • Efficient module destruction that automatically removes events.

Installation

yarn add @psc-44/module-js

Usage

Main file

import {App} from "@psc-44/module-js";
import {MyModule} from "./modules/MyModule.js";

const app = new App({
    modules: [
        MyModule,
    ],
});

app.init();

Module example

<div data-module-my-module data-my-module-say="Hello world">
    <h1>
        MyModule
    </h1>
    
    <button data-my-module="button">
        Click here
    </button>
</div>
import { Module } from "@psc-44/module-js";

export class MyModule extends Module {
    
    constructor(options) {
        super(options);
    }
	
    init() {
        this.addEventListener("button", "click", () => {
            console.log(this.getData("say")); // Output: "Hello world"
        });
    }
}

Module options

Set these option in your module sub class.

| Option | Type | Default | Description | |--------------------------|---------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------| | this.autoQueryElements | boolean | false | Query all module-scoped elements on init() | | this.autoBind | boolean | true | Bind this to all module methods on init() | | this.el | HTMLElement | | The modules root element. This is set automatically when the module is created. | | this.$elements | object | null | Object of html elements bound by the modules data attributes.This is set automatically on init() when the this.autoQueryElements is set to true. |

Module Methods

| Method | Description | Example | |-------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| | this.init() | Automatically called on app init.Override this method in subclasses instead of using the constructor. | this.init() | | this.destroy() | Automatically called on app destroy.Override this method if you need to destroy anything specific.The events bound by the EventEmitter, this.addEventListener() or this.addFilteredEventListener() are automatically destroyed by the Module class. | this.destroy() | | this.addEventListener(targets, type, listener[, options]) | Adds an event listener to one or more DOM elements.targets can be one or more DOM elements, module scoped selector (as shown in the previous example with the say hello button) or a CSS selector. Note that selectors such as "input" or "button" are considered module-scoped selectors. | this.addEventListener("button", "click", (event) => console.log("onClickButton", event)) | | this.addFilteredEventListener(targets, type, listener, selector[, options]) | Adds a filtered event listener to one or more DOM elements.See the note on targets and selector in this.addEventListener. | this.addFilteredEventListener(this.el, "click", (event) => console.log("onClickPost", event), "postAnchor") | | this.removeEventListener(targets, type[, options]) | Removes an event listener to one or more DOM elements.See the note on targets in this.addEventListener. Note that all events bound by the modules container or EventEmitter functions are removed automatically on this.destroy. | this.removeEventListener("button", "click") | | this.dispatchDomEvent(type[, detail]) | Dispatches a custom DOM event from the current element with the specified type and optional details. The module is automatically set in the detail object. | this.dispatchDomEvent("updated", { value: 3 }) | | this.$(selector[, context, useModuleSelector]) | Finds the first element matching the selector within the module's or specified context. You can also use basic CSS selectors like ., #, or []. Note that selectors such as "input" or "button" are considered module-scoped selectors unless you set useModuleSelector to false. | this.$("button") | | this.$all("selector"[, context, useModuleSelector]) | Finds all elements matching the selector within the module's or specified context.See the note on selector in this.$. | this.$all("item") | | this.$parent("selector"[, context, useModuleSelector]) | Finds the first parent element matching the selector within the module's or specified context. See the note on selector in this.$. | this.$parent("wrapper") | | this.getData(name[, context]) | Retrieves data attribute value from the module's or context element. | this.getData("repeat-animation") | | this.setData(name, value[, context]) | Sets the data attribute value on the module's or context element. | this.setData("count", "5") | | static getModuleSelector() | Returns the CSS selector of the module's data attribute. | MyModule.getModuleSelector() | | static create(element[, recreate = false]) | Creates a new instance of the module with the provided options. If an instance already exists for the element, it returns the existing instance unless recreate is true. | MyModule.create(element) | | static findModuleInTree(element) | Finds and returns the module associated with the given HTML element within the module tree. | MyModule.findModuleInTree(element) |

App Methods

You can call these functions from everywhere using the static instance property on the App class.

| Method | Description | Example | |---------------------------|-----------------------------------------------------------------------|----------------------------------------| | this.init([context]) | Initialize modules within a specified context or the entire document. | this.init() or App.instance.init() | | this.destroy([context]) | Destroy modules within a specified context or the entire document. | this.destroy() | | this.update([context]) | Update modules within a specified context or the entire document. | this.update() |

Examples

Dialog example

<dialog class="data-module-dialog">
    <h2 data-dialog="label">
        Dialog
    </h2>
    
    <button data-dialog="accept">
        Accept
    </button>
    <button data-dialog="decline">
        Decline
    </button>
</dialog>
import { Module } from "@psc-44/module-js";

export class Dialog extends Module {

    constructor(options) {
        super(options);
    }

    init() {
        this.addEventListener("accept", "click", this.accept);
        this.addEventListener("decline", "click", this.close);
        
        this.open();
    }
    
    open() {
        this.el.classList.add("is-open");
    }
    
    accept() {
        this.$("label").textContent = "Thank you!";
        this.$("accept").style.display = "none";
        this.$("decline").textContent = "Close";
    }
    
    close() {
        this.el.classList.remove("is-open");
    }
}

Accordion example

<div data-module-accordion data-accordion-open="true">
    <section data-accordion="section">
        <header data-accordion="header">
            <h2>
                Title
            </h2>
        </header>
        <div data-accordion="main">
            <p>
                Content
            </p>
        </div>
    </section>
    <section data-accordion="section">
        <header data-accordion="header">
            <h2>
                Title
            </h2>
        </header>
        <div data-accordion="main">
            <p>
                Content
            </p>
        </div>
    </section>
</div>
import { Module } from "@psc-44/module-js";

export class Accordion extends Module {

    constructor(options) {
        super(options);
    }

    init() {
        this.addEventListener("header", "click", this.toggleSection);
        
        if (this.getData("open")) {
            this.$("section").classList.add("is-open");
        }
    }

    toggleSection(event) {
        const target = event.currentTarget;
        const section = this.$parent("section", target);

        if (section.classList.contains("is-open")) {
            section.classList.remove("is-open");
        } else {
            this.$("section.is-open").classList.remove("is-open");
            section.classList.add("is-open");
            Scroll.instance.scrollTo(this.el); // Add your scroll to logic here
        }
    }
}

Advanced accordion example

<div data-module-accordion-group>
    <section data-module-accordion class="is-open">
        <header data-accordion="header">
            <h2>
                Title
            </h2>
        </header>
        <div data-accordion="main">
            <p>
                Content
            </p>
        </div>
    </section>
    <section data-module-accordion>
        <header data-accordion="header">
            <h2>
                Title
            </h2>
        </header>
        <div data-accordion="main">
            <p>
                Content
            </p>
        </div>
    </section>
</div>
import { Module } from "@psc-44/module-js";

export class Accordion extends Module {

    constructor(options) {
        super(options);
    }

    init() {
        this.addEventListener(this.el, "click", this.toggle);
    }
    
    open() {
        this.el.classList.add("is-open");
        this.dispatchDomEvent("open");
        Scroll.instance.scrollTo(this.el);
    }
    
    close() {
        this.el.classList.remove("is-open");
    }
    
    isOpen() {
        return this.el.classList.contains("is-open");
    }
    
    toggle() {
        if (this.isOpen()) {
            this.close();
            return;
        }
        
        this.open();
    }
}
import { Module } from "@psc-44/module-js";

export class AccordionGroup extends Module {

    constructor(options) {
        super(options);
    }

    init() {
        this.$items = this.$all(Accordion.getModuleSelector());
        
        this.accordions = this.$items.map(($item) => {
            const instance = Accorion.create($item);
            this.addEventListener(instance.el, "open", this.onAccordionOpen);
            return instance;
        });
    }
    
    destroy() {
        this.accordions.forEach((accordion) => accordion.destroy());
    }
    
    onAccordionOpen(event) {
        this.accordions.forEach((accordion) => {
            if (accordion === event.detail.module) return;
            
            accordion.close();
        });
    }
}

Good to know

Since modules are identified by the class name, problems may arise with long names or when using minification tools that alter class names. To address this issue, a static property name can be set in classes that extend from the Module class. Note that this name should be specified in either kebab-case or PascalCase.Here are a few examples:

// Long name

class ExampleModuleWithARaellyLongFuckingNameThatNoOneWillEverHave extends Module {
    static name = "Example"; // Elements with "data-module-example" are associated with this class.
}

class CookieConsentDialog extends Module {
    static name = "cc-dialog"; // Elements with "data-module-cc-dialog" are associated with this class.
}


// Minify

class YourModule extends Module {
    static name = "YourModule"; // Elements with "data-module-your-module" are associated with this class.
}

class YourModule extends Module {
    static name = "your-module"; // Elements with "data-module-your-module" are associated with this class.
}

Credits

This code is inspired by modularJS.