Test Setup Failed
Push — dev ( 61c8f1...460265 )
by
unknown
02:47
created

TreeWidget   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 21
c 2
b 0
f 1
lcom 1
cbo 9
dl 0
loc 215
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A run() 0 51 3
B contextMenuOptions() 0 19 5
A prepareJs() 0 9 3
B adjacecncyJs() 0 35 5
B nestedSetJs() 0 28 5
1
<?php
2
3
namespace devgroup\JsTreeWidget\widgets;
4
5
use Yii;
6
use yii\base\InvalidConfigException;
7
use yii\base\Widget;
8
use yii\helpers\Html;
9
use yii\helpers\Json;
10
use yii\helpers\Url;
11
use yii\web\JsExpression;
12
use yii\helpers\ArrayHelper;
13
use yii\web\View;
14
15
/**
16
 * JsTree widget for Yii Framework 2
17
 */
18
class TreeWidget extends Widget
19
{
20
21
    const TREE_TYPE_ADJACENCY = 'adjacency';
22
    const TREE_TYPE_NESTED_SET = 'nested-set';
23
24
    public $treeType = self::TREE_TYPE_ADJACENCY;
25
    /**
26
     * @var array Enabled jsTree plugins
27
     * @see http://www.jstree.com/plugins/
28
     */
29
    public $plugins = [
30
        'wholerow',
31
        'contextmenu',
32
        'dnd',
33
        'types',
34
        'state',
35
    ];
36
37
    /**
38
     * @var array Configuration for types plugin
39
     * @see http://www.jstree.com/api/#/?f=$.jstree.defaults.types
40
     */
41
    public $types = [
42
        'show' => [
43
            'icon' => 'fa fa-file-o',
44
        ],
45
        'list' => [
46
            'icon' => 'fa fa-list',
47
        ],
48
    ];
49
50
    /**
51
     * Context menu actions configuration.
52
     * @var array
53
     */
54
    public $contextMenuItems = [];
55
56
    /**
57
     * Various options for jsTree plugin. Will be merged with default options.
58
     * @var array
59
     */
60
    public $options = [];
61
62
    /**
63
     * Route to action which returns json data for tree
64
     * @var array
65
     */
66
    public $treeDataRoute = null;
67
68
    /**
69
     * Translation category for Yii::t() which will be applied to labels.
70
     * If translation is not needed - use false.
71
     */
72
    public $menuLabelsTranslationCategory = 'app';
73
74
    /**
75
     * JsExpression for action(callback function) on double click. You can use JsExpression or make custom expression.
76
     * Warning! Callback function differs from native jsTree function - it consumes only one attribute - node(similar to contextmenu action).
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 141 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
77
     * Use false if no action needed.
78
     * @var bool|JsExpression
79
     */
80
    public $doubleClickAction = false;
81
82
    public $changeParentAction = false;
83
84
    public $reorderAction = false;
85
86
    public function run()
87
    {
88
        if (!is_array($this->treeDataRoute)) {
89
            throw new InvalidConfigException("Attribute treeDataRoute is required to use TreeWidget.");
90
        }
91
92
        $options = [
93
            'plugins' => $this->plugins,
94
            'core' => [
95
                'check_callback' => true,
96
                'data' => [
97
                    'url' => new JsExpression(
98
                        "function (node) {
99
                            return " . Json::encode(Url::to($this->treeDataRoute)) . ";
100
                        }"
101
                    ),
102
                    'data' => new JsExpression(
103
                        "function (node) {
104
                        return { 'id' : node.id };
105
                        }"
106
                    ),
107
                ]
108
            ]
109
        ];
110
111
        // merge with contextmenu configuration
112
        $options = ArrayHelper::merge($options, $this->contextMenuOptions());
113
114
        // merge with attribute-provided options
115
        $options = ArrayHelper::merge($options, $this->options);
116
117
        $options = Json::encode($options);
118
119
        $this->getView()->registerAssetBundle('devgroup\JsTreeWidget\widgets\JsTreeAssetBundle');
120
121
        $doubleClick = '';
122
        if ($this->doubleClickAction !== false) {
123
            $doubleClick = "
124
            jsTree_{$this->getId()}.on('dblclick.jstree', function (e) {
125
                var node = $(e.target).closest('.jstree-node').children('.jstree-anchor');
126
                var callback = " . $this->doubleClickAction . ";
127
                callback(node);
128
                return false;
129
            });\n";
130
        }
131
        $treeJs = $this->prepareJs();
132
        $this->getView()->registerJs("
133
        var jsTree_{$this->getId()} = \$('#{$this->getId()}').jstree($options);
134
        $doubleClick $treeJs", View::POS_READY);
135
        return Html::tag('div', '', ['id' => $this->getId()]);
136
    }
137
138
    private function contextMenuOptions()
139
    {
140
        $options = [];
141
        if (count($this->contextMenuItems) > 0) {
142
            if (!in_array('contextmenu', $this->plugins)) {
143
                // add missing contextmenu plugin
144
                $options['plugins'] = ['contextmenu'];
145
            }
146
147
            $options['contextmenu']['items'] = [];
148
            foreach ($this->contextMenuItems as $index => $item) {
149
                if ($this->menuLabelsTranslationCategory !== false) {
150
                    $item['label'] = Yii::t($this->menuLabelsTranslationCategory, $item['label']);
151
                }
152
                $options['contextmenu']['items'][$index] = $item;
153
            }
154
        }
155
        return $options;
156
    }
157
158
    private function prepareJs()
159
    {
160
        switch ($this->treeType) {
161
            case self::TREE_TYPE_ADJACENCY :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
162
                return $this->adjacecncyJs();
163
            case self::TREE_TYPE_NESTED_SET :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
164
                return $this->nestedSetJs();
165
        }
166
    }
167
168
    private function adjacecncyJs()
169
    {
170
        $changeParent = '';
171
        if ($this->changeParentAction !== false) {
172
            $changeParentUrl = is_array($this->changeParentAction) ? Url::to($this->changeParentAction) : $this->changeParentAction;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
173
            $changeParent = "
174
             jsTree_{$this->getId()}.bind('move_node.jstree', function(e, data) {
175
                jQuery.get(
176
                    '" . $changeParentUrl . "',
177
                    {
178
                        'id': data.node.id,
179
                        'parent_id': data.parent
180
                    }
181
                );
182
                return false;
183
            });\n";
184
        }
185
        $reorder = '';
186
        if ($this->reorderAction !== false) {
187
            $reorderUrl = is_array($this->reorderAction) ? Url::to($this->reorderAction) : $this->reorderAction;
188
            $reorder = "
189
             jsTree_{$this->getId()}.bind('move_node.jstree', function(e, data) {
190
             var params = [];
191
                 jQuery('.jstree-node').each(function(i, e) {
192
                    params[e.id] = i;
193
                 });
194
                  jQuery.post(
195
                    '" . $reorderUrl . "',
196
                    {'order':params }
197
                );
198
                return false;
199
            });\n";
200
        }
201
        return $changeParent . $reorder;
202
    }
203
204
    private function nestedSetJs()
205
    {
206
        $js = "";
207
        if (false !== $this->reorderAction || false !== $this->changeParentAction) {
208
            $action = $this->reorderAction ?: $this->changeParentAction;
209
            $url = is_array($action) ? Url::to($action) : $action;
210
            $js = new JsExpression(
211
                "jsTree_{$this->getId()}.on('move_node.jstree', function(e, data) {
212
                    \$parent = $(this).jstree(true).get_node(data.parent);
213
                    siblings = \$parent.children || {};
214
                    $.post(
215
                        '{$url}',
216
                         {
217
                            'node_id' : data.node.id,
218
                            'parent': data.parent,
219
                            'position': data.position,
220
                            'old_parent': data.old_parent,
221
                            'old_position': data.old_position,
222
                            'is_multi': data.is_multi,
223
                            'siblings': siblings
224
                         }
225
                    );
226
                    return false;
227
                });\n"
228
            );
229
        }
230
        return $js;
231
    }
232
}
233