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

Reply via email to