Completed
Push — master ( f1f594...1d7015 )
by Alexsandr
42:51 queued 33:21
created

SuggestionsWidget::renderWidget()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
crap 2
1
<?php
2
3
namespace corpsepk\DaData;
4
5
use yii\helpers\Html;
6
use yii\helpers\Json;
7
use yii\widgets\InputWidget;
8
use yii\base\InvalidConfigException;
9
10
/**
11
 * SuggestionsWidget widget is a Yii2 wrapper for the DaData Suggestions jQuery plugin.
12
 *
13
 * ```php
14
 * echo SuggestionsWidget::widget([
15
 *     'model' => $model,
16
 *     'attribute' => 'inn',
17
 *     'token' => 'your apiKey'
18
 * ]);
19
 * ```
20
 *
21
 * The following example will use the name property instead:
22
 *
23
 * ```php
24
 * echo SuggestionsWidget::widget([
25
 *     'name' => 'inn',
26
 *     'token' => 'your apiKey'
27
 * ]);
28
 * ```
29
 *
30
 * You can also use this widget in an [[yii\widgets\ActiveForm|ActiveForm]] using the [[yii\widgets\ActiveField::widget()|widget()]]
31
 * method, for example like this:
32
 *
33
 * ```php
34
 * <?= $form->field($model, 'inn')->widget(SuggestionsWidget::class, [
35
 *     'token' => 'your apiKey'
36
 * ]) ?>
37
 * ```
38
 *
39
 * Параметры jQuery плагина
40
 * @see https://confluence.hflabs.ru/pages/viewpage.action?pageId=466681916
41
 *
42
 * @author Alexsandr Khramov <[email protected]>
43
 * @see https://github.com/corpsepk/...
44
 */
45
class SuggestionsWidget extends InputWidget
46
{
47
    const TYPE_NAME = 'NAME';
48
    const TYPE_ADDRESS = 'ADDRESS';
49
    const TYPE_PARTY = 'PARTY';
50
    const TYPE_BANK = 'BANK';
51
    const TYPE_EMAIL = 'EMAIL';
52
53
    /**
54
     * @see https://confluence.hflabs.ru/pages/viewpage.action?pageId=466681917
55
     */
56
    const CALLBACK_BEFORE_RENDER = 'beforeRender';
57
    const CALLBACK_FORMAT_RESULT = 'formatResult';
58
    const CALLBACK_FORMAT_SELECTED = 'formatSelected';
59
    const CALLBACK_ON_VALIDATE_SELECTION = 'onInvalidateSelection';
60
    const CALLBACK_ON_SEARCH_START = 'onSearchStart';
61
    const CALLBACK_ON_SEARCH_COMPLETE = 'onSearchComplete';
62
    const CALLBACK_ON_SEARCH_ERROR = 'onSearchError';
63
    const CALLBACK_ON_SUGGESTION_FETCH = 'onSuggestionsFetch';
64
    const CALLBACK_ON_SELECT = 'onSelect';
65
    const CALLBACK_ON_SELECT_NOTHING = 'onSelectNothing';
66
67
    /**
68
     * @see https://confluence.hflabs.ru/pages/viewpage.action?pageId=466681916
69
     */
70
    const ADDON_SPINNER = 'spinner';
71
    const ADDON_CLEAR = 'clear';
72
    const ADDON_NONE = 'none';
73
74
    /**
75
     * Тип подсказок:
76
     * NAME — ФИО;
77
     * ADDRESS — адреса;
78
     * PARTY — организации и ИП;
79
     * EMAIL — адрес электронной почты;
80
     * BANK — банковские организации.
81
     * @var string
82
     */
83
    public $type;
84
85
    /**
86
     * Тип подсказок по умолчанию
87
     * @var string
88
     */
89
    public $defaultType = self::TYPE_NAME;
90
91
    /**
92
     * @var array
93
     */
94
    public $inputOptions = [];
95
96
    /**
97
     * API-ключ вашей учетной записи на DaData.ru.
98
     * Можно посмотреть в личном кабинете - https://dadata.ru/profile/#info
99
     * @var string
100
     */
101
    public $token;
102
103
    /**
104
     * Что показывать в правом углу текстового поля подсказок:
105
     * по умолчанию — индикатор загрузки в десктопной версии и крестик очистки в мобильной;
106
     * spinner — индикатор загрузки;
107
     * clear — крестик очистки;
108
     * none — ничего не показывать.
109
     * Обратите внимание, значение нужно передавать как строку (например, addon: "clear").
110
     * @var string
111
     */
112
    public $addon;
113
114
    /**
115
     * Всегда выбирать первую подсказку, если пользователь явно не выбрал другую.
116
     * default - false
117
     * @var bool
118
     */
119
    public $autoSelectFirst;
120
121
    /**
122
     * Максимальное количество подсказок в выпадающем списке. Не может быть больше 20.
123
     * default - 5
124
     * @var int
125
     */
126
    public $count;
127
128
    /**
129
     * Период ожидания перед отправкой запроса на сервер подсказок, в миллисекундах.
130
     * Позволяет не перегружать сервер запросами, если пользователь очень быстро печатает.
131
     * default - 100
132
     * @var int
133
     */
134
    public $deferRequestBy;
135
136
    /**
137
     * Если опция установлена в true,
138
     * выпадающий список отображается поверх всего документа, и ничем не обрезается.
139
     * default - false
140
     * @var bool
141
     */
142
    public $floating;
143
144
    /**
145
     * Объект с дополнительными HTTP-заголовками, которые необходимо передать на сервер.
146
     * @var string
147
     */
148
    public $headers;
149
150
    /**
151
     * Поясняющий текст, который показывается в выпадающем списке над подсказками.
152
     * @var string
153
     */
154
    public $hint;
155
156
    /**
157
     * Периодическая проверка, не стали ли невидимые поля видимыми (время в миллисекундах).
158
     * default - 100
159
     * @var int
160
     */
161
    public $initializeInterval;
162
163
    /**
164
     * Минимальная длина текста, после которой включаются подсказки.
165
     * @var int
166
     */
167
    public $minChars;
168
169
    /**
170
     * Максимальная ширина экрана, при которой будет применен вид,
171
     * адаптированный для мобильных устройств.
172
     * default - 980
173
     * @var int
174
     */
175
    public $mobileWidth;
176
177
    /**
178
     * Кэширование ответов сервера.
179
     * default - false
180
     * @var bool
181
     */
182
    public $noCache;
183
184
    /**
185
     * Прокрутка поля ввода к верхнему краю экрана при фокусе. Для мобильных устройств.
186
     * Если передать jQuery-объект с другим элементом, страница будет прокручиваться до этого элемента.
187
     * default - true
188
     * @var bool
189
     */
190
    public $scrollOnFocus;
191
192
    /**
193
     * URL сервиса standalone-подсказок.
194
     * @var
195
     */
196
    public $serviceUrl;
197
198
    /**
199
     * Не показывать подсказки до ввода символа "@" в подсказках по e-mail.
200
     * default - true
201
     * @var bool
202
     */
203
    public $suggest_local;
204
205
    /**
206
     * Таймаут для отправки ajax-запросов на сервер. Указывается в миллисекундах.
207
     * default - 3000
208
     * @var integer
209
     */
210
    public $timeout;
211
212
    /**
213
     * Автоматически подставлять подходящую подсказку из списка при потере фокуса.
214
     * default - true
215
     * @var bool
216
     */
217
    public $triggerSelectOnBlur;
218
219
    /**
220
     * Автоматически подставлять подходящую подсказку из списка при нажатии на Enter.
221
     * default - true
222
     * @var bool
223
     */
224
    public $triggerSelectOnEnter;
225
226
    /**
227
     * Автоматически подставлять подходящую подсказку из списка
228
     * при нажатии на пробел (по умолчанию отключено).
229
     * default - false
230
     * @var bool
231
     */
232
    public $triggerSelectOnSpace;
233
234
    /**
235
     * Ширина выпадающего списка в пикселях. 'auto' - по ширине текстбокса.
236
     * default - auto
237
     * @var string|int
238
     */
239
    public $width;
240
241
    /**
242
     * @inheritdoc
243
     */
244 9
    public function init()
245
    {
246 9
        parent::init();
247 9
        $this->initOptions();
248 6
        $this->initInputOptions();
249 6
    }
250
251
    /**
252
     * @throws InvalidConfigException
253
     */
254 9
    protected function initOptions()
255
    {
256 9
        if (!$this->type) {
257 9
            $this->type = $this->defaultType;
258 3
        }
259
260
        $attributes = [
261 9
            'addon',
262 3
            'autoSelectFirst',
263 3
            'count',
264 3
            'deferRequestBy',
265 3
            'floating',
266 3
            'headers',
267 3
            'hint',
268 3
            'initializeInterval',
269 3
            'minChars',
270 3
            'mobileWidth',
271 3
            'noCache',
272 3
            'scrollOnFocus',
273 3
            'serviceUrl',
274 3
            'suggest_local',
275 3
            'timeout',
276 3
            'token',
277 3
            'triggerSelectOnBlur',
278 3
            'triggerSelectOnEnter',
279 3
            'triggerSelectOnSpace',
280 3
            'type',
281 3
            'width',
282 3
        ];
283 9
        foreach ($attributes as $attribute) {
284 9
            if ($this->$attribute === null || isset($this->options[$attribute])) {
285 9
                continue;
286
            }
287
288 9
            $this->options[$attribute] = $this->$attribute;
289 3
        }
290
291
        // `type` required
292 9
        if (!isset($this->options['type'])) {
293
            throw new InvalidConfigException('`type` param required');
294
        }
295
296
        // `token` required
297 9
        if (!isset($this->options['token'])) {
298 3
            throw new InvalidConfigException('`token` param required');
299
        }
300 6
    }
301
302 6
    protected function initInputOptions()
303
    {
304 6
        $this->inputOptions['id'] = $this->options['id'];
305 6
        Html::addCssClass($this->inputOptions, 'form-control');
306 6
    }
307
308
    /**
309
     * Renders the widget.
310
     */
311 6
    public function run()
312
    {
313 6
        $this->registerAssets();
314 6
        echo $this->renderWidget();
315 6
    }
316
317
    /**
318
     * Renders the SuggestionsWidget widget.
319
     * @return string the rendering result.
320
     */
321 6
    protected function renderWidget()
322
    {
323 6
        if ($this->hasModel()) {
324 3
            return Html::activeTextInput($this->model, $this->attribute, $this->inputOptions);
325
        } else {
326 6
            return Html::textInput($this->name, $this->value, $this->inputOptions);
327
        }
328
    }
329
330
    /**
331
     * Registers the needed assets
332
     */
333 6
    protected function registerAssets()
334
    {
335 6
        SuggestionsWidgetAsset::register($this->getView());
336 6
        $this->registerPlugin();
337 6
    }
338
339
    /**
340
     * Registers plugin
341
     */
342 6
    protected function registerPlugin()
343
    {
344 6
        $id = $this->options['id'];
345 6
        $options = empty($this->options) ? '' : Json::htmlEncode($this->options);
346 6
        $js = "jQuery('#$id').suggestions($options);";
347 6
        $this->getView()->registerJs($js);
348
    }
349
}