artifishional / air-m2

MIT License
5 stars 4 forks source link

m2

Inline plugging

view plugin

the simplest plug-in has the form:

<unit>
    <view-source> 
import { stream } from "m2"

export default ({ source/*, targets */}) => {
        return stream( (emt, { over }) => {
            over.add(source.on((evt, src) => {
                emt(evt, src);
            }));
        } );
    }
    </view-source>
</unit>

you can access nested nodes and modify them

<unit>
    <view-source> 
import { stream } from "m2"

export default ({ source, targets: [ { node } ] }) => {
        const inner = node.querySelector("[custom-plugin-inner]");
        return stream( (emt, { over }) => {
            over.add(source.on(emt ));
            inner.textContent = 77;
        } );
    }
    </view-source>

    <div>
      <span custom-plugin-inner>custom-value</span>
    </div>

</unit>

stream plugin

you can also modify the data stream before use:

<unit>
    <stream-source> 
import { stream } from "m2"

export default ({ obtain }) => 
    obtain().map( data => [data] )
    </stream-source>
</unit>

View engine

Simple

Templates definition

data transmission from events

<span>`${property}`</span>

the event source must have the form

[{property: 77}]

the data source can be an object with a nested structure

<span>`${somefield.nested}`</span>
[{somefield: {nested: 77} }]

formatting

if you provide number settings, you can use formatting:

<span>`${intl.formatter-resource-name(value)}`</span>

,where {formatter-resource-name} - data transmission template

the event source must have the form

[{property: 77}]

formatting resource file example:

[
  "formatters",
  ["currency", { "style": "currency", "splitter": ".", "currencyDisplay": "symbol" }],
  ["compact-currency", { "style": "currency", "splitter": ".", "currencyDisplay": "symbol",
    "minimumFractionDigits": 0
  }],
  ["number", { "style": "decimal", "splitter": "." }],
  ["percent", { "style": "percent" }]
]

localization

if you supply localization resources you can use automatic literal substitution:

<span>`${lang.localization-string-resource-name}`</span>

localization resource file example:

<?xml version="1.0" encoding="utf-8"?>
<languages>
    <en>
        <string name="example-literal-string">Example literal text content</string>
        <string name="example-literal-string-2">Example literal text content 2</string>
    </en>
    <ru>
        <string name="example-literal-string">Пример случайной строки</string>
        <string name="example-literal-string-2">Пример случайной строки 2</string>
    </ru>
</languages>

Actions definition

Animation
<keyframe [name = default] [prop = {easing:"linear",duration:5}] >
    <key [offset = 0] prop = {scale:0}></key>
    <key [offset = 1] prop = {scale:1}></key>
</keyframe>

inline fade-in fade-out supported

<keyframe name = fade-in [duration = 5]>
    <key [offset = 0] prop = {translateX:0}></key>
    <key [offset = 1] prop = {translateX:100}></key>
</keyframe>

binding data from stream

<keyframe name = fade-in [duration = 5]>
    <key prop = {scaleX:(x)}></key>
</keyframe>
[{x: 1.5}]
Inline (local) CSS styles & SASS
<unit>
   <style [type="text/scss"]> <!-- to enable SASS processing -->
    body { /* global selector */
        padding: 0;
        margin: 0;
    }

    :scope { /* local selector */
        width: 100%;
        background-color: #0026ff;
        height: 100%;
    }
   </style>
   <div></div>
</unit>
Class controllers
<keyframe>
    <key prop = {classList:{active:(isactive)}}></key>
</keyframe>
[{isactive: true}]

or

<keyframe>
    <key prop = {classList:{red|green|black:(selectedColor)}}></key>
</keyframe>
[{selectedColor: "red"}]
Sound controls
<keyframe name="animation-name">
    <key prop={sound:'sound-name'}></key>
</keyframe>

Sound will be played once

or

<keyframe name="animation-name" prop="{duration: 2}">
    <key offset="0.2" prop={sound:'sound-name'} ></key>
    <key offset="0.7" prop={sound:'sound-name'} ></key>
</keyframe>

Sounds will be played 2 times with certain offsets and will be stopped if duration of animation less than duration of sounds

, where

Sound resource declaration

<sound name="sound-name" rel="sound-resource"></sound>

, where

if you want to use the general sounds for components, you can go up the nesting levels

rel="../../sound-resource"

Reactions definition

<unit onclick = req("action-name",{/*args*/})></unit>

where environment variables:

List of supported events
<view-source>
import { stream } from "m2"

        class MyEvent extends Event {
            constructor() {
                super("my-event");
                this.myData = 100;
            }
            log() {
                console.log("check", this);
            }
        }

        export default ({ source, targets }) => {
            return stream( (emt, { over }) => {

                over.add(source.on((evt, src) => {

                    setTimeout( () => {
                        targets[0].node.dispatchEvent(new MyEvent());
                    }, 1000);

                    emt(evt, src);

                }));
            } );
        }

</view-source>

Switcher

selects one view state available according to the model.

<unit tee = {a:10,b:-1}></unit>

rendered to the page if the condition when mapping data from the stream is fully met

[{a: 10, b: -1, ...other}]

or not rendered

[{a: 10, b: -2, ...other}]

allowed to use attachments and abbreviated forms

<unit tee = {obj:{prop}}></unit>
[{obj: {prop: 1}}]

functional form is also now supported

<unit tee() = "obj.prop > 0"></unit>
[{obj: {prop: 1}}]

you can even use a static form

<unit tee() = 1></unit>

however, the view component will still wait for the model stream

Common features

Coupling with model

you can link your view to the stream to get actions and process reactions

<unit stream = ./path>

any relative path will be calculated relative to the parent view, which is related to the model.

you can use the constant $name as a parameter to pass the current name of the view to the model

<unit stream = ./path/to/model[key=$name]>

Submodules

you can use the included submodules

<unit use = url(./path-to-src-module)></unit>

or

<unit use = ./path-to-registered-module></unit>

Model unit

each model is a function that returns a stream:

it is a new stream

import { stream } from "m2"

export default ( { /*...args*/ } ) => 
    stream(emt => {
        emt( "something" );
    })

, where

or an existing converted stream

import { stream } from "m2"

export default ( { obtain, /*...args*/ } ) => 
    obtain("../some/existing-stream/path")
    .map( count => count + 1 )
    .controller( 
        obtain("../some/existing-stream-controller/path"),
        ({action}) => ({ action, data: "ok" })
    )

, where

can be specified in the "obtain" method

obtain("./path", { argv: 10 })

or right on the path

obtain("./path[argv=10]")

Paths

the simplest path has the form:

"./cat-a/cat-b/cat-c"

Supported features

Note: when using search by id or key it begins from parent layer and move upward until root layer. So, sometimes you MUST specify exact path to model