Your IP : 216.73.216.79


Current Path : /var/www/html/dynawebv3.blunetdev.com/common/widgets/
Upload File :
Current File : /var/www/html/dynawebv3.blunetdev.com/common/widgets/VueSort.php

<?php
/***
 * vue sort 
 * simple widget for sorting list
 * author allif@bluberry.net
 **/
namespace common\widgets;

use yii\base\Widget;
use yii\debug\models\timeline\DataProvider;

class VueSort extends Widget {
    
    /**
     * Json string
     * @var string
     */
    private $json;
    
    /**
     * 
     * @var string Body concatenate
     */
    private $body;
    
    /**
     * table head div
     * @var string
     */
    private $trdiv;
    
    /**
     * @var DataProvider
     */
    public $dataProvider;
    
    /**
     * Url that need for vuesort send ajax request for saving sorting item
     * @var string
     */
    public $callbackUrl;
    
    /**
     * Column setting and property that need to be display
     * @var array
     */
    public $columns;
   
    /**
     * Success message for ajax request success
     * @var string
     */
    public $successMessage = 'Sorting updated!';

    /**
     * Failed message for ajax request failed
     * @var string
     */
    public $failedMessage = 'Failed to sort!';

    /**
     * calback for id use to determined what column need to use as primary key when update the sorting data trough ajax
     * $var callback
     */
    public $callbackId = 'callbackId';

    public function init()
    { 
        $this->sortId = $this->sortId ?? 'sort_' . time();
        parent::init();
        $this->view->registerJsFile("https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js", ['depends' => [\yii\web\JqueryAsset::className()]]);
        $this->view->registerJsFile("https://cdn.jsdelivr.net/npm/vue-resource@1.4.0", ['depends' => [\yii\web\JqueryAsset::className()]]);

        $this->view->params['extra-asset']['JS'][] = 'plugins/toastr/toastr.min.js';
        $this->view->params['extra-asset']['CSS'][] = 'plugins/toastr/toastr.min.css';

        /*prepare the json*/
        $item = null;
        $this->trdiv = null;
        $models = $this->dataProvider->models;
        $class  = $this->dataProvider->query->modelClass;
        $instance  = $class::instance();
        $primarykey = current($instance::primaryKey());
        array_walk($models, function ($v, $i) use (&$item, $primarykey) {
            foreach ($this->columns as $val) {
                if (!is_array($val) && !empty($v->{$val})) {
                    $item[$i][$val] = $v->{$val};
                } else {
                    if (isset($val['html']) && is_callable($val['html'])) {
                        $item[$i][$val['field']] = $val['html']($v);
                    }
                }
                /* callback for ajax */
                if (isset($this->callbackId)) {
                  $item[$i][$this->callbackId] = $v->{$primarykey};
                }
            }
        });
        $this->json = json_encode($item);

        /* prepare column value interpolation */
        $body = null;
        array_walk($this->columns, function ($v, $i) use (&$body) {
            $i = $i + 1;
            if (!is_array($v)) {
              $body .= "<p  class=\"vhtml vhtml-{$i}\">{{item.{$v}}}</p>";
            } else {
                if (!is_array($v)) {
                    $body .= "<p  class=\"vhtml vhtml-{$i}\">{{item.{$v}}}</p>";
                } elseif (is_array($v)) {
                    if (!isset($v['id'])) {
                        if (!isset($v['html'])) {
                            $body .= "<p class=\"v-field\">{{item.{$v['field']}}}</p>";
                        } else {
                            $body .= "<div class=\"vhtml vhtml-{$i}\" v-html=\"item.{$v['field']}\"></div>";
                        }
                    }
                }
            }
        });

        /* table head */
        array_walk($this->columns, function($v, $i) use ($instance) {
          $label = null;
          $i= $i + 1;
          if (!is_array($v)) {
            $label = $instance->getAttributeLabel($v) ?? $v;
          } elseif (is_array($v) && !isset($v['label'])){
            $label = $instance->getAttributeLabel($v['field']) ?? $v['field'];
          } else {
            $label = $v['label'];
          }
          $this->trdiv .= '<div class="item-tr item-tr-'.$i.'">'.$label.'</div>';
        });

        $this->body = $body;
    }
    public function run()
    {
        $this->view->registerJs(
        <<<JS
        new Vue({
              el: "#$this->sortId",
              data: {
                  items: $this->json,
                _placeholder: null,
                _dropIndex: null,
                positionChange: false
              },
              mounted: function() {
                this._placeholder = this.\$refs.ph;
              },
              methods: {
                reorder: function(array, moveIndex, toIndex) {
                  var item = array[moveIndex];
                  var length = array.length;
                  var diff = moveIndex - toIndex;

                  if (diff > 0) {
                    // move left
                    return [].concat(
                      array.slice(0, toIndex),
                      [item],
                      array.slice(toIndex, moveIndex),
                      array.slice(moveIndex + 1, length)
                    );
                  } else if (diff < 0) {
                    // move right
                    return [].concat(
                      array.slice(0, moveIndex),
                      array.slice(moveIndex + 1, toIndex),
                      [item],
                      array.slice(toIndex, length)
                    );
                  }
                  return array;
                },
                dragstart: function(event) {
                  this.\$nextTick(function() {
                    event.target.style.display = "none";
                    this.\$el.insertBefore(this._placeholder, event.target.nextSibling || event.target);
                    let t = event.target.children[0].children[0].innerText;
                    this._placeholder.children[0].innerText = t;
                    this._placeholder.style.display = "block";
                  }.bind(this));
                },
                dragenter: function(event, index) {
                  var box = event.target.getBoundingClientRect();
                  var next = (event.clientY - box.top) / (box.bottom - box.top) > .5;
                  if(event.target.nodeName === 'A'){
                    if(this.\$el.insertBefore(this._placeholder, next && event.target.nextSibling || event.target))
                    this.positionChange = true;
                    this._dropIndex = index;
                  }
                  this._placeholder.style.display = "block";
                },
                async dragend (event, index) {
                  let dropindex = null;
                  if(this._placeholder.nextSibling.dataset){
                    dropindex = this._placeholder.nextSibling.dataset.index;
                  }
                  this.items = this.reorder(this.items, index, dropindex);
                  this._placeholder.style.display = "none";
                  event.target.style.display = "block";
                  this._dropIndex = null;
                  
                  if (this.positionChange) {
                    const data = { items: this.items };         
                    await fetch('$this->callbackUrl',{
                      method: 'POST',
                      headers: {
                          'Content-Type': 'application/json',
                      },
                      body: JSON.stringify(data)
                      })
                      .then(resp => {return resp.json()})
                      .then(json => {
                        if (json.success){
                          //  success
                          toastr.info('$this->successMessage')
                        } else {
                          toastr.error('$this->failedMessage')
                        }
                      }).catch((e) => {
                        toastr.error('$this->failedMessage')
                      })
                  }
                }
              }
            });
JS
            );

        $this->view->registerCss(
        <<<CSS
        .ph {
          display: none;
          text-align: center;
          color: #d6d6d6;
          background-color: #fff;
          padding: 10px 15px;
          margin-bottom: -1px;
          background-color: #fff;
          border: 1px solid #ddd;
        }
        .ph:hover {
          transition: none;
          box-shadow: none;
        }

        .list-group-item-text{
          display: inline-block;
        }
        .list-group-item{
          padding:10px 0px;
        }
        .v-itemwrapper, .thflex{
          display: flex;
          flex-direction: row;
        }
        .list-group-item-healjding{
          flex: 2;
          max-width:250px;
          min-width:250px;
          align-self: center;
        }
        .v-field{
          flex: 1;
          align-self: center;
        }
        .item-tr{
          flex: 1;
          text-align: center;
          padding:10px;
          border:1px solid #eee;
        }
        .vhtml{
          flex: 1;
          padding:10px;
          text-align:center;
        }
CSS
        );
        return <<<HTML
          <div class="thflex">
          <!-- table head -->
            $this->trdiv
          </div>
          <ul id="$this->sortId" class="list-group">
              <a class="list-group-item" draggable="true"
                  v-for="(item, index) in items"
                  :data-index="index"
                  v-on:dragstart="dragstart(\$event)"
                  v-on:dragenter="dragenter(\$event, index)"
                  v-on:dragend="dragend(\$event, index)">
                  <div class="v-itemwrapper">
                    $this->body
                    <!--html-->
                    $this->html
                  </div>
              </a>
                <a class="list-group-item active ph" ref="ph" id="ph" href="#">
                <h2 class="ph-title"><i class="fa fa-sign-in" aria-hidden="true"></i></i> --- </h2>
              </a>
          </ul>
HTML;
    }
}