}
}
+ &-selected {
+ background-color: #eaf3ff; // Accent90 AAA
+ }
+
&-label {
&-title {
font-weight: bold;
e.stopPropagation();
};
+ /**
+ * Emit a click event when the capsule is clicked so we can aggregate this
+ * in the parent (the capsule)
+ */
+ mw.rcfilters.ui.CapsuleItemWidget.prototype.onClick = function () {
+ this.emit( 'click' );
+ };
+
/**
* Override the event listening to the item close button click
*/
* @param {OO.ui.InputWidget} filterInput A filter input that focuses the capsule widget
* @param {Object} config Configuration object
* @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
- * @cfg {number} [topScrollOffset=10] When scrolling the entire widget to the top, leave this
- * much space (in pixels) above the widget.
*/
mw.rcfilters.ui.FilterCapsuleMultiselectWidget = function MwRcfiltersUiFilterCapsuleMultiselectWidget( controller, model, filterInput, config ) {
var title = new OO.ui.LabelWidget( {
this.controller = controller;
this.model = model;
this.filterInput = filterInput;
-
- this.topScrollOffset = config.topScrollOffset || 10;
+ this.isSelecting = false;
this.resetButton = new OO.ui.ButtonWidget( {
icon: 'trash',
itemUpdate: 'onModelItemUpdate',
highlightChange: 'onModelHighlightChange'
} );
- this.popup.connect( this, { toggle: 'onPopupToggle' } );
+ this.aggregate( { click: 'capsuleItemClick' } );
// Add the filterInput as trigger
this.filterInput.$input
}
};
- /**
- * Respond to popup toggle event
- *
- * @param {boolean} isVisible Popup is visible
- */
- mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.onPopupToggle = function ( isVisible ) {
- if ( isVisible ) {
- this.scrollToTop();
- }
- };
-
- /**
- * Scroll the capsule to the top of the screen
- */
- mw.rcfilters.ui.FilterCapsuleMultiselectWidget.prototype.scrollToTop = function () {
- var container = OO.ui.Element.static.getClosestScrollableContainer( this.$element[ 0 ], 'y' );
-
- $( container ).animate( {
- scrollTop: this.$element.offset().top - this.topScrollOffset
- } );
- };
-
/**
* Reevaluate the restore state for the widget between setting to defaults and clearing all filters
*/
this.controller = controller;
this.model = model;
+ this.filters = {};
// Mixin constructors
OO.ui.mixin.GroupWidget.call( this, config );
this.$element
.addClass( 'mw-rcfilters-ui-filterGroupWidget' )
+ .addClass( 'mw-rcfilters-ui-filterGroupWidget-name-' + this.model.getName() )
.append(
this.$label,
this.$group
);
};
+ /**
+ * Get an item widget from its filter name
+ *
+ * @param {string} filterName Filter name
+ * @return {mw.rcfilters.ui.FilterItemWidget} Item widget
+ */
+ mw.rcfilters.ui.FilterGroupWidget.prototype.getItemWidget = function ( filterName ) {
+ return this.filters[ filterName ];
+ };
+
+ /**
+ * Populate data from the model
+ */
mw.rcfilters.ui.FilterGroupWidget.prototype.populateFromModel = function () {
var widget = this;
+ this.clearItems();
+ this.filters = {};
+
this.addItems(
this.model.getItems().map( function ( filterItem ) {
- return new mw.rcfilters.ui.FilterItemWidget(
+ var groupWidget = new mw.rcfilters.ui.FilterItemWidget(
widget.controller,
filterItem,
{
$overlay: widget.$overlay
}
);
+
+ widget.filters[ filterItem.getName() ] = groupWidget;
+
+ return groupWidget;
} )
);
};
this.controller = controller;
this.model = model;
+ this.selected = false;
this.checkboxWidget = new mw.rcfilters.ui.CheckboxInputWidget( {
value: this.model.getName(),
this.setCurrentMuteState();
};
+ /**
+ * Set selected state on this widget
+ *
+ * @param {boolean} [isSelected] Widget is selected
+ */
+ mw.rcfilters.ui.FilterItemWidget.prototype.toggleSelected = function ( isSelected ) {
+ isSelected = isSelected !== undefined ? isSelected : !this.selected;
+
+ if ( this.selected !== isSelected ) {
+ this.selected = isSelected;
+
+ this.$element.toggleClass( 'mw-rcfilters-ui-filterItemWidget-selected', this.selected );
+ }
+ };
+
/**
* Set the current mute state for this item
*/
// Events
this.model.connect( this, {
- initialize: 'onModelInitialize'
+ initialize: 'onModelInitialize',
+ itemUpdate: 'onModelItemUpdate'
} );
this.textInput.connect( this, {
change: 'onTextInputChange'
} );
+ this.capsule.connect( this, { capsuleItemClick: 'onCapsuleItemClick' } );
+ this.capsule.popup.connect( this, { toggle: 'onCapsulePopupToggle' } );
+
+ // Initialize
this.$element
.addClass( 'mw-rcfilters-ui-filterWrapperWidget' )
.append( this.capsule.$element, this.textInput.$element );
OO.inheritClass( mw.rcfilters.ui.FilterWrapperWidget, OO.ui.Widget );
OO.mixinClass( mw.rcfilters.ui.FilterWrapperWidget, OO.ui.mixin.PendingElement );
+ /**
+ * Respond to capsule item click and make the popup scroll down to the requested item
+ *
+ * @param {mw.rcfilters.ui.CapsuleItemWidget} item Clicked item
+ */
+ mw.rcfilters.ui.FilterWrapperWidget.prototype.onCapsuleItemClick = function ( item ) {
+ var filterName = item.getData(),
+ // Find the item in the popup
+ filterWidget = this.filterPopup.getItemWidget( filterName );
+
+ // Highlight item
+ this.filterPopup.select( filterName );
+
+ this.scrollToTop( filterWidget.$element );
+ };
+
+ /**
+ * Respond to popup toggle event. Reset selection in the list when the popup is closed.
+ *
+ * @param {boolean} isVisible Popup is visible
+ */
+ mw.rcfilters.ui.FilterWrapperWidget.prototype.onCapsulePopupToggle = function ( isVisible ) {
+ if ( !isVisible ) {
+ this.filterPopup.resetSelection();
+ } else {
+ this.scrollToTop( this.capsule.$element, 10 );
+ }
+ };
+
/**
* Respond to text input change
*
* @param {string} newValue Current value
*/
mw.rcfilters.ui.FilterWrapperWidget.prototype.onTextInputChange = function ( newValue ) {
+ this.filterPopup.resetSelection();
+
// Filter the results
this.filterPopup.filter( this.model.findMatches( newValue ) );
this.capsule.popup.clip();
}
} );
};
+
+ /**
+ * Respond to item update and reset the selection. This will make it so that
+ * any actual interaction with the system resets the selection state of any item.
+ */
+ mw.rcfilters.ui.FilterWrapperWidget.prototype.onModelItemUpdate = function () {
+ this.filterPopup.resetSelection();
+ };
+
+ /**
+ * Scroll the element to top within its container
+ *
+ * @private
+ * @param {jQuery} $element Element to position
+ * @param {number} [marginFromTop] When scrolling the entire widget to the top, leave this
+ * much space (in pixels) above the widget.
+ */
+ mw.rcfilters.ui.FilterWrapperWidget.prototype.scrollToTop = function ( $element, marginFromTop ) {
+ var container = OO.ui.Element.static.getClosestScrollableContainer( $element[ 0 ], 'y' ),
+ pos = OO.ui.Element.static.getRelativePosition( $element, $( container ) );
+
+ // Scroll to item
+ $( container ).animate( {
+ scrollTop: $( container ).scrollTop() + pos.top + ( marginFromTop || 0 )
+ } );
+ };
}( mediaWiki ) );
this.controller = controller;
this.model = model;
this.$overlay = config.$overlay || this.$element;
+ this.groups = {};
+ this.selected = null;
this.highlightButton = new OO.ui.ButtonWidget( {
label: mw.message( 'rcfilters-highlightbutton-title' ).text(),
// Reset
this.clearItems();
+ this.groups = {};
this.addItems(
Object.keys( this.model.getFilterGroups() ).map( function ( groupName ) {
- return new mw.rcfilters.ui.FilterGroupWidget(
+ var groupWidget = new mw.rcfilters.ui.FilterGroupWidget(
widget.controller,
widget.model.getGroup( groupName ),
{
$overlay: widget.$overlay
}
);
+
+ widget.groups[ groupName ] = groupWidget;
+ return groupWidget;
} )
);
};
this.controller.toggleHighlight();
};
+ /**
+ * Find the filter item widget that corresponds to the item name
+ *
+ * @param {string} itemName Filter name
+ * @return {mw.rcfilters.ui.FilterItemWidget} Filter widget
+ */
+ mw.rcfilters.ui.FiltersListWidget.prototype.getItemWidget = function ( itemName ) {
+ var filterItem = this.model.getItemByName( itemName ),
+ // Find the group
+ groupWidget = this.groups[ filterItem.getGroupName() ];
+
+ // Find the item inside the group
+ return groupWidget.getItemWidget( itemName );
+ };
+
+ /**
+ * Mark an item widget as selected
+ *
+ * @param {string} itemName Filter name
+ */
+ mw.rcfilters.ui.FiltersListWidget.prototype.select = function ( itemName ) {
+ var filterWidget;
+
+ if ( this.selected !== itemName ) {
+ // Unselect previous
+ if ( this.selected ) {
+ filterWidget = this.getItemWidget( this.selected );
+ filterWidget.toggleSelected( false );
+ }
+
+ // Select new one
+ this.selected = itemName;
+ if ( this.selected ) {
+ filterWidget = this.getItemWidget( this.selected );
+ filterWidget.toggleSelected( true );
+ }
+ }
+ };
+
+ /**
+ * Reset selection and remove selected states from all items
+ */
+ mw.rcfilters.ui.FiltersListWidget.prototype.resetSelection = function () {
+ if ( this.selected !== null ) {
+ this.selected = null;
+ this.getItems().forEach( function ( groupWidget ) {
+ groupWidget.getItems().forEach( function ( filterItemWidget ) {
+ filterItemWidget.toggleSelected( false );
+ } );
+ } );
+ }
+ };
+
/**
* Switch between showing the 'no results' message for filtering results or the result list.
*