Commit 31813a8d by Иван Кубота

1. Subscribe as component

2. Reflection (collect reactivities and subscription for recreation ability) 3. clone Component method 4. Lots of unsubscribes 5. Set child component properties as parents properties
parent b0ffe5cc
......@@ -2,12 +2,38 @@ import {PropertySetterInterface, PropSettersInterface} from "./Rjsx";
const byPass = a => a;
const emptyFn = ()=>void 0;
const falseFn = ()=>false;
const REFLECTION = true;
export const Reactivity = function(args) {
this.args = args;
};
const Sub = function(obj, key) {
this.value = obj.get(key);
if(REFLECTION){
this.key = key;
this.obj = obj;
}
this.FN = this.FN.bind(this);
};
Sub.prototype = {
value: void 0,
trigger: emptyFn,
key: '',
obj: null,
FN: function (_, val, oldVal) {
this.value = val;
this.trigger && this.trigger(val);
},
'~destroy': function() {
this.obj.unsub(this.key, this.FN);
}
};
Reactivity.prototype = {
_deps: [],
args: [],
scope: null,
key: null,
......@@ -16,8 +42,17 @@ Reactivity.prototype = {
TaskManager.add(()=>{
this.scope._setKey(this.key, 0, this.fn.apply(this.scope, this.args), this.scope.state, this.scope.setters);
});
},
'~destroy': function() {
let deps = this._deps, i = 0, _i = deps.length;
for(;i<_i;i++){
deps[i]['~destroy']();
}
}
};
if(REFLECTION){
Reactivity.prototype._deps = [];
}
let jobSwap = [];
const Task = function(fn, scope) {
......@@ -76,8 +111,14 @@ export const R = function() {
if(arguments.length === 1){
reactivity.fn = byPass;
if(REFLECTION){
reactivity._deps = [].slice.call( arguments );
}
}else{
reactivity.fn = arguments[arguments.length-1];
if(REFLECTION){
reactivity._deps = [].slice.call( arguments, 0, arguments.length - 1 );
}
}
return reactivity;
......@@ -94,31 +135,73 @@ ReactiveContent.prototype = {
const index = children.indexOf(this);
if(index>-1){
let i, _i = children.length, child;
let i, _i = children.length, child, childInserted = false;
for( i = index + 1; i < _i; i++ ){
child = children[i];
if(child instanceof Component){
child.el.parentNode.insertBefore(val.el, child.el);
childInserted = true;
break;
}
}
// TODO other cases!
}else{
throw new Error('Fix it. My parent does not contain me (ReactiveChild)');
}
}
};
export const clone = function(what, where, transformator){
let key, val;
let proxy = transformator || falseFn;
let transformed;
for(key in what){
val = what[key];
if(typeof val === 'object'){
transformed = proxy(val);
if(transformed === false){
if(Array.isArray(val)){
if(!(key in where))
where[key] = [];
}else{
if(!(key in where))
where[key] = {};
}
clone(val, where[key], transformator);
}else{
where[key] = transformed;
}
}else{
where[key] = val;
}
}
return where;
};
const walk = function(obj, fn) {
clone(obj, {}, fn);
};
export const Reactive = function(cfg){
if(cfg === false) {
return;
}
if(REFLECTION){
this._rState = {};
}else{
this._r = [];
}
this.state = cfg ? Object.create(cfg) : {};
this.subs = {};
};
Reactive.prototype = {
state: {},
_rState: {},
_r: [],
subs: {},
setters: {},
......@@ -131,25 +214,25 @@ Reactive.prototype = {
}
},
sub: function (key, fn) {
let FN, obj;// TODO unAny
if (arguments.length === 1) {
obj = {
value: key in this.state ? this.state[key] : void 0
};
FN = function (_, val, oldVal) {
obj.value = val;
obj.trigger && obj.trigger(val)
};
} else {
FN = fn;
}
(this.subs[key] || (this.subs[key] = [])).push(FN);
if (key in this.state)
FN.call(this, this, this.state[key]);
sub: function (key) {
let obj = new Sub(this, key);
(this.subs[key] || (this.subs[key] = [])).push(obj.FN);
if (this.has(key))
obj.FN.call(this, this, this.get(key));
return obj;
},
unsub: function(key, fn) {
const index = this.subs[key].indexOf(fn);
if(index === -1){
throw new Error('Already unsubscribed');
}else{
this.subs[key].splice(index, 1);
}
},
get: function(key) {
let ptr = this.state,
tokens = key.split('.'),
......@@ -161,6 +244,10 @@ Reactive.prototype = {
}
return ptr;
},
has: function(key) {
// TODO rewrite recursive
return key in this.state;
},
set: function (obj, statePtr, settersPtr) {
let state, setters, key;
......@@ -180,7 +267,47 @@ Reactive.prototype = {
beforeSetKey: function (key, val, lastVal) {
return val;
},
_getReflectiveState: function() {
const cloningState = {};
clone(this.state, cloningState, function(val) {
if(val instanceof Component){
// serialize
return val;
}
return false;
});
clone(this._rState, cloningState, function(val) {
if(val instanceof Reactivity){
var args = val._deps.map((dep)=>dep.obj.sub(dep.key)).concat(val.fn);
console.log(val, args)
return R.apply(this, args);
}
return false;
});
return cloningState;
},
_storeReactivity: function(r) {
if(REFLECTION){
let pointer = this._rState,
key = r.key,
subKey,
i = 0, _i = key.length - 1;
for(;i<_i; i++){
subKey = key[i];
pointer = pointer[subKey] || (pointer[subKey] = {});
}
pointer[key[i]] = r;//{from: r._deps, to: {obj: r.scope, key: r.key}, fn: r.fn};
}else{
this._r.push(r);
}
},
_setKey: function (key, keyCursor, val, state, setters) {
let valueKey;
......@@ -199,6 +326,7 @@ Reactive.prototype = {
val.scope = this;
val.key = key;
val.emit();
this._storeReactivity(val);
return true;
}
......@@ -232,11 +360,22 @@ Reactive.prototype = {
val = this.beforeSetKey(theKey, val, lastVal);
let shouldSetVal = true;
const isComponent = val instanceof Component,
wasComponent = lastVal instanceof Component;
if(!(isComponent) && wasComponent){
lastVal.set(val);
return false;
}
if(isComponent && wasComponent){
lastVal['~destroy']();
}
if (keyInSetters) {
if(setterIsFn){
let setterResult = setters[ theKey ].call( this, this, val, lastVal, theKey, state );
if(setterResult !== false){
shouldSetVal = false !== setters[ theKey ].call( this, this, val, lastVal, theKey, state );
if(shouldSetVal !== false){
state[ theKey ] = val;
}
}else{
......@@ -257,9 +396,22 @@ Reactive.prototype = {
state[theKey] = val;
this.afterSetKey(theKey, val, lastVal);
}
this.fire(theKey, val, lastVal);
if(shouldSetVal){
this.fire( theKey, val, lastVal );
}
return true;
},
'~destroy': function() {
if(REFLECTION){
walk(this._rState, function(val) {
if(val instanceof Reactivity){
val['~destroy']();
return true;
}else{
return false;
}
});
}
}
};
......@@ -346,6 +498,7 @@ Object.assign(Component.prototype, {
val[i].scope = _;
val[i].key = ['style', i];
val[i].emit();
_._storeReactivity(val[i]);
continue;
}
......@@ -382,7 +535,13 @@ Object.assign(Component.prototype, {
clone: function() {
const clone = new this._ctor(this.nodeName);
clone.init();
if(REFLECTION){
clone.set( this._getReflectiveState() );
}else{
clone.set(this.state);
}
return clone;
},
renderTo: function (where) {
......@@ -433,6 +592,7 @@ Object.assign(Component.prototype, {
val.call(this, e)
};
} else {
if(val instanceof Component && !this._inited){
// magically defined subcomponents
return val.clone();
......
......@@ -70,7 +70,7 @@ const Check = UIComponent.extend('Check', {
}
});
let gg = 0;
const Slider = UIComponent.extend('Slider', {
def: {
from: 0,
......@@ -80,7 +80,21 @@ const Slider = UIComponent.extend('Slider', {
sliderWidth: 10,
background: 'red',
mover: <div
style={{cursor: 'pointer', width: 10, height: 10, border: '3px solid #fff', position: 'absolute', boxSizing: 'border-box'}}
style={{
cursor: 'pointer',
width: 10,
height: R(but.sub('text'), (v1)=>{
if(gg<30){
console.log( v1 );
gg++;
}
return (v1-0)%20
}),
border: '3px solid #fff',
position: 'absolute',
boxSizing: 'border-box'
}}
/>
},
recalc: function() {
......@@ -102,7 +116,8 @@ const Slider = UIComponent.extend('Slider', {
'from,to,step,value,width': (_)=>{
_.later('recalc', _.recalc);
},
mover: function(_, val) {
mover: function(_, val, lastVal) {
let sliderRect;
const moveFn = (e)=>{
const newRawVal = Math.max(0, Math.min(1,(e.clientX-sliderRect.left)/(sliderRect.width))),
......@@ -141,7 +156,9 @@ console.log(chhh);
let slider1 = <Slider from={-10} to={100} step={1}/>;
let RS, GS, BS;
<div className='wrapper'>
let fr, tt;
var D = <div className='wrapper'>
<div text="text of div"/><div>text in div</div>
<Check value={true} label={{text: 'Ti pidor'}} hidden={R(()=>chhh.sub('value'))}/>
{chhh}
<Check value={R(()=>chhh.sub('value'))}/>
......@@ -164,7 +181,9 @@ let RS, GS, BS;
{RS = <Slider from={0} to={255} step={1} value={123} background={R(
()=>RS.sub('value'),
(r)=>'rgba('+[r,0,0,1]+')'
)}/>}
)}
mover={{style: {borderColor: '#0ff'}}}
/>}
{GS = <Slider from={0} to={255} step={1} value={123} background={R(
()=>GS.sub('value'),
(g)=>'rgba('+[0,g,0,1]+')'
......@@ -179,7 +198,7 @@ let RS, GS, BS;
width: 300,
height: 300,
padding: '30px',
position: 'absolute',
position: 'relative',
background: R(
()=>RS.sub('value'),
()=>GS.sub('value'),
......@@ -203,9 +222,22 @@ let RS, GS, BS;
B: ${b}`
)}
/>
{fr = <Slider from={0} to={R(()=>tt.sub('value'))} step={1} value={123}/>}
<div style={{background: '#400'}} text={R(fr.sub('from'),fr.sub('to'), fr.sub('value'), (from,to,value)=>`
from: ${from}
to: ${to}
value: ${value}
`)}/>
{tt = <Slider from={R(()=>fr.sub('value'))} to={255} step={1} value={123}/>}
<div text={R(tt.sub('from'),tt.sub('to'), tt.sub('value'), (from,to,value)=>`
from: ${from}
to: ${to}
value: ${value}
`)}/>
</div>.mount(document.body);
</div>;
//D.mount(document.body);
window.D = D;
let counter = 0;
setInterval(()=>{
i.set({text: counter++});
......@@ -218,13 +250,14 @@ setTimeout(()=>{
//debugger;
but.set({
text: R(but.sub('text'), (v1)=>{
return (v1-0)+148
return (v1-0)+1
})
});
}, 100);
// Do not read after this line
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment