Releases: gramener/uifactory
Better vega charts
The <vega-chart>
component is now significantly revamped, and supports standard attributes for most of the Vega spec.
In addition:
- Adding
<script src="uifactory.js">
to the<head>
tag is valid and no longer causes an error - Setting an attribute value to 0 (e.g. `min:number="0") no longer ignores the attribute
1.23.0 (10 Nov 2021)
<script $inline>
runs scripts while rendering
To add logic to your component, add any JavaScript inside <script $inline>
. This runs when the component is rendered.
<template $name="repeat-script" icon="X" value="30">
<script $inline>
let count = +value
let result = isNaN(count) ? 'error' : icon.repeat(count)
</script>
${result}
</template>
When you add the component to your page:
<repeat-script icon="★" value="8"></repeat-html>
<repeat-script icon="★" value="a"></repeat-html>
... it renders this output:
★★★★★★★★ error
Note:
- Add
return
anywhere in the script to stop rendering a component.
(Set{parserOptions: {ecmaFeatures: {globalReturn: true}}
to avoid ESLint errors.)
Security fix
Earlier, x:number
, y:boolean
, etc were parsed as JavaScript. If you component.setAttribute('x', userInput.value)
, and userInput.value
had JavaScript, it would be executed. Now, the values for :number
, :boolean
, etc must be JSON. Only the :js
type is evaluated as JavaScript.
1.22.1 (9 Oct 2021)
uifactory.gramener.com
UIFactory docs are now hosted at uifactory.gramener.com
New features
- If the
<template>
is empty, the instance's contents are used as the template. - Slot contents are also available as
this.$slot[slotName]
.${this.$slot.good}
is just like
<slot name="good"></slot>
.
1.21.0 (4 Oct 2021)
Add dynamic classes and styles with :=
For dynamic classes, set the class:=
attribute to a array, object, or string:
class:="['x', 'y']"
becomesclass="x y"
class:="{x: true, y: false}"
becomesclass="x"
class:="['x', {y: true, z: false}]"
becomesclass="x y"
class:="${active ? 'yes' : 'no'}"
becomesclass="yes"
is active is true, elseclass="no"
For dynamic styles, set the style:=
attribute to an object or string:
style:="{'font-size': `${size}px`, color: 'red'}"
becomesstyle="font-size:20px;color:red"
(when size=20).style:="font-size="${size}px; color: red"
also becomesstyle="font-size:20px;color:red"
(when size=20).
For dynamic attributes, set the <attr>:=
attribute to any string or boolean expression:
disabled:="true"
becomes "disabled"disabled:="false"
does not add the disabled attributetype:="isNumeric ? 'number' : 'text'"
setstype="number"
if isNumeric is truthy, elsetype="text"
For example, this defines an <add-class>
component:
<template $name="custom-input" active:boolean="true">
<style>
.round { border-radius: 20px; }
.active { border: 1px solid red; }
</style>
<input
class:="['round', {active: active}]"
style:="{background-color: active ? 'lightblue' : 'white'}"
disabled:="!active">
</template>
When you add this to your page:
Active: <custom-input active="true"></custom-input>
Inactive: <custom-input active="false"></custom-input>
... it renders:
1.20.0 (28 Sep 2021)
Add re-usable blocks with <script type="text/html" $block="...">
To re-use HTML later, add it into a <script type="text/html" $block="blockname">...</script>
. For example:
<template $name="block-example" greeting="hello">
<script type="text/html" $block="one">one says ${greeting}.</script>
<script type="text/html" $block="two">two says ${greeting}.</script>
<%= one() %>
<%= two({ greeting: 'Ola' }) %>
</template>
When you add the component to your page:
<block-example></block-example>
... it renders this output:
one says hello. two says Ola.
Note:
- You can use
this
and
all properties as variables. - If multiple
<script type="text/html">
have the same$block
value, the last one is used
this.$id
hold a unique ID for each component
If you generate an id=
attribute in your component, you need a unique identifier for each
component. this.$id
has a string that's unique for each component instance.
For example, this creates a label-input combination with a unique ID for each input:
<template $name="label-input" type="text" label="">
<div style="display: flex; gap: 10px">
<label for="${this.$id}-input">${label} <small>ID: ${this.$id}-input</small></label>
<input id="${this.$id}-input" type="${type}">
</div>
</template>
Now, if you repeatedly use this component in a page:
<label-input label="X"></label-input>
<label-input label="Y"></label-input>
... it creates elements with different IDs:
1.19.0 (25 Sep 2021)
Add events with <script $on...>
To add an click
event listener to your component, write the code inside a
<script $onclick>...</script>
, like this:
<template $name="count-items" count:number="0" step:number="2">
Click to count ${count} items in steps of ${step}.
<script $onclick>
this.count += step
</script>
</template>
When you add the component to your page:
<count-items></count-items>
... it renders this output:
To add a click
event listener to a child, use <script $onclick="child-selector">...</script>
:
<template $name="count-button" count:number="0" step:number="2">
<button>Click here</button>
<span>Count: ${count}</span>
<script $onclick="button">
this.count += step
</script>
</template>
Now, <count-button></count-button>
renders:
Listeners can use these variables:
e
is the event objectthis
is the component instance- Any property, e.g.
count
,step
To call the listener only once, add the $once
attribute to <script>
:
<template $name="count-once" count:number="0" step:number="2">
<button>Click here once</button>
<span>Count: ${count}</span>
<script $onclick="button" $once>
this.count += step
</script>
</template>
Now, <count-once></count-once>
renders:
Styles only affect the component
You can't pollute styles outside the component. UIFactory adds the component name before every
selector (if it's missing). For example:
repeat-style.highlight {...}
stays as-is -- it already hasrepeat-style
.highlight b {...}
becomesrepeat-style .highlight b {...}
b { color: green}
becomesrepeat-style b { color:green; }
So any <b>
outside the component does not change color.
Note: This isn't foolproof. It's simply to prevent accidental pollution.
1.18.0 (19 Sep 2021)
Release v1 with Major API rewrite
To migrate your component from UIFactory v0.x, make these changes in your component (where applicable):
- Replace
<template component="...">
with<template $name="...">
- Replace
<template @render:js="...">
with<template $render:js="...">
- Replace
this
withthis.$contents
- Replace
$target
withthis
- Replace
this.data
withthis.$data
- Replace
this.ui.ready
withthis.$ready
- Rewrite any
<style scoped>
styles without the scoped attribute -- by prefixing all selectors withcomponent-name
- Rewrite properties specified as
<script type="application/json">
as attributes.
For example:should be written as:<template component="..."> <script type="application/json"> { "properties": [ { "name": "list", "type": "array", "value": [] } ] } </script> </template>
<template $name="..." list:array="[]"> </template>
- Rewrite the
properties
inuifactory.register({properties})
as a dict, not list.
For example:should be written as:uifactory.register({ properties: [ { "name": "list", "type": "array", "value": [] } ] })
uifactory.register({ properties: { "list": { "type": "array", "value": [] } } })
Lifecycle events are supported
Components fire these events at different stages of their lifecycle:
preconnect
: before the instance is created and properties are definedconnect
: after the instance is created and properties are definedprerender
: before the instance is renderedrender
: after the instance is rendereddisconnect
: after the element is disconnected from the DOM
Add <script $onpreconnect>...</script>
, <script $onrender>...</script>
, etc to create listeners.
For example:
<template $name="repeat-events" icon="★" value:number="1">
<script $onrender>
this.innerHTML = icon.repeat(value)
</script>
</template>
Now, <repeat-events icon="★" value="8"><repeat-events>
renders this output:
NOTE:
<script $onrender $once>
creates a listener that runs only once<script $onprerender $onrender>
runs the listener both on prerender AND render- Multiple
<script $onrender>...</script>
creates multiple listeners this.addEventListener('render', ...)
is exactly the same as<script $onrender>
0.0.17 (18 Sep 2021)
Remove lodash dependency
UIFactory no longer needs lodash. It's a standalone library
0.0.16 (1 Sep 2021)
<style scoped>
applies style only to componentel.property = value
re-rendersel
el.update({'attr:type': ...})
supported<vega-chart>
component added with signals support
0.0.15 (11 Aug 2021)
@render:js
attribute supports custom renderers