m2
Inline plugging
view plugin
the simplest plug-in has the form:
<unit>
<view-source>
import { stream } from "m2"
export default ({ source}) => {
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"]>
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
- name of resource declared in <sound>
tag
Sound resource declaration
<sound name="sound-name" rel="sound-resource"></sound>
, where
name
- name of resourcerel
- file name without extension from the directory
component/res/sounds/sound-resource
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:
- req - stream fallback method
- key - view-component name
- options - current view-component options
- event - system event data
List of supported events
-
"onclick"
-
"onclickoutside" (synthetic)
-
"onpointermove"
-
"onpointerenter"
-
"onpointerleave"
-
"onpointerup"
-
"onpointerdown"
-
"onmouseenter"
-
"onmouseleave"
-
"onmouseover"
-
"onmouseout"
-
"onchange"
-
"oninput"
-
"onglobalkeydown" (synthetic)
-
"onglobalkeyup" (synthetic)
-
"onkeydown"
-
"onkeyup"
-
"onwheel"
-
"onscroll"
also supported custom events
-
"on:custom-event"
Creating and triggering events in JavaScript
<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 ( { } ) =>
stream(emt => {
emt( "something" );
})
, where
or an existing converted stream
import { stream } from "m2"
export default ( { obtain, } ) =>
obtain("../some/existing-stream/path")
.map( count => count + 1 )
.controller(
obtain("../some/existing-stream-controller/path"),
({action}) => ({ action, data: "ok" })
)
, where
- obtain - method of accessing an existing model from the schema
- args - init options that were specified when accessing the stream
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
"./cat-a"
- entry to the directory"./"
- current directory"../"
- parent directory"./{name: abc, kind: 10}"
- directories with a complex name"./cat-a[kind=10]"
- passing arguments"./#component-id"
- search by id"./@component-key"
- search by key
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