Skip to content

Instantly share code, notes, and snippets.

@aghuddleston
Last active August 9, 2022 16:45
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save aghuddleston/5513157 to your computer and use it in GitHub Desktop.
Ext.ux.form.field.ClearButton - my version
.ext-ux-clearbutton {
width: 12px;
height: 12px;
background-image: url(./images/clear-text-icon.gif);
background-position: 0 0;
background-repeat: no-repeat;
-moz-user-focus: ignore; /* https://developer.mozilla.org/en/CSS/-moz-user-focus */
cursor: pointer;
position: absolute;
overflow: hidden; /* IE 6 :-( */
margin-top: 4px;
background-color: white;
}
.ext-ux-clearbutton-mouse-over-input {
background-position: 0 -12px;
}
.ext-ux-clearbutton-mouse-over-button {
background-position: 0 -24px;
}
.ext-ux-clearbutton-mouse-down {
background-position: 0 -36px;
}
.ext-ux-clearbutton-on {
opacity: 1;
visibility: visible;
transition: opacity .35s linear;
-webkit-transition: opacity .35s linear;
-moz-transition: opacity .35s linear;
-o-transition: opacity .35s linear;
-ms-transition: opacity .35s linear;
-khtml-transition: opacity .35s linear;
}
.ext-ux-clearbutton-off {
opacity: 0;
visibility: hidden;
transition: opacity .35s linear, visibility .0s linear .35s;
-webkit-transition: opacity .35s linear, visibility .0s linear .35s;
-moz-transition: opacity .35s linear, visibility .0s linear .35s;
-o-transition: opacity .35s linear, visibility .0s linear .35s;
-ms-transition: opacity .35s linear, visibility .0s linear .35s;
-khtml-transition: opacity .35s linear, visibility .0s linear .35s;
}
(function() {
/**
* @class Ext.ux.form.field.ClearButton
*
* Plugin for text components that shows a "clear" button over the text field.
* When the button is clicked the text field is set empty.
* Icon image and positioning can be controlled using CSS.
* Works with Ext.form.field.Text, Ext.form.field.TextArea, Ext.form.field.ComboBox and Ext.form.field.Date.
*
* Plugin alias is 'clearbutton' (use "plugins: 'clearbutton'" in GridPanel config).
*
* @author <a href="mailto:stephen.friedrich@fortis-it.de">Stephen Friedrich</a>
* @author <a href="mailto:fabian.urban@fortis-it.de">Fabian Urban</a>
*
* @copyright (c) 2011 Fortis IT Services GmbH
* @license Ext.ux.form.field.ClearButton is released under the
* <a target="_blank" href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.
*
*/
Ext.define('Ext.ux.form.field.ClearButton', {
alias: 'plugin.clearbutton',
/**
* @cfg {Boolean} Hide the clear button when the field is empty (default: true).
*/
hideClearButtonWhenEmpty: true,
/**
* @cfg {Boolean} Hide the clear button until the mouse is over the field (default: true).
*/
hideClearButtonWhenMouseOut: true,
/**
* @cfg {Boolean} When the clear buttons is hidden/shown, this will animate the button to its new state (using opacity) (default: true).
*/
animateClearButton: true,
/**
* @cfg {Boolean} Empty the text field when ESC is pressed while the text field is focused.
*/
clearOnEscape: true,
/**
* @cfg {String} CSS class used for the button div.
* Also used as a prefix for other classes (suffixes: '-mouse-over-input', '-mouse-over-button', '-mouse-down', '-on', '-off')
*/
clearButtonCls: 'ext-ux-clearbutton',
/**
* The text field (or text area, combo box, date field) that we are attached to
*/
textField: null,
/**
* Will be set to true if animateClearButton is true and the browser supports CSS 3 transitions
* @private
*/
animateWithCss3: false,
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Set up and tear down
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
constructor: function(cfg) {
Ext.apply(this, cfg);
this.callParent(arguments);
},
/**
* Called by plug-in system to initialize the plugin for a specific text field (or text area, combo box, date field).
* Most all the setup is delayed until the component is rendered.
*/
init: function(textField) {
this.textField = textField;
this.textField.addEvents('clear');
if (!textField.rendered) {
textField.on('afterrender', this.handleAfterRender, this);
}
else {
// probably an existing input element transformed to extjs field
this.handleAfterRender();
}
},
/**
* After the field has been rendered sets up the plugin (create the Element for the clear button, attach listeners).
* @private
*/
handleAfterRender: function(textField) {
this.isTextArea = (this.textField.inputEl.dom.type.toLowerCase() == 'textarea');
this.createClearButtonEl();
this.addListeners();
this.repositionClearButton();
this.updateClearButtonVisibility();
this.addEscListener();
},
/**
* Creates the Element and DOM for the clear button
*/
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');
}
},
/**
* Returns true iff the browser supports CSS 3 transitions
* @param el an element that is checked for support of the "transition" CSS property (considering any
* vendor prefixes)
*/
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') {
// Supported property will result in empty string
return true;
}
}
return false;
},
/**
* If config option "clearOnEscape" is true, then add a key listener that will clear this field
*/
addEscListener: function() {
if (!this.clearOnEscape) {
return;
}
// Using a KeyMap did not work: ESC is swallowed by combo box and date field before it reaches our own KeyMap
this.textField.inputEl.on('keydown',
function(e) {
if (e.getKey() == Ext.EventObject.ESC) {
if (this.textField.isExpanded) {
// Let combo box or date field first remove the popup
return;
}
// No idea why the defer is necessary, but otherwise the call to setValue('') is ignored
Ext.Function.defer(this.clearValue, 1, this);
e.stopEvent();
}
},
this);
},
/**
* Adds listeners to the field, its input element and the clear button to handle resizing, mouse over/out events, click events etc.
*/
addListeners: function() {
// listeners on input element (DOM/El level)
var textField = this.textField;
var bodyEl = textField.bodyEl;
bodyEl.on('mouseover', this.handleMouseOverInputField, this);
bodyEl.on('mouseout', this.handleMouseOutOfInputField, this);
// listeners on text field (component level)
textField.on('destroy', this.handleDestroy, this);
textField.on('resize', this.repositionClearButton, this);
textField.on('change', function() {
this.repositionClearButton();
this.updateClearButtonVisibility();
}, this);
// listeners on clear button (DOM/El level)
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);
},
/**
* When the field is destroyed, we also need to destroy the clear button Element to prevent memory leaks.
*/
handleDestroy: function() {
this.clearButtonEl.destroy();
},
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Mouse event handlers
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Tada - the real action: If user left clicked on the clear button, then empty the field
*/
handleMouseClickOnClearButton: function(event, htmlElement, object) {
if (!this.isLeftButton(event)) {
return;
}
this.clearValue();
this.textField.focus();
},
handleMouseOverInputField: function(event, htmlElement, object) {
this.clearButtonEl.addCls(this.clearButtonCls + '-mouse-over-input');
if (event.getRelatedTarget() == this.clearButtonEl.dom) {
// Moused moved to clear button and will generate another mouse event there.
// Handle it here to avoid duplicate updates (else animation will break)
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) {
// Moused moved from clear button and will generate another mouse event there.
// Handle it here to avoid duplicate updates (else animation will break)
this.clearButtonEl.addCls(this.clearButtonCls + '-mouse-over-button');
}
this.updateClearButtonVisibility();
},
handleMouseOverClearButton: function(event, htmlElement, object) {
event.stopEvent();
if (this.textField.bodyEl.contains(event.getRelatedTarget())) {
// has been handled in handleMouseOutOfInputField() to prevent double update
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())) {
// will be handled in handleMouseOverInputField() to prevent double update
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');
},
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Utility methods
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Repositions the clear button element based on the textfield.inputEl element
* @private
*/
repositionClearButton: function() {
var clearButtonEl = this.clearButtonEl;
if (!clearButtonEl) {
return;
}
var right = 0;
if (this.fieldHasScrollBar()) {
right += Ext.getScrollBarWidth();
}
// See http://www.sencha.com/forum/showthread.php?132775-Ext.ux.form.field.ClearButton-Small-clear-button-icon-over-field&p=961461&viewfull=1#post961461
//if (this.textField.triggerWrap) {
// right += this.textField.getTriggerWidth();
//}
//clearButtonEl.alignTo(this.textField.bodyEl, 'tr-tr', [-1 * (right + 3), 5]);
clearButtonEl.alignTo(this.textField.inputEl, 'tr-tr', [-1 * (right + 3), 5]);
},
// /**
// * Calculates the position of the clear button based on the textfield.inputEl element
// * @private
// */
// 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
// };
// },
/**
* Checks if the field we are attached to currently has a scrollbar
*/
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;
}
//noinspection RedundantIfStatementJS
if (inputEl.dom.scrollHeight <= inputEl.dom.clientHeight) {
return false;
}
return true;
},
/**
* Small wrapper around clearButtonEl.isVisible() to handle setVisible animation that may still be in progress.
*/
isButtonCurrentlyVisible: function() {
if (this.animateClearButton && this.animateWithCss3) {
return this.clearButtonEl.hasCls(this.clearButtonCls + '-on');
}
// This should not be necessary (see Element.setVisible/isVisible), but else there is confusion about visibility
// when moving the mouse out and _quickly_ over then input again.
var cachedVisible = Ext.core.Element.data(this.clearButtonEl.dom, 'isVisible');
if (typeof(cachedVisible) == 'boolean') {
return cachedVisible;
}
return this.clearButtonEl.isVisible();
},
/**
* Checks config options and current mouse status to determine if the clear button should be visible.
*/
shouldButtonBeVisible: function() {
if (this.hideClearButtonWhenEmpty && Ext.isEmpty(this.textField.getValue())) {
return false;
}
var clearButtonEl = this.clearButtonEl;
//noinspection RedundantIfStatementJS
if (this.hideClearButtonWhenMouseOut
&& !clearButtonEl.hasCls(this.clearButtonCls + '-mouse-over-button')
&& !clearButtonEl.hasCls(this.clearButtonCls + '-mouse-over-input')) {
return false;
}
return true;
},
/**
* Called after any event that may influence the clear button 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);
}
// Set background-color of clearButton to same as field's background-color (for those browsers/cases
// where the padding-right (see below) does not work)
clearButtonEl.setStyle('background-color', this.textField.inputEl.getStyle('background-color'));
// Adjust padding-right of the input tag to make room for the button
// IE (up to v9) just ignores this and Gecko handles padding incorrectly with textarea scrollbars
// if (!(this.isTextArea && Ext.isGecko) && !Ext.isIE) {
// // See https://bugzilla.mozilla.org/show_bug.cgi?id=157846
// 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';
// }
}
},
isLeftButton: function(event) {
return event.button === 0;
},
clearValue : function() {
if (Ext.isFunction(this.textField.clearValue)) {
this.textField.clearValue();
} else {
this.textField.setValue('');
}
this.textField.fireEvent('clear', this.textField);
}
});
})();
@valioDOTch
Copy link

Why are lines 405-411 commented out?

@valioDOTch
Copy link

BTW: deprecated stuff

  • replace Ext.getScrollbarWidth() with Ext.getScrollbarSize().width()
  • remove this.textField.addEvents('clear'); [not necessary anymore]

Probably we should consider putting up a separate Github repo. It appears as if the original devs are not active anymore.

@aghuddleston
Copy link
Author

Originally commented out for this reason:

Otherwise, when in Chrome in 18 or Firefox 11, if I moused over a field with no width set and managed by a hbox layout, the field would add extra padding to the right. For a combobox, it would look combo box, clearbutton, whitespace, triggerfield. But, removing above worked for me. I looked at in IE 8, Chrome 18 and Firefox 11. One disclaimer, I only have single line text fields, combo boxes or date fields using the clearbutton.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment