| Current Path : /var/www/html/dynawebv3.blunetdev.com/common/widgets/ |
| 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;
}
}