jenkins-bot has submitted this change and it was merged.
Change subject: Create a combo box widget
......................................................................
Create a combo box widget
Changes:
TextInputWidget.js
* Added optional icon and indicator using mixin, replacing the ad-hoc
icon implementation
* Added icon and indicator events which fire when the icon or indicator
is clicked
MenuWidget.js
* Fixed toggle to actually toggle
ComboBoxWidget.js
* New class, a mixture of an indicated TextInputWidget and MenuWidget.
Provides a drop-down type menu while allowing the user to specify a
custom value. Intended to be used for VE's reference group input,
among other things.
GroupElement.js
* Added isEmpty method for convenience
Bug: 61573
Change-Id: Ic5b939256eda6e0fa6d9f5c9ce4127abec499292
---
M build/modules.json
M demos/widgets.js
M src/elements/GroupElement.js
A src/styles/widgets/ComboBoxWidget.less
M src/styles/widgets/TextInputWidget.less
A src/themes/apex/widgets/ComboBoxWidget.less
M src/themes/apex/widgets/TextInputWidget.less
A src/widgets/ComboBoxWidget.js
M src/widgets/MenuWidget.js
M src/widgets/TextInputWidget.js
10 files changed, 372 insertions(+), 75 deletions(-)
Approvals:
Catrope: Looks good to me, approved
jenkins-bot: Verified
diff --git a/build/modules.json b/build/modules.json
index 23c4660..311789c 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -67,6 +67,8 @@
"src/widgets/InputWidget.js",
"src/widgets/CheckboxInputWidget.js",
"src/widgets/TextInputWidget.js",
+ "src/widgets/ComboBoxWidget.js",
+
"src/widgets/ComboBoxInputWidget.js",
"src/widgets/LabelWidget.js",
"src/widgets/OptionWidget.js",
"src/widgets/DecoratedOptionWidget.js",
@@ -115,6 +117,7 @@
"src/styles/widgets/OptionWidget.less",
"src/styles/widgets/DecoratedOptionWidget.less",
"src/styles/widgets/MenuWidget.less",
+ "src/styles/widgets/ComboBoxWidget.less",
"src/styles/widgets/PopupWidget.less",
"src/styles/widgets/ButtonGroupWidget.less",
"src/styles/widgets/ButtonOptionWidget.less",
@@ -160,6 +163,7 @@
"src/themes/apex/widgets/ButtonGroupWidget.less",
"src/themes/apex/widgets/ButtonOptionWidget.less",
"src/themes/apex/widgets/ButtonSelectWidget.less",
+ "src/themes/apex/widgets/ComboBoxWidget.less",
"src/themes/apex/widgets/InlineMenuWidget.less",
"src/themes/apex/widgets/MenuItemWidget.less",
"src/themes/apex/widgets/MenuSectionItemWidget.less",
diff --git a/demos/widgets.js b/demos/widgets.js
index 004afa3..8dbd57c 100644
--- a/demos/widgets.js
+++ b/demos/widgets.js
@@ -394,61 +394,6 @@
}
),
new OO.ui.FieldLayout(
- new OO.ui.TextInputWidget( { value: 'Text
input' } ),
- {
- label: 'TextInputWidget',
- align: 'top'
- }
- ),
- new OO.ui.FieldLayout(
- new OO.ui.TextInputWidget( { placeholder:
'Placeholder' } ),
- {
- label: 'TextInputWidget (placeholder)',
- align: 'top'
- }
- ),
- new OO.ui.FieldLayout(
- new OO.ui.TextInputWidget( {
- value: 'Readonly',
- readOnly: true
- } ),
- {
- label: 'TextInputWidget (readonly)',
- align: 'top'
- }
- ),
- new OO.ui.FieldLayout(
- new OO.ui.TextInputWidget( {
- value: 'Disabled',
- disabled: true
- } ),
- {
- label: 'TextInputWidget (disabled)',
- align: 'top'
- }
- ),
- new OO.ui.FieldLayout(
- new OO.ui.TextInputWidget( {
- multiline: true,
- value: 'Multiline'
- } ),
- {
- label: 'TextInputWidget (multiline)',
- align: 'top'
- }
- ),
- new OO.ui.FieldLayout(
- new OO.ui.TextInputWidget( {
- multiline: true,
- autosize: true,
- value: 'Autosize'
- } ),
- {
- label: 'TextInputWidget (autosize)',
- align: 'top'
- }
- ),
- new OO.ui.FieldLayout(
new OO.ui.InlineMenuWidget( {
label: 'Select one',
align: 'top',
@@ -532,6 +477,117 @@
label: 'InlineMenuWidget (disabled)',
align: 'top'
}
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ComboBoxWidget( {
+ menu: {
+ items: [
+ new
OO.ui.MenuItemWidget( 'asd', { label: 'Label for asd' } ),
+ new
OO.ui.MenuItemWidget( 'fgh', { label: 'Label for fgh' } ),
+ new
OO.ui.MenuItemWidget( 'jkl', { label: 'Label for jkl' } ),
+ new
OO.ui.MenuItemWidget( 'zxc', { label: 'Label for zxc' } ),
+ new
OO.ui.MenuItemWidget( 'vbn', { label: 'Label for vbn' } )
+ ]
+ }
+ } ),
+ {
+ label: 'ComboBoxWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ComboBoxWidget( {
+ disabled: true,
+ menu: {
+ items: [
+ new
OO.ui.MenuItemWidget( 'asd', { label: 'Label for asd' } ),
+ new
OO.ui.MenuItemWidget( 'fgh', { label: 'Label for fgh' } ),
+ new
OO.ui.MenuItemWidget( 'jkl', { label: 'Label for jkl' } ),
+ new
OO.ui.MenuItemWidget( 'zxc', { label: 'Label for zxc' } ),
+ new
OO.ui.MenuItemWidget( 'vbn', { label: 'Label for vbn' } )
+ ]
+ }
+ } ),
+ {
+ label: 'ComboBoxWidget (disabled)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.ComboBoxWidget(),
+ {
+ label: 'ComboBoxWidget (empty)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { value: 'Text
input' } ),
+ {
+ label: 'TextInputWidget',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { icon: 'search' } ),
+ {
+ label: 'TextInputWidget (icon)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { indicator:
'required' } ),
+ {
+ label: 'TextInputWidget (indicator)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( { placeholder:
'Placeholder' } ),
+ {
+ label: 'TextInputWidget (placeholder)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ value: 'Readonly',
+ readOnly: true
+ } ),
+ {
+ label: 'TextInputWidget (readonly)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ value: 'Disabled',
+ disabled: true
+ } ),
+ {
+ label: 'TextInputWidget (disabled)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ multiline: true,
+ value: 'Multiline'
+ } ),
+ {
+ label: 'TextInputWidget (multiline)',
+ align: 'top'
+ }
+ ),
+ new OO.ui.FieldLayout(
+ new OO.ui.TextInputWidget( {
+ multiline: true,
+ autosize: true,
+ value: 'Autosize'
+ } ),
+ {
+ label: 'TextInputWidget (autosize)',
+ align: 'top'
+ }
)
];
diff --git a/src/elements/GroupElement.js b/src/elements/GroupElement.js
index 0e54f43..57ac1df 100644
--- a/src/elements/GroupElement.js
+++ b/src/elements/GroupElement.js
@@ -21,6 +21,15 @@
/* Methods */
/**
+ * Check if there are no items.
+ *
+ * @return {boolean} Group is empty
+ */
+OO.ui.GroupElement.prototype.isEmpty = function () {
+ return !this.items.length;
+};
+
+/**
* Get items.
*
* @return {OO.ui.Element[]} Items
diff --git a/src/styles/widgets/ComboBoxWidget.less
b/src/styles/widgets/ComboBoxWidget.less
new file mode 100644
index 0000000..e96f615
--- /dev/null
+++ b/src/styles/widgets/ComboBoxWidget.less
@@ -0,0 +1,6 @@
+@import '../mixins';
+
+.oo-ui-comboBoxWidget > .oo-ui-selectWidget {
+ z-index: 1;
+ min-width: 20em;
+}
diff --git a/src/styles/widgets/TextInputWidget.less
b/src/styles/widgets/TextInputWidget.less
index 5457749..bc2ad1f 100644
--- a/src/styles/widgets/TextInputWidget.less
+++ b/src/styles/widgets/TextInputWidget.less
@@ -12,12 +12,22 @@
.oo-ui-box-sizing(border-box);
}
- &-icon {
+ > .oo-ui-iconedElement-icon,
+ > .oo-ui-indicatedElement-indicator {
position: absolute;
top: 0;
- left: 0;
height: 100%;
- background-position: right center;
background-repeat: no-repeat;
+ cursor: pointer;
+
+ .oo-ui-unselectable();
+ }
+
+ > .oo-ui-iconedElement-icon {
+ left: 0;
+ }
+
+ > .oo-ui-indicatedElement-indicator {
+ right: 0;
}
}
diff --git a/src/themes/apex/widgets/ComboBoxWidget.less
b/src/themes/apex/widgets/ComboBoxWidget.less
new file mode 100644
index 0000000..927c76f
--- /dev/null
+++ b/src/themes/apex/widgets/ComboBoxWidget.less
@@ -0,0 +1,22 @@
+@import '../../../styles/mixins';
+
+.oo-ui-comboBoxWidget {
+ &-handle {
+ border: solid 1px rgba(0,0,0,0.1);
+ border-radius: 0.25em;
+
+ &:hover {
+ border-color: rgba(0,0,0,0.2);
+ }
+ }
+
+ &.oo-ui-widget-disabled,
+ &-empty {
+ .oo-ui-textInputWidget.oo-ui-indicatedElement {
+ .oo-ui-indicatedElement-indicator {
+ cursor: default;
+ opacity: 0.2;
+ }
+ }
+ }
+}
diff --git a/src/themes/apex/widgets/TextInputWidget.less
b/src/themes/apex/widgets/TextInputWidget.less
index a1c18e6..6b68af0 100644
--- a/src/themes/apex/widgets/TextInputWidget.less
+++ b/src/themes/apex/widgets/TextInputWidget.less
@@ -59,4 +59,33 @@
background-color: #f3f3f3;
}
}
+
+ .oo-ui-iconedElement-icon,
+ .oo-ui-indicatedElement-indicator {
+ opacity: 0.8;
+ }
+
+ &.oo-ui-iconedElement {
+ input,
+ textarea {
+ padding-left: 2em;
+ }
+
+ .oo-ui-iconedElement-icon {
+ width: 2em;
+ background-position: right center;
+ }
+ }
+
+ &.oo-ui-indicatedElement {
+ input,
+ textarea {
+ padding-right: 1.5em;
+ }
+
+ .oo-ui-indicatedElement-indicator {
+ width: 1.5em;
+ background-position: left center;
+ }
+ }
}
diff --git a/src/widgets/ComboBoxWidget.js b/src/widgets/ComboBoxWidget.js
new file mode 100644
index 0000000..3ea9a2e
--- /dev/null
+++ b/src/widgets/ComboBoxWidget.js
@@ -0,0 +1,121 @@
+/**
+ * Text input with a menu of optional values.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} [menu] Configuration options to pass to menu widget
+ * @cfg {Object} [input] Configuration options to pass to input widget
+ */
+OO.ui.ComboBoxWidget = function OoUiComboBoxWidget( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Parent constructor
+ OO.ui.ComboBoxWidget.super.call( this, config );
+
+ // Properties
+ this.input = new OO.ui.TextInputWidget( $.extend(
+ { $: this.$, indicator: 'down', disabled: this.isDisabled() },
+ config.input
+ ) );
+ this.menu = new OO.ui.MenuWidget( $.extend(
+ { $: this.$, widget: this, input: this.input, disabled:
this.isDisabled() },
+ config.menu
+ ) );
+
+ // Events
+ this.input.connect( this, {
+ change: 'onInputChange',
+ indicator: 'onInputIndicator',
+ enter: 'onInputEnter'
+ } );
+ this.menu.connect( this, {
+ choose: 'onMenuChoose',
+ add: 'onMenuItemsChange',
+ remove: 'onMenuItemsChange'
+ } );
+
+ // Initialization
+ this.$element.addClass( 'oo-ui-comboBoxWidget' ).append(
+ this.input.$element,
+ this.menu.$element
+ );
+ this.onMenuItemsChange();
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.ComboBoxWidget, OO.ui.Widget );
+
+/* Methods */
+
+/**
+ * Handle input change events.
+ *
+ * @param {string} value New value
+ */
+OO.ui.ComboBoxWidget.prototype.onInputChange = function ( value ) {
+ var match = this.menu.getItemFromData( value );
+
+ this.menu.selectItem( match );
+
+ if ( !this.isDisabled() ) {
+ this.menu.toggle( true );
+ }
+};
+
+/**
+ * Handle input indicator events.
+ */
+OO.ui.ComboBoxWidget.prototype.onInputIndicator = function () {
+ if ( !this.isDisabled() ) {
+ this.menu.toggle();
+ }
+};
+
+/**
+ * Handle input enter events.
+ */
+OO.ui.ComboBoxWidget.prototype.onInputEnter = function () {
+ if ( !this.isDisabled() ) {
+ this.menu.toggle( false );
+ }
+};
+
+/**
+ * Handle menu choose events.
+ *
+ * @param {OO.ui.OptionWidget} item Chosen item
+ */
+OO.ui.ComboBoxWidget.prototype.onMenuChoose = function ( item ) {
+ if ( item ) {
+ this.input.setValue( item.getData() );
+ }
+};
+
+/**
+ * Handle menu item change events.
+ */
+OO.ui.ComboBoxWidget.prototype.onMenuItemsChange = function () {
+ this.$element.toggleClass( 'oo-ui-comboBoxWidget-empty',
this.menu.isEmpty() );
+};
+
+/**
+ * @inheritdoc
+ */
+OO.ui.ComboBoxWidget.prototype.setDisabled = function ( disabled ) {
+ // Parent method
+ OO.ui.ComboBoxWidget.super.prototype.setDisabled.call( this, disabled );
+
+ if ( this.input ) {
+ this.input.setDisabled( this.isDisabled() );
+ }
+ if ( this.menu ) {
+ this.menu.setDisabled( this.isDisabled() );
+ }
+
+ return this;
+};
diff --git a/src/widgets/MenuWidget.js b/src/widgets/MenuWidget.js
index 9975f1b..8470299 100644
--- a/src/widgets/MenuWidget.js
+++ b/src/widgets/MenuWidget.js
@@ -199,7 +199,7 @@
* @inheritdoc
*/
OO.ui.MenuWidget.prototype.toggle = function ( visible ) {
- visible = !!visible && !!this.items.length;
+ visible = ( visible === undefined ? !this.visible : !!visible ) &&
!!this.items.length;
var i, len,
change = visible !== this.isVisible();
diff --git a/src/widgets/TextInputWidget.js b/src/widgets/TextInputWidget.js
index fb872f8..aa143da 100644
--- a/src/widgets/TextInputWidget.js
+++ b/src/widgets/TextInputWidget.js
@@ -3,45 +3,43 @@
*
* @class
* @extends OO.ui.InputWidget
+ * @mixins OO.ui.IconedElement
+ * @mixins OO.ui.IndicatedElement
*
* @constructor
* @param {Object} [config] Configuration options
* @cfg {string} [placeholder] Placeholder text
- * @cfg {string} [icon] Symbolic name of icon
* @cfg {boolean} [multiline=false] Allow multiple lines of text
* @cfg {boolean} [autosize=false] Automatically resize to fit content
* @cfg {boolean} [maxRows=10] Maximum number of rows to make visible when
autosizing
*/
OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
- var widget = this;
- config = $.extend( { maxRows: 10 }, config );
+ // Configuration initialization
+ config = config || {};
// Parent constructor
OO.ui.TextInputWidget.super.call( this, config );
+
+ // Mixin constructors
+ OO.ui.IconedElement.call( this, this.$( '<span>' ), config );
+ OO.ui.IndicatedElement.call( this, this.$( '<span>' ), config );
// Properties
this.pending = 0;
this.multiline = !!config.multiline;
this.autosize = !!config.autosize;
- this.maxRows = config.maxRows;
+ this.maxRows = config.maxRows !== undefined ? config.maxRows : 10;
// Events
this.$input.on( 'keypress', OO.ui.bind( this.onKeyPress, this ) );
this.$element.on( 'DOMNodeInsertedIntoDocument', OO.ui.bind(
this.onElementAttach, this ) );
+ this.$icon.on( 'mousedown', OO.ui.bind( this.onIconMouseDown, this ) );
+ this.$indicator.on( 'mousedown', OO.ui.bind( this.onIndicatorMouseDown,
this ) );
// Initialization
- this.$element.addClass( 'oo-ui-textInputWidget' );
- if ( config.icon ) {
- this.$element.addClass( 'oo-ui-textInputWidget-decorated' );
- this.$element.append(
- this.$( '<span>' )
- .addClass( 'oo-ui-textInputWidget-icon
oo-ui-icon-' + config.icon )
- .mousedown( function () {
- widget.$input[0].focus();
- return false;
- } )
- );
- }
+ this.$element
+ .addClass( 'oo-ui-textInputWidget' )
+ .append( this.$icon, this.$indicator );
if ( config.placeholder ) {
this.$input.attr( 'placeholder', config.placeholder );
}
@@ -51,6 +49,8 @@
/* Setup */
OO.inheritClass( OO.ui.TextInputWidget, OO.ui.InputWidget );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IconedElement );
+OO.mixinClass( OO.ui.TextInputWidget, OO.ui.IndicatedElement );
/* Events */
@@ -62,9 +62,49 @@
* @event enter
*/
+/**
+ * User clicks the icon.
+ *
+ * @event icon
+ */
+
+/**
+ * User clicks the indicator.
+ *
+ * @event indicator
+ */
+
/* Methods */
/**
+ * Handle icon mouse down events.
+ *
+ * @param {jQuery.Event} e Mouse down event
+ * @fires icon
+ */
+OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
+ if ( e.which === 1 ) {
+ this.$input[0].focus();
+ this.emit( 'icon' );
+ return false;
+ }
+};
+
+/**
+ * Handle indicator mouse down events.
+ *
+ * @param {jQuery.Event} e Mouse down event
+ * @fires indicator
+ */
+OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
+ if ( e.which === 1 ) {
+ this.$input[0].focus();
+ this.emit( 'indicator' );
+ return false;
+ }
+};
+
+/**
* Handle key press events.
*
* @param {jQuery.Event} e Key press event
--
To view, visit https://gerrit.wikimedia.org/r/143097
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Ic5b939256eda6e0fa6d9f5c9ce4127abec499292
Gerrit-PatchSet: 16
Gerrit-Project: oojs/ui
Gerrit-Branch: master
Gerrit-Owner: Alex Monk <[email protected]>
Gerrit-Reviewer: Alex Monk <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: Krinkle <[email protected]>
Gerrit-Reviewer: Trevor Parscal <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits