Commit 5a8ff13b by talequale

merge

parents 7de55a5e dbe6dd03
const Action = function(fn) {
this.fn = fn;
};
Action.prototype = {
execute: function() {
this.fn.apply(this, arguments);
}
};
const ACTION = {
CARD: {
LEARN_MORE: new Action((item)=>{
store.set({
'navigation.current': 'InfoPage',
'navigation.data': item
});
})
},
Action
};
export default ACTION
export {ACTION};
......@@ -30,8 +30,8 @@ store.sub(['navigation.current', 'loaded.cards'], async function(page, loaded) {
});
tmpStore.sub([
'isMobile', 'navigation.current'
], (is, page)=>{
if(!is && page === 'Profile')
'isMobile', 'navigation.current', 'loaded'
], (is, page, loaded)=>{
if(!is && page === 'Profile' && loaded)
store.set('navigation.current', 'Account');
});
\ No newline at end of file
......@@ -27,6 +27,15 @@ Observable.prototype = {
const listener = listeners[ i ];
listener.apply(this, [].slice.call(arguments, 1));
}
},
once: function(k, v) {
var _self = this,
wrap = function() {
v.apply(this, arguments);
_self.un(k, wrap)
};
this.on(k, wrap);
}
};
typeof module === 'object' && (module.exports = Observable);
\ No newline at end of file
......@@ -168,7 +168,13 @@ Store.prototype = {
valEqual: function(key, val) {
const me = this;
return function backwardCallback(update) {
me.equal(key, val, compareResult => update(compareResult));
me.equal(key, val, compareResult => {update(compareResult)});
}
},
valEqualOnly: function(key, val) {
const me = this;
return function backwardCallback(update) {
me.equal(key, val, compareResult => {compareResult && update(compareResult)});
}
},
valTrue: function(key) {
......
......@@ -203,7 +203,7 @@ const Consts = {
MONTH_YEAR: "LLLL yyyy",
DAY_MONTH: "dd.MM",
},
}
};
Consts.LOCAL_STORAGE.TOKEN.GET_TOKEN = (token) => `Bearer ${token}`
......@@ -219,4 +219,6 @@ for(let key in Consts.API.ENDPOINTS){
}
export default Consts
export const API = Consts.API;
const API = Consts.API;
export {API};
export {Consts};
......@@ -16,7 +16,12 @@ export default function() {
let dom = <div renderTo={document.body} cls="page-content">
<view.block.Header/>
{Switch({cls: 'page-content__inner', key: 'navigation.current'},
{Switch({
cls: 'page-content__inner',
key: 'navigation.current',
beforeSwitch: (val, old)=>{tmpStore.set('beforeNavigation', {from:old, to: val});},
afterSwitch: (val, old)=>{tmpStore.set('afterNavigation', {from:old, to: val});}
},
Object.keys(Page).reduce((store, pageName)=>{
store[pageName] = new Page[pageName]();
return store;
......
......@@ -81,3 +81,8 @@ const tmpStore = new Store({
width: window.innerWidth
});
let lastNav = null;
store.sub('navigation.current', function(val) {
console.log('APP:navigation', lastNav,'→', val);
lastNav = val;
});
\ No newline at end of file
......@@ -2,8 +2,10 @@ import './card.scss';
import Eye from '/svg/eye.svg';
import Button from "../button/Button";
import Format from "../format/Format";
import ACTION from "../../../controller/Action";
const noFn = ()=>{};
const Card = D.declare('view.cmp.Card', (cfg) => {
const action = cfg.action || noFn;
return <div className={D.cls('card card--'+
(cfg.disabled?'disabled':
cfg.seen?'seen':cfg.type))}>
......@@ -19,7 +21,7 @@ const Card = D.declare('view.cmp.Card', (cfg) => {
<Eye width="20" height="13"/>
</div>
:
<Button class={"button card__button"} onclick={cfg.onclick}>
<Button class={"button card__button"} onclick={()=>cfg.action(ACTION.CARD.LEARN_MORE)}>
<span>Изучить</span>
</Button>
)
......
......@@ -3,225 +3,243 @@ import ArrPrev from '/svg/arrow_active.svg';
import ArrNext from '/svg/arrow_active-back.svg';
import './CardSlider.scss';
const CardSlider = D.declare('view.cmp.CardSlider', (cfg) => {
let current = [],
hash = {},
items = [],
cardWidth = 160,
minPadding = 10,
mobilePadding = 10,
wrap,
width,
fullCardsCount,
padding,
updateSize = function () {
width = dom.clientWidth - paddingLeft - paddingRight;
fullCardsCount = width / (cardWidth + minPadding) | 0;
padding = mobile ? mobilePadding: (width - fullCardsCount * cardWidth) / (fullCardsCount - 1);
wrap.style.width = (items.length * (padding + cardWidth) - padding) + 'px';
},
update = function () {
updateSize();
updateVisibleDom();
const CardSlider = (function(){
const Slider = function(cfg){
this.cfg = cfg;
this.name = this.cfg.name || (Math.random()+'').substr(2);
let itemClick = cfg.itemClick || (()=>{});
let current = [],
hash = {},
items = [],
cardWidth = 160,
minPadding = 10,
mobilePadding = 10,
wrap,
width,
fullCardsCount,
padding,
updateSize = function () {
if(dom.clientWidth){
width = dom.clientWidth - paddingLeft - paddingRight;
fullCardsCount = width / ( cardWidth + minPadding ) | 0;
padding = mobile ? mobilePadding : ( width - fullCardsCount * cardWidth ) / ( fullCardsCount - 1 );
wrap.style.width = ( items.length * ( padding + cardWidth ) - padding ) + 'px';
return true;
}
return false;
},
update = function () {
updateSize() && updateVisibleDom();
};
let paddingLeft = 30, paddingRight = 30, mobile = false;
let waitTimeout = false;
let tuning;
let leftestItem = 0;
let lastLeftestItem = 0;
let leftButtonDisabled = new Store.Value.Boolean(true), rightButtonDisabled = new Store.Value.Boolean(true);
let updateButtonsState = function() {
let fromLeft = dom.scrollLeft;
let firstItem = Math.round( fromLeft / ( cardWidth + padding ));
leftButtonDisabled.set(firstItem===0);
rightButtonDisabled.set(firstItem+fullCardsCount>=items.length);
};
let paddingLeft = 30, paddingRight = 30, mobile = false;
let waitTimeout = false;
let tuning;
let leftestItem = 0;
let lastLeftestItem = 0;
let leftButtonDisabled = new Store.Value.Boolean(true), rightButtonDisabled = new Store.Value.Boolean(true);
let updateButtonsState = function() {
let fromLeft = dom.scrollLeft;
let firstItem = Math.round( fromLeft / ( cardWidth + padding ));
leftButtonDisabled.set(firstItem===0);
rightButtonDisabled.set(firstItem+fullCardsCount>=items.length);
};
let untune = function() {
updateButtonsState();
tuning = false;
};
let tuneScrollPosition = function (x) {
if(tuning)
return untune();
waitTimeout = false;
tuning = true;
let fromLeft = dom.scrollLeft;
let firstItem, toLeft;
if(x){
firstItem = Math.round( fromLeft / ( cardWidth + padding ));
if(firstItem+x<0 || firstItem+fullCardsCount+x>items.length){
let untune = function() {
updateButtonsState();
tuning = false;
};
let tuneScrollPosition = function (x) {
if(tuning)
return untune();
}
waitTimeout = false;
tuning = true;
let fromLeft = dom.scrollLeft;
let firstItem, toLeft;
if(x){
firstItem = Math.round( fromLeft / ( cardWidth + padding ));
if(firstItem+x<0 || firstItem+fullCardsCount+x>items.length){
return untune();
}
toLeft = Math.round( (firstItem + x) * ( cardWidth + padding ));
}else{
firstItem = Math.round( fromLeft / ( cardWidth + padding ) - ( lastLeftestItem > leftestItem ? 0.4 : -0.4 ) );
toLeft = Math.round( firstItem * ( cardWidth + padding ) );
}
let deltaMove = (toLeft - fromLeft) / Math.ceil(200 / (1000 / 60));
while (deltaMove > 0 && deltaMove < 1) {
deltaMove *= 2;
}
if (Math.abs(fromLeft + deltaMove - toLeft) > Math.abs(fromLeft - toLeft)) {
deltaMove = toLeft - fromLeft;
}
let tune = function () {
if (Math.abs(toLeft - dom.scrollLeft) > (cardWidth + padding)*1.3) {
return untune();
toLeft = Math.round( (firstItem + x) * ( cardWidth + padding ));
}else{
firstItem = Math.round( fromLeft / ( cardWidth + padding ) - ( lastLeftestItem > leftestItem ? 0.4 : -0.4 ) );
toLeft = Math.round( firstItem * ( cardWidth + padding ) );
}
if (Math.sign(toLeft - dom.scrollLeft) !== Math.sign(deltaMove)) {
return untune();
let deltaMove = (toLeft - fromLeft) / Math.ceil(200 / (1000 / 60));
while (deltaMove > 0 && deltaMove < 1) {
deltaMove *= 2;
}
if (Math.abs(dom.scrollLeft - toLeft) < Math.abs(deltaMove)) {
dom.scrollLeft = toLeft;
return untune();
} else {
dom.scrollLeft += deltaMove;
if (Math.abs(fromLeft + deltaMove - toLeft) > Math.abs(fromLeft - toLeft)) {
deltaMove = toLeft - fromLeft;
}
let tune = function () {
if (Math.abs(toLeft - dom.scrollLeft) > (cardWidth + padding)*1.3) {
return untune();
}
if (Math.sign(toLeft - dom.scrollLeft) !== Math.sign(deltaMove)) {
return untune();
}
if (Math.abs(dom.scrollLeft - toLeft) < Math.abs(deltaMove)) {
dom.scrollLeft = toLeft;
return untune();
} else {
dom.scrollLeft += deltaMove;
requestAnimationFrame(tune);
}
};
if (Math.abs(deltaMove) > 0) {
requestAnimationFrame(tune);
} else {
return untune();
}
};
if (Math.abs(deltaMove) > 0) {
requestAnimationFrame(tune);
} else {
return untune();
}
};
let waitUntilDoNotTouchScroll = function () {
if (tuning)
return;
};
let waitUntilDoNotTouchScroll = function () {
if (tuning)
return;
lastLeftestItem = leftestItem;
leftestItem = dom.scrollLeft / (cardWidth + padding);
lastLeftestItem = leftestItem;
leftestItem = dom.scrollLeft / (cardWidth + padding);
if (waitTimeout)
clearTimeout(waitTimeout);
waitTimeout = setTimeout(tuneScrollPosition, 80);
};
let dom;
if (waitTimeout)
clearTimeout(waitTimeout);
waitTimeout = setTimeout(tuneScrollPosition, 80);
};
let dom;
let theDOM = <div class={D.cls('card-slider', {'card-slider--no-arrow-next': rightButtonDisabled})}>
<button class={D.cls(
'button',
'button--round',
'card-slider__control',
'card-slider__control--prev',
{'card-slider__control-disabled': leftButtonDisabled}
<button class={D.cls(
'button',
'button--round',
'card-slider__control',
'card-slider__control--prev',
{'card-slider__control-disabled': leftButtonDisabled}
)} type="button"
onClick={()=>{
tuneScrollPosition(-1);
}}
aria-label="Показать предыдущую карточку">
<ArrPrev width="14" height="12"/>
</button>
<button class={D.cls(
'button',
'button--round',
'card-slider__control',
'card-slider__control--next',
{'card-slider__control-disabled': rightButtonDisabled}
onClick={()=>{
tuneScrollPosition(-1);
}}
aria-label="Показать предыдущую карточку">
<ArrPrev width="14" height="12"/>
</button>
<button class={D.cls(
'button',
'button--round',
'card-slider__control',
'card-slider__control--next',
{'card-slider__control-disabled': rightButtonDisabled}
)} type="button"
onClick={()=>{
tuneScrollPosition(1);
}}
aria-label="Показать следующую карточку">
<ArrNext width="14" height="12"/>
</button>
{dom = <div class='card-slider__scroller'>
{wrap = <div class='card-slider__wrapper'>{' '}</div>}
</div>}
</div>;
let lastFrom, lastTo, last = false;
let updateVisibleDom = function () {
let from = Math.floor(dom.scrollLeft / (cardWidth + padding) - 1),
count = Math.ceil(dom.clientWidth / (cardWidth + padding)),
to;
to = from + count * 2;
from -= count;
from = Math.max(0, from);
to = Math.min(to, items.length);
onClick={()=>{
tuneScrollPosition(1);
}}
aria-label="Показать следующую карточку">
<ArrNext width="14" height="12"/>
</button>
{dom = this.scroller = <div class='card-slider__scroller'>
{wrap = <div class='card-slider__wrapper'>{' '}</div>}
</div>}
</div>;
let lastFrom, lastTo, last = false;
let updateVisibleDom = function () {
let from = Math.floor(dom.scrollLeft / (cardWidth + padding) - 1),
count = Math.ceil(dom.clientWidth / (cardWidth + padding)),
to;
to = from + count * 2;
from -= count;
from = Math.max(0, from);
to = Math.min(to, items.length);
//console.log({from, to, cardWidth, padding})
for (let i = from; i < to; i++) {
if (!(i in hash)) {
hash[i] = {
dom: (function (item, n) {
return <div className="card-slider__item">
<Card
type={'product,info,other'.split(',')[n % 3]}
disabled={n % 5 === 0}
seen={n % 4 === 0}
title={item.name}
image={item.image}
/>
</div>
})(items[i], i),
inDOM: false,
x: 0
};
for (let i = from; i < to; i++) {
if (!(i in hash)) {
hash[i] = {
dom: (function (item, n) {
return <div className="card-slider__item">
<Card
type={'product,info,other'.split(',')[n % 3]}
disabled={n % 5 === 0}
seen={n % 4 === 0}
title={item.name}
image={item.image}
action={action => itemClick(item, action)}
/>
</div>
})(items[i], i),
inDOM: false,
x: 0
};
}
}
}
if (last) {
for (let i = lastFrom; i < lastTo; i++) {
if (i < from || i >= to) {
if (hash[i].inDOM) {
hash[i].inDOM = false;
wrap.removeChild(hash[i].dom);
if (last) {
for (let i = lastFrom; i < lastTo; i++) {
if (i < from || i >= to) {
if (hash[i].inDOM) {
hash[i].inDOM = false;
wrap.removeChild(hash[i].dom);
}
}
}
}
}
for (let i = from; i < to; i++) {
let x = paddingLeft + i * (cardWidth + padding);
if (hash[i].x !== x) {
hash[i].dom.style.left = (hash[i].x = x) + 'px';
}
if (!hash[i].inDOM) {
wrap.appendChild(hash[i].dom);
hash[i].inDOM = true;
for (let i = from; i < to; i++) {
let x = paddingLeft + i * (cardWidth + padding);
if (hash[i].x !== x) {
hash[i].dom.style.left = (hash[i].x = x) + 'px';
}
if (!hash[i].inDOM) {
wrap.appendChild(hash[i].dom);
hash[i].inDOM = true;
}
}
}
lastFrom = from;
lastTo = to;
last = true;
updateButtonsState();
};
dom.addEventListener('scroll', Store.debounce(function () {
requestAnimationFrame(updateVisibleDom);
waitUntilDoNotTouchScroll();
}, 1000 / 60));
cfg.items(function (i) {
items = i;
//update();
lastFrom = from;
lastTo = to;
last = true;
updateButtonsState();
};
update();
dom.addEventListener('scroll', Store.debounce(function (e) {
requestAnimationFrame(updateVisibleDom);
waitUntilDoNotTouchScroll();
}, 1000 / 60));
cfg.items(function (i) {
items = i;
//update();
});
update();
tmpStore.sub('width', Store.debounce(function (w) {
let firstItem = Math.round(dom.scrollLeft / (cardWidth + padding));
update();
dom.scrollLeft = firstItem * (cardWidth + padding);
}, 30));
});
tmpStore.sub('isMobile', function(is) {
mobile = is;
paddingLeft = paddingRight = is ? 10 : 30;
update();
});
tmpStore.sub('width', Store.debounce(function (w) {
let firstItem = Math.round(dom.scrollLeft / (cardWidth + padding));
update();
dom.scrollLeft = firstItem * (cardWidth + padding);
}, 30));
tmpStore.sub('isMobile', function(is) {
mobile = is;
paddingLeft = paddingRight = is ? 10 : 30;
update();
});
return theDOM;
});
this.dom = theDOM;
};
Slider.prototype = {
save: function() {
tmpStore.set(this.name, {left: this.scroller.scrollLeft});
},
restore: function() {
this.scroller.scrollLeft = tmpStore.get([this.name, 'left'].join('.'));
}
};
Slider.prototype.constructor = Slider;
return Slider;
})();
export default CardSlider;
export {CardSlider};
......@@ -3,6 +3,8 @@ const Switch = D.declare('Switch', (cfg, contentHash) => {
if(cfg.content){
contentHash = cfg.content;
}
let {beforeSwitch, afterSwitch} = cfg;
if(Array.isArray(contentHash)){
if(contentHash.length === 0){
if(cfg.content)
......@@ -23,12 +25,15 @@ const Switch = D.declare('Switch', (cfg, contentHash) => {
'cmp-switch',
cfg.cls,
{ 'cmp-switch__filled': val in contentHash } ) ) ) } );
let oldValue;
(cfg.store || store).sub( cfg.key, (val)=> {
beforeSwitch && beforeSwitch(val, oldValue);
D.removeChildren(cmp);
if(val in contentHash)
D.appendChild( cmp, contentHash[ val ] );
afterSwitch && (tmpStore.get('loaded') ? afterSwitch(val, oldValue) : tmpStore.once('loaded', ()=> afterSwitch(val, oldValue)));
oldValue = val;
});
return cmp;
......
......@@ -4,13 +4,42 @@ import {AsyncAuthAjax} from "../../../controller/Ajax";
import {API} from "../../../dict/Consts";
import Card from "../../cmp/card/Card";
import CardSlider from "../../cmp/cardSlider/CardSlider";
import AccountNavigation from "../../block/accountNav/AccountNavigation.jsx";
import AccountNavigation from "../../block/accountNav/AccountNavigation";
const Account = D.declare('view.page.Account', () => {
let Slider = new CardSlider( {
name: "newCardsSlider",
itemClick: ( item, action ) => {
action.execute( item );
},
return <div class="account-page">
items: _ => store.sub(
[
'loaded.cards',
'navigation.current',
tmpStore.bind( 'afterNavigation.to' ),
tmpStore.bind( 'newCardsLoaded' ),
],
function( loadedCards, to, newCardsLoaded ){
if( newCardsLoaded !== loadedCards ){
if( loadedCards && to === 'Account' ){
_( cards
.getArray()
.filter( card => card.seen === false )
.sort( ( a, b ) => a.id - b.id )
);
tmpStore.set( 'newCardsLoaded', loadedCards )
}
}
}
)
} );
const dom = <div class="account-page">
<h1 class="readers-only">Страница личного кабинета сотрудника</h1>
<div class="account-page__wrapper">
<div class="account-page__menu">
......@@ -22,24 +51,7 @@ const Account = D.declare('view.page.Account', () => {
<div class="account-page__content-inner">
<div class="account-page__cards">
<h2 class="account-page__title">Новые карточки для вашей должности:</h2>
<CardSlider
items={_=> store.sub(
[
'loaded.cards',
'navigation.current',
tmpStore.bind('loaded')
],
(loadedCards, page, loaded,w)=>{
if(loadedCards && page === 'Account' && loaded)
_(cards
.getArray()
.filter(card=>card.seen === false)
.sort((a,b)=>a.id-b.id),
w+(w<1024?0:320+30*2+30*2)
)
})}
/>
{Slider}
<div className="account-page__no-cards">
<div className="account-page__no-cards-info">
<div className="account-page__no-cards-item">
......@@ -65,100 +77,22 @@ const Account = D.declare('view.page.Account', () => {
<div className="account-page__nav">
<AccountNavigation/>
</div>
{/* {(()=> {
let c = <div/>;
(async () => {
const result = await AsyncAuthAjax.get(API.ENDPOINTS.GET_USER_NEW_CARDS());
D.appendChild(c, result.map((item, n) =>
<div class="cards-list__item">
<Card
type={'product,info,other'.split(',')[n % 3]}
disabled={n % 5 === 0}
seen={n % 4 === 0}
title={item.name}
image={item.image}
/>
</div>
));
})();
return c
})()}*/}
{/*
<div class="cards-list__item">
<div class="card card--info">
<div class="card__image">
<img src="/uploads/images/card_product1.jpg" alt=""/>
</div>
<h3 class="card__title">Название карточки</h3>
<div class="card__footer">
<Button class={"button card__button"}>
<span>Изучить</span>
</Button>
</div>
</div>
</div>
<div class="cards-list__item">
<div class="card card--product">
<div class="card__image">
<img src="/uploads/images/card_product2.jpg" alt=""/>
</div>
<h3 class="card__title">Название карточки</h3>
<div class="card__footer">
<Button class={"button card__button"}>
<span>Изучить</span>
</Button>
</div>
</div>
</div>
<div class="cards-list__item">
<div class="card card--other">
<div class="card__image">
<img src="/uploads/images/card_product3.jpg" alt=""/>
</div>
<h3 class="card__title">Название карточки</h3>
<div class="card__footer">
<Button class={"button card__button"}>
<span>Изучить</span>
</Button>
</div>
</div>
</div>
<div class="cards-list__item">
<div class="card card--seen">
<div class="card__image">
<img src="/uploads/images/card_product4.jpg" alt=""/>
</div>
<h3 class="card__title">Название карточки</h3>
<div class="card__footer">
<div class="card__note">
<span>Просмотрена</span>
<Eye width="20" height="13" />
</div>
</div>
</div>
</div>
<div class="cards-list__item">
<div class="card card--disabled">
<div class="card__image">
<img src="/uploads/images/card-disabled.png" alt=""/>
</div>
<h3 class="card__title">Карточек нет</h3>
<div class="card__footer">
</div>
</div>
</div>*/}
</div>
</div>
</div>
</div>
</div>
</div>;
tmpStore.valEqualOnly('afterNavigation.to', 'Account')(function() {
Slider.restore();
});
tmpStore.valEqualOnly('beforeNavigation.from', 'Account')(function() {
Slider.save();
});
return dom;
});
export default Account;
......
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