From 3dcba7679a51a0deaaf598a0caef78971787db75 Mon Sep 17 00:00:00 2001 From: Trevor Parscal Date: Tue, 12 Nov 2013 11:52:12 -0800 Subject: [PATCH] Add OO.ui.ToggleWidget Compact toggle widget with short labels for on/off states. Change-Id: I582e81126d5f1564302409c1b262e28e4a9505ac --- VisualEditor.i18n.php | 4 + VisualEditor.php | 3 + demos/ve/index.php | 1 + modules/oojs-ui/OO.ui.js | 4 + modules/oojs-ui/styles/OO.ui.Widget.css | 118 ++++++++++++ modules/oojs-ui/widgets/OO.ui.ToggleWidget.js | 175 ++++++++++++++++++ modules/ve/test/index.php | 1 + 7 files changed, 306 insertions(+) create mode 100644 modules/oojs-ui/widgets/OO.ui.ToggleWidget.js diff --git a/VisualEditor.i18n.php b/VisualEditor.i18n.php index 01962b0bbb..86aa9a03d9 100644 --- a/VisualEditor.i18n.php +++ b/VisualEditor.i18n.php @@ -22,6 +22,8 @@ $messages['en'] = array( 'ooui-dialog-action-close' => 'Close', 'ooui-outline-control-move-down' => 'Move item down', 'ooui-outline-control-move-up' => 'Move item up', + 'ooui-toggle-on' => 'On', + 'ooui-toggle-off' => 'Off', 'ooui-toolbar-more' => 'More', 'tag-visualeditor' => '[[{{MediaWiki:visualeditor-descriptionpagelink}}|VisualEditor]]', 'tag-visualeditor-description' => 'Edit made using the [[{{MediaWiki:visualeditor-descriptionpagelink}}|VisualEditor]]', @@ -253,6 +255,8 @@ $messages['qqq'] = array( {{Identical|Close}}', 'ooui-outline-control-move-down' => 'Tool tip for a button that moves items in a list down one place', 'ooui-outline-control-move-up' => 'Tool tip for a button that moves items in a list up one place', + 'ooui-toggle-on' => 'Label for toggle on state', + 'ooui-toggle-off' => 'Label for toggle off state', 'ooui-toolbar-more' => 'Label for the toolbar group that contains a list of all other available tools. {{Identical|More}}', 'tag-visualeditor' => 'Short description of the visualeditor tag. diff --git a/VisualEditor.php b/VisualEditor.php index 6f54118601..04e770565b 100644 --- a/VisualEditor.php +++ b/VisualEditor.php @@ -145,6 +145,7 @@ $wgResourceModules += array( 'oojs-ui/widgets/OO.ui.SearchWidget.js', 'oojs-ui/widgets/OO.ui.TextInputWidget.js', 'oojs-ui/widgets/OO.ui.TextInputMenuWidget.js', + 'oojs-ui/widgets/OO.ui.ToggleWidget.js', ), 'styles' => array( 'oojs-ui/styles/OO.ui.css', @@ -162,6 +163,8 @@ $wgResourceModules += array( 'ooui-dialog-action-close', 'ooui-outline-control-move-down', 'ooui-outline-control-move-up', + 'ooui-toggle-on', + 'ooui-toggle-off', 'ooui-toolbar-more', ), 'dependencies' => array( diff --git a/demos/ve/index.php b/demos/ve/index.php index 07cceb2a1b..611f0bca69 100644 --- a/demos/ve/index.php +++ b/demos/ve/index.php @@ -154,6 +154,7 @@ $html = file_get_contents( $page ); + diff --git a/modules/oojs-ui/OO.ui.js b/modules/oojs-ui/OO.ui.js index 4bba1844e3..6258a7c47e 100644 --- a/modules/oojs-ui/OO.ui.js +++ b/modules/oojs-ui/OO.ui.js @@ -49,6 +49,10 @@ var messages = { 'ooui-outline-control-move-down': 'Move item down', // Tool tip for a button that moves items in a list up one place 'ooui-outline-control-move-up': 'Move item up', + // Label for toggle on state + 'ooui-toggle-on': 'On', + // Label for toggle off state + 'ooui-toggle-off': 'Off', // Label for the toolbar group that contains a list of all other available tools 'ooui-toolbar-more': 'More' }; diff --git a/modules/oojs-ui/styles/OO.ui.Widget.css b/modules/oojs-ui/styles/OO.ui.Widget.css index e73a722589..c314f2f92b 100644 --- a/modules/oojs-ui/styles/OO.ui.Widget.css +++ b/modules/oojs-ui/styles/OO.ui.Widget.css @@ -553,3 +553,121 @@ a.oo-ui-buttonWidget-button { overflow-y: auto; line-height: 0; } + +/* OO.ui.ToggleWidget */ + +.oo-ui-toggleWidget { + position: relative; + display: inline-block; + vertical-align: middle; + height: 2em; + width: 6em; + border-radius: 0.5em; + overflow: hidden; + box-shadow: 0 0 0 white, inset 0 0.1em 0.2em #ddd; + border: solid 1px #ccc; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + cursor: pointer; + -webkit-transition: background-color 200ms; + -moz-transition: background-color 200ms; + -o-transition: background-color 200ms; + transition: background-color 200ms; +} + +.oo-ui-toggleWidget-slide { + position: absolute; + top: 0; + display: block; + height: 2em; + width: 10em; + -webkit-transition: left 200ms ease-in-out, margin-left 200ms ease-in-out; + -moz-transition: left 200ms ease-in-out, margin-left 200ms ease-in-out; + -o-transition: left 200ms ease-in-out, margin-left 200ms ease-in-out; + transition: left 200ms ease-in-out, margin-left 200ms ease-in-out; +} + +.oo-ui-toggleWidget-dragging .oo-ui-toggleWidget-slide { + -webkit-transition: left 200ms ease-in-out; + -moz-transition: left 200ms ease-in-out; + -o-transition: left 200ms ease-in-out; + transition: left 200ms ease-in-out; +} + +.oo-ui-toggleWidget-on .oo-ui-toggleWidget-slide { + left: 0; +} + +.oo-ui-toggleWidget-off .oo-ui-toggleWidget-slide { + left: -4em; +} + +.oo-ui-toggleWidget-grip { + position: absolute; + top: 0; + left: 4em; + display: block; + width: 2em; + height: 2em; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: -1px; + box-shadow: 0 0.1em 0.25em rgba(0, 0, 0, 0.1); + border-radius: 0.5em; + + /* Gray */ + border: 1px #c9c9c9 solid; + background-color: #ffffff; + filter: progid:DXImageTransform.Microsoft.gradient( + GradientType=0,startColorstr=#ffffff, endColorstr=#f0f0f0 + ); + background-image: -webkit-gradient( + linear, right top, right bottom, color-stop(0%,#ffffff), color-stop(100%,#f0f0f0) + ); + background-image: -webkit-linear-gradient(top, #ffffff 0%, #f0f0f0 100%); + background-image: -moz-linear-gradient(top, #ffffff 0%, #f0f0f0 100%); + background-image: -ms-linear-gradient(top, #ffffff 0%, #f0f0f0 100%); + background-image: -o-linear-gradient(top, #ffffff 0%, #f0f0f0 100%); + background-image: linear-gradient(top, #ffffff 0%, #f0f0f0 100%); +} + +.oo-ui-toggleWidget:hover, +.oo-ui-toggleWidget-dragging, +.oo-ui-toggleWidget:hover .oo-ui-toggleWidget-grip, +.oo-ui-toggleWidget-dragging .oo-ui-toggleWidget-grip { + border-color: #aaa; +} + +.oo-ui-toggleWidget-label { + position: absolute; + top: 0; + display: block; + width: 5em; + height: 2em; + line-height: 2em; + color: black; + text-align: center; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.oo-ui-toggleWidget-label-on { + left: -0.5em; + padding: 0 0.5em 0 0.75em; + background-color: rgba(0,255,0,0.25); +} + +.oo-ui-toggleWidget-label-off { + left: 5.5em; + padding: 0 0.75em 0 0.5em; + background-color: rgba(128,128,128,0.25); +} diff --git a/modules/oojs-ui/widgets/OO.ui.ToggleWidget.js b/modules/oojs-ui/widgets/OO.ui.ToggleWidget.js new file mode 100644 index 0000000000..2cffc81f3c --- /dev/null +++ b/modules/oojs-ui/widgets/OO.ui.ToggleWidget.js @@ -0,0 +1,175 @@ +/*! + * ObjectOriented UserInterface ToggleWidget class. + * + * @copyright 2011-2013 OOJS Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * Creates an OO.ui.ToggleWidget object. + * + * @class + * @abstract + * @extends OO.ui.Widget + * + * @constructor + * @param {Object} [config] Configuration options + * @cfg {boolean} [value=false] Initial value + * @cfg {string} [onLabel='On'] Label for on state + * @cfg {string} [offLabel='Off'] Label for off state + */ +OO.ui.ToggleWidget = function OoUiToggleWidget( config ) { + // Configuration initialization + config = $.extend( { + 'onLabel': OO.ui.msg( 'ooui-toggle-on' ), + 'offLabel': OO.ui.msg( 'ooui-toggle-on' ) + }, config ); + + // Parent constructor + OO.ui.Widget.call( this, config ); + + // Properties + this.value = null; + this.dragging = false; + this.dragStart = null; + this.sliding = false; + this.$slide = this.$( '' ); + this.$grip = this.$( '' ); + this.$onLabel = this.$( '' ); + this.$offLabel = this.$( '' ); + this.onDocumentMouseMoveHandler = OO.ui.bind( this.onDocumentMouseMove, this ); + this.onDocumentMouseUpHandler = OO.ui.bind( this.onDocumentMouseUp, this ); + + // Events + this.$slide.on( 'mousedown', OO.ui.bind( this.onMouseDown, this ) ); + + // Initialization + this.$grip.addClass( 'oo-ui-toggleWidget-grip' ); + this.$onLabel + .addClass( 'oo-ui-toggleWidget-label oo-ui-toggleWidget-label-on' ) + .text( config.onLabel || '' ); + this.$offLabel + .addClass( 'oo-ui-toggleWidget-label oo-ui-toggleWidget-label-off' ) + .text( config.offLabel || '' ); + this.$slide + .addClass( 'oo-ui-toggleWidget-slide' ) + .append( this.$onLabel, this.$offLabel, this.$grip ); + this.$element + .addClass( 'oo-ui-toggleWidget' ) + .append( this.$slide ); + this.setValue( !!config.value ); +}; + +/* Inheritance */ + +OO.inheritClass( OO.ui.ToggleWidget, OO.ui.Widget ); + +/* Events */ + +/** + * @event change + * @param {boolean} value Changed value + */ + +/* Methods */ + +/** + * Handles mouse down events. + * + * @method + * @param {jQuery.Event} e Mouse down event + */ +OO.ui.ToggleWidget.prototype.onMouseDown = function ( e ) { + if ( e.which === 1 ) { + this.dragging = true; + this.dragStart = e.pageX; + this.$( this.$.context ).on( { + 'mousemove': this.onDocumentMouseMoveHandler, + 'mouseup': this.onDocumentMouseUpHandler + } ); + this.$element.addClass( 'oo-ui-toggleWidget-dragging' ); + return false; + } +}; + +/** + * Handles document mouse up events. + * + * @method + * @param {jQuery.Event} e Mouse up event + */ +OO.ui.ToggleWidget.prototype.onDocumentMouseUp = function ( e ) { + var overlap, dragOffset; + + if ( e.which === 1 ) { + this.$element.removeClass( 'oo-ui-toggleWidget-dragging' ); + + if ( !this.sliding ) { + this.setValue( !this.value ); + } else { + this.$slide.css( 'margin-left', 0 ); + dragOffset = e.pageX - this.dragStart; + overlap = this.$element.outerWidth() - this.$slide.outerWidth(); + if ( this.value ? overlap / 2 > dragOffset : -overlap / 2 < dragOffset ) { + this.setValue( !this.value ); + } + } + this.dragging = false; + this.sliding = false; + this.$( this.$.context ).off( { + 'mousemove': this.onDocumentMouseMoveHandler, + 'mouseup': this.onDocumentMouseUpHandler + } ); + } +}; + +/** + * Handles document mouse move events. + * + * @method + * @param {jQuery.Event} e Mouse move event + */ +OO.ui.ToggleWidget.prototype.onDocumentMouseMove = function ( e ) { + var overlap, dragOffset, left; + + if ( this.dragging ) { + dragOffset = e.pageX - this.dragStart; + if ( dragOffset !== 0 || this.sliding ) { + this.sliding = true; + overlap = this.$element.outerWidth() - this.$slide.outerWidth(); + left = this.value ? + Math.min( 0, Math.max( overlap, dragOffset ) ) : + Math.min( -overlap, Math.max( 0, dragOffset ) ); + this.$slide.css( 'margin-left', left ); + } + } +}; + +/** + * Get the value of the toggle. + * + * @method + * @returns {boolean} Toggle value + */ +OO.ui.ToggleWidget.prototype.getValue = function () { + return this.value; +}; + +/** + * Set the value of the toggle. + * + * @method + * @param {boolean} value New value + * @fires change + * @chainable + */ +OO.ui.ToggleWidget.prototype.setValue = function ( value ) { + if ( this.value !== value ) { + this.value = value; + this.$element + .toggleClass( 'oo-ui-toggleWidget-on', value ) + .toggleClass( 'oo-ui-toggleWidget-off', !value ); + this.emit( 'change', this.value ); + } + return this; +}; diff --git a/modules/ve/test/index.php b/modules/ve/test/index.php index 0df4cc2b0f..f38be30ab1 100644 --- a/modules/ve/test/index.php +++ b/modules/ve/test/index.php @@ -98,6 +98,7 @@ +