Subversion Repositories web.creative

Rev

Blame | Last modification | View Log | Download

<?php namespace ProcessWire;

/**
 * An Inputfield for handling "submit" buttons
 * 
 * ProcessWire 3.x, Copyright 2019 by Ryan Cramer
 * https://processwire.com
 * License: MPL 2.0
 * 
 * @property bool $header Whether or not button will also appear in header (default=false). 
 * @property bool $secondary Whether or not button is secondary (default=false). 
 * @property bool $small Whether or not button should be small, where supported (default=false). 
 * @property string $dropdownInputName Name of input to receive selected dropdown value (default='_action_value') #pw-internal
 * @property bool $dropdownSubmit Selected dropdown value becomes submit value? (default=true) #pw-internal 
 * @property string $html Button inner HTML label (default='') @since 3.0.134
 * @property string $text Button inner TEXT label, if $html not provided. (default='') @since 3.0.134
 * @property string $value Button value attribute and inner TEXT label, if $text it provided (default='Submit')
 * @property string $textClass Class applied to span for inner html/text, omitted if blank (default='ui-button-text') @since 3.0.134
 * @property-read string|false $submitValue Value that was submitted if clicked (default=false) @since 3.0.134
 *
 */
class InputfieldSubmit extends Inputfield {

  public static function getModuleInfo() {
    return array(
      'title' => 'Submit', // Module Title
      'summary' => __('Form submit button', __FILE__), // Module Summary
      'version' => 102,
      'permanent' => true, 
      );
  }

  /**
   * Names of submit buttons created
   * 
   * @var array
   * 
   */ 
  protected static $submitNames = array();

  /**
   * Additional dropdown actions added to the button
   * 
   * @var array
   * 
   */
  protected $dropdownItems = array();

  /**
   * Init
   * 
   */
  public function init() {
    parent::init();
    $this->attr('type', 'submit'); 
    $this->attr('name', 'submit'); 
    $this->attr('value', $this->_('Submit')); // Standard submit button label
    $this->set('submitValue', false); // becomes string after processInput
    $this->attr('class', 'ui-button ui-widget ui-state-default ui-corner-all');
    $this->set('textClass', 'ui-button-text');
    $this->skipLabel = Inputfield::skipLabelBlank; 
    $this->set('small', false);
    $this->set('html', '');
    $this->set('text', '');
    
    // name of 'hidden' input that will receive the clicked dropdown item value
    $this->set('dropdownInputName', '_action_value');
    
    // Selected dropdown value becomes submit value? If set to false, then only the 
    // dropdownInputName above will contain the selected value
    $this->set('dropdownSubmit', true);
  }
  
  public function set($key, $value) {
    if($key == 'header') {
      $this->showInHeader($value);
    } else if($key == 'secondary') {
      $this->setSecondary($value);
    }
    return parent::set($key, $value);
  }
  
  public function get($key) {
    if($key == 'header') return $this->hasClass('pw-head-button');
    if($key == 'secondary') return $this->hasClass('ui-priority-secondary');
    return parent::get($key);
  }
  
  public function setAttribute($key, $value) {
    if($key === 'name') self::$submitNames[$value] = $value;
    return parent::setAttribute($key, $value);
  } 
    
  /**
   * Show another copy of this button in the header? 
   * 
   * @param bool $show True=yes, false=no (default=true)
   * @return $this
   * 
   */
  public function showInHeader($show = true) {
    if($show) {
      $this->addClass('pw-head-button');
    } else {
      $this->removeClass('pw-head-button');
    }
    return $this;
  }

  /**
   * Make this button secondary? (slightly faded)
   * 
   * Note: by default, buttons are not secondary
   * 
   * @param bool $secondary Default=true
   * @return $this
   * 
   */
  public function setSecondary($secondary = true) {
    if($secondary) {
      $this->addClass('ui-priority-secondary');
    } else {
      $this->removeClass('ui-priority-secondary');
    }
    return $this;
  }
  
  /**
   * Make this button small?
   * 
   * By default, buttons are regular size. This makes them small.
   * Supported only for non-dropdown, non-header buttons. 
   *
   * @param bool $small Default=true
   * @return $this
   *
   */
  public function setSmall($small = true) {
    $this->set('small', $small ? true : false);
    return $this;
  }

  /**
   * Render ready
   * 
   * @param Inputfield|InputfieldWrapper|null The parent InputfieldWrapper that is rendering it, or null if no parent.
   * @param bool $renderValueMode Specify true only if this is for `Inputfield::renderValue()` rather than `Inputfield::render()`.
   * @return bool True if assets were just added, false if already added.
   *
   */
  public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
    $class = $this->attr('class');  
    if(strpos($class, 'head_button_clone') !== false) {
      // if legacy class name used, convert to updated pw- class name to accomodate 3rd party usages
      $class = str_replace('head_button_clone', 'pw-head-button', $class);
      $this->attr('class', $class);
    }
    if($this->getSetting('small')) {
      $this->addClass('InputfieldSubmitSmall', 'wrapClass');
    }
    return parent::renderReady($parent, $renderValueMode);
  }

  /**
   * Render the button
   * 
   * @return string
   * 
   */
  public function ___render() {
    $attrs  = $this->getAttributesString();
    $icon = $this->icon ? $this->wire('sanitizer')->name($this->icon) : '';
    $icon = $icon ? wireIconMarkup($icon) . ' ' : '';
    $buttonText = $this->getSetting('html'); // option for non-encoded button text
    if(empty($buttonText)) {
      $buttonText = $this->getSetting('text');
      if(empty($buttonText)) $buttonText = $this->attr('value');
      $buttonText = $this->entityEncode($buttonText);
    }
    $buttonText = $icon . $buttonText;
    $textClass = $this->wire('sanitizer')->entities($this->getSetting('textClass'));
    if(!empty($textClass)) $buttonText = "<span class='$textClass'>$buttonText</span>";
    $out = "<button $attrs>$buttonText</button>";
    if($this->getSetting('small')) $out = "<small>$out</small>";
    if(count($this->dropdownItems)) $out .= $this->renderDropdown();
    return $out; 
  }

  /**
   * Render the dropdown to accompany the button
   * 
   * @return string
   * 
   */
  protected function renderDropdown() {

    if($this->wire('input')->get('modal')) return '';
    
    $config = $this->wire('config');
    $file = $config->debug ? 'dropdown.js' : 'dropdown.min.js';
    $config->scripts->add($config->urls->InputfieldSubmit . $file);
    $numValues = 0;
    $dropdownID = $this->attr('id') . '_dropdown';
    $out  = "<ul id='$dropdownID' class='pw-button-dropdown' data-my='left top' data-at='left bottom+1'>";

    foreach($this->dropdownItems as $item) {
      
      // entity encode all the labels in the dropdown
      foreach($item as $k => $v) {
        if($k == 'type') continue;
        $item[$k] = htmlentities($v, ENT_QUOTES, "UTF-8");
      }
      
      if($item['type'] == 'link') {
        // direct link
        $out .= "<li><a href='$item[value]'>";
      } else {
        // populate hidden input with value before submit
        $out .= "<li><a data-pw-dropdown-value='$item[value]' href='#'>";
        $numValues++;
      }
      // icon to accompany label
      if($item['icon']) $out .= "<i class='fa fa-fw fa-$item[icon]'></i>";
          
      // label and finish item
      $out .= "$item[label]</a></li>";
    }
    
    $out .= "</ul>";
    
    if($numValues) {
      // there are values that can be populated to a hidden input
      $inputID = $dropdownID . '_value';
      $attr = "type='hidden' name='{$this->dropdownInputName}' id='$inputID' value='' ";
      // copy the submitted dropdown value to the submit button value?
      if($this->dropdownSubmit) $attr .= "data-pw-dropdown-submit='1' ";
      // render the output
      $out = "<input $attr />" . str_replace("<ul ", "<ul data-pw-dropdown-input='#$inputID' ", $out);
    }
  
    // script to initialize this dropdown immediately
    $out .= "<script" . ">InputfieldSubmitDropdown.init('#{$this->id}')</script>";
    
    return $out; 
  }

  /**
   * Process input
   * 
   * @param WireInputData $input
   * @return $this
   * 
   */
  public function ___processInput(WireInputData $input) {
    
    $this->submitValue = '';
    $name = $this->attr('name');
    $value = $input->$name;
    
    if($value === $this->attr('value')) {
      $this->submitValue = $value;
      return $this;
    } 
    
    if(!count($this->dropdownItems)) return $this;
    
    if(!$this->dropdownSubmit) {
      $name = $this->dropdownInputName;
      $value = $input->$name;
    }
    
    if($value === null) return $this;
    
    foreach($this->dropdownItems as $item) {
      if($value !== $item['value']) continue;
      $this->submitValue = $item['value'];
      break;
    }
    
    return $this; 
  }

  /**
   * Add a dropdown item to this button
   * 
   * @param string $type Either 'link' or 'value'
   * @param string|int $value
   * @param string $label
   * @param string $icon
   * 
   */
  protected function addActionItem($type, $value, $label, $icon) {
    if(!$icon) $icon = 'angle-double-right';
    $this->dropdownItems[] = array(
      'type' => $type,
      'value' => $value,
      'label' => $label,
      'icon' => $icon,
    );
  }

  /**
   * Add a dropdown action item that populates a new 'value' for the submit button
   * 
   * This also populates the value to $_POST['_action_value']
   * 
   * @param string $value Value to populate to hidden input when dropdown item is selected/clicked.
   * @param string $label Text label to accompany the item
   * @param string $icon Icon name (optional)
   * 
   */
  public function addActionValue($value, $label, $icon = '') {
    $this->addActionItem('value', $value, $label, $icon);
  }

  /**
   * Add a dropdown action item that links to a URL
   *
   * @param string $url URL to link to
   * @param string $label Text label to accompany the item
   * @param string $icon Icon name (optional)
   *
   */
  public function addActionLink($url, $label, $icon = '') {
    $this->addActionItem('link', $url, $label, $icon);
  }
  
  /**
   * #pw-internal
   * 
   * @return array()
   * 
   */
  static public function getSubmitNames() {
    return self::$submitNames;
  }
  
}