be-computed
v0.0.3
Published
Hydrate and compute values reactively from other (server side rendered/generated) HTML signals via local script tags.
Downloads
8
Maintainers
Readme
be-computed [WIP]
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 | Matches element based on matching itemprop attribute. If contenteditible, listens for input events. Otherwise, uses be-value-added. | | #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:
- First, do a "closest" for an element with attribute itemscope, where the tag name has a dash in it. Do that search recursively.
- 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:
- 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;
}
- 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...
- Install git.
- Fork/clone this repo.
- Install node.js.
- Open command window to folder where you cloned this repo.
npm install
npm run serve
- 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>