Planning and coding of the clear button

To develop this plugin we will first create the clear button that will be rendered over the text component, and will apply the CSS styles according to the configuration options. After that, we need to add several event handlers for the clear button, such as click, mouseover, mouseout, mouseup, and mousedown, and also, several event handlers for the text component, such as destroy, resize, change, mouseover, and mouseout. Let us now start coding:

Ext.define('Examples.plugin.ClearButton', {
  alias : 'plugin.clearbutton',

  hideClearButtonWhenEmpty : true,
  hideClearButtonWhenMouseOut : true,
  animateClearButton : true,
  clearOnEscape : true,
  clearButtonCls : 'ext-ux-clearbutton',
  textField : null,
  animateWithCss3 : false,

  constructor : function(cfg) {
    Ext.apply(this, cfg);

    this.callParent(arguments);
  },

  init : function(textField) {
    this.textField = textField;
    if (!textField.rendered) {
      textField.on('afterrender', this.handleAfterRender, this);
    }
    else {
      this.handleAfterRender();
    }
  }

In the preceding code snippet, you can see that we have defined several configuration options, and the required init function.

Now let us define the handleAfterRender function:

handleAfterRender : function(textField) {
  this.isTextArea = (this.textField.inputEl.dom.type.toLowerCase() == 'textarea'),
  this.createClearButtonEl();
  this.addListeners();
  this.repositionClearButton();
  this.updateClearButtonVisibility();
  this.addEscListener();
}

Within this handleAfterRender function, at first, we are checking whether the textfield is a textarea or not, as we need to handle textarea with custom functionality since this field may have a scrollbar. And then we call the createClearButtonEl function to create the element and the DOM for the clear button.

Now let us define the createClearButtonEl function:

createClearButtonEl : function() {
  var animateWithClass = this.animateClearButton &&this.animateWithCss3;
  this.clearButtonEl = this.textField.bodyEl.createChild({
    tag : 'div',
    cls : this.clearButtonCls
  });
  if (this.animateClearButton) {
    this.animateWithCss3 = this.supportsCssTransition(this.clearButtonEl);
  }
  if (this.animateWithCss3) {
    this.clearButtonEl.addCls(this.clearButtonCls + '-off'),
  }
  else {
    this.clearButtonEl.setStyle('visibility', 'hidden'),
  }
}

In the preceding function the clear button has been created and assigned an animation, based on the configuration options. In this function we also checked whether the browser supports CSS3 transitions or not, by calling the supportsCssTransition function.

Now, let us define the supportsCssTransition function:

supportsCssTransition: function(el) {
  var styles = ['transitionProperty', 'WebkitTransitionProperty','MozTransitionProperty', 'OTransitionProperty','msTransitionProperty', 'KhtmlTransitionProperty'];

  var style = el.dom.style;
  for(var i = 0, length = styles.length; i < length; ++i) {
    if(style[styles[i]] !== 'undefined') { 
      return true;
    }
  }
  return false;
}

The next function we are calling within the handleAfterRender function is the addListeners function to add listeners to the field, its input element, and the clear button to handle resizing events such as mouseover, mouseout, and click.

Now, let us define the addListeners function:

addListeners: function() { 
  var textField = this.textField;
  var bodyEl = textField.bodyEl;
  bodyEl.on('mouseover', this.handleMouseOverInputField, this);
  bodyEl.on('mouseout', this.handleMouseOutOfInputField, this);

  textField.on('destroy', this.handleDestroy, this);
  textField.on('resize', this.repositionClearButton, this);
  textField.on('change', function() {
    this.repositionClearButton();
    this.updateClearButtonVisibility();
  }, this);

  var clearButtonEl = this.clearButtonEl;
  clearButtonEl.on('mouseover', this.handleMouseOverClearButton,this);
  clearButtonEl.on('mouseout', this.handleMouseOutOfClearButton,this);
  clearButtonEl.on('mousedown', this.handleMouseDownOnClearButton,this);
    clearButtonEl.on('mouseup', this.handleMouseUpOnClearButton,this);
  clearButtonEl.on('click', this.handleMouseClickOnClearButton,this);
}

Next we define the mouseover event handler – handleMouseOverInputField, and the mouseout event handler – handleMouseOutOfInputField, for bodyEl of textField:

handleMouseOverInputField: function(event, htmlElement, object) {
  this.clearButtonEl.addCls(this.clearButtonCls +'-mouse-over-input'),
  if (event.getRelatedTarget() == this.clearButtonEl.dom) {
    this.clearButtonEl.removeCls(this.clearButtonCls +'-mouse-over-button'),
    this.clearButtonEl.removeCls(this.clearButtonCls +'-mouse-down'),
  }
  this.updateClearButtonVisibility();
},
handleMouseOutOfInputField: function(event, htmlElement, object) {
  this.clearButtonEl.removeCls(this.clearButtonCls +'-mouse-over-input'),
  if (event.getRelatedTarget() == this.clearButtonEl.dom) { 
    this.clearButtonEl.addCls(this.clearButtonCls +'-mouse-over-button'),
  }
  this.updateClearButtonVisibility();
}

Now let us define the "destroy" event handler of textField since when the field is destroyed, we also need to destroy the clear button element to prevent memory leaks:

handleDestroy: function() {
  this.clearButtonEl.destroy();
}

Now let us start defining the handlers for the clear button's mouseover, mouseout, mousedown, mouseup, and click events:

handleMouseOverClearButton: function(event, htmlElement, object) {
  event.stopEvent();
  if (this.textField.bodyEl.contains(event.getRelatedTarget())) {
    return;
  }
  this.clearButtonEl.addCls(this.clearButtonCls +'-mouse-over-button'),
  this.updateClearButtonVisibility();
},

handleMouseOutOfClearButton: function(event, htmlElement, object){
  event.stopEvent();
  if (this.textField.bodyEl.contains(event.getRelatedTarget())) { 
    return;
  }
  this.clearButtonEl.removeCls(this.clearButtonCls +'-mouse-over-button'),
    this.clearButtonEl.removeCls(this.clearButtonCls +'-mouse-down'),
  this.updateClearButtonVisibility();
},

handleMouseDownOnClearButton: function(event, htmlElement,object){
  if (!this.isLeftButton(event)) {
    return;
  }
  this.clearButtonEl.addCls(this.clearButtonCls +'-mouse-down'),
},

handleMouseUpOnClearButton: function(event, htmlElement, object) {
  if (!this.isLeftButton(event)) {
    return;
  }
  this.clearButtonEl.removeCls(this.clearButtonCls +'-mouse-down'),
},

handleMouseClickOnClearButton: function(event, htmlElement, object) {
  if (!this.isLeftButton(event)) {
    return;
  }
  this.textField.setValue(''),
  this.textField.focus();
}

The next function we will call within the handleAfterRender function is the repositionClearButton function, to reposition the clear button element based on the inputEl element of textField. Now, let us define this function:

repositionClearButton: function() {
  var clearButtonEl = this.clearButtonEl;
  if (!clearButtonEl) {
    return;
  }
  var clearButtonPosition = this.calculateClearButtonPosition(this.textField);
  clearButtonEl.dom.style.right = clearButtonPosition.right +'px';
  clearButtonEl.dom.style.top = clearButtonPosition.top + 'px';
}

You can see that we get the clear button's position value by calling the calculateClearButtonPosition function. This function calculates the position of the clear button, based on the inputEl element of textField. Now, let us define this function:

calculateClearButtonPosition: function(textField) {
  var positions = textField.inputEl.getBox(true, true);
  var top = positions.y;
  var right = positions.x;
  if (this.fieldHasScrollBar()) {
    right += Ext.getScrollBarWidth();
  }
  if (this.textField.triggerWrap) {
    right += this.textField.getTriggerWidth();
  }
  return {
    right: right,
    top: top
  };
}

You can see that we checked whether the field has a scrollbar or not, and if the field has a scrollbar, we add the value of the Ext.getScrollBarWidth function to the right position. Now, let us define the fieldHasScrollBar function:

fieldHasScrollBar: function() {
  if (!this.isTextArea) {
    return false;
  }

  var inputEl = this.textField.inputEl;
  var overflowY = inputEl.getStyle('overflow-y'),
  if (overflowY == 'hidden' || overflowY == 'visible') {
    return false;
  }
  if (overflowY == 'scroll') {
    return true;
  }
  if (inputEl.dom.scrollHeight <= inputEl.dom.clientHeight) {
    return false;
  }
  return true;
}

And then we called the updateClearButtonVisibility function within the handleAfterRender function for fixing the clear button's visibility:

updateClearButtonVisibility: function() {
  var oldVisible = this.isButtonCurrentlyVisible();
  var newVisible = this.shouldButtonBeVisible();

  var clearButtonEl = this.clearButtonEl;
  if (oldVisible != newVisible) {
    if(this.animateClearButton && this.animateWithCss3) {
      this.clearButtonEl.removeCls(this.clearButtonCls +(oldVisible ? '-on' : '-off'));
      clearButtonEl.addCls(this.clearButtonCls + (newVisible ? '-on' : '-off'));
    }
    else {
      clearButtonEl.stopAnimation();
      clearButtonEl.setVisible(newVisible,this.animateClearButton);
    }

    clearButtonEl.setStyle('background-color',this.textField.inputEl.getStyle('background-color'));

    if (!(this.isTextArea && Ext.isGecko) && !Ext.isIE) {
      var deltaPaddingRight = clearButtonEl.getWidth() - this.clearButtonEl.getMargin('l'),
      var currentPaddingRight = this.textField.inputEl.getPadding('r'),
      var factor = (newVisible ? +1 : -1);
      this.textField.inputEl.dom.style.paddingRight = (currentPaddingRight + factor * deltaPaddingRight) + 'px';
    }
  }
}

You can see that we took the value of the current visible state, and what will be the new visible state, by calling the isButtonCurrentlyVisible and shouldButtonBeVisible functions. The isButtonCurrentlyVisible function is a wrapper around clearButtonEl.isVisible() to handle the setVisible animation that may still be in progress, and the shouldButtonBeVisible function checks the configuration options and the current mouse status to determine whether the clear button should be visible or not. Now, let us define these functions:

isButtonCurrentlyVisible: function() {
  if (this.animateClearButton && this.animateWithCss3) {
    return this.clearButtonEl.hasCls(this.clearButtonCls + '-on'),
  } 
  var cachedVisible = Ext.core.Element.data(this.clearButtonEl.dom, 'isVisible'),
  if (typeof(cachedVisible) == 'boolean') {
    return cachedVisible;
  }
  return this.clearButtonEl.isVisible();
},

shouldButtonBeVisible: function() {
  if (this.hideClearButtonWhenEmpty && Ext.isEmpty(this.textField.getValue())) {

    return false;
  }

  var clearButtonEl = this.clearButtonEl;

  if (this.hideClearButtonWhenMouseOut && !clearButtonEl.hasCls(this.clearButtonCls + '-mouse-over-button') && !clearButtonEl.hasCls(this.clearButtonCls + '-mouse-over-input')) {

    return false;
  }

  return true;
}

And the last function that we called within the handleAfterRender function is the addEscListener function. What we need to do is, if the configuration option clearOnEscape is set to true, add a key listener that will clear this field. Now, let us define this function:

addEscListener: function() {
  if (!this.clearOnEscape) {
    return;
  }

  this.textField.inputEl.on('keydown', function(e) {
    if (e.getKey() == Ext.EventObject.ESC) {
      if (this.textField.isExpanded) {
        return;
      }
      Ext.Function.defer(this.textField.setValue, 1,this.textField, ['']);
      e.stopEvent();
    }
  },
  this);
}

The following screenshot is the output where we've used this plugin for textfield, textareafield, combobox, and the date field:

Planning and coding of the clear button

You can see that on hovering over the textfield component, the clear button is visible and clicking on this button will clear the respective field.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset