view.page.Products = function() {

  let productTable, productFilterByTitle, productFilterByComponent, productFilterByTag, productFilterText,
    tagField;

  this.dom = div({cls: 'content-products'},
    div({cls: 'product-filter__area title-gradient'},
      div({cls: 'product-filter'},
        div({cls: 'product-filter__title'},'Фильтр'),
        D.input({
          attr: {
            value: (update)=>store.sub('productFilterText', (val)=>update(val))},

          cls: 'product-filter__input',
          on: {input: (e)=>store.set('productFilterText', e.target.value)}})
      ),
      div({cls: 'product-filter'},
        span({cls: 'product-filter__hint'},'Область фильтрации:'),
        D.label({cls: 'product-filter__label'},
          D.input({
            attr:{
              type: 'checkbox',
              checked: (update)=>store.sub('productFilterByTitle', (val)=>update(val))},

            on: {input: (e)=>{store.set('productFilterByTitle', e.target.checked)}}
          }), 'Название'
        ),
        D.label({cls: 'product-filter__label'},
          D.input({
            attr:{
              type: 'checkbox',
              checked: (update)=>store.sub('productFilterByComponent', (val)=>update(val))},

            on: {input: (e)=>{store.set('productFilterByComponent', e.target.checked)}}
          }), 'Ингредиенты'
        ),
        D.label({cls: 'product-filter__label'},
          D.input({
            attr:{
              type: 'checkbox',
              checked: (update)=>store.sub('productFilterByTag', (val)=>update(val))},

            on: {input: (e)=>{store.set('productFilterByTag', e.target.checked)}}
          }), 'Тэги'
        )
      )
    ),
    div({cls: 'tag-manipulations'},
      D.input({attr:{type: 'button', value: 'Удалить тэг'}, on: {
        click: ()=> {
          var val = tagField.getValue().trim();
          if(val!==''){
            var tag = dP.Tag.getByName(val);

            if(!tag)
              return;

            productTable.getSelected().forEach(function(item) {
              dP.Tag.disconnectFromProduct(dP.Product.getByID(item.id), tag);
              productTable.updateChildByData(item);
            });

            store.set('lastUpdate', +new Date())
          }
        }}}),

      tagField = new view.cmp.TagField(),

      D.input({
        cls: update =>
          store.sub('productFilteredCount', count =>
            update(D.cls('tag-manipulations-add-tag', {
              'tag-manipulations__not-active': count === 0 || count > 30}))),

        attr:{type: 'button', value: 'Добавить тэг'},
        on: {
          click: ()=> {
            var val = tagField.getValue().trim();

            if(val==='')
              return;

            const tag = dP.Tag.getOrCreate(val);

            productTable.getSelected().forEach(function(item) {
              dP.Tag.connectToProduct(dP.Product.getByID(item.id), tag);
              productTable.updateChildByData(item);
            });
            store.set('lastUpdate', +new Date())
          }
        }
      }),
      D.span({cls: 'tag-manipulations-add-tag__comment'},
          _=> store.sub('productFilteredCount', count =>
            _(
              'Найдено: '+count,
              count === 0 ? D.span({}, 'Надо больше результатов!') : (count > 30 ? D.span({}, 'Не цепляй тэг на всё сразу!') : null)
              )))
    ),
    productTable = new view.cmp.Table({
      sorters: [{id: 'title', type: String}],
      sort: [{title: 'asc'}],
      filterBy: {title: true, tag: true, component: true},
      filterFn: function(text, me) {
        return !text ||
          (me.filterBy.title && textFilterMatched(this._titleFilter, text)) ||
          (me.filterBy.tag && textFilterMatched(this._tagsFilter,text)) ||
          (me.filterBy.component && textFilterMatched(this._componentsFilter,text))
      },
      afterFilter: (items)=>store.set('productFilteredCount', items.length),
      itemTpl: (item, me, bonus) => {
        const dom = div({cls: 'table-item table-item__product'},
          div( {
            cls: 'table-item-action table-item-collapse',
            attr: { title: 'Скрыть', 'data-action': 'hide:' + item.id },
            on: { click: tableAction }
          }, '←' ),
          span( {
            cls: 'product-title',
            on: { click: tableAction },
            attr:{ 'data-action': 'toggle:'+ item.id}

          }, me.highlight( item.title ),' ≡' ),


          ( item.tags || [] ).map( t => me.highlight( dP.tagsHash[ t ].name ) ).map( view.cmp.Tag ),

          bonus.full && D.html( { cls: 'product-description' }, textFormat(item.description, true) ),

          div(
            { cls: 'table-item__product-components' },
            D.join( ( dP.componentsListHashByProduct[ item.id ] || [] ).map( a => D.span( { cls: 'product-cmp' }, me.highlight( a.name ) ) ), ', ' )
          )
        );

        if(bonus.hidden){
          if(bonus.hiddenFull){
            dom.classList.add( 'table-item__hidden-full' );
          }else{
            setTimeout( () => {
              dom.classList.add( 'table-item__hidden' );
            }, 1 )
            setTimeout( () => {
              bonus.hiddenFull = true;
              dom.classList.add( 'table-item__hidden-full' );
            }, 300 )
          }
        }
        return dom;
      },
      items: dP.slice.productTable.getItems()
    })
  );
  const tableAction = productTable.getActionDelegate();
  store.sub([
    'productFilterText',
    'productFilterByTitle',
    'productFilterByComponent',
    'productFilterByTag'
  ], function(text, title, component, tag) {
    Object.assign(productTable.filterBy, {title, component, tag});
    productTable.filter(text.trim().toLowerCase());
    productTable.updateChildren();
  });
};