diff options
author | Alexander Schmitz <arschmitz@gmail.com> | 2014-01-22 12:02:32 -0500 |
---|---|---|
committer | Alexander Schmitz <arschmitz@gmail.com> | 2015-10-07 10:57:59 -0400 |
commit | 02033262ee0fb1d9f33c361b3c2ddfa168604854 (patch) | |
tree | 645ed2427d14a598d02754b79e9eb8b889baf846 /ui | |
parent | 79d312f3f5cb5629d1bd7f9f899b41f304c388ef (diff) | |
download | jquery-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.js | 597 |
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; } ) ); |