|
| 1 | +<?php |
| 2 | +/** |
| 3 | + * @link https://github.com/MacGyer/yii2-materializecss |
| 4 | + * @copyright Copyright (c) 2016 ... MacGyer for pluspunkt coding |
| 5 | + * @license https://github.com/MacGyer/yii2-materializecss/blob/master/LICENSE |
| 6 | + */ |
| 7 | + |
| 8 | +namespace macgyer\yii2materializecss\widgets\form; |
| 9 | + |
| 10 | +use macgyer\yii2materializecss\assets\NoUiSliderAsset; |
| 11 | +use macgyer\yii2materializecss\lib\BaseInputWidget; |
| 12 | +use macgyer\yii2materializecss\lib\Html; |
| 13 | +use yii\helpers\ArrayHelper; |
| 14 | +use yii\helpers\Json; |
| 15 | +use yii\web\JsExpression; |
| 16 | + |
| 17 | +/** |
| 18 | + * RangeInput renders a slider input element. You can specify to use a simple HTML5 range `<input>` or make use of the versatile |
| 19 | + * noUiSlider JS plugin. |
| 20 | + * |
| 21 | + * All noUiSlider settings provided as member variables are needed for out-of-the-box functionality. See the noUiSlider |
| 22 | + * docs for more options and events. |
| 23 | + * |
| 24 | + * <strong>Please note:</strong> This widget provides functionality for sliders with one handle only. If you want to use |
| 25 | + * noUiSlider with multiple handles or more complex functionality you need to create your own class and inherit from this class. |
| 26 | + * |
| 27 | + * @author Christoph Erdmann <[email protected]> |
| 28 | + * @package widgets |
| 29 | + * @subpackage form |
| 30 | + * |
| 31 | + * @see http://materializecss.com/forms.html#range |
| 32 | + * @see https://refreshless.com/nouislider/ |
| 33 | + */ |
| 34 | +class RangeInput extends BaseInputWidget |
| 35 | +{ |
| 36 | + /** |
| 37 | + * @var array the HTML attributes for the widget container tag. The following special options are recognized: |
| 38 | + * |
| 39 | + * - tag: string, defaults to "div", the name of the container tag. |
| 40 | + * |
| 41 | + * @see [yii\helpers\BaseHtml::renderTagAttributes()](http://www.yiiframework.com/doc-2.0/yii-helpers-basehtml.html#renderTagAttributes()-detail) |
| 42 | + * for details on how attributes are being rendered. |
| 43 | + */ |
| 44 | + public $ptions = []; |
| 45 | + |
| 46 | + /** |
| 47 | + * @var array the HTML attributes for the slider element. The following special options are recognized: |
| 48 | + * |
| 49 | + * - tag: string, defaults to "div", the name of the slider element's tag. |
| 50 | + * |
| 51 | + * @see [yii\helpers\BaseHtml::renderTagAttributes()](http://www.yiiframework.com/doc-2.0/yii-helpers-basehtml.html#renderTagAttributes()-detail) |
| 52 | + * for details on how attributes are being rendered. |
| 53 | + */ |
| 54 | + public $sliderOptions = []; |
| 55 | + |
| 56 | + /** |
| 57 | + * @var boolean whether to use noUiSlider. |
| 58 | + * @see http://materializecss.com/forms.html#range |
| 59 | + */ |
| 60 | + public $useNoUiSlider = true; |
| 61 | + |
| 62 | + /** |
| 63 | + * @var boolean whether to render an hidden input field to hold the slider value. |
| 64 | + * This options only takes effect, when [[useNoUiSlider]] has been set to `true`. |
| 65 | + */ |
| 66 | + public $renderHiddenInput = true; |
| 67 | + |
| 68 | + /** |
| 69 | + * @var array the HTML attributes for the actual input element. |
| 70 | + * |
| 71 | + * If you have set [[useNoUiSlider]] and [[renderHiddenInput]] both to `true`, the input element will be used as a hidden |
| 72 | + * input field holding the value issued by the slider and these options applied. |
| 73 | + * |
| 74 | + * When setting [[useNoUiSlider]] to `false`, the input options will be applied to the `<input type="range">` element. |
| 75 | + * |
| 76 | + * @see [yii\helpers\BaseHtml::renderTagAttributes()](http://www.yiiframework.com/doc-2.0/yii-helpers-basehtml.html#renderTagAttributes()-detail) |
| 77 | + * for details on how attributes are being rendered. |
| 78 | + */ |
| 79 | + public $inputOptions = []; |
| 80 | + |
| 81 | + /** |
| 82 | + * @var integer the start value of the slider. |
| 83 | + * This options only takes effect, when [[useNoUiSlider]] has been set to `true`. |
| 84 | + * @see https://refreshless.com/nouislider/slider-options/#section-start |
| 85 | + */ |
| 86 | + public $start = 0; |
| 87 | + |
| 88 | + /** |
| 89 | + * @var array the min and max values of the slider. |
| 90 | + * This options only takes effect, when [[useNoUiSlider]] has been set to `true`. |
| 91 | + */ |
| 92 | + public $range = [ |
| 93 | + 'min' => 0, |
| 94 | + 'max' => 100 |
| 95 | + ]; |
| 96 | + /** |
| 97 | + * @var integer the stepping value when moving the slider handle. |
| 98 | + * This options only takes effect, when [[useNoUiSlider]] has been set to `true`. |
| 99 | + * @see https://refreshless.com/nouislider/slider-options/#section-step |
| 100 | + */ |
| 101 | + public $step = 1; |
| 102 | + |
| 103 | + /** |
| 104 | + * @var boolean whether to use a vertical slider. |
| 105 | + * This options only takes effect, when [[useNoUiSlider]] has been set to `true`. |
| 106 | + * @see https://refreshless.com/nouislider/slider-options/#section-orientation |
| 107 | + */ |
| 108 | + public $vertical = false; |
| 109 | + |
| 110 | + /** |
| 111 | + * Initialize the widget. |
| 112 | + */ |
| 113 | + public function init() |
| 114 | + { |
| 115 | + parent::init(); |
| 116 | + if ($this->useNoUiSlider) { |
| 117 | + if (!ArrayHelper::keyExists('start', $this->clientOptions)) { |
| 118 | + $this->clientOptions['start'] = $this->start; |
| 119 | + } |
| 120 | + |
| 121 | + if (!ArrayHelper::keyExists('range', $this->clientOptions)) { |
| 122 | + $this->clientOptions['range'] = $this->range; |
| 123 | + } |
| 124 | + |
| 125 | + if (!ArrayHelper::keyExists('step', $this->clientOptions)) { |
| 126 | + $this->clientOptions['step'] = $this->step; |
| 127 | + } |
| 128 | + |
| 129 | + if ($this->vertical) { |
| 130 | + $this->clientOptions['orientation'] = 'vertical'; |
| 131 | + } |
| 132 | + |
| 133 | + if (!isset($this->sliderOptions['id'])) { |
| 134 | + $this->sliderOptions['id'] = $this->getId(); |
| 135 | + } |
| 136 | + |
| 137 | + $this->addUpdateEvent(); |
| 138 | + $this->registerClientScripts(); |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + /** |
| 143 | + * Adds default noUiSlider update event to [[clientEvents]]. |
| 144 | + * @see https://refreshless.com/nouislider/events-callbacks/#section-update |
| 145 | + */ |
| 146 | + protected function addUpdateEvent() |
| 147 | + { |
| 148 | + $sliderId = $this->sliderOptions['id']; |
| 149 | + $this->inputOptions['data-slider-target'] = $sliderId; |
| 150 | + |
| 151 | + if (!isset($this->clientEvents['update'])) { |
| 152 | + $this->clientEvents['update'] = new JsExpression(<<<JS |
| 153 | +function (values, handle) { |
| 154 | + var value = values[handle]; |
| 155 | + $('[data-slider-target="{$sliderId}"]').val(value); |
| 156 | +} |
| 157 | +JS |
| 158 | + ); |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * Execute the widget. |
| 164 | + * |
| 165 | + * @return string the rendered markup. |
| 166 | + */ |
| 167 | + public function run() |
| 168 | + { |
| 169 | + $tag = ArrayHelper::remove($this->options, 'tag', 'div'); |
| 170 | + $html[] = Html::beginTag($tag, $this->options); |
| 171 | + |
| 172 | + if (!$this->useNoUiSlider) { |
| 173 | + $html[] = $this->renderHtml5RangeInput(); |
| 174 | + } else { |
| 175 | + $html[] = $this->renderNoUiSlider(); |
| 176 | + } |
| 177 | + |
| 178 | + $html[] = Html::endTag($tag); |
| 179 | + return implode("\n", $html); |
| 180 | + } |
| 181 | + |
| 182 | + /** |
| 183 | + * Renders a standard HTML5 range input field. |
| 184 | + * This is only being effective when [[$useNoUiSlider]] is set to 'false'. |
| 185 | + * |
| 186 | + * @return string the field markup. |
| 187 | + */ |
| 188 | + protected function renderHtml5RangeInput() |
| 189 | + { |
| 190 | + $html[] = Html::beginTag('div', ['class' => 'range-field']); |
| 191 | + |
| 192 | + if ($this->hasModel()) { |
| 193 | + $html[] = Html::activeInput('range', $this->model, $this->attribute, $this->inputOptions); |
| 194 | + } else { |
| 195 | + $html[] = Html::input('range', $this->name, $this->value, $this->inputOptions); |
| 196 | + } |
| 197 | + |
| 198 | + $html[] = Html::endTag('div'); |
| 199 | + |
| 200 | + return implode("\n", $html); |
| 201 | + } |
| 202 | + |
| 203 | + /** |
| 204 | + * Renders the markup for the noUiSlider plugin. |
| 205 | + * This is only being effective when [[$useNoUiSlider]] is set to 'true'. |
| 206 | + * |
| 207 | + * @return string the field markup. |
| 208 | + */ |
| 209 | + protected function renderNoUiSlider() |
| 210 | + { |
| 211 | + $html[] = Html::tag('div', '', $this->sliderOptions); |
| 212 | + if ($this->renderHiddenInput) { |
| 213 | + $html[] = $this->renderHiddenInput(); |
| 214 | + } |
| 215 | + |
| 216 | + return implode("\n", $html); |
| 217 | + } |
| 218 | + |
| 219 | + /** |
| 220 | + * Renders a hidden input field which holds the value issued by the slider. |
| 221 | + * This is only being effective when [[$useNoUiSlider]] is set to 'true'. |
| 222 | + * |
| 223 | + * @return string the input field markup. |
| 224 | + */ |
| 225 | + protected function renderHiddenInput() |
| 226 | + { |
| 227 | + if ($this->hasModel()) { |
| 228 | + return Html::activeTextInput($this->model, $this->attribute, $this->inputOptions); |
| 229 | + } else { |
| 230 | + return Html::hiddenInput($this->name, $this->value, $this->inputOptions); |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + /** |
| 235 | + * Registers all scripts needed by the underlying JS plugin. |
| 236 | + */ |
| 237 | + protected function registerClientScripts() |
| 238 | + { |
| 239 | + $view = $this->getView(); |
| 240 | + NoUiSliderAsset::register($view); |
| 241 | + |
| 242 | + $id = $this->sliderOptions['id']; |
| 243 | + $varName = 'slider_' . sha1(uniqid()); |
| 244 | + |
| 245 | + if ($this->clientOptions !== false) { |
| 246 | + $options = empty($this->clientOptions) ? '{}' : Json::htmlEncode($this->clientOptions); |
| 247 | + $view->registerJs(<<<JS |
| 248 | +var $varName = document.getElementById('$id'); |
| 249 | +noUiSlider.create($varName, {$options}); |
| 250 | +JS |
| 251 | + ); |
| 252 | + } |
| 253 | + |
| 254 | + $this->registerClientEvents($varName); |
| 255 | + } |
| 256 | + |
| 257 | + /** |
| 258 | + * Registers the events of the slider. |
| 259 | + * |
| 260 | + * @param string $sliderVarName the JS variable name of the slider element. |
| 261 | + */ |
| 262 | + protected function registerClientEvents($sliderVarName = null) |
| 263 | + { |
| 264 | + if (!empty($this->clientEvents)) { |
| 265 | + $js = []; |
| 266 | + foreach ($this->clientEvents as $event => $handler) { |
| 267 | + $js[] = "$sliderVarName.noUiSlider.on('$event', $handler);"; |
| 268 | + } |
| 269 | + $this->getView()->registerJs(implode("\n", $js)); |
| 270 | + } |
| 271 | + } |
| 272 | +} |
0 commit comments