A frontend JavaScript library to manage execution of javascript code inside HTML attributes and bodies.
-
no build tool required.
-
completely client-side;
-
fin-1.1
- provide
context
that is setup per element with the original html as input and processed html as output. variable
declarations and management percontext
/element
.
<div $let-count="{0}>
- scoped
variable
lookup.
{$count} {$?count} {$:count++}
context
bound javascript code block parsing in attributes and child nodes.- optimal
variable
update per referencing node.
{$:count++ /* causes update to referencing nodes */}
- set attributes from processed and evaluated code blocks.
<div $id="{'eid'+$count}" $disabled="{!$?disable}">
- remove attributes based on falsely values except
''
. - handle special attributes
$if
and$style
with$style
accepting both string and object values.
<FlexBox $style="{{ gap: '.5rem' }}"></FlexBox> <!-- --> <div $if="{$isLoggedIn}"><Dashboard></Dashboard></div> <div $if="{!$isLoggedIn}"><Auth></Auth></div>
- capturing, getting and updating attributes like
variables
but as functions.
capture: <div $:value> get: ${value()} set: {$:value(10)} </div>
- conditional html processing.
<div $if="{$count%2}>that is odd</div>"
- provide
-
fin-components-1.0
- provide
context-store
that to store and manage component definitions and client. - allow setting of component's tag type using the
is
attribute. - allow attribute, class-name and innerHTML
inheritance
using theextends
attribute.
<MyComponent component="MyComponent" extends="BaseComponent"></MyComponent>
- automatically add all inherited and self component names as css class names to the class list of the component.
<div component="MyComponent" class="MyComponent BaseComponent"></div>
- smart innerHTML choosing and overriding between component definition and usage.
<Button component="Button" is="button">button</Button> -> <button ...>button</button> <MyButton component="MyButton" extends="Button">my button</MyButton> -> <button ...>my button</button> <MyButton>click me</MyButton> -> <button ...>click me</button>
- powerful when combined with
fin-1.1
using captured attributes as component parameters.
<!-- client --> <LabeledInput label="fruit" $onInput="{console.log($value())}"></LabeledInput> <!-- definition --> <LabeledInput component="LabeledInput" extends="Box" $:label $:value $:type > <label> {$label() || ''} <input $type="{$type() || 'text'}" $value="{$value()}" $onInput="{$:value(this.output.value)}" ></input> </label> </LabeledInput>
- provide
-
what about references? just use an
$attribute
and set an upper scope variable the the element's context using thethis
keyword. And access it using that variable elsewhere under that variable's scope.<div $let-fruit> <LabeledInput $ref="{$:fruit = this}" label="fruit"></LabeledInput> <button $onClick="{console.log($fruit.output.value)}">get fruit</button> </div>
fin-1.1.js
+fin-components-1.0.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First fin.js project</title>
<!-- <link rel="stylesheet" href="css/components.css"> -->
<script src="js/fin-components-1.0.js"></script>
<script src="js/fin-1.1.js"></script>
</head>
<body>
<!-- root -->
<div id="root"></div>
<!-- components -->
<div id="components"></div>
<!-- components' styles. best to use .css file -->
<!-- <style></style> -->
<script>
const componentStore = new ComponentStore(
document.getElementById('components'),
document.getElementById('root')
).removeDefinitionElement();
</script>
<script>
const fin = new Fin(document.getElementById('root'));
const rootContext = fin.updateRoot();
</script>
</body>
</html>
fin-1.1
+ fin-components-1.0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demo</title>
<script src="js/fin-components-1.0.js"></script>
<script src="js/fin-1.1.js"></script>
</head>
<body>
<!-- root -->
<div id="root"
$let-count="{0}"
>
<FlexBox $style="{{
gap: '.5rem',
flexDirection: 'column'
}}"
>
<Flexbox>
<CounterDec></CounterDec>
<Display $style="{{
width: '3rem',
justifyContent: 'center'
}}"
>{$count}</Display>
<CounterInc></CounterInc>
</Flexbox>
<Flexbox $let-show="{true}">
<Button $onClick="{#
$:show = !$show;
this.output.innerHTML = $show ? 'hide' : 'show';
#}"
>show</Button>
<div $if="{$show}">
<Display>shown</Display>
</div>
</Flexbox>
<FlexBox $:value value="hello">
<MInput
$value="{$value()}"
$onInput="{$:value(this.output.value)}"
>
</MInput>
<Display $style="{{padding: '.5rem'}}">{$value()}</Display>
</FlexBox>
<FlexBox $style="{{ gap: '.5rem' }}">
<Button></Button>
<Button $onCLick="{alert('hello')}">click me</Button>
<Button disabled>click me not</Button>
</FlexBox>
</FlexBox>
</div>
<!-- components -->
<div id="components">
<!-- Box -->
<Box component="Box" is="div"></Box>
<InlineBox component="InlineBox" is="div"></InlineBox>
<FlexBox component="FlexBox" extends="Box"></FlexBox>
<InlineFlexBox component="InlineFlexBox" extends="InlineBox"></InlineFlexBox>
<!-- MInput -->
<MInput component="MInput" is="input"
type="text" title="input"
></MInput>
<Display component="Display" extends="InlineFlexBox"></Display>
<!-- Button -->
<Button component="Button" is="button" type="button" title="button">button</Button>
<!-- Counter -->
<Counter component="Counter" extends="Button"></Counter>
<!-- CounterInc -->
<CounterInc component="CounterInc" extends="Counter"
$onClick="{$:count++}"
>+ inc</CounterInc>
<!-- CounterDec -->
<CounterDec component="CounterDec" extends="Counter"
$onClick="{$:count--}"
>- dec</CounterDec>
</div>
<!-- components' styles -->
<style>
/* ... */
</style>
<script>
const componentStore = new ComponentStore(
document.getElementById('components'),
document.getElementById('root')
).removeDefinitionElement();
</script>
<script>
const fin = new Fin(document.getElementById('root'));
const rootContext = fin.updateRoot();
</script>
</body>
</html>
/* <style> */
* {
transition: 100ms;
}
.Box {
display: block;
}
.InlineBox {
display: inline-block;
}
.FlexBox {
display: flex;
}
.InlineFlexBox {
display: inline-flex;
}
.MInput {
margin: unset;
padding: .5rem;
border: 1px solid #7777;
}
.Display {
margin: unset;
padding: .5rem;
border: 1px solid #7777;
background-color: khaki;
}
.Button {
margin: unset;
padding: .5rem 1rem;
appearance: none;
border: 1px solid #3335;
background-color: #f0f0f0;
color: #333;
}
.Button:hover:not(:active):not(:disabled) {
background-color: hsl(from #f0f0f0 h s calc(min(l*1.1, 1)));
}
.Button:disabled {
background-color: hsl(from #f0f0f0 h s calc(l*.8));
}
.Counter {
color: white;
}
.CounterInc {
background-color: #079c2f;
border-radius: 0 .5rem .5rem 0;
}
.CounterInc:hover:not(:active):not(:disabled) {
background-color: hsl(from #079c2f h calc(s*.7) calc(min(l*1.4, 1)));
}
.CounterDec {
background-color: #b91546;
border-radius: .5rem 0 0 .5rem;
}
.CounterDec:hover:not(:active):not(:disabled) {
background-color: hsl(from #b91546 h calc(s*.7) calc(min(l*1.4, 1)));
}
/* </style> */
<head>
<title>fin-1.0 demo</title>
<script src="js/fin-1.0.js"></script>
</head>
<body>
<main id="root"
fin-let-name="Adam"
fin-let-message="{'Hello '+$name}"
fin-class="{'border p-2'}"
>
<button
fin-let-count="{0}"
fin-onclick="{this.set('count', $count+1)}"
><p>count: {$count}</p></button>
<p>{$message}</p>
</main>
<script>
const [root, rootContext] = fin.update(document.getElementById('root'));
</script>
</body>