aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorAlexander Schmitz <arschmitz@gmail.com>2014-01-22 12:02:32 -0500
committerAlexander Schmitz <arschmitz@gmail.com>2015-10-07 10:57:59 -0400
commit02033262ee0fb1d9f33c361b3c2ddfa168604854 (patch)
tree645ed2427d14a598d02754b79e9eb8b889baf846 /ui
parent79d312f3f5cb5629d1bd7f9f899b41f304c388ef (diff)
downloadjquery-ui-02033262ee0fb1d9f33c361b3c2ddfa168604854.tar.gz
jquery-ui-02033262ee0fb1d9f33c361b3c2ddfa168604854.zip
Button: Initial commit of button re-factor
Move to using element stats rather then js class states remove ui-button-text spans. Removed button set
Diffstat (limited to 'ui')
-rw-r--r--ui/widgets/button.js597
1 files changed, 278 insertions, 319 deletions
diff --git a/ui/widgets/button.js b/ui/widgets/button.js
index 94fc75331..34a2afa40 100644
--- a/ui/widgets/button.js
+++ b/ui/widgets/button.js
@@ -22,10 +22,13 @@
// AMD. Register as an anonymous module.
define( [
"jquery",
- "../data",
+
+ // These are only for backcompat
+ // TODO: Remove after 1.12
+ "./controlgroup",
+ "./checkboxradio",
+
"../keycode",
- "../labels",
- "../version",
"../widget"
], factory );
} else {
@@ -35,391 +38,347 @@
}
}( function( $ ) {
-var lastActive,
- baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
- typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
- formResetHandler = function() {
- var form = $( this );
- setTimeout( function() {
- form.find( ":ui-button" ).button( "refresh" );
- }, 1 );
- },
- radioGroup = function( radio ) {
- var name = radio.name,
- form = radio.form,
- radios = $( [] );
- if ( name ) {
- name = name.replace( /'/g, "\\'" );
- if ( form ) {
- radios = $( form ).find( "[name='" + name + "'][type=radio]" );
- } else {
- radios = $( "[name='" + name + "'][type=radio]", radio.ownerDocument )
- .filter( function() {
- return !this.form;
- } );
- }
- }
- return radios;
- };
-
$.widget( "ui.button", {
version: "@VERSION",
defaultElement: "<button>",
options: {
+ classes: {
+ "ui-button": "ui-corner-all"
+ },
disabled: null,
- text: true,
+ icon: null,
+ iconPosition: "beginning",
label: null,
- icons: {
- primary: null,
- secondary: null
- }
+ showLabel: true
},
- _create: function() {
- this.element.closest( "form" )
- .off( "reset" + this.eventNamespace )
- .on( "reset" + this.eventNamespace, formResetHandler );
- if ( typeof this.options.disabled !== "boolean" ) {
- this.options.disabled = !!this.element.prop( "disabled" );
- } else {
- this.element.prop( "disabled", this.options.disabled );
- }
+ _getCreateOptions: function() {
+ var disabled,
- this._determineButtonType();
- this.hasTitle = !!this.buttonElement.attr( "title" );
+ // This is to support cases like in jQuery Mobile where the base widget does have
+ // an implementation of _getCreateOptions
+ options = this._super() || {};
- var that = this,
- options = this.options,
- toggleButton = this.type === "checkbox" || this.type === "radio",
- activeClass = !toggleButton ? "ui-state-active" : "";
+ this.isInput = this.element.is( "input" );
- if ( options.label === null ) {
- options.label = ( this.type === "input" ? this.buttonElement.val() : this.buttonElement.html() );
+ disabled = this.element[ 0 ].disabled;
+ if ( disabled != null ) {
+ options.disabled = disabled;
}
- this._hoverable( this.buttonElement );
+ this.originalLabel = this.isInput ? this.element.val() : this.element.html();
+ if ( this.originalLabel ) {
+ options.label = this.originalLabel;
+ }
- this.buttonElement
- .addClass( baseClasses )
- .attr( "role", "button" )
- .on( "mouseenter" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return;
- }
- if ( this === lastActive ) {
- $( this ).addClass( "ui-state-active" );
- }
- } )
- .on( "mouseleave" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return;
- }
- $( this ).removeClass( activeClass );
- } )
- .on( "click" + this.eventNamespace, function( event ) {
- if ( options.disabled ) {
- event.preventDefault();
- event.stopImmediatePropagation();
- }
- } );
+ return options;
+ },
- // Can't use _focusable() because the element that receives focus
- // and the element that gets the ui-state-focus class are different
- this._on( {
- focus: function() {
- this.buttonElement.addClass( "ui-state-focus" );
- },
- blur: function() {
- this.buttonElement.removeClass( "ui-state-focus" );
- }
- } );
+ _create: function() {
+ if ( !this.option.showLabel & !this.options.icon ) {
+ this.options.showLabel = true;
+ }
- if ( toggleButton ) {
- this.element.on( "change" + this.eventNamespace, function() {
- that.refresh();
- } );
+ // We have to check the option again here even though we did in _getCreateOptions,
+ // because null may have been passed on init which would override what was set in
+ // _getCreateOptions
+ if ( this.options.disabled == null ) {
+ this.options.disabled = this.element[ 0 ].disabled || false;
}
- if ( this.type === "checkbox" ) {
- this.buttonElement.on( "click" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return false;
- }
- } );
- } else if ( this.type === "radio" ) {
- this.buttonElement.on( "click" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return false;
- }
- $( this ).addClass( "ui-state-active" );
- that.buttonElement.attr( "aria-pressed", "true" );
-
- var radio = that.element[ 0 ];
- radioGroup( radio )
- .not( radio )
- .map( function() {
- return $( this ).button( "widget" )[ 0 ];
- } )
- .removeClass( "ui-state-active" )
- .attr( "aria-pressed", "false" );
- } );
- } else {
- this.buttonElement
- .on( "mousedown" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return false;
- }
- $( this ).addClass( "ui-state-active" );
- lastActive = this;
- that.document.one( "mouseup", function() {
- lastActive = null;
- } );
- } )
- .on( "mouseup" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return false;
- }
- $( this ).removeClass( "ui-state-active" );
- } )
- .on( "keydown" + this.eventNamespace, function( event ) {
- if ( options.disabled ) {
- return false;
- }
- if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
- $( this ).addClass( "ui-state-active" );
- }
- } )
+ this.hasTitle = !!this.element.attr( "title" );
- // see #8559, we bind to blur here in case the button element loses
- // focus between keydown and keyup, it would be left in an "active" state
- .on( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
- $( this ).removeClass( "ui-state-active" );
- } );
+ // Check to see if the label needs to be set or if its already correct
+ if ( this.options.label && this.options.label !== this.originalLabel ) {
+ if ( this.isInput ) {
+ this.element.val( this.options.label );
+ } else {
+ this.element.html( this.options.label );
+ }
+ }
+ this._addClass( "ui-button", "ui-widget" );
+ this._setOption( "disabled", this.options.disabled );
+ this._enhance();
- if ( this.buttonElement.is( "a" ) ) {
- this.buttonElement.on( "keyup", function( event ) {
+ if ( this.element.is( "a" ) ) {
+ this._on( {
+ "keyup": function( event ) {
if ( event.keyCode === $.ui.keyCode.SPACE ) {
-
- // TODO pass through original event correctly (just as 2nd argument doesn't work)
- $( this ).trigger( "click" );
+ event.preventDefault();
+
+ // Support: PhantomJS <= 1.9, IE 8 Only
+ // If a native click is available use it so we actually cause navigation
+ // otherwise just trigger a click event
+ if ( this.element[ 0 ].click ) {
+ this.element[ 0 ].click();
+ } else {
+ this.element.trigger( "click" );
+ }
}
- } );
- }
+ }
+ } );
}
+ },
+
+ _enhance: function() {
+ this.element.attr( "role", "button" );
- this._setOption( "disabled", options.disabled );
- this._resetButton();
+ if ( this.options.icon ) {
+ this._updateIcon( "icon", this.options.icon );
+ this._updateTooltip();
+ }
},
- _determineButtonType: function() {
- var ancestor, labelSelector, checked;
+ _updateTooltip: function() {
+ this.title = this.element.attr( "title" );
- if ( this.element.is( "[type=checkbox]" ) ) {
- this.type = "checkbox";
- } else if ( this.element.is( "[type=radio]" ) ) {
- this.type = "radio";
- } else if ( this.element.is( "input" ) ) {
- this.type = "input";
- } else {
- this.type = "button";
+ if ( !this.options.showLabel && !this.title ) {
+ this.element.attr( "title", this.options.label );
}
+ },
- if ( this.type === "checkbox" || this.type === "radio" ) {
-
- // we don't search against the document in case the element
- // is disconnected from the DOM
- ancestor = this.element.parents().last();
- labelSelector = "label[for='" + this.element.attr( "id" ) + "']";
- this.buttonElement = ancestor.find( labelSelector );
- if ( !this.buttonElement.length ) {
- ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
- this.buttonElement = ancestor.filter( labelSelector );
- if ( !this.buttonElement.length ) {
- this.buttonElement = ancestor.find( labelSelector );
- }
+ _updateIcon: function( option, value ) {
+ var icon = option !== "iconPosition",
+ position = icon ? this.options.iconPosition : value,
+ displayBlock = position === "top" || position === "bottom";
+
+ // Create icon
+ if ( !this.icon ) {
+ this.icon = $( "<span>" );
+
+ this._addClass( this.icon, "ui-button-icon", "ui-icon" );
+
+ if ( !this.options.showLabel ) {
+ this._addClass( "ui-button-icon-only" );
}
- this.element.addClass( "ui-helper-hidden-accessible" );
+ } else if ( icon ) {
+
+ // If we are updating the icon remove the old icon class
+ this._removeClass( this.icon, null, this.options.icon );
+ }
+
+ // If we are updating the icon add the new icon class
+ if ( icon ) {
+ this._addClass( this.icon, null, value );
+ }
- checked = this.element.is( ":checked" );
- if ( checked ) {
- this.buttonElement.addClass( "ui-state-active" );
+ this._attachIcon( position );
+
+ // If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
+ // the iconSpace if there is one.
+ if ( displayBlock ) {
+ this._addClass( this.icon, null, "ui-widget-icon-block" );
+ if ( this.iconSpace ) {
+ this.iconSpace.remove();
}
- this.buttonElement.prop( "aria-pressed", checked );
} else {
- this.buttonElement = this.element;
- }
- },
- widget: function() {
- return this.buttonElement;
+ // Position is beginning or end so remove the ui-widget-icon-block class and add the
+ // space if it does not exist
+ if ( !this.iconSpace ) {
+ this.iconSpace = $( "<span> </span>" );
+ this._addClass( this.iconSpace, "ui-button-icon-space" );
+ }
+ this._removeClass( this.icon, null, "ui-wiget-icon-block" );
+ this._attachIconSpace( position );
+ }
},
_destroy: function() {
- this.element
- .removeClass( "ui-helper-hidden-accessible" );
- this.buttonElement
- .removeClass( baseClasses + " ui-state-active " + typeClasses )
- .removeAttr( "role aria-pressed" )
- .html( this.buttonElement.find( ".ui-button-text" ).html() );
+ this.element.removeAttr( "role" );
+ if ( this.icon ) {
+ this.icon.remove();
+ }
+ if ( this.iconSpace ) {
+ this.iconSpace.remove();
+ }
if ( !this.hasTitle ) {
- this.buttonElement.removeAttr( "title" );
+ this.element.removeAttr( "title" );
}
},
- _setOption: function( key, value ) {
- this._super( key, value );
- if ( key === "disabled" ) {
- this.widget().toggleClass( "ui-state-disabled", !!value );
- this.element.prop( "disabled", !!value );
- if ( value ) {
- if ( this.type === "checkbox" || this.type === "radio" ) {
- this.buttonElement.removeClass( "ui-state-focus" );
- } else {
- this.buttonElement.removeClass( "ui-state-focus ui-state-active" );
- }
- }
- return;
- }
- this._resetButton();
+ _attachIconSpace: function( iconPosition ) {
+ this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
},
- refresh: function() {
+ _attachIcon: function( iconPosition ) {
+ this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
+ },
- //See #8237 & #8828
- var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
+ _setOptions: function( options ) {
+ var newShowLabel = options.showLabel === undefined ?
+ this.options.showLabel :
+ options.showLabel,
+ newIcon = options.icon === undefined ? this.options.icon : options.icon;
- if ( isDisabled !== this.options.disabled ) {
- this._setOption( "disabled", isDisabled );
- }
- if ( this.type === "radio" ) {
- radioGroup( this.element[ 0 ] ).each( function() {
- if ( $( this ).is( ":checked" ) ) {
- $( this ).button( "widget" )
- .addClass( "ui-state-active" )
- .attr( "aria-pressed", "true" );
- } else {
- $( this ).button( "widget" )
- .removeClass( "ui-state-active" )
- .attr( "aria-pressed", "false" );
- }
- } );
- } else if ( this.type === "checkbox" ) {
- if ( this.element.is( ":checked" ) ) {
- this.buttonElement
- .addClass( "ui-state-active" )
- .attr( "aria-pressed", "true" );
- } else {
- this.buttonElement
- .removeClass( "ui-state-active" )
- .attr( "aria-pressed", "false" );
- }
+ if ( !newShowLabel && !newIcon ) {
+ options.showLabel = true;
}
+ this._super( options );
},
- _resetButton: function() {
- if ( this.type === "input" ) {
- if ( this.options.label ) {
- this.element.val( this.options.label );
+ _setOption: function( key, value ) {
+ if ( key === "icon" ) {
+ if ( value ) {
+ this._updateIcon( key, value );
+ } else if ( this.icon ) {
+ this.icon.remove();
+ if ( this.iconSpace ) {
+ this.iconSpace.remove();
+ }
}
- return;
}
- var buttonElement = this.buttonElement.removeClass( typeClasses ),
- buttonText = $( "<span></span>", this.document[ 0 ] )
- .addClass( "ui-button-text" )
- .html( this.options.label )
- .appendTo( buttonElement.empty() )
- .text(),
- icons = this.options.icons,
- multipleIcons = icons.primary && icons.secondary,
- buttonClasses = [];
-
- if ( icons.primary || icons.secondary ) {
- if ( this.options.text ) {
- buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
- }
- if ( icons.primary ) {
- buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
- }
+ if ( key === "iconPosition" ) {
+ this._updateIcon( key, value );
+ }
- if ( icons.secondary ) {
- buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
- }
+ // Make sure we can't end up with a button that has neither text nor icon
+ if ( key === "showLabel" ) {
+ this._toggleClass( this._classes( "ui-button-icon-only" ), null, !value );
+ this._updateTooltip();
+ }
- if ( !this.options.text ) {
- buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
+ if ( key === "label" ) {
+ if ( this.isInput ) {
+ this.element.val( value );
+ } else {
- if ( !this.hasTitle ) {
- buttonElement.attr( "title", $.trim( buttonText ) );
+ // If there is an icon, append it, else nothing then append the value
+ // this avoids removal of the icon when setting label text
+ this.element.html( value );
+ if ( this.icon ) {
+ this._attachIcon( this.options.iconPosition );
+ this._attachIconSpace( this.options.iconPosition );
}
}
- } else {
- buttonClasses.push( "ui-button-text-only" );
}
- buttonElement.addClass( buttonClasses.join( " " ) );
- }
-} );
-
-$.widget( "ui.buttonset", {
- version: "@VERSION",
- options: {
- items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
- },
- _create: function() {
- this.element.addClass( "ui-buttonset" );
- },
-
- _init: function() {
- this.refresh();
- },
+ this._super( key, value );
- _setOption: function( key, value ) {
if ( key === "disabled" ) {
- this.buttons.button( "option", key, value );
+ this._toggleClass( null, "ui-state-disabled", value );
+ this.element[ 0 ].disabled = value;
+ if ( value ) {
+ this.element.blur();
+ }
}
-
- this._super( key, value );
},
refresh: function() {
- var rtl = this.element.css( "direction" ) === "rtl",
- allButtons = this.element.find( this.options.items ),
- existingButtons = allButtons.filter( ":ui-button" );
-
- // Initialize new buttons
- allButtons.not( ":ui-button" ).button();
-
- // Refresh existing buttons
- existingButtons.button( "refresh" );
-
- this.buttons = allButtons
- .map( function() {
- return $( this ).button( "widget" )[ 0 ];
- } )
- .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
- .filter( ":first" )
- .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
- .end()
- .filter( ":last" )
- .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
- .end()
- .end();
- },
- _destroy: function() {
- this.element.removeClass( "ui-buttonset" );
- this.buttons
- .map( function() {
- return $( this ).button( "widget" )[ 0 ];
- } )
- .removeClass( "ui-corner-left ui-corner-right" )
- .end()
- .button( "destroy" );
+ // Make sure to only check disabled if its an element that supports this otherwise
+ // check for the disabled class to determine state
+ var isDisabled = this.element.is( "input, button" ) ?
+ this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );
+
+ if ( isDisabled !== this.options.disabled ) {
+ this._setOptions( { disabled: isDisabled } );
+ }
+
+ this._updateTooltip();
}
} );
+// DEPRECATED
+if ( $.uiBackCompat !== false ) {
+
+ // Text and Icons options
+ $.widget( "ui.button", $.ui.button, {
+ options: {
+ text: true,
+ icons: {
+ primary: null,
+ secondary: null
+ }
+ },
+
+ _create: function() {
+ if ( this.options.showLabel && !this.options.text ) {
+ this.options.showLabel = this.options.text;
+ }
+ if ( !this.options.showLabel && this.options.text ) {
+ this.options.text = this.options.showLabel;
+ }
+ if ( !this.options.icon && ( this.options.icons.primary ||
+ this.options.icons.secondary ) ) {
+ if ( this.options.icons.primary ) {
+ this.options.icon = this.options.icons.primary;
+ } else {
+ this.options.icon = this.options.icons.secondary;
+ this.options.iconPosition = "end";
+ }
+ } else if ( this.options.icon ) {
+ this.options.icons.primary = this.options.icon;
+ }
+ this._super();
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "text" ) {
+ this._super( "showLabel", value );
+ return;
+ }
+ if ( key === "showLabel" ) {
+ this.options.text = value;
+ }
+ if ( key === "icon" ) {
+ this.options.icons.primary = value;
+ }
+ if ( key === "icons" ) {
+ if ( value.primary ) {
+ this._super( "icon", value.primary );
+ this._super( "iconPosition", "beginning" );
+ } else if ( value.secondary ) {
+ this._super( "icon", value.secondary );
+ this._super( "iconPosition", "end" );
+ }
+ }
+ this._superApply( arguments );
+ }
+ } );
+
+ $.fn.button = ( function( orig ) {
+ return function() {
+ if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) ||
+ ( this.length && this[ 0 ].tagName === "INPUT" && (
+ this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio"
+ ) ) ) {
+ return orig.apply( this, arguments );
+ }
+ if ( !$.ui.checkboxradio ) {
+ $.error( "Checkboxradio widget missing" );
+ }
+ if ( arguments.length === 0 ) {
+ return this.checkboxradio( {
+ "icon": false
+ } );
+ }
+ return this.checkboxradio.apply( this, arguments );
+ };
+ } )( $.fn.button );
+
+ $.fn.buttonset = function() {
+ if ( !$.ui.controlgroup ) {
+ $.error( "Controlgroup widget missing" );
+ }
+ if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
+ return this.controlgroup.apply( this,
+ [ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
+ }
+ if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
+ return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
+ }
+ if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
+ arguments[ 0 ].items = {
+ button: arguments[ 0 ].items
+ };
+ }
+ return this.controlgroup.apply( this, arguments );
+ };
+}
+
return $.ui.button;
} ) );