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

xtal-decor

v0.0.55

Published

Enables attaching ES6 proxies onto other Shadow DOM peer citizens

Downloads

24

Readme

xtal-decor [deprecated]

xtal-decor is now deprecated. be-decorated replaces it.

Published on webcomponents.org

Syntax

xtal-decor provides a base class which enables attaching ES6 proxies onto other "Shadow DOM peer citizens" -- native DOM or custom elements in the same Shadow DOM realm.

xtal-decor provides a much more "conservative" alternative approach to enhancing existing DOM elements, in place of the controversial "is"-based customized built-in element standard-ish.

Like xtal-deco, properties "init", "on", "actions" and "finale" allow us to define the behavior of the ES6 proxy with a minimum of fuss. And the property "virtualProps" is also supported, which allows us to define properties that aren't already part of the native DOM element or custom element we are enhancing.

Use of virtualProps is critical if you want to be guaranteed that your component doesn't break, should the native DOM element or custom element be enhanced with a new property with the same name.

xtal-decor provides the base class and web component. Like xtal-deco, we can avoid having to inherit from this class, instead "informally" defining a new behavior by using an inline script via something like nomodule:

<xtal-decor upgrade=button if-wants-to-be=a-butterbeer-counter virtual-props='["count"]'><script nomodule=ish>
    const decoProps = {
        actions: [
            ({count}) => {
                window.alert(count + " butterbeers sold");
            }
        ],
        on: {
            click: ({self}, event) => {
                self.count++;
            }
        },
        init: ({self}) =>{
            self.count = 0;
        },
        finale: ({self}, elementBeingRemoved) => {
            console.log({self, elementBeingRemoved});
        }
    }
    Object.assign(selfish.parentElement, decoProps);
</script></xtal-decor>

<button id=myButton be-a-butterbeer-counter='{"count": 1000}' disabled>Click me to Order Your Drink</button>

<button onclick="setCount()">Set count to 2000</button>
<script>
    function setCount(){
        butterBeerCounter.setAttribute('be-a-butterbeer-counter', '{"count": 2000}');
    }
</script>

A more "formal" way of defining new behavior is to extend the base class XtalDecor, and to set the "virtualProps", "actions", "on" and/or "init" properties during field initialization or in the constructor. You can then define a custom element with any name you want using your extended class.

An instance of your custom element needs to be added somewhere in the shadowDOM realm where you want it to affect behavior (or outside any Shadow DOM Realm to affect elements outside any Shadow DOM).

The attributes of your instance tag needs to define what element (optional - use * for all elements) and attribute (required) to look for.

For example:

#shadow-root (open)
    <be-on-the-next-level upgrade=blacked-eyed-peas if-wants-to-be=on-the-next-level></be-on-the-next-level>
    <be-rocking-over-that-bass-tremble upgrade=black-eyed-peas if-wants-to-be=rocking-over-that-bass-tremble></be-rocking-over-that-bass-tremble>
    <be-chilling-with-my-motherfuckin-crew upgrade=blacked-eyed-peas if-wants-to-be=chilling-with-my-motherfuckin-crew></be-chilling-with-my-motherfuckin-crew>
    ...



    <black-eyed-peas 
        be-on-the-next-level='{"level":"level 11"}' 
        be-rocking-over-that-bass-tremble
        be-chilling-with-my-motherfuckin-crew
    ></black-eyed-peas>

    <!-- Becomes, after upgrading -->
    <black-eyed-peas 
        is-on-the-next-level='{"level":"level 11"}'
        is-rocking-over-that-bass-tremble
        is-chilling-with-my-motherfuckin-crew
    ></black-eyed-peas>

Setting properties of the proxy externally

Just as we need to be able to pass property values to custom elements, we need a way to do this with xtal-decor. But how?

The tricky thing about proxies is they're great if you have access to them, useless if you don't.

Approach I. Programmatically (Ugly)

If you need to modify a property of a proxy, you can do that via the xtal-decor element, or the element extending xtal-decor:

<xtal-decor id=decor upgrade=button if-wants-to-be=a-butterbeer-counter virtual-props='["count"]'>
    ...
</xtal-decor>
<button id=butterBeerCounter be-a-butterbeer-counter='{"count": 1000}' disabled>Click me to Order Your Drink</button>
...
<script>
    function setCount(newCount){
        if(decor.targetToProxyMap === undefined || !decor.targetToProxyMap.has(butterBeerCounter)){
            setTimeout(() => setCount(newCount), 50);
            return;
        }
        const proxy = decor.targetToProxyMap.get(butterBeerCounter);
        proxy.count = newCount;
    }
</script>

Approach II. Setting properties via the controlling attribute:

A more elegant solution, perhaps, which xtal-decor supports, is to pass in properties via its custom attribute:

<list-sorter upgrade=* if-wants-to-be=sorted></list-sorter>

...

<ul be-sorted='{"direction":"asc","nodeSelectorToSortOn":"span"}'>
    <li>
        <span>Zorse</span>
    </li>
    <li>
        <span>Aardvark</span>
    </li>
</ul>

After list-sorter does its thing, the attribute "be-sorted" switches to "is-sorted":


<ul is-sorted='{"direction":"asc","nodeSelectorToSortOn":"span"}'>
    <li>
        <span>Aardvark</span>
    </li>
    <li>
        <span>Zorse</span>
    </li>
</ul>

You cannot pass in new values by using the is-sorted attribute. Instead, you need to continue to use the be-sorted attribute:


<ul id=list is-sorted='{"direction":"asc","nodeSelectorToSortOn":"span"}'>
    <li>
        <span>Aardvark</span>
    </li>
    <li>
        <span>Zorse</span>
    </li>
</ul>

<script>
    list.setAttribute('be-sorted', JSON.stringify({direction: 'desc'}))
</script>

A vscode plug-in is available that makes editing JSON attributes like these much less susceptible to human fallibility.

Approach III. Proxy Forwarding with a Light Touch

A reusable component, https://github.com/bahrus/proxy-decor serves as a useful companion to xtal-decor. Whereas xtal-decor can have specialized logic (either via prop setting or class extension), proxy-decor is very light-weight and generic. Think of it as a very thin client that easily connects to / switch between different remote, fully loaded desktop/server/VMs, sitting in some well-ventilated server room.

proxy-decor not only allows properties to be passed in to the proxy, it also raises custom events after any property of the proxy changes.

proxy-decor uses a "for" attribute, similar to the "for" attribute for a label.

Sample syntax:

<xtal-decor upgrade=button if-wants-to-be=a-butterbeer-counter virtual-props='["count"]'><script nomodule=ish>
    const decoProps = {
        actions: [
            ({count, self}) => {
                window.alert(count + " butterbeers sold");
            }
        ],
        on: {
            'click': ({self}) => {
                console.log(self);
                self.count++;
            }
        },
        init: ({self}) =>{
            self.count = 0;
        }
    }
    Object.assign(selfish.parentElement, decoProps);
</script></xtal-decor>


<button id=butterbeerCounter be-a-butterbeer-counter='{"count": 1000}' disabled>
    Click Me to Order Your Drink
</button>
<proxy-decor id=proxyDecor for=butterbeerCounter></proxy-decor>
<pass-down
    on="a-butterbeer-counter:count-changed" 
    to=[-text-content] 
    m=1 
    val-from-target=aButterBeerCounter.count
    init-event=a-butterbeer-counter:initialized>
</pass-down>
<span -text-content></span> drinks sold.

<button onclick="setCount()">Set count to 2000</button>
<script>
    function setCount(){
        const bbc = (proxyDecor.aButterbeerCounter ??= {});
        bbc.count = 2000;
    }
</script>

proxy-decor:

  1. Does an id search within the shadow dom realm (like label for).
  2. Multiple proxies can be "fronted" by a single proxy-decor tag.
  3. Event names are namespaced only for virtual properties.

Approach IV. Integrate with other decorators -- binding decorators -- that hide the complexity

WIP

Demo

API

This web component base class builds on the provided api:

import { upgrade } from 'xtal-decor/upgrade.js';
upgrade({
    shadowDOMPeer: ... //Apply trait to all elements within the same ShadowDOM realm as this node.
    upgrade: ... //CSS query to monitor for matching elements within ShadowDOM Realm.
    ifWantsToBe: // monitor for attributes that start with be-[ifWantsToBe], 
}, callback);

API example:

import {upgrade} from 'xtal-decor/upgrade.js';
upgrade({
    shadowDOMPeer: document.body,
    upgrade: 'black-eyed-peas',
    ifWantsToBe: 'on-the-next-level',
}, target => {
    ...
});

The API by itself is much more open ended, as you will need to entirely define what to do in your callback. In other words, the api provides no built-in support for creating a proxy.

For the sticklers

If you are concerned about using attributes that are prefixed with the non standard be-, use data-be instead:

<list-sorter upgrade=* if-wants-to-be=sorted></list-sorter>

...

<ul data-be-sorted='{"direction":"asc","nodeSelectorToSortOn":"span"}'>
    <li>
        <span>Zorse</span>
    </li>
    <li>
        <span>Aardvark</span>
    </li>
</ul>

Viewing example from git clone or github fork:

Install node.js. Then, from a command prompt from the folder of your git clone or github fork:

$ npm install
$ npm run serve

Open http://localhost:3030/demo/dev.html