Catrope has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/334861 )
Change subject: [VERY WIP] FilterCapsuleMultiselectWidget rewrite proof of
concept
......................................................................
[VERY WIP] FilterCapsuleMultiselectWidget rewrite proof of concept
Change-Id: Iee6487eed5e679163963a600c0da0e056b4ee6d1
---
M resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
M resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
M resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
M
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less
M
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterGroupWidget.less
M resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterItemWidget.less
6 files changed, 298 insertions(+), 1 deletion(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/61/334861/1
diff --git
a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
index 3f7fa53..6aeaefc 100644
--- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
+++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
@@ -236,6 +236,16 @@
};
/**
+ * Get the definition object for an individual group.
+ * @param {string} groupName Group name
+ * @return {Object|null} Filter group
+ * @see #getFilterGroups
+ */
+ mw.rcfilters.dm.FiltersViewModel.prototype.getFilterGroup = function (
groupName ) {
+ return this.groups[ groupName ] || null;
+ };
+
+ /**
* Get the current state of the filters.
*
* Checks whether the filter group is active. This means at least one
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
index 28d9f28..1d0cb8e 100644
--- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
+++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
@@ -28,6 +28,14 @@
};
/**
+ * Get the view model.
+ * @return {mw.rcfilters.FiltersViewModel} View model
+ */
+ mw.rcfilters.Controller.prototype.getModel = function () {
+ return this.model;
+ };
+
+ /**
* Reset to default filters
*/
mw.rcfilters.Controller.prototype.resetToDefaults = function () {
diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
b/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
index 1f02bc4..7e42cab 100644
--- a/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
+++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
@@ -2,6 +2,269 @@
* JavaScript for Special:RecentChanges
*/
( function ( mw, $ ) {
+
+ mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget = function (
controller, config ) {
+ // Parent constructor
+ mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.parent.call(
this, config );
+
+ this.controller = controller;
+ this.model = controller.getModel();
+
+ // CapsuleMultiselectWidget unhelpfully creates a raw <input>
rather than a TextInputWidget,
+ // and also attaches tab indexing and event handlers to
this.$input
+ // HACK: create a TextInputWidget that hijacks this.$input
+ this.filterInput = new OO.ui.TextInputWidget( {
+ $input: this.$input,
+ icon: 'search',
+ placeholder: mw.msg( 'rcfilters-search-placeholder' ),
+ classes: [
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-input' ]
+ } );
+
+ this.resetButton = new OO.ui.ButtonWidget( {
+ icon: 'trash',
+ framed: false,
+ title: mw.msg( 'rcfilters-clear-all-filters' ),
+ classes: [
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-resetButton' ]
+ } );
+
+ this.emptyFilterMessage = new OO.ui.LabelWidget( {
+ label: mw.msg( 'rcfilters-empty-filter' ),
+ classes: [
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-emptyFilters' ]
+ } );
+
+ this.resetButton.connect( this, { click: 'onResetButtonClick' }
);
+ this.model.connect( this, { itemUpdate: 'onModelItemUpdate' } );
+
+ this.$content.prepend(
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-content-title' )
+ .text( mw.msg( 'rcfilters-activefilters' ) )
+ );
+ this.$content.append( this.emptyFilterMessage.$element );
+ this.$handle.append(
+ // The content and button should appear side by side
regardless of how
+ // wide the button is; the button also changes its
width depending
+ // on language and its state, so the safest way to
present both side
+ // by side is with a table layout
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-table' )
+ .append(
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-row' )
+ .append(
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-content' )
+ .addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-cell' )
+ .append(
this.$content ),
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-cell' )
+ .append(
this.resetButton.$element )
+ )
+ )
+ );
+ this.$handle.after( this.filterInput.$element );
+ this.$element.addClass(
'mw-rcfilters-ui-filterCapsuleMultiselectWidget' );
+
+ this.getMenu().addItems( this.buildMenuItems() );
+ this.addItemsFromData(
+ this.model.getItems()
+ .filter( function ( filterItem ) { return
filterItem.isSelected(); } )
+ .map( function ( filterItem ) { return
filterItem.getName(); } )
+ );
+ this.reevaluateResetRestoreState();
+
+ };
+
+ OO.inheritClass( mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget,
OO.ui.CapsuleMultiselectWidget );
+
+ /**
+ * @private
+ */
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.buildMenuItems =
function () {
+ // TODO inline in the constructor?
+ var group,
+ menuItems = [],
+ groups = this.model.getFilterGroups(),
+ controller = this.controller;
+
+ for ( group in groups ) {
+ menuItems.push( new
mw.rcfilters.ui.FilterGroupMenuSectionOptionWidget( this.model, group ) );
+ menuItems.push.apply( menuItems, groups[ group
].filters.map( function ( filterItem ) {
+ return new
mw.rcfilters.ui.FilterItemMenuOptionWidget( controller, filterItem );
+ } ) );
+ }
+ return menuItems;
+ };
+
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.createItemWidget =
function ( data, label ) {
+ var filterItem = this.model.getItemByName( data );
+ if ( !filterItem ) {
+ return null;
+ }
+ return new mw.rcfilters.ui.RoanFilterCapsuleItemWidget(
filterItem );
+ };
+
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.onModelItemUpdate
= function ( filterItem ) {
+ var groupWidget;
+ if ( filterItem.isSelected() ) {
+ this.addItemsFromData( [ filterItem.getName() ] );
+ } else {
+ this.removeItemsFromData ( [ filterItem.getName() ] );
+ }
+
+ groupWidget = this.getMenu().getItemFromData( 'filtergroup-' +
filterItem.getGroup() );
+ if ( groupWidget ) {
+ groupWidget.reevaluateActiveState();
+ }
+
+ this.reevaluateResetRestoreState();
+ };
+
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.onResetButtonClick
=
+
mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onResetButtonClick;
+
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.reevaluateResetRestoreState
=
+
mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.reevaluateResetRestoreState;
+
+ mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.onKeyDown
= function () {};
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.updateInputSize =
function () {};
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.onMenuChoose =
function () {};
+
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.prototype.removeItems =
function ( items ) {
+ var i, filterItem;
+
+ // Parent method
+
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget.parent.prototype.removeItems.call(
this, items );
+
+ for ( i = 0; i < items.length; i++ ) {
+ items[ i ].getModel().toggleSelected( false );
+ }
+ };
+
+
+
+ mw.rcfilters.ui.RoanFilterCapsuleItemWidget = function ( filterItem,
config ) {
+ this.model = filterItem;
+
+ // Parent constructor
+ mw.rcfilters.ui.RoanFilterCapsuleItemWidget.parent.call( this,
$.extend( {
+ data: this.model.getName(),
+ label: this.model.getLabel()
+ }, config ) );
+
+ this.model.connect( this, { update: 'reevaluateActiveState' } );
+ this.reevaluateActiveState();
+ };
+
+ OO.inheritClass( mw.rcfilters.ui.RoanFilterCapsuleItemWidget,
OO.ui.CapsuleItemWidget );
+
+ mw.rcfilters.ui.RoanFilterCapsuleItemWidget.prototype.getModel =
function () {
+ return this.model;
+ };
+
+
mw.rcfilters.ui.RoanFilterCapsuleItemWidget.prototype.reevaluateActiveState =
function () {
+ this.$element.toggleClass(
+
'mw-rcfilters-ui-filterCapsuleMultiselectWidget-item-inactive',
+ !this.model.isActive()
+ );
+ };
+
+ mw.rcfilters.ui.FilterGroupMenuSectionOptionWidget = function ( model,
groupName, config ) {
+ var groupData = model.getFilterGroup( groupName );
+ this.model = model;
+ this.groupName = groupName;
+
+ // Parent constructor
+ mw.rcfilters.ui.FilterGroupMenuSectionOptionWidget.parent.call(
this, $.extend( {
+ label: groupData && groupData.title || this.groupName,
+ data: 'filtergroup-' + this.groupName
+ }, config ) );
+
+ this.$element.addClass(
'mw-rcfilters-ui-filterGroupMenuSectionOptionWidget' );
+
+ this.reevaluateActiveState();
+ };
+
+ OO.inheritClass( mw.rcfilters.ui.FilterGroupMenuSectionOptionWidget,
OO.ui.MenuSectionOptionWidget );
+
+
mw.rcfilters.ui.FilterGroupMenuSectionOptionWidget.prototype.reevaluateActiveState
= function () {
+ this.$element.toggleClass(
+
'mw-rcfilters-ui-filterGroupMenuSectionOptionWidget-active',
+ this.model.isFilterGroupActive( this.groupName )
+ );
+ };
+
+ mw.rcfilters.ui.FilterItemMenuOptionWidget = function ( controller,
filterItem, config ) {
+ var layout,
+ $label = $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterItemWidget-label' );
+ this.controller = controller;
+ this.model = filterItem;
+
+ // Parent constructor
+ mw.rcfilters.ui.FilterItemMenuOptionWidget.parent.call( this,
$.extend( {
+ data: this.model.getName(),
+ label: this.model.getLabel()
+ }, config ) );
+
+
+ this.checkboxWidget = new OO.ui.CheckboxInputWidget( {
+ value: this.model.getName(),
+ selected: this.model.isSelected()
+ } );
+
+ // Wrap this.$label and optionally add a description below it
+ $label.append(
+ this.$label
+ .addClass(
'mw-rcfilters-ui-filterItemWidget-label-title' )
+ );
+ if ( this.model.getDescription() ) {
+ $label.append(
+ $( '<div>' )
+ .addClass(
'mw-rcfilters-ui-filterItemWidget-label-desc' )
+ .text( this.model.getDescription() )
+ );
+ }
+
+ layout = new OO.ui.FieldLayout( this.checkboxWidget, {
+ label: $label,
+ align: 'inline'
+ } );
+
+ // Event
+ this.checkboxWidget.connect( this, { change: 'onCheckboxChange'
} );
+ this.model.connect( this, { update: 'onModelUpdate' } );
+
+ this.$element
+ .addClass( 'mw-rcfilters-ui-filterItemWidget' )
+ .append(
+ layout.$element
+ );
+
+ // HACK: Intercept mousedown/mouseup and stop them from
propagating up to
+ // the SelectWidget
+ // TODO: Instead of this we could also embrace 'choose', since
that will happen
+ // for keyboard interaction anyway. This would be simple to do
with an event handler,
+ // the only problem is that MenuSelectWidget wants to hide
itself on choose
+ this.$element.on( 'mousedown mouseup', function ( e ) {
+ e.stopPropagation();
+ } );
+ };
+
+ OO.inheritClass( mw.rcfilters.ui.FilterItemMenuOptionWidget,
OO.ui.MenuOptionWidget );
+
+ //mw.rcfilters.ui.FilterItemMenuOptionWidget.static.selectable = false;
+
+ mw.rcfilters.ui.FilterItemMenuOptionWidget.prototype.onCheckboxChange =
+ mw.rcfilters.ui.FilterItemWidget.prototype.onCheckboxChange;
+
+ mw.rcfilters.ui.FilterItemMenuOptionWidget.prototype.onModelUpdate =
+ mw.rcfilters.ui.FilterItemWidget.prototype.onModelUpdate;
+
+ mw.rcfilters.ui.FilterItemMenuOptionWidget.prototype.getName =
+ mw.rcfilters.ui.FilterItemWidget.prototype.getName;
+
+
/**
* @class mw.rcfilters
* @singleton
@@ -11,7 +274,8 @@
init: function () {
var model = new mw.rcfilters.dm.FiltersViewModel(),
controller = new mw.rcfilters.Controller( model
),
- widget = new
mw.rcfilters.ui.FilterWrapperWidget( controller, model );
+ widget = new
mw.rcfilters.ui.FilterWrapperWidget( controller, model ),
+ widget2;
model.initializeFilters( {
registration: {
@@ -148,6 +412,8 @@
} );
$( '.rcoptions' ).before( widget.$element );
+ widget2 = new
mw.rcfilters.ui.RoanFilterCapsuleMultiselectWidget( controller, model );
+ widget.$element.after( widget2.$element );
// Initialize values
controller.initialize();
diff --git
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less
index c409d58..3e3f26f 100644
---
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less
+++
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterCapsuleMultiselectWidget.less
@@ -9,6 +9,10 @@
}
+ &-input {
+ max-width: none;
+ }
+
&-content-title {
font-weight: bold;
color: #54595d;
diff --git
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterGroupWidget.less
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterGroupWidget.less
index 948de37..1fb7ed3 100644
---
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterGroupWidget.less
+++
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterGroupWidget.less
@@ -16,6 +16,14 @@
font-weight: bold;
}
}
+}
+.mw-rcfilters-ui-filterGroupMenuSectionOptionWidget {
+ background: #eaecf0;
+ color: #555a5d;
+ padding: 0.5em 0.75em;
+
+ &-active {
+ font-weight: bold;
}
}
diff --git
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterItemWidget.less
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterItemWidget.less
index e072440..fe226cd 100644
---
a/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterItemWidget.less
+++
b/resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterItemWidget.less
@@ -1,6 +1,7 @@
@import "mediawiki.mixins";
.mw-rcfilters-ui-filterItemWidget {
+ padding: 0;
padding-left: 0.5em;
.box-sizing( border-box );
--
To view, visit https://gerrit.wikimedia.org/r/334861
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iee6487eed5e679163963a600c0da0e056b4ee6d1
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Catrope <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits