Commit 3b507e0c by Иван Кубота

Store:

reSet method for clear old data load standard cards: subItems: isView => seen Slider: Move Item template to config View: Standard view card with page scrolling Create PaginationSlider singleton component. For not think about memory leaks. DevServer: Add gZip for hail of Satan
parents 9df06ae6 f4a8f204
var env = process.env;
const compression = require('compression')
const APP_HOST = env.APP_HOST || "127.0.0.1";
const APP_PORT = env.APP_PORT || 4000;
const DB_PATH = env.DB_PATH || "./db/users.json";
......@@ -391,6 +393,9 @@ app.use('/', function(req, res, next){
next();
}
});
app.use(compression());
app.use(App.static(dir(pack.public)));
app.use(App.static(dir(pack.src)))
......
......@@ -24,6 +24,7 @@
"@babel/template": "^7.8.3",
"babel-plugin-module-resolver": "^4.0.0",
"chai": "^4.2.0",
"compression": "^1.7.4",
"mocha": "^7.0.1"
}
}
......@@ -110,6 +110,12 @@ Store.prototype = {
return this;
},
reSet: function(key, val, changeList) {
this._props = {};
if(key===void 0)
return this;
return this.set(key, val, changeList);
},
_notify: function(changeList) {
for( let i = changeList.length; i; ){
const changeListElement = changeList[ --i ];
......
......@@ -31,7 +31,7 @@ const Consts = {
GET_CARD_INFOS: "/api/card_infos",
GET_CARD_PRODUCTS_ITEM: id => `/api/card_products/${id}`,
GET_CARD_INFO_CARD: id => `/api/card_infos/${id}`,
GET_CARD_INFO_ITEM: id => `/api/card_info_items/${id}`,
//REDUNDANT GET_CARD_INFO_ITEM: id => `/api/card_info_items/${id}`,
CATEGORIES_CATEGORY: id => `/api/categories/${id}`,
// Categories
GET_CATEGORIES: "/api/categories",
......
......@@ -70,3 +70,17 @@
outline: none;
}
}
@mixin flagSeen {
display: flex;
align-items: center;
font-weight: 400;
font-size: 12px;
line-height: 30px;
color: #B2C5B7;
svg {
display: block;
margin-left: 9px;
}
}
......@@ -19,6 +19,9 @@ $red: #FF0000;
$brick: #C94C0A;
$sienna: #D8960D;
$standardCard: #BDD6DE;
$standardCardSeen: #f8f8f8;
$font: 'SF Pro Text', Arial, sans-serif;
$font-mobile: 'Helvetica Neue', Arial, sans-serif;
......
......@@ -160,8 +160,12 @@ cards.load = AsyncResourceLoaderQueue({
},
async fetch( {category, id, type, key}){
console.log(type)
const result = await AsyncAuthAjax.get( API.ENDPOINTS[type===1?'GET_CARD_PRODUCTS_ITEM' : 'GET_CARD_INFO_ITEM']( id ) ),
data = {...result, seen: result.isView, type: type, category_id: category, detailed: true};
const result = await AsyncAuthAjax.get( API.ENDPOINTS[type===1?'GET_CARD_PRODUCTS_ITEM' : 'GET_CARD_INFO_CARD']( id ) );
if(type!==1){
result.items.map(item=>{item.seen = item.isView; delete item.isView});
}
const data = {...result, seen: result.isView, type: type, category_id: category, detailed: true};
this.set(key, data);
return data
}
......
......@@ -135,17 +135,7 @@ button.card__button {
}
.card__note {
display: flex;
align-items: center;
font-weight: 400;
font-size: 12px;
line-height: 30px;
color: #B2C5B7;
}
.card__note svg {
display: block;
margin-left: 9px;
@include flagSeen;
}
.card {
......
import Card from "../card/Card";
import ArrPrev from '/svg/arrow_active.svg';
import ArrNext from '/svg/arrow_active-back.svg';
import './CardSlider.scss';
......@@ -8,6 +8,7 @@ const CardSlider = (function(){
this.world = Object.create(this.world);
this.preset = Object.create(this.preset);
this.cfg = cfg;
cfg.preset && Object.assign(this.preset, cfg.preset);
this.name = this.cfg.name || (Math.random()+'').substr(2);
......@@ -211,18 +212,7 @@ const CardSlider = (function(){
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),
dom: this.cfg.tpl(items[i], itemClick, i),
inDOM: false,
x: 0
};
......@@ -255,7 +245,7 @@ const CardSlider = (function(){
this.updateButtonsState();
},
initDom: function() {
this.dom = <div class={D.cls('card-slider', {'card-slider--no-arrow-next': this.rightButtonDisabled})}>
this.dom = <div class={D.cls('card-slider', {'card-slider--no-arrow-next': this.rightButtonDisabled}, this.cfg.cls)}>
<button class={D.cls(
'button',
'button--round',
......
import './infoCard.scss';
import Button from "../button/Button";
import ProductCard from '../productCard/ProductCard.jsx';
import PaginationSlider from '../paginationSlider/PaginationSlider.jsx';
import Back from '/svg/arr-back.svg';
import Arr from '/svg/arr.svg';
......@@ -11,7 +13,7 @@ const InfoCard = D.declare('view.cmp.InfoCard', ({item, category}) => {
forwardHidden = true,
sorted = [];
try{
sorted = category.cardProduct.slice().sort((a,b)=>a.id - b.id);
sorted = category[item.type === 1?'cardProduct':'cardInfo'].slice().sort((a,b)=>a.id - b.id);
backHidden = sorted[0].id === item.id;
forwardHidden = sorted[sorted.length - 1].id === item.id;
}catch(e){
......@@ -43,13 +45,15 @@ const InfoCard = D.declare('view.cmp.InfoCard', ({item, category}) => {
if(item.type === 1){
content = <ProductCard item={item} category={category}/>;
}else{
//content = <ProductCard item={item} category={category}/>;
content = <PaginationSlider item={item} category={category}/>;
}
return <div class="info-card">
<div className="info-card__header">
<div>TYPE: {item.type}</div>
<h1 className="info-card__title">{item.name}</h1>
<p className="info-card__category">{category.name}</p>
<div className="info-card__headline">
<h1 className="info-card__title">{item.name}</h1>
<p className="info-card__category">{category.name}</p>
</div>
<div className="info-card__stats">
<p>Карточек в категории: <b>{category.count||0}</b></p>
<p>Просмотрено: <em>{category.countView||0}</em></p>
......@@ -67,3 +71,30 @@ const InfoCard = D.declare('view.cmp.InfoCard', ({item, category}) => {
export default InfoCard;
export {InfoCard};
......@@ -7,6 +7,14 @@
}
}
.info-card__headline {
min-height: 69px;
@media (max-width: $mobile) {
min-height: 50px;
}
}
.info-card__title {
@include title;
margin: 0 0 5px;
......@@ -65,10 +73,6 @@
box-shadow: 0 4px 10px rgba(190, 190, 190, 0.25);
border-radius: 6px;
@media (max-width: $tablet) {
padding: 20px 30px 40px;
}
@media (max-width: $mobile) {
margin-bottom: 14px;
background-color: transparent;
......
import './paginationSlider.scss';
import StandardCard from "../standardCard/StandardCard.jsx";
import ArrPrev from '/svg/arrow_active.svg';
import ArrNext from '/svg/arrow_active-back.svg';
import Eye from '/svg/eye.svg';
import CardSlider from "../cardSlider/CardSlider";
const {IF} = Store;
// Let it be singleton
const PaginationSliderSingleton = function(){
this.store = new Store({
seen: false
});
let Slider = new CardSlider( {
name: "standardSlider",
tpl: StandardCard,
preset: {
cardWidth: 290,
minPadding: 10,
mobilePadding: 10,
desktopPadding: 30,
},
cls: 'standard-slider'
} );
const _self = this;
this.backHidden = new Store.Value.Boolean(true);
this.forwardHidden = new Store.Value.Boolean(true);
this.dom = <div className="pagination-slider">
<IF condition={this.store.valEqual('seen', false)}>
<div className="pagination-slider__flag">
<span>Просмотрена</span>
<Eye width="20" height="13"/>
</div>
</IF>
<ul className="pagination-slider__bullets">
{(update)=>{
this.store.sub('items', function(items) {
Slider.setItems(items || []);
update((items || []).map((item, n)=>
<li>
<button class={D.cls('pagination-slider__bullet', {
'pagination-slider__bullet--seen': item.seen,
'pagination-slider__bullet--current': n === _self.current
})} type="button"/>
</li>
))
});
}}
</ul>
{Slider}
{/*<button className={D.cls(
'button',
'button--round',
'pagination-slider__control',
'pagination-slider__control--prev',
{'pagination-slider__control-hidden': this.backHidden}
)} type="button"
aria-label="Показать предыдущую карточку">
<ArrPrev width="14" height="12"/>
</button>
<button className={D.cls(
'button',
'button--round',
'pagination-slider__control',
'pagination-slider__control--next',
{'pagination-slider__control-hidden': this.forwardHidden}
)} type="button"
aria-label="Показать следующую карточку">
<ArrNext width="14" height="12"/>
</button>
<div className="pagination-slider__scroller">
<div className="pagination-slider__wrapper">
<div className="pagination-slider__item">
<StandardCard item={this.store}/>
</div>
</div>
</div>*/}
</div>;
};
PaginationSliderSingleton.prototype = {
current: 0,
apply: function({item, category}) {
this.current = 0;
this.store.reSet(item);
return this;
}
};
let instance;
const PaginationSlider = D.declare('view.cmp.PaginationSlider', (cfg) => {
return (instance || (instance = new PaginationSliderSingleton)).apply(cfg).dom;
});
export default PaginationSlider;
export {PaginationSlider};
.pagination-slider {
position: relative;
padding: 5px 46px 20px;
}
.pagination-slider__scroller {
margin: 0 auto;
padding: 0 86px;
width: 290px;
}
.pagination-slider__item {
display: flex;
flex-direction: column;
}
.pagination-slider__flag {
@include flagSeen;
position: absolute;
top: 16px;
left: 32px;
}
.pagination-slider__bullets {
@include list-reset;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
max-width: 100%;
}
.pagination-slider__bullets li {
flex: none;
margin: 15px 7.5px 7px;
}
.pagination-slider__bullet {
@include btn-reset;
@include hover;
position: relative;
display: block;
box-sizing: border-box;
width: 15px;
height: 15px;
border: 2px solid $accent-main;
border-radius: 50%;
}
.pagination-slider__bullet::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateY(4px) translateX(-50%);
width: 15px;
height: 2px;
background-color: $text-main;
border-radius: 2px;
opacity: 0;
transition: opacity 0.3s ease;
}
.pagination-slider__bullet--seen {
background-color: $accent-main;
}
.pagination-slider__bullet--current::after {
opacity: 1;
}
.pagination-slider__control {
position: absolute;
top: 50%;
transform: translateY(-50%) translateY(14px);
}
.pagination-slider__control--prev {
left: 50%;
margin-left: -231px;
}
.pagination-slider__control--next {
right: 50%;
margin-right: -231px;
}
import 'standardCard.scss';
import Format from '../../cmp/format/Format.jsx';
const StandardCard = D.declare('view.cmp.StandardCard', (item) => {
return<div className="card-slider__item">
<div class={D.cls('standard-card', {'standard-card--seen': item.seen})}>
<div className="standard-card__image">
<img src={item.image} alt=""/>
</div>
<div className="standard-card__text">
<Format>{item.text}</Format>
</div>
</div>
</div>
})
export default StandardCard;
export {StandardCard};
.standard-card {
flex-grow: 1;
box-sizing: border-box;
padding: 10px 10px 30px;
width: 290px;
min-height: 400px;
color: $text-main;
background-color: $standardCard;
border-radius: 8px;
}
.standard-card--seen {
background-color: $standardCardSeen;
}
.standard-card__image {
margin-bottom: 10px;
width: 100%;
height: 161px;
background-image: linear-gradient(180deg, #FFFFFF 0%, #FFFFFF 47.4%);
border-radius: 6px;
overflow: hidden;
}
.standard-card__image img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.standard-card__text {
@include bodyText;
padding: 5px;
}
......@@ -43,6 +43,18 @@ const Account = D.declare('view.page.Account', () => {
let Slider = new CardSlider( {
tpl: function (item, itemClick, 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>
},
name: "newCardsSlider",
itemClick: ( item, action ) => {
......
......@@ -246,7 +246,7 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
accepts@~1.3.7:
accepts@~1.3.5, accepts@~1.3.7:
version "1.3.7"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
......@@ -472,6 +472,11 @@ browser-stdout@1.3.1:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
bytes@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
......@@ -620,6 +625,26 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
dependencies:
mime-db ">= 1.43.0 < 2"
compression@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
dependencies:
accepts "~1.3.5"
bytes "3.0.0"
compressible "~2.0.16"
debug "2.6.9"
on-headers "~1.0.2"
safe-buffer "5.1.2"
vary "~1.1.2"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
......@@ -1512,7 +1537,7 @@ methods@^1.1.2, methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
mime-db@1.43.0:
mime-db@1.43.0, "mime-db@>= 1.43.0 < 2":
version "1.43.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
......@@ -1747,6 +1772,11 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
......
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