RCFilters UI: Create base classes for shared objects
Preparing for adding other types of items than filters (namespaces, users, tags, etc) this commit creates base classes for the model and relevant widgets and extends Filter* from them. Bug: T159942 Bug: T163521 Change-Id: I61c88a1f14a3ca9d91aa831187eda156468a6591
This commit is contained in:
parent
273eea7357
commit
856cc85f4b
13 changed files with 678 additions and 513 deletions
|
|
@ -1742,6 +1742,7 @@ return [
|
|||
'mediawiki.rcfilters.filters.dm' => [
|
||||
'scripts' => [
|
||||
'resources/src/mediawiki.rcfilters/mw.rcfilters.js',
|
||||
'resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js',
|
||||
'resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterItem.js',
|
||||
'resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js',
|
||||
'resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js',
|
||||
|
|
@ -1757,11 +1758,13 @@ return [
|
|||
'scripts' => [
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.CheckboxInputWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ItemMenuOptionWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuOptionWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.TagItemWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagItemWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterFloatingMenuSelectWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FloatingMenuSelectWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js',
|
||||
'resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js',
|
||||
|
|
@ -1776,11 +1779,12 @@ return [
|
|||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.Overlay.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterTagMultiselectWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ItemMenuOptionWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterMenuOptionWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterMenuSectionOptionWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterTagItemWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.TagItemWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterMenuHeaderWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterFloatingMenuSelectWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FloatingMenuSelectWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less',
|
||||
'resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.HighlightColorPickerWidget.less',
|
||||
|
|
|
|||
|
|
@ -2,37 +2,29 @@
|
|||
/**
|
||||
* Filter item model
|
||||
*
|
||||
* @mixins OO.EventEmitter
|
||||
* @extends mw.rcfilters.dm.ItemModel
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} param Filter param name
|
||||
* @param {mw.rcfilters.dm.FilterGroup} groupModel Filter group model
|
||||
* @param {Object} config Configuration object
|
||||
* @cfg {string} [group] The group this item belongs to
|
||||
* @cfg {string} [label] The label for the filter
|
||||
* @cfg {string} [description] The description of the filter
|
||||
* @cfg {boolean} [active=true] The filter is active and affecting the result
|
||||
* @cfg {string[]} [excludes=[]] A list of filter names this filter, if
|
||||
* selected, makes inactive.
|
||||
* @cfg {boolean} [selected] The item is selected
|
||||
* @cfg {string[]} [subset] Defining the names of filters that are a subset of this filter
|
||||
* @cfg {Object} [conflicts] Defines the conflicts for this filter
|
||||
* @cfg {string} [cssClass] The class identifying the results that match this filter
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem = function MwRcfiltersDmFilterItem( param, groupModel, config ) {
|
||||
config = config || {};
|
||||
|
||||
this.groupModel = groupModel;
|
||||
|
||||
// Parent
|
||||
mw.rcfilters.dm.FilterItem.parent.call( this, param, $.extend( {
|
||||
namePrefix: this.groupModel.getNamePrefix()
|
||||
}, config ) );
|
||||
// Mixin constructor
|
||||
OO.EventEmitter.call( this );
|
||||
|
||||
this.param = param;
|
||||
this.groupModel = groupModel;
|
||||
this.name = this.groupModel.getNamePrefix() + param;
|
||||
|
||||
this.label = config.label || this.name;
|
||||
this.description = config.description;
|
||||
this.selected = !!config.selected;
|
||||
|
||||
// Interaction definitions
|
||||
this.subset = config.subset || [];
|
||||
this.conflicts = config.conflicts || {};
|
||||
|
|
@ -42,25 +34,11 @@
|
|||
this.included = false;
|
||||
this.conflicted = false;
|
||||
this.fullyCovered = false;
|
||||
|
||||
// Highlight
|
||||
this.cssClass = config.cssClass;
|
||||
this.highlightColor = null;
|
||||
this.highlightEnabled = false;
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
OO.initClass( mw.rcfilters.dm.FilterItem );
|
||||
OO.mixinClass( mw.rcfilters.dm.FilterItem, OO.EventEmitter );
|
||||
|
||||
/* Events */
|
||||
|
||||
/**
|
||||
* @event update
|
||||
*
|
||||
* The state of this filter has changed
|
||||
*/
|
||||
OO.inheritClass( mw.rcfilters.dm.FilterItem, mw.rcfilters.dm.ItemModel );
|
||||
|
||||
/* Methods */
|
||||
|
||||
|
|
@ -78,27 +56,10 @@
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of this filter
|
||||
*
|
||||
* @return {string} Filter name
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getName = function () {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the param name or value of this filter
|
||||
*
|
||||
* @return {string} Filter param name
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getParamName = function () {
|
||||
return this.param;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the message for the display area for the currently active conflict
|
||||
*
|
||||
* @private
|
||||
* @return {string} Conflict result message key
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getCurrentConflictResultMessage = function () {
|
||||
|
|
@ -117,6 +78,7 @@
|
|||
/**
|
||||
* Get the details of the active conflict on this filter
|
||||
*
|
||||
* @private
|
||||
* @param {Object} conflicts Conflicts to examine
|
||||
* @param {string} [key='contextDescription'] Message key
|
||||
* @return {Object} Object with conflict message and conflict items
|
||||
|
|
@ -153,9 +115,7 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* Get the message representing the state of this model.
|
||||
*
|
||||
* @return {string} State message
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getStateMessage = function () {
|
||||
var messageKey, details, superset,
|
||||
|
|
@ -227,33 +187,6 @@
|
|||
return this.groupModel.getName();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the label of this filter
|
||||
*
|
||||
* @return {string} Filter label
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getLabel = function () {
|
||||
return this.label;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the description of this filter
|
||||
*
|
||||
* @return {string} Filter description
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getDescription = function () {
|
||||
return this.description;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the default value of this filter
|
||||
*
|
||||
* @return {boolean} Filter default
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getDefault = function () {
|
||||
return this.default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get filter subset
|
||||
* This is a list of filter names that are defined to be included
|
||||
|
|
@ -276,15 +209,6 @@
|
|||
return this.superset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected state of this filter
|
||||
*
|
||||
* @return {boolean} Filter is selected
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.isSelected = function () {
|
||||
return this.selected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the filter is currently in a conflict state
|
||||
*
|
||||
|
|
@ -431,21 +355,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the selected state of the item
|
||||
*
|
||||
* @param {boolean} [isSelected] Filter is selected
|
||||
* @fires update
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.toggleSelected = function ( isSelected ) {
|
||||
isSelected = isSelected === undefined ? !this.selected : isSelected;
|
||||
|
||||
if ( this.selected !== isSelected ) {
|
||||
this.selected = isSelected;
|
||||
this.emit( 'update' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the fully covered state of the item
|
||||
*
|
||||
|
|
@ -460,90 +369,4 @@
|
|||
this.emit( 'update' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the highlight color
|
||||
*
|
||||
* @param {string|null} highlightColor
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.setHighlightColor = function ( highlightColor ) {
|
||||
if ( this.highlightColor !== highlightColor ) {
|
||||
this.highlightColor = highlightColor;
|
||||
this.emit( 'update' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the highlight color
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.clearHighlightColor = function () {
|
||||
this.setHighlightColor( null );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the highlight color, or null if none is configured
|
||||
*
|
||||
* @return {string|null}
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getHighlightColor = function () {
|
||||
return this.highlightColor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the CSS class that matches changes that fit this filter
|
||||
* or null if none is configured
|
||||
*
|
||||
* @return {string|null}
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.getCssClass = function () {
|
||||
return this.cssClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the highlight feature on and off for this filter.
|
||||
* It only works if highlight is supported for this filter.
|
||||
*
|
||||
* @param {boolean} enable Highlight should be enabled
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.toggleHighlight = function ( enable ) {
|
||||
enable = enable === undefined ? !this.highlightEnabled : enable;
|
||||
|
||||
if ( !this.isHighlightSupported() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( enable === this.highlightEnabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.highlightEnabled = enable;
|
||||
this.emit( 'update' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the highlight feature is currently enabled for this filter
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.isHighlightEnabled = function () {
|
||||
return !!this.highlightEnabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the highlight feature is supported for this filter
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.isHighlightSupported = function () {
|
||||
return !!this.getCssClass();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the filter is currently highlighted
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.rcfilters.dm.FilterItem.prototype.isHighlighted = function () {
|
||||
return this.isHighlightEnabled() && !!this.getHighlightColor();
|
||||
};
|
||||
}( mediaWiki ) );
|
||||
|
|
|
|||
|
|
@ -0,0 +1,257 @@
|
|||
( function ( mw ) {
|
||||
/**
|
||||
* RCFilter base item model
|
||||
*
|
||||
* @mixins OO.EventEmitter
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} param Filter param name
|
||||
* @param {Object} config Configuration object
|
||||
* @cfg {string} [label] The label for the filter
|
||||
* @cfg {string} [description] The description of the filter
|
||||
* @cfg {boolean} [active=true] The filter is active and affecting the result
|
||||
* @cfg {boolean} [selected] The item is selected
|
||||
* @cfg {boolean} [inverted] The item is inverted, meaning the search is excluding
|
||||
* this parameter.
|
||||
* @cfg {string} [namePrefix='item_'] A prefix to add to the param name to act as a unique
|
||||
* identifier
|
||||
* @cfg {string} [cssClass] The class identifying the results that match this filter
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel = function MwRcfiltersDmItemModel( param, config ) {
|
||||
config = config || {};
|
||||
|
||||
// Mixin constructor
|
||||
OO.EventEmitter.call( this );
|
||||
|
||||
this.param = param;
|
||||
this.namePrefix = config.namePrefix || 'item_';
|
||||
this.name = this.namePrefix + param;
|
||||
|
||||
this.label = config.label || this.name;
|
||||
this.description = config.description;
|
||||
this.selected = !!config.selected;
|
||||
|
||||
this.inverted = !!config.inverted;
|
||||
|
||||
// Highlight
|
||||
this.cssClass = config.cssClass;
|
||||
this.highlightColor = null;
|
||||
this.highlightEnabled = false;
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
OO.initClass( mw.rcfilters.dm.ItemModel );
|
||||
OO.mixinClass( mw.rcfilters.dm.ItemModel, OO.EventEmitter );
|
||||
|
||||
/* Events */
|
||||
|
||||
/**
|
||||
* @event update
|
||||
*
|
||||
* The state of this filter has changed
|
||||
*/
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Return the representation of the state of this item.
|
||||
*
|
||||
* @return {Object} State of the object
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getState = function () {
|
||||
return {
|
||||
selected: this.isSelected(),
|
||||
inverted: this.isInverted()
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of this filter
|
||||
*
|
||||
* @return {string} Filter name
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getName = function () {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the param name or value of this filter
|
||||
*
|
||||
* @return {string} Filter param name
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getParamName = function () {
|
||||
return this.param;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the message representing the state of this model.
|
||||
*
|
||||
* @return {string} State message
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getStateMessage = function () {
|
||||
// Display description
|
||||
return this.getDescription();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the label of this filter
|
||||
*
|
||||
* @return {string} Filter label
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getLabel = function () {
|
||||
return this.label;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the description of this filter
|
||||
*
|
||||
* @return {string} Filter description
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getDescription = function () {
|
||||
return this.description;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the default value of this filter
|
||||
*
|
||||
* @return {boolean} Filter default
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getDefault = function () {
|
||||
return this.default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected state of this filter
|
||||
*
|
||||
* @return {boolean} Filter is selected
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.isSelected = function () {
|
||||
return this.selected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the selected state of the item
|
||||
*
|
||||
* @param {boolean} [isSelected] Filter is selected
|
||||
* @fires update
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.toggleSelected = function ( isSelected ) {
|
||||
isSelected = isSelected === undefined ? !this.selected : isSelected;
|
||||
|
||||
if ( this.selected !== isSelected ) {
|
||||
this.selected = isSelected;
|
||||
this.emit( 'update' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the inverted state of this item
|
||||
*
|
||||
* @return {boolean} Item is inverted
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.isInverted = function () {
|
||||
return this.inverted;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the inverted state of the item
|
||||
*
|
||||
* @param {boolean} [isInverted] Item is inverted
|
||||
* @fires update
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.toggleInverted = function ( isInverted ) {
|
||||
isInverted = isInverted === undefined ? !this.inverted : isInverted;
|
||||
|
||||
if ( this.inverted !== isInverted ) {
|
||||
this.inverted = isInverted;
|
||||
this.emit( 'update' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the highlight color
|
||||
*
|
||||
* @param {string|null} highlightColor
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.setHighlightColor = function ( highlightColor ) {
|
||||
if ( this.highlightColor !== highlightColor ) {
|
||||
this.highlightColor = highlightColor;
|
||||
this.emit( 'update' );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the highlight color
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.clearHighlightColor = function () {
|
||||
this.setHighlightColor( null );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the highlight color, or null if none is configured
|
||||
*
|
||||
* @return {string|null}
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getHighlightColor = function () {
|
||||
return this.highlightColor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the CSS class that matches changes that fit this filter
|
||||
* or null if none is configured
|
||||
*
|
||||
* @return {string|null}
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.getCssClass = function () {
|
||||
return this.cssClass;
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle the highlight feature on and off for this filter.
|
||||
* It only works if highlight is supported for this filter.
|
||||
*
|
||||
* @param {boolean} enable Highlight should be enabled
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.toggleHighlight = function ( enable ) {
|
||||
enable = enable === undefined ? !this.highlightEnabled : enable;
|
||||
|
||||
if ( !this.isHighlightSupported() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( enable === this.highlightEnabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.highlightEnabled = enable;
|
||||
this.emit( 'update' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the highlight feature is currently enabled for this filter
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.isHighlightEnabled = function () {
|
||||
return !!this.highlightEnabled;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the highlight feature is supported for this filter
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.isHighlightSupported = function () {
|
||||
return !!this.getCssClass();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the filter is currently highlighted
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
mw.rcfilters.dm.ItemModel.prototype.isHighlighted = function () {
|
||||
return this.isHighlightEnabled() && !!this.getHighlightColor();
|
||||
};
|
||||
}( mediaWiki ) );
|
||||
|
|
@ -1,59 +1,11 @@
|
|||
@import 'mediawiki.mixins';
|
||||
|
||||
.mw-rcfilters-ui-filterMenuOptionWidget {
|
||||
padding: 0 0.5em;
|
||||
.box-sizing( border-box );
|
||||
|
||||
&:not( :last-child ) {
|
||||
border-bottom: solid 1px #eaecf0; // Base 80 AAA
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
.mw-rcfilters-ui-table {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
&-muted {
|
||||
&.oo-ui-flaggedElement-muted {
|
||||
background-color: #f8f9fa; // Base90 AAA
|
||||
.mw-rcfilters-ui-filterMenuOptionWidget-label-title,
|
||||
.mw-rcfilters-ui-filterMenuOptionWidget-label-desc {
|
||||
.mw-rcfilters-ui-itemMenuOptionWidget-label-title,
|
||||
.mw-rcfilters-ui-itemMenuOptionWidget-label-desc {
|
||||
color: #54595d; // Base20 AAA
|
||||
}
|
||||
}
|
||||
|
||||
&.oo-ui-optionWidget-selected {
|
||||
background-color: #eaf3ff; // Accent90 AAA
|
||||
}
|
||||
|
||||
&-label {
|
||||
&-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.15em;
|
||||
color: #222;
|
||||
}
|
||||
&-desc {
|
||||
color: #464a4f;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&-filterCheckbox {
|
||||
.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
|
||||
// Override margin-top and -bottom rules from FieldLayout
|
||||
margin: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.oo-ui-checkboxInputWidget {
|
||||
// Workaround for IE11 rendering issues. T162098
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&-highlightButton {
|
||||
width: 4em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@import 'mediawiki.mixins';
|
||||
|
||||
.mw-rcfilters-ui-filterFloatingMenuSelectWidget {
|
||||
.mw-rcfilters-ui-floatingMenuSelectWidget {
|
||||
z-index: auto;
|
||||
max-width: 650px;
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
@import 'mediawiki.mixins';
|
||||
|
||||
.mw-rcfilters-ui-itemMenuOptionWidget {
|
||||
padding: 0 0.5em;
|
||||
.box-sizing( border-box );
|
||||
|
||||
&:not( :last-child ) {
|
||||
border-bottom: solid 1px #eaecf0; // Base 80 AAA
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
.mw-rcfilters-ui-table {
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
&.oo-ui-optionWidget-selected {
|
||||
background-color: #eaf3ff; // Accent90 AAA
|
||||
}
|
||||
|
||||
&-label {
|
||||
&-title {
|
||||
font-weight: bold;
|
||||
font-size: 1.15em;
|
||||
color: #222;
|
||||
}
|
||||
&-desc {
|
||||
color: #464a4f;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
&-itemCheckbox {
|
||||
.oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
|
||||
// Override margin-top and -bottom rules from FieldLayout
|
||||
margin: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.oo-ui-checkboxInputWidget {
|
||||
// Workaround for IE11 rendering issues. T162098
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&-highlightButton {
|
||||
width: 4em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
@import 'mw.rcfilters.mixins';
|
||||
|
||||
.mw-rcfilters-ui-filterTagItemWidget {
|
||||
.mw-rcfilters-ui-tagItemWidget {
|
||||
// Background and color of the capsule widget need a bit
|
||||
// more specificity to override ooui internals
|
||||
&.oo-ui-flaggedElement-muted.oo-ui-tagItemWidget.oo-ui-widget-enabled {
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
/**
|
||||
* A widget representing a single toggle filter
|
||||
*
|
||||
* @extends OO.ui.MenuOptionWidget
|
||||
* @extends mw.rcfilters.ui.ItemMenuOptionWidget
|
||||
*
|
||||
* @constructor
|
||||
* @param {mw.rcfilters.Controller} controller RCFilters controller
|
||||
|
|
@ -10,88 +10,23 @@
|
|||
* @param {Object} config Configuration object
|
||||
*/
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget = function MwRcfiltersUiFilterMenuOptionWidget( controller, model, config ) {
|
||||
var layout,
|
||||
$label = $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterMenuOptionWidget-label' );
|
||||
|
||||
config = config || {};
|
||||
|
||||
this.controller = controller;
|
||||
this.model = model;
|
||||
|
||||
// Parent
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.parent.call( this, $.extend( {
|
||||
// Override the 'check' icon that OOUI defines
|
||||
icon: '',
|
||||
data: this.model.getName(),
|
||||
label: this.model.getLabel()
|
||||
}, config ) );
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.parent.call( this, controller, model, config );
|
||||
|
||||
this.checkboxWidget = new mw.rcfilters.ui.CheckboxInputWidget( {
|
||||
value: this.model.getName(),
|
||||
selected: this.model.isSelected()
|
||||
} );
|
||||
|
||||
$label.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterMenuOptionWidget-label-title' )
|
||||
.append( this.$label )
|
||||
);
|
||||
if ( this.model.getDescription() ) {
|
||||
$label.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterMenuOptionWidget-label-desc' )
|
||||
.text( this.model.getDescription() )
|
||||
);
|
||||
}
|
||||
|
||||
this.highlightButton = new mw.rcfilters.ui.FilterItemHighlightButton(
|
||||
this.controller,
|
||||
this.model,
|
||||
{
|
||||
$overlay: config.$overlay || this.$element,
|
||||
title: mw.msg( 'rcfilters-highlightmenu-help' )
|
||||
}
|
||||
);
|
||||
this.highlightButton.toggle( this.model.isHighlightEnabled() );
|
||||
|
||||
layout = new OO.ui.FieldLayout( this.checkboxWidget, {
|
||||
label: $label,
|
||||
align: 'inline'
|
||||
} );
|
||||
// Event
|
||||
this.model.connect( this, { update: 'onModelUpdate' } );
|
||||
this.model.getGroupModel().connect( this, { update: 'onGroupModelUpdate' } );
|
||||
// HACK: Prevent defaults on 'click' for the label so it
|
||||
// doesn't steal the focus away from the input. This means
|
||||
// we can continue arrow-movement after we click the label
|
||||
// and is consistent with the checkbox *itself* also preventing
|
||||
// defaults on 'click' as well.
|
||||
layout.$label.on( 'click', false );
|
||||
|
||||
this.$element
|
||||
.addClass( 'mw-rcfilters-ui-filterMenuOptionWidget' )
|
||||
.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-table' )
|
||||
.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-row' )
|
||||
.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-cell mw-rcfilters-ui-filterMenuOptionWidget-filterCheckbox' )
|
||||
.append( layout.$element ),
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-cell mw-rcfilters-ui-filterMenuOptionWidget-highlightButton' )
|
||||
.append( this.highlightButton.$element )
|
||||
)
|
||||
)
|
||||
);
|
||||
.addClass( 'mw-rcfilters-ui-filterMenuOptionWidget' );
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
OO.inheritClass( mw.rcfilters.ui.FilterMenuOptionWidget, OO.ui.MenuOptionWidget );
|
||||
OO.inheritClass( mw.rcfilters.ui.FilterMenuOptionWidget, mw.rcfilters.ui.ItemMenuOptionWidget );
|
||||
|
||||
/* Static properties */
|
||||
|
||||
|
|
@ -101,10 +36,11 @@
|
|||
/* Methods */
|
||||
|
||||
/**
|
||||
* Respond to item model update event
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.prototype.onModelUpdate = function () {
|
||||
this.checkboxWidget.setSelected( this.model.isSelected() );
|
||||
// Parent
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.parent.prototype.onModelUpdate.call( this );
|
||||
|
||||
this.setCurrentMuteState();
|
||||
};
|
||||
|
|
@ -117,36 +53,21 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* Set the current mute state for this item
|
||||
* Set the current muted view of the widget based on its state
|
||||
*/
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.prototype.setCurrentMuteState = function () {
|
||||
this.$element.toggleClass(
|
||||
'mw-rcfilters-ui-filterMenuOptionWidget-muted',
|
||||
this.model.isConflicted() ||
|
||||
(
|
||||
// Item is also muted when any of the items in its group is active
|
||||
this.model.getGroupModel().isActive() &&
|
||||
// But it isn't selected
|
||||
!this.model.isSelected() &&
|
||||
// And also not included
|
||||
!this.model.isIncluded()
|
||||
this.setFlags( {
|
||||
muted: (
|
||||
this.model.isConflicted() ||
|
||||
(
|
||||
// Item is also muted when any of the items in its group is active
|
||||
this.model.getGroupModel().isActive() &&
|
||||
// But it isn't selected
|
||||
!this.model.isSelected() &&
|
||||
// And also not included
|
||||
!this.model.isIncluded()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
this.highlightButton.toggle( this.model.isHighlightEnabled() );
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of this filter
|
||||
*
|
||||
* @return {string} Filter name
|
||||
*/
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.prototype.getName = function () {
|
||||
return this.model.getName();
|
||||
};
|
||||
|
||||
mw.rcfilters.ui.FilterMenuOptionWidget.prototype.getModel = function () {
|
||||
return this.model;
|
||||
};
|
||||
|
||||
}( mediaWiki ) );
|
||||
|
|
|
|||
|
|
@ -1,99 +1,32 @@
|
|||
( function ( mw, $ ) {
|
||||
( function ( mw ) {
|
||||
/**
|
||||
* Extend OOUI's FilterTagItemWidget to also display a popup on hover.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.FilterTagItemWidget
|
||||
* @mixins OO.ui.mixin.PopupElement
|
||||
* @extends mw.rcfilters.ui.TagItemWidget
|
||||
*
|
||||
* @constructor
|
||||
* @param {mw.rcfilters.Controller} controller
|
||||
* @param {mw.rcfilters.dm.FilterItem} model Item model
|
||||
* @param {Object} config Configuration object
|
||||
* @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget = function MwRcfiltersUiFilterTagItemWidget( controller, model, config ) {
|
||||
// Configuration initialization
|
||||
config = config || {};
|
||||
|
||||
this.controller = controller;
|
||||
this.model = model;
|
||||
this.selected = false;
|
||||
mw.rcfilters.ui.FilterTagItemWidget.parent.call( this, controller, model, config );
|
||||
|
||||
mw.rcfilters.ui.FilterTagItemWidget.parent.call( this, $.extend( {
|
||||
data: this.model.getName(),
|
||||
label: this.model.getLabel()
|
||||
}, config ) );
|
||||
|
||||
this.$overlay = config.$overlay || this.$element;
|
||||
this.popupLabel = new OO.ui.LabelWidget();
|
||||
|
||||
// Mixin constructors
|
||||
OO.ui.mixin.PopupElement.call( this, $.extend( {
|
||||
popup: {
|
||||
padded: false,
|
||||
align: 'center',
|
||||
position: 'above',
|
||||
$content: $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterTagItemWidget-popup-content' )
|
||||
.append( this.popupLabel.$element ),
|
||||
$floatableContainer: this.$element,
|
||||
classes: [ 'mw-rcfilters-ui-filterTagItemWidget-popup' ]
|
||||
}
|
||||
}, config ) );
|
||||
|
||||
this.positioned = false;
|
||||
this.popupTimeoutShow = null;
|
||||
this.popupTimeoutHide = null;
|
||||
|
||||
this.$highlight = $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterTagItemWidget-highlight' );
|
||||
|
||||
// Events
|
||||
this.model.connect( this, { update: 'onModelUpdate' } );
|
||||
|
||||
// Initialization
|
||||
this.$overlay.append( this.popup.$element );
|
||||
this.$element
|
||||
.addClass( 'mw-rcfilters-ui-filterTagItemWidget' )
|
||||
.prepend( this.$highlight )
|
||||
.attr( 'aria-haspopup', 'true' )
|
||||
.on( 'mouseenter', this.onMouseEnter.bind( this ) )
|
||||
.on( 'mouseleave', this.onMouseLeave.bind( this ) );
|
||||
|
||||
this.setCurrentMuteState();
|
||||
this.setHighlightColor();
|
||||
.addClass( 'mw-rcfilters-ui-filterTagItemWidget' );
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
OO.inheritClass( mw.rcfilters.ui.FilterTagItemWidget, OO.ui.TagItemWidget );
|
||||
OO.mixinClass( mw.rcfilters.ui.FilterTagItemWidget, OO.ui.mixin.PopupElement );
|
||||
OO.inheritClass( mw.rcfilters.ui.FilterTagItemWidget, mw.rcfilters.ui.TagItemWidget );
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Respond to model update event
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.onModelUpdate = function () {
|
||||
this.setCurrentMuteState();
|
||||
|
||||
this.setHighlightColor();
|
||||
};
|
||||
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.setHighlightColor = function () {
|
||||
var selectedColor = this.model.isHighlightEnabled() ? this.model.getHighlightColor() : null;
|
||||
|
||||
this.$highlight
|
||||
.attr( 'data-color', selectedColor )
|
||||
.toggleClass(
|
||||
'mw-rcfilters-ui-filterTagItemWidget-highlight-highlighted',
|
||||
!!selectedColor
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the current mute state for this item
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.setCurrentMuteState = function () {
|
||||
this.setFlags( {
|
||||
|
|
@ -105,88 +38,4 @@
|
|||
invalid: this.model.isSelected() && this.model.isConflicted()
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to mouse enter event
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.onMouseEnter = function () {
|
||||
var labelText = this.model.getStateMessage();
|
||||
|
||||
if ( labelText ) {
|
||||
this.popupLabel.setLabel( labelText );
|
||||
|
||||
if ( !this.positioned ) {
|
||||
// Recalculate anchor position to be center of the capsule item
|
||||
this.popup.$anchor.css( 'margin-left', ( this.$element.width() / 2 ) );
|
||||
this.positioned = true;
|
||||
}
|
||||
|
||||
// Set timeout for the popup to show
|
||||
this.popupTimeoutShow = setTimeout( function () {
|
||||
this.popup.toggle( true );
|
||||
}.bind( this ), 500 );
|
||||
|
||||
// Cancel the hide timeout
|
||||
clearTimeout( this.popupTimeoutHide );
|
||||
this.popupTimeoutHide = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to mouse leave event
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.onMouseLeave = function () {
|
||||
this.popupTimeoutHide = setTimeout( function () {
|
||||
this.popup.toggle( false );
|
||||
}.bind( this ), 250 );
|
||||
|
||||
// Clear the show timeout
|
||||
clearTimeout( this.popupTimeoutShow );
|
||||
this.popupTimeoutShow = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set selected state on this widget
|
||||
*
|
||||
* @param {boolean} [isSelected] Widget is selected
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.toggleSelected = function ( isSelected ) {
|
||||
isSelected = isSelected !== undefined ? isSelected : !this.selected;
|
||||
|
||||
if ( this.selected !== isSelected ) {
|
||||
this.selected = isSelected;
|
||||
|
||||
this.$element.toggleClass( 'mw-rcfilters-ui-filterTagItemWidget-selected', this.selected );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected state of this widget
|
||||
*
|
||||
* @return {boolean} Tag is selected
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.isSelected = function () {
|
||||
return this.selected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get item name
|
||||
*
|
||||
* @return {string} Filter name
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.getName = function () {
|
||||
return this.model.getName();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove and destroy external elements of this widget
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagItemWidget.prototype.destroy = function () {
|
||||
// Destroy the popup
|
||||
this.popup.$element.detach();
|
||||
|
||||
// Disconnect events
|
||||
this.model.disconnect( this );
|
||||
this.closeButton.disconnect( this );
|
||||
};
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@
|
|||
* @inheritdoc
|
||||
*/
|
||||
mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.createMenuWidget = function ( menuConfig ) {
|
||||
return new mw.rcfilters.ui.FilterFloatingMenuSelectWidget(
|
||||
return new mw.rcfilters.ui.FloatingMenuSelectWidget(
|
||||
this.controller,
|
||||
this.model,
|
||||
$.extend( {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
|
||||
* @cfg {jQuery} [$footer] An optional footer for the menu
|
||||
*/
|
||||
mw.rcfilters.ui.FilterFloatingMenuSelectWidget = function MwRcfiltersUiFilterFloatingMenuSelectWidget( controller, model, config ) {
|
||||
mw.rcfilters.ui.FloatingMenuSelectWidget = function MwRcfiltersUiFloatingMenuSelectWidget( controller, model, config ) {
|
||||
var header;
|
||||
|
||||
config = config || {};
|
||||
|
|
@ -23,16 +23,16 @@
|
|||
this.$overlay = config.$overlay || this.$element;
|
||||
this.$footer = config.$footer;
|
||||
this.$body = $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterFloatingMenuSelectWidget-body' );
|
||||
.addClass( 'mw-rcfilters-ui-floatingMenuSelectWidget-body' );
|
||||
|
||||
// Parent
|
||||
mw.rcfilters.ui.FilterFloatingMenuSelectWidget.parent.call( this, $.extend( {
|
||||
mw.rcfilters.ui.FloatingMenuSelectWidget.parent.call( this, $.extend( {
|
||||
$autoCloseIgnore: this.$overlay,
|
||||
width: 650
|
||||
}, config ) );
|
||||
this.setGroupElement(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-filterFloatingMenuSelectWidget-group' )
|
||||
.addClass( 'mw-rcfilters-ui-floatingMenuSelectWidget-group' )
|
||||
);
|
||||
this.setClippableElement( this.$body );
|
||||
this.setClippableContainer( this.$element );
|
||||
|
|
@ -47,11 +47,11 @@
|
|||
|
||||
this.noResults = new OO.ui.LabelWidget( {
|
||||
label: mw.msg( 'rcfilters-filterlist-noresults' ),
|
||||
classes: [ 'mw-rcfilters-ui-filterFloatingMenuSelectWidget-noresults' ]
|
||||
classes: [ 'mw-rcfilters-ui-floatingMenuSelectWidget-noresults' ]
|
||||
} );
|
||||
|
||||
this.$element
|
||||
.addClass( 'mw-rcfilters-ui-filterFloatingMenuSelectWidget' )
|
||||
.addClass( 'mw-rcfilters-ui-floatingMenuSelectWidget' )
|
||||
.append(
|
||||
this.$body
|
||||
.append( header.$element, this.$group, this.noResults.$element )
|
||||
|
|
@ -60,14 +60,14 @@
|
|||
if ( this.$footer ) {
|
||||
this.$element.append(
|
||||
this.$footer
|
||||
.addClass( 'mw-rcfilters-ui-filterFloatingMenuSelectWidget-footer' )
|
||||
.addClass( 'mw-rcfilters-ui-floatingMenuSelectWidget-footer' )
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/* Initialize */
|
||||
|
||||
OO.inheritClass( mw.rcfilters.ui.FilterFloatingMenuSelectWidget, OO.ui.FloatingMenuSelectWidget );
|
||||
OO.inheritClass( mw.rcfilters.ui.FloatingMenuSelectWidget, OO.ui.FloatingMenuSelectWidget );
|
||||
|
||||
/* Events */
|
||||
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
* @fires itemVisibilityChange
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.rcfilters.ui.FilterFloatingMenuSelectWidget.prototype.updateItemVisibility = function () {
|
||||
mw.rcfilters.ui.FloatingMenuSelectWidget.prototype.updateItemVisibility = function () {
|
||||
var i,
|
||||
itemWasHighlighted = false,
|
||||
inputVal = this.$input.val(),
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
// call it unless the input actually changed
|
||||
if ( this.inputValue !== inputVal ) {
|
||||
// Parent method
|
||||
mw.rcfilters.ui.FilterFloatingMenuSelectWidget.parent.prototype.updateItemVisibility.call( this );
|
||||
mw.rcfilters.ui.FloatingMenuSelectWidget.parent.prototype.updateItemVisibility.call( this );
|
||||
|
||||
if ( inputVal !== '' ) {
|
||||
// Highlight the first item in the list
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
mw.rcfilters.ui.FilterFloatingMenuSelectWidget.prototype.getItemMatcher = function ( s ) {
|
||||
mw.rcfilters.ui.FloatingMenuSelectWidget.prototype.getItemMatcher = function ( s ) {
|
||||
var results = this.model.findMatches( s, true );
|
||||
|
||||
return function ( item ) {
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
/**
|
||||
* Scroll to the top of the menu
|
||||
*/
|
||||
mw.rcfilters.ui.FilterFloatingMenuSelectWidget.prototype.scrollToTop = function () {
|
||||
mw.rcfilters.ui.FloatingMenuSelectWidget.prototype.scrollToTop = function () {
|
||||
this.$body.scrollTop( 0 );
|
||||
};
|
||||
}( mediaWiki ) );
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
( function ( mw ) {
|
||||
/**
|
||||
* A widget representing a base toggle item
|
||||
*
|
||||
* @extends OO.ui.MenuOptionWidget
|
||||
*
|
||||
* @constructor
|
||||
* @param {mw.rcfilters.Controller} controller RCFilters controller
|
||||
* @param {mw.rcfilters.dm.ItemModel} model Item model
|
||||
* @param {Object} config Configuration object
|
||||
*/
|
||||
mw.rcfilters.ui.ItemMenuOptionWidget = function MwRcfiltersUiItemMenuOptionWidget( controller, model, config ) {
|
||||
var layout,
|
||||
$label = $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-label' );
|
||||
|
||||
config = config || {};
|
||||
|
||||
this.controller = controller;
|
||||
this.model = model;
|
||||
|
||||
// Parent
|
||||
mw.rcfilters.ui.ItemMenuOptionWidget.parent.call( this, $.extend( {
|
||||
// Override the 'check' icon that OOUI defines
|
||||
icon: '',
|
||||
data: this.model.getName(),
|
||||
label: this.model.getLabel()
|
||||
}, config ) );
|
||||
|
||||
this.checkboxWidget = new mw.rcfilters.ui.CheckboxInputWidget( {
|
||||
value: this.model.getName(),
|
||||
selected: this.model.isSelected()
|
||||
} );
|
||||
|
||||
$label.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-label-title' )
|
||||
.append( this.$label )
|
||||
);
|
||||
if ( this.model.getDescription() ) {
|
||||
$label.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-itemMenuOptionWidget-label-desc' )
|
||||
.text( this.model.getDescription() )
|
||||
);
|
||||
}
|
||||
|
||||
this.highlightButton = new mw.rcfilters.ui.FilterItemHighlightButton(
|
||||
this.controller,
|
||||
this.model,
|
||||
{
|
||||
$overlay: config.$overlay || this.$element,
|
||||
title: mw.msg( 'rcfilters-highlightmenu-help' )
|
||||
}
|
||||
);
|
||||
this.highlightButton.toggle( this.model.isHighlightEnabled() );
|
||||
|
||||
layout = new OO.ui.FieldLayout( this.checkboxWidget, {
|
||||
label: $label,
|
||||
align: 'inline'
|
||||
} );
|
||||
|
||||
// Events
|
||||
this.model.connect( this, { update: 'onModelUpdate' } );
|
||||
// HACK: Prevent defaults on 'click' for the label so it
|
||||
// doesn't steal the focus away from the input. This means
|
||||
// we can continue arrow-movement after we click the label
|
||||
// and is consistent with the checkbox *itself* also preventing
|
||||
// defaults on 'click' as well.
|
||||
layout.$label.on( 'click', false );
|
||||
|
||||
this.$element
|
||||
.addClass( 'mw-rcfilters-ui-itemMenuOptionWidget' )
|
||||
.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-table' )
|
||||
.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-row' )
|
||||
.append(
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-cell mw-rcfilters-ui-itemMenuOptionWidget-itemCheckbox' )
|
||||
.append( layout.$element ),
|
||||
$( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-cell mw-rcfilters-ui-itemMenuOptionWidget-highlightButton' )
|
||||
.append( this.highlightButton.$element )
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
OO.inheritClass( mw.rcfilters.ui.ItemMenuOptionWidget, OO.ui.MenuOptionWidget );
|
||||
|
||||
/* Static properties */
|
||||
|
||||
// We do our own scrolling to top
|
||||
mw.rcfilters.ui.ItemMenuOptionWidget.static.scrollIntoViewOnSelect = false;
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Respond to item model update event
|
||||
*/
|
||||
mw.rcfilters.ui.ItemMenuOptionWidget.prototype.onModelUpdate = function () {
|
||||
this.checkboxWidget.setSelected( this.model.isSelected() );
|
||||
|
||||
this.highlightButton.toggle( this.model.isHighlightEnabled() );
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the name of this filter
|
||||
*
|
||||
* @return {string} Filter name
|
||||
*/
|
||||
mw.rcfilters.ui.ItemMenuOptionWidget.prototype.getName = function () {
|
||||
return this.model.getName();
|
||||
};
|
||||
|
||||
mw.rcfilters.ui.ItemMenuOptionWidget.prototype.getModel = function () {
|
||||
return this.model;
|
||||
};
|
||||
|
||||
}( mediaWiki ) );
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
( function ( mw, $ ) {
|
||||
/**
|
||||
* Extend OOUI's TagItemWidget to also display a popup on hover.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.TagItemWidget
|
||||
* @mixins OO.ui.mixin.PopupElement
|
||||
*
|
||||
* @constructor
|
||||
* @param {mw.rcfilters.Controller} controller
|
||||
* @param {mw.rcfilters.dm.FilterItem} model Item model
|
||||
* @param {Object} config Configuration object
|
||||
* @cfg {jQuery} [$overlay] A jQuery object serving as overlay for popups
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget = function MwRcfiltersUiTagItemWidget( controller, model, config ) {
|
||||
// Configuration initialization
|
||||
config = config || {};
|
||||
|
||||
this.controller = controller;
|
||||
this.model = model;
|
||||
this.selected = false;
|
||||
|
||||
mw.rcfilters.ui.TagItemWidget.parent.call( this, $.extend( {
|
||||
data: this.model.getName(),
|
||||
label: this.model.getLabel()
|
||||
}, config ) );
|
||||
|
||||
this.$overlay = config.$overlay || this.$element;
|
||||
this.popupLabel = new OO.ui.LabelWidget();
|
||||
|
||||
// Mixin constructors
|
||||
OO.ui.mixin.PopupElement.call( this, $.extend( {
|
||||
popup: {
|
||||
padded: false,
|
||||
align: 'center',
|
||||
position: 'above',
|
||||
$content: $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-tagItemWidget-popup-content' )
|
||||
.append( this.popupLabel.$element ),
|
||||
$floatableContainer: this.$element,
|
||||
classes: [ 'mw-rcfilters-ui-tagItemWidget-popup' ]
|
||||
}
|
||||
}, config ) );
|
||||
|
||||
this.positioned = false;
|
||||
this.popupTimeoutShow = null;
|
||||
this.popupTimeoutHide = null;
|
||||
|
||||
this.$highlight = $( '<div>' )
|
||||
.addClass( 'mw-rcfilters-ui-tagItemWidget-highlight' );
|
||||
|
||||
// Events
|
||||
this.model.connect( this, { update: 'onModelUpdate' } );
|
||||
|
||||
// Initialization
|
||||
this.$overlay.append( this.popup.$element );
|
||||
this.$element
|
||||
.addClass( 'mw-rcfilters-ui-tagItemWidget' )
|
||||
.prepend( this.$highlight )
|
||||
.attr( 'aria-haspopup', 'true' )
|
||||
.on( 'mouseenter', this.onMouseEnter.bind( this ) )
|
||||
.on( 'mouseleave', this.onMouseLeave.bind( this ) );
|
||||
|
||||
this.setCurrentMuteState();
|
||||
this.setHighlightColor();
|
||||
};
|
||||
|
||||
/* Initialization */
|
||||
|
||||
OO.inheritClass( mw.rcfilters.ui.TagItemWidget, OO.ui.TagItemWidget );
|
||||
OO.mixinClass( mw.rcfilters.ui.TagItemWidget, OO.ui.mixin.PopupElement );
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Respond to model update event
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.onModelUpdate = function () {
|
||||
this.setCurrentMuteState();
|
||||
|
||||
this.setHighlightColor();
|
||||
};
|
||||
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.setHighlightColor = function () {
|
||||
var selectedColor = this.model.isHighlightEnabled() ? this.model.getHighlightColor() : null;
|
||||
|
||||
this.$highlight
|
||||
.attr( 'data-color', selectedColor )
|
||||
.toggleClass(
|
||||
'mw-rcfilters-ui-tagItemWidget-highlight-highlighted',
|
||||
!!selectedColor
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the current mute state for this item
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.setCurrentMuteState = function () {};
|
||||
|
||||
/**
|
||||
* Respond to mouse enter event
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.onMouseEnter = function () {
|
||||
var labelText = this.model.getStateMessage();
|
||||
|
||||
if ( labelText ) {
|
||||
this.popupLabel.setLabel( labelText );
|
||||
|
||||
if ( !this.positioned ) {
|
||||
// Recalculate anchor position to be center of the capsule item
|
||||
this.popup.$anchor.css( 'margin-left', ( this.$element.width() / 2 ) );
|
||||
this.positioned = true;
|
||||
}
|
||||
|
||||
// Set timeout for the popup to show
|
||||
this.popupTimeoutShow = setTimeout( function () {
|
||||
this.popup.toggle( true );
|
||||
}.bind( this ), 500 );
|
||||
|
||||
// Cancel the hide timeout
|
||||
clearTimeout( this.popupTimeoutHide );
|
||||
this.popupTimeoutHide = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to mouse leave event
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.onMouseLeave = function () {
|
||||
this.popupTimeoutHide = setTimeout( function () {
|
||||
this.popup.toggle( false );
|
||||
}.bind( this ), 250 );
|
||||
|
||||
// Clear the show timeout
|
||||
clearTimeout( this.popupTimeoutShow );
|
||||
this.popupTimeoutShow = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set selected state on this widget
|
||||
*
|
||||
* @param {boolean} [isSelected] Widget is selected
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.toggleSelected = function ( isSelected ) {
|
||||
isSelected = isSelected !== undefined ? isSelected : !this.selected;
|
||||
|
||||
if ( this.selected !== isSelected ) {
|
||||
this.selected = isSelected;
|
||||
|
||||
this.$element.toggleClass( 'mw-rcfilters-ui-tagItemWidget-selected', this.selected );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selected state of this widget
|
||||
*
|
||||
* @return {boolean} Tag is selected
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.isSelected = function () {
|
||||
return this.selected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get item name
|
||||
*
|
||||
* @return {string} Filter name
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.getName = function () {
|
||||
return this.model.getName();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove and destroy external elements of this widget
|
||||
*/
|
||||
mw.rcfilters.ui.TagItemWidget.prototype.destroy = function () {
|
||||
// Destroy the popup
|
||||
this.popup.$element.detach();
|
||||
|
||||
// Disconnect events
|
||||
this.model.disconnect( this );
|
||||
this.closeButton.disconnect( this );
|
||||
};
|
||||
}( mediaWiki, jQuery ) );
|
||||
Loading…
Reference in a new issue