Skip to content

Compute values from other HTML signals via local script tags.

License

Notifications You must be signed in to change notification settings

bahrus/be-computed

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

86 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

be-computed [WIP]

Playwright Tests NPM version How big is this package in your project?

Hydrate and compute values reactively from other (server side rendered/generated) HTML signals via local script tags.

be-computed is very close in purpose to be-overloading. be-overloading focuses more on user-initiated event driven reactions. be-computed is more focused on observing peer elements (and/or the host) and calculating values based on these dependencies reactively.

Obscure note (ignore if it not understanding the context): This behavior probably doesn't make sense to be used where it makes sense to use the trans-render web component. For that reason, not separating the be-hive registration from the be-computed class.

Special Symbols

In the examples below, we will encounter special symbols used in order to keep the statements small, as far as identifying which elements to pull in property values from, and observing those elements for property value changes:

Symbol Meaning Notes
/propName "Hostish" Attaches listeners to getters/setters on properties of "hostish".
@propName Name attribute Listens for input events on form elements based on matching the name attribute.
propName Itemprop attribute
#propName Id attribute Matches element based on id within ShadowDOM realm. Listens for input events.
-prop-name Marker indicates prop Matches elements based on finding the exact attribute starting with a dash. Attaches listeners to getters/setters.

"Hostish" means:

  1. First, do a "closest" for an element with attribute itemscope, where the tag name has a dash in it. Do that search recursively.
  2. If no match found, use getRootNode().host.

Example 1a -- Locality of behavior notation with inline expression

<div itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <link itemprop=isWealthy href=https://schema.org/False>

    ...
    
    <link itemprop=isInNirvana 
        onload="isHappy && !isWealthy"
        be-computed='from onload expression, passing in |isHappy, |isWealthy.' 
    >
</div>

What this does:

  1. Since the onload attribute expression doesn't start with export const ..., and doesn't start with an open parenthesis, be-computed wraps the expression like so:
export const expr = async ({isHappy, isWealthy}) => {
    return isHappy && !isWealthy;
}
  1. Since the return statement returns a primitive, it applies the value to the adorned element, based on context. In this case, it sets:
<link itemprop=isInNirvana href=https://schema.org/True>

if the conditions are met, and attaches the be-value-added enhancement.

The value of the computation can be obtained via oLink.beEnhanced.beValueAdded.value.

Example 1b -- Verbose notation with external script tag

<div itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <link itemprop=isWealthy href=https://schema.org/False>

    ...

    <script nomodule>
        isHappy && !isWealthy
    </script>
    <link itemprop=isInNirvana be-computed='from previous script element expression, passing in |isHappy, |isWealthy.'>
</div>

Advantages of using script element -- less issues with characters that cause problems inside an attribute, may get better IDE support. Disadvantages -- a little further away, a little more verbose, if you need to move the element, need to remember to move the associated script element along with it.

Example 1c -- compact notation with inline expression

<div itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <link itemprop=isWealthy href=https://schema.org/False>

    ...
    
    <link itemprop=isInNirvana 
        onload="isHappy && !isWealthy" 
        be-computed='from |isHappy, |isWealthy.' 
    >
</div>

Example 1d -- compact notation with external script tag

<div itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <link itemprop=isWealthy href=https://schema.org/False>

    ...

    <script nomodule>
        isHappy && !isWealthy
    </script>
    <link itemprop=isInNirvana be-computed='from |isHappy, |isWealthy.'>
</div>

Example 1e -- bind to named elements and id'd elements

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...
    <link itemprop=isInNirvana
      onload="isHappy && !isWealthy && liberated?.length > 17"
      be-computed='from |isHappy, @isWealthy, #liberated.'
    >
</form>

Example 1f -- Add more context to the scripting

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...

    <link itemprop=isInNirvana 
        onload="
            ({isHappy, isWealthy, liberated}) => {
                console.log({isHappy, isWealthy, liberated});
                return isHappy && !isWealthy && liberated?.length > 17;
            }
        "
        be-computed='from |isHappy, @isWealthy, #liberated.'
    >
</form>

Since the expression starts with open parenthesis, wrapping is more lightweight. Just adds export const default.

Example 1g

Specify export symbol

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...

    <link itemprop=isInNirvana 
        onload="
            export const calculateInNirvana = ({isHappy, isWealthy, liberated}) => {
                console.log({isHappy, isWealthy, liberated});
                return isHappy && !isWealthy && liberated?.length > 17;
            }
        "
        be-computed='from onload export of calculateInNirvana, passing in |isHappy, @isWealthy, #liberated.'
    >
</form>

This allows for multiple expressions that can be used by different enhancements.

Example 1h -- Values coming from host.

<my-custom-element>
    #shadow
        <script nomodule>
            myProp ** 2
        </script>
        <data itemprop=squared be-computed='from /numValue.'></data>
        <be-hive></be-hive>
</my-custom-element>

The slash is optional, so this will also work:

Example 1i -- Values coming from host, take II.

<my-custom-element>
    #shadow
        <script nomodule>
            myProp ** 2
        </script>
        <data itemprop=squared be-computed='from myProp.'>
        <be-hive></be-hive>
</my-custom-element>

Example 1j

Value coming from marker

<form itemscope>
    <my-custom-element -num-value></my-custom-element>
    

    <meta itemprop=square
        onload="numValue ** 2"
        be-computed='from -num-value.'>
</form>

Example 2a Assigning objects, verbose notation, external script

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...

    <script nomodule>
        ({
            prop1: isHappy && !isWealthy && liberated?.length > 17,
            prop2: liberated?.blink()
        })
    </script>
    <any-element itemprop=isInNirvana be-computed='from previous script element expression, passing in |isHappy, @isWealthy, #liberated, and assign result.'></any-element>
</form>

Detecting such expressions: Starts and ends with ({...}), no arrow. If need to use arrow functions inside, need to provide the context.

Example 2b Assigning objects, compact notation, external script

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...

    <script nomodule>
        {
            prop1: isHappy && !isWealthy && liberated?.length > 17,
            prop2: liberated?.blink()
        }
    </script>
    <any-element itemprop=isInNirvana be-computed='from |isHappy, @isWealthy, #liberated, and assign result.'></any-element>
</form>

Example 2c Assigning objects, verbose notation, inline attribute

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...

    <any-element itemprop=isInNirvana
        onload="({
            prop1: isHappy && !isWealthy && liberated?.length > 17,
            prop2: liberated?.blink()
        })" 
        be-computed='from onload expression, passing in |isHappy, @isWealthy, #liberated, and assign result.'></any-element>
</form>

Example 2d Assigning objects, compact notation, inline attribute

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...
    <any-element itemprop=isInNirvana
        onload="
        {
            prop1: isHappy && !isWealthy && liberated?.length > 17,
            prop2: liberated?.blink()
        }
        " 
        be-computed='from |isHappy, @isWealthy, #liberated, and assign result.'>
    </any-element>
</form>

Example 2e Assigning object to sub property [TODO]

Example 2f Assigning object to be-scoped enhancement

This would allow transforms to be based on.

We can assign the result of a computation to an enhancement, or the "stateProp" property of an enhancement (as defined in the static config property of the enhancement). So one prominent use case is assigning to local "scope" of an element:

<form itemscope>
    <link itemprop=isHappy href=https://schema.org/True>
    <input type=checkbox name=isWealthy>
    <div contenteditable id=liberated>abc</div>
    ...
    <div itemscope
        onload="
        {
            prop1: isHappy && !isWealthy && liberated?.length > 17,
            prop2: liberated?.blink()
        }
        " 
        be-computed='from |isHappy, @isWealthy, #liberated, and assign result to $0+beScoped.'>
    </div>
</form>

Viewing Your Element Locally

Any web server that can serve static files will do, but...

  1. Install git.
  2. Fork/clone this repo.
  3. Install node.js.
  4. Open command window to folder where you cloned this repo.
  5. npm install

  6. npm run serve

  7. Open http://localhost:3030/demo/ in a modern browser.

Running Tests

> npm run test

Using from ESM Module:

import 'be-computed/be-computed.js';

Using from CDN:

<script type=module crossorigin=anonymous>
    import 'https://esm.run/be-computed';
</script>

About

Compute values from other HTML signals via local script tags.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published