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"; ...@@ -2,12 +2,38 @@ import {PropertySetterInterface, PropSettersInterface} from "./Rjsx";
const byPass = a => a; const byPass = a => a;
const emptyFn = ()=>void 0; const emptyFn = ()=>void 0;
const falseFn = ()=>false;
const REFLECTION = true;
export const Reactivity = function(args) { export const Reactivity = function(args) {
this.args = 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 = { Reactivity.prototype = {
_deps: [],
args: [], args: [],
scope: null, scope: null,
key: null, key: null,
...@@ -16,8 +42,17 @@ Reactivity.prototype = { ...@@ -16,8 +42,17 @@ Reactivity.prototype = {
TaskManager.add(()=>{ TaskManager.add(()=>{
this.scope._setKey(this.key, 0, this.fn.apply(this.scope, this.args), this.scope.state, this.scope.setters); 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 = []; let jobSwap = [];
const Task = function(fn, scope) { const Task = function(fn, scope) {
...@@ -76,8 +111,14 @@ export const R = function() { ...@@ -76,8 +111,14 @@ export const R = function() {
if(arguments.length === 1){ if(arguments.length === 1){
reactivity.fn = byPass; reactivity.fn = byPass;
if(REFLECTION){
reactivity._deps = [].slice.call( arguments );
}
}else{ }else{
reactivity.fn = arguments[arguments.length-1]; reactivity.fn = arguments[arguments.length-1];
if(REFLECTION){
reactivity._deps = [].slice.call( arguments, 0, arguments.length - 1 );
}
} }
return reactivity; return reactivity;
...@@ -94,31 +135,73 @@ ReactiveContent.prototype = { ...@@ -94,31 +135,73 @@ ReactiveContent.prototype = {
const index = children.indexOf(this); const index = children.indexOf(this);
if(index>-1){ if(index>-1){
let i, _i = children.length, child; let i, _i = children.length, child, childInserted = false;
for( i = index + 1; i < _i; i++ ){ for( i = index + 1; i < _i; i++ ){
child = children[i]; child = children[i];
if(child instanceof Component){ if(child instanceof Component){
child.el.parentNode.insertBefore(val.el, child.el); child.el.parentNode.insertBefore(val.el, child.el);
childInserted = true;
break; break;
} }
} }
// TODO other cases!
}else{ }else{
throw new Error('Fix it. My parent does not contain me (ReactiveChild)'); 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){ export const Reactive = function(cfg){
if(cfg === false) { if(cfg === false) {
return; return;
} }
if(REFLECTION){
this._rState = {};
}else{
this._r = [];
}
this.state = cfg ? Object.create(cfg) : {}; this.state = cfg ? Object.create(cfg) : {};
this.subs = {}; this.subs = {};
}; };
Reactive.prototype = { Reactive.prototype = {
state: {}, state: {},
_rState: {},
_r: [],
subs: {}, subs: {},
setters: {}, setters: {},
...@@ -131,25 +214,25 @@ Reactive.prototype = { ...@@ -131,25 +214,25 @@ Reactive.prototype = {
} }
}, },
sub: function (key, fn) { sub: function (key) {
let FN, obj;// TODO unAny let obj = new Sub(this, key);
if (arguments.length === 1) {
obj = { (this.subs[key] || (this.subs[key] = [])).push(obj.FN);
value: key in this.state ? this.state[key] : void 0 if (this.has(key))
}; obj.FN.call(this, this, this.get(key));
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]);
return obj; 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) { get: function(key) {
let ptr = this.state, let ptr = this.state,
tokens = key.split('.'), tokens = key.split('.'),
...@@ -161,6 +244,10 @@ Reactive.prototype = { ...@@ -161,6 +244,10 @@ Reactive.prototype = {
} }
return ptr; return ptr;
}, },
has: function(key) {
// TODO rewrite recursive
return key in this.state;
},
set: function (obj, statePtr, settersPtr) { set: function (obj, statePtr, settersPtr) {
let state, setters, key; let state, setters, key;
...@@ -180,7 +267,47 @@ Reactive.prototype = { ...@@ -180,7 +267,47 @@ Reactive.prototype = {
beforeSetKey: function (key, val, lastVal) { beforeSetKey: function (key, val, lastVal) {
return val; 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) { _setKey: function (key, keyCursor, val, state, setters) {
let valueKey; let valueKey;
...@@ -199,6 +326,7 @@ Reactive.prototype = { ...@@ -199,6 +326,7 @@ Reactive.prototype = {
val.scope = this; val.scope = this;
val.key = key; val.key = key;
val.emit(); val.emit();
this._storeReactivity(val);
return true; return true;
} }
...@@ -232,11 +360,22 @@ Reactive.prototype = { ...@@ -232,11 +360,22 @@ Reactive.prototype = {
val = this.beforeSetKey(theKey, val, lastVal); 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 (keyInSetters) {
if(setterIsFn){ if(setterIsFn){
let setterResult = setters[ theKey ].call( this, this, val, lastVal, theKey, state ); shouldSetVal = false !== setters[ theKey ].call( this, this, val, lastVal, theKey, state );
if(setterResult !== false){ if(shouldSetVal !== false){
state[ theKey ] = val; state[ theKey ] = val;
} }
}else{ }else{
...@@ -257,9 +396,22 @@ Reactive.prototype = { ...@@ -257,9 +396,22 @@ Reactive.prototype = {
state[theKey] = val; state[theKey] = val;
this.afterSetKey(theKey, val, lastVal); this.afterSetKey(theKey, val, lastVal);
} }
if(shouldSetVal){
this.fire(theKey, val, lastVal); this.fire( theKey, val, lastVal );
}
return true; 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, { ...@@ -346,6 +498,7 @@ Object.assign(Component.prototype, {
val[i].scope = _; val[i].scope = _;
val[i].key = ['style', i]; val[i].key = ['style', i];
val[i].emit(); val[i].emit();
_._storeReactivity(val[i]);
continue; continue;
} }
...@@ -382,7 +535,13 @@ Object.assign(Component.prototype, { ...@@ -382,7 +535,13 @@ Object.assign(Component.prototype, {
clone: function() { clone: function() {
const clone = new this._ctor(this.nodeName); const clone = new this._ctor(this.nodeName);
clone.init(); clone.init();
if(REFLECTION){
clone.set( this._getReflectiveState() );
}else{
clone.set(this.state); clone.set(this.state);
}
return clone; return clone;
}, },
renderTo: function (where) { renderTo: function (where) {
...@@ -433,6 +592,7 @@ Object.assign(Component.prototype, { ...@@ -433,6 +592,7 @@ Object.assign(Component.prototype, {
val.call(this, e) val.call(this, e)
}; };
} else { } else {
if(val instanceof Component && !this._inited){ if(val instanceof Component && !this._inited){
// magically defined subcomponents // magically defined subcomponents
return val.clone(); return val.clone();
......
...@@ -70,7 +70,7 @@ const Check = UIComponent.extend('Check', { ...@@ -70,7 +70,7 @@ const Check = UIComponent.extend('Check', {
} }
}); });
let gg = 0;
const Slider = UIComponent.extend('Slider', { const Slider = UIComponent.extend('Slider', {
def: { def: {
from: 0, from: 0,
...@@ -80,7 +80,21 @@ const Slider = UIComponent.extend('Slider', { ...@@ -80,7 +80,21 @@ const Slider = UIComponent.extend('Slider', {
sliderWidth: 10, sliderWidth: 10,
background: 'red', background: 'red',
mover: <div 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() { recalc: function() {
...@@ -102,7 +116,8 @@ const Slider = UIComponent.extend('Slider', { ...@@ -102,7 +116,8 @@ const Slider = UIComponent.extend('Slider', {
'from,to,step,value,width': (_)=>{ 'from,to,step,value,width': (_)=>{
_.later('recalc', _.recalc); _.later('recalc', _.recalc);
}, },
mover: function(_, val) { mover: function(_, val, lastVal) {
let sliderRect; let sliderRect;
const moveFn = (e)=>{ const moveFn = (e)=>{
const newRawVal = Math.max(0, Math.min(1,(e.clientX-sliderRect.left)/(sliderRect.width))), const newRawVal = Math.max(0, Math.min(1,(e.clientX-sliderRect.left)/(sliderRect.width))),
...@@ -141,7 +156,9 @@ console.log(chhh); ...@@ -141,7 +156,9 @@ console.log(chhh);
let slider1 = <Slider from={-10} to={100} step={1}/>; let slider1 = <Slider from={-10} to={100} step={1}/>;
let RS, GS, BS; 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'))}/> <Check value={true} label={{text: 'Ti pidor'}} hidden={R(()=>chhh.sub('value'))}/>
{chhh} {chhh}
<Check value={R(()=>chhh.sub('value'))}/> <Check value={R(()=>chhh.sub('value'))}/>
...@@ -164,7 +181,9 @@ let RS, GS, BS; ...@@ -164,7 +181,9 @@ let RS, GS, BS;
{RS = <Slider from={0} to={255} step={1} value={123} background={R( {RS = <Slider from={0} to={255} step={1} value={123} background={R(
()=>RS.sub('value'), ()=>RS.sub('value'),
(r)=>'rgba('+[r,0,0,1]+')' (r)=>'rgba('+[r,0,0,1]+')'
)}/>} )}
mover={{style: {borderColor: '#0ff'}}}
/>}
{GS = <Slider from={0} to={255} step={1} value={123} background={R( {GS = <Slider from={0} to={255} step={1} value={123} background={R(
()=>GS.sub('value'), ()=>GS.sub('value'),
(g)=>'rgba('+[0,g,0,1]+')' (g)=>'rgba('+[0,g,0,1]+')'
...@@ -179,7 +198,7 @@ let RS, GS, BS; ...@@ -179,7 +198,7 @@ let RS, GS, BS;
width: 300, width: 300,
height: 300, height: 300,
padding: '30px', padding: '30px',
position: 'absolute', position: 'relative',
background: R( background: R(
()=>RS.sub('value'), ()=>RS.sub('value'),
()=>GS.sub('value'), ()=>GS.sub('value'),
...@@ -203,9 +222,22 @@ let RS, GS, BS; ...@@ -203,9 +222,22 @@ let RS, GS, BS;
B: ${b}` 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; let counter = 0;
setInterval(()=>{ setInterval(()=>{
i.set({text: counter++}); i.set({text: counter++});
...@@ -218,13 +250,14 @@ setTimeout(()=>{ ...@@ -218,13 +250,14 @@ setTimeout(()=>{
//debugger; //debugger;
but.set({ but.set({
text: R(but.sub('text'), (v1)=>{ text: R(but.sub('text'), (v1)=>{
return (v1-0)+148 return (v1-0)+1
}) })
}); });
}, 100); }, 100);
// Do not read after this line // 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