* @since 1.0 * @see http://www.bootstrap-switch.org/ */ class SwitchInput extends \kartik\base\InputWidget { const CHECKBOX = 1; const RADIO = 2; /** * @inherit doc */ public $pluginName = 'bootstrapSwitch'; /** * * @var integer the input type - one * of the constants above. */ public $type = self::CHECKBOX; /** * * @var boolean whether to enable third indeterminate behavior when type * is `SwitchInput::CHECKBOX`. Defaults to `false`. */ public $tristate = false; /** * * @var string|int the value for indeterminate state when `tristate` is true and * type is `SwitchInput::CHECKBOX`. Defaults to `null`. */ public $indeterminateValue = null; /** * * @var array | boolean HTML attributes for the toggle indicator to turn indeterminate * state on and off. The following special attributes are recognized: * - `label`: string, the indeterminate toggle icon markup. Defaults to `×` * If this is set to `false` the indeterminate toggle icon will not be shown. */ public $indeterminateToggle = [ ]; /** * * @var array the list of items for radio input * (applicable only if `type` = 2). The following * keys could be setup: * - label: string the label of each radio item. If this is * set to false or null, the label will not be displayed. * - value: string the value of each radio item * - options: HTML attributes for the radio item * - labelOptions: HTML attributes for each radio item label */ public $items = [ ]; /** * * @var boolean whether label is aligned on same line. Defaults to true. * If set to false, the label and input will be on separate lines. */ public $inlineLabel = true; /** * * @var array default HTML attributes for each radio item * (applicable only if `type` = 2) */ public $itemOptions = [ ]; /** * * @var array default HTML attributes for each radio item label */ public $labelOptions = [ ]; /** * * @var string the separator content between each radio item * (applicable only if `type` = 2) */ public $separator = "  "; /** * * @var array HTML attributes for the container * (applicable only if `type` = 2) */ public $containerOptions = [ 'class' => 'form-group' ]; /** * Initializes the widget * * @throw InvalidConfigException */ public function init() { parent::init (); if (empty ( $this->type ) && $this->type !== self::CHECKBOX && $this->type !== self::RADIO) { throw new InvalidConfigException ( "You must define a valid 'type' which must be either 1 (for checkbox) or 2 (for radio)." ); } if ($this->type == self::RADIO) { if (empty ( $this->items ) || ! is_array ( $this->items )) { throw new InvalidConfigException ( "You must setup the 'items' array for the 'radio' type." ); } } $this->registerAssets (); echo $this->renderInput (); } /** * Renders the source Input for the Switch plugin. * Graceful fallback to a normal HTML checkbox or radio input * in case JQuery is not supported by the browser * * @return string */ protected function renderInput() { if ($this->type == self::CHECKBOX) { if (empty ( $this->options ['label'] )) { $this->options ['label'] = null; } $input = $this->getInput ( 'checkbox' ); $output = ($this->inlineLabel) ? $input : Html::tag ( 'div', $input ); $output = $this->mergeIndToggle ( $output ); return Html::tag ( 'div', $output, $this->containerOptions ) . "\n"; } $output = ''; Html::addCssClass ( $this->containerOptions, 'kv-switch-container' ); foreach ( $this->items as $item ) { if (! is_array ( $item )) { continue; } $label = ArrayHelper::getValue ( $item, 'label', false ); $options = ArrayHelper::merge ( $this->itemOptions, ArrayHelper::getValue ( $item, 'options', [ ] ) ); $labelOptions = ArrayHelper::merge ( $this->labelOptions, ArrayHelper::getValue ( $item, 'labelOptions', [ ] ) ); $value = ArrayHelper::getValue ( $item, 'value', null ); $options ['value'] = $value; $input = Html::radio ( $this->name, ($value == $this->value), $options ); $output .= Html::label ( $label, $this->name, $labelOptions ) . "\n" . (($this->inlineLabel) ? $input : Html::tag ( 'div', $input )) . "\n" . $this->separator; } return Html::tag ( 'div', $output, $this->containerOptions ) . "\n"; } /** * Merges the rendered indeterminate toggle indicator * * @var string $output the content to merge with the output * @return string */ protected function mergeIndToggle($output) { if (! $this->tristate || $this->indeterminateToggle === false) { return $output; } $icon = ArrayHelper::remove ( $this->indeterminateToggle, 'label', '×' ); $this->indeterminateToggle ['data-kv-switch'] = ($this->type == self::CHECKBOX) ? $this->options ['id'] : $this->name; Html::addCssClass ( $this->indeterminateToggle, 'close kv-ind-toggle' ); $icon = Html::tag ( 'span', $icon, $this->indeterminateToggle ); $options = ArrayHelper::remove ( $this->indeterminateToggle, 'containerOptions', [ ] ); $size = 'kv-size-' . ArrayHelper::getValue ( $this->pluginOptions, 'size', 'normal' ); Html::addCssClass ( $options, 'kv-ind-container ' . $size ); return Html::tag ( 'div', $icon . "\n" . $output, $options ); } /** * Registers the needed assets */ public function registerAssets() { $view = $this->getView (); SwitchInputAsset::register ( $view ); if (empty ( $this->pluginOptions ['animate'] )) { $this->pluginOptions ['animate'] = true; } $this->pluginOptions ['indeterminate'] = ($this->tristate && $this->value === $this->indeterminateValue && $this->type !== self::RADIO); $this->pluginOptions ['disabled'] = $this->disabled; $this->pluginOptions ['readonly'] = $this->readonly; $id = $this->type == self::RADIO ? 'jQuery("[name = \'' . $this->name . '\']")' : 'jQuery("#' . $this->options ['id'] . '")'; $this->registerPlugin ( $this->pluginName, $id ); if (! $this->tristate || $this->indeterminateToggle === false || $this->type == self::RADIO) { return; } $tog = 'jQuery("[data-kv-switch=\'' . $this->options ['id'] . '\']")'; $js = "{$tog}.on('click',function(){ var \$el={$id}, val; \$el.bootstrapSwitch('toggleIndeterminate'); val = \$el.prop('indeterminate') ? '{$this->indeterminateValue}' : (\$el.is(':checked').length > 0 ? 1 : 0); \$el.val(val); });"; $view->registerJs ( $js ); } }