From 2d3367238ee76a63bf6bba4d61d9ee8c5fb33216 Mon Sep 17 00:00:00 2001 From: Volker E Date: Tue, 11 Apr 2017 16:41:59 -0700 Subject: [PATCH] Update OOjs UI to v0.21.0 Release notes: https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/History.md;v0.21.0 Change-Id: I94d5b7a89551e7d34d53223872ad3005f0ac4a04 --- composer.json | 2 +- resources/lib/oojs-ui/oojs-ui-apex.js | 4 +- resources/lib/oojs-ui/oojs-ui-core-apex.css | 53 +- .../lib/oojs-ui/oojs-ui-core-mediawiki.css | 229 +-- resources/lib/oojs-ui/oojs-ui-core.js | 83 +- resources/lib/oojs-ui/oojs-ui-mediawiki.js | 4 +- .../lib/oojs-ui/oojs-ui-toolbars-apex.css | 34 +- .../oojs-ui/oojs-ui-toolbars-mediawiki.css | 50 +- resources/lib/oojs-ui/oojs-ui-toolbars.js | 4 +- .../lib/oojs-ui/oojs-ui-widgets-apex.css | 198 ++- .../lib/oojs-ui/oojs-ui-widgets-mediawiki.css | 276 +++- resources/lib/oojs-ui/oojs-ui-widgets.js | 1425 ++++++++++++++++- .../lib/oojs-ui/oojs-ui-windows-apex.css | 6 +- .../lib/oojs-ui/oojs-ui-windows-mediawiki.css | 4 +- resources/lib/oojs-ui/oojs-ui-windows.js | 113 +- .../themes/apex/images/icons/articles-ltr.svg | 2 +- .../themes/apex/images/icons/articles-rtl.svg | 2 +- .../images/icons/articles-ltr-invert.svg | 2 +- .../images/icons/articles-ltr-progressive.svg | 2 +- .../mediawiki/images/icons/articles-ltr.svg | 2 +- .../images/icons/articles-rtl-invert.svg | 2 +- .../images/icons/articles-rtl-progressive.svg | 2 +- .../mediawiki/images/icons/articles-rtl.svg | 2 +- .../images/indicators/arrow-down-invert.png | Bin 117 -> 174 bytes .../images/indicators/arrow-down-invert.svg | 4 +- .../images/indicators/arrow-down.png | Bin 118 -> 181 bytes .../images/indicators/arrow-down.svg | 4 +- .../images/indicators/arrow-ltr-invert.png | Bin 134 -> 163 bytes .../images/indicators/arrow-ltr-invert.svg | 4 +- .../mediawiki/images/indicators/arrow-ltr.png | Bin 127 -> 158 bytes .../mediawiki/images/indicators/arrow-ltr.svg | 4 +- .../images/indicators/arrow-rtl-invert.png | Bin 132 -> 160 bytes .../images/indicators/arrow-rtl-invert.svg | 4 +- .../mediawiki/images/indicators/arrow-rtl.png | Bin 126 -> 163 bytes .../mediawiki/images/indicators/arrow-rtl.svg | 4 +- .../images/indicators/arrow-up-invert.png | Bin 121 -> 195 bytes .../images/indicators/arrow-up-invert.svg | 4 +- .../mediawiki/images/indicators/arrow-up.png | Bin 116 -> 181 bytes .../mediawiki/images/indicators/arrow-up.svg | 4 +- .../oojs-ui/themes/mediawiki/indicators.json | 12 - 40 files changed, 2139 insertions(+), 406 deletions(-) diff --git a/composer.json b/composer.json index 81abe4025e..44a5eb415e 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "ext-xml": "*", "liuggio/statsd-php-client": "1.0.18", "mediawiki/at-ease": "1.1.0", - "oojs/oojs-ui": "0.20.2", + "oojs/oojs-ui": "0.21.0", "oyejorge/less.php": "1.7.0.14", "php": ">=5.5.9", "psr/log": "1.0.2", diff --git a/resources/lib/oojs-ui/oojs-ui-apex.js b/resources/lib/oojs-ui/oojs-ui-apex.js index f390b561ab..be94720c3b 100644 --- a/resources/lib/oojs-ui/oojs-ui-apex.js +++ b/resources/lib/oojs-ui/oojs-ui-apex.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:37Z + * Date: 2017-04-11T22:51:05Z */ ( function ( OO ) { diff --git a/resources/lib/oojs-ui/oojs-ui-core-apex.css b/resources/lib/oojs-ui/oojs-ui-core-apex.css index 624cc57c76..0313da4fe7 100644 --- a/resources/lib/oojs-ui/oojs-ui-core-apex.css +++ b/resources/lib/oojs-ui/oojs-ui-core-apex.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ .oo-ui-element-hidden { display: none !important; @@ -838,14 +838,6 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { .oo-ui-popupButtonWidget .oo-ui-popupWidget { cursor: auto; } -.oo-ui-popupButtonWidget-frameless-popup.oo-ui-popupWidget-anchored-top .oo-ui-popupWidget-anchor, -.oo-ui-popupButtonWidget-frameless-popup.oo-ui-popupWidget-anchored-bottom .oo-ui-popupWidget-anchor { - margin-left: 0.9375em; -} -.oo-ui-popupButtonWidget-framed-popup.oo-ui-popupWidget-anchored-top .oo-ui-popupWidget-anchor, -.oo-ui-popupButtonWidget-framed-popup.oo-ui-popupWidget-anchored-bottom .oo-ui-popupWidget-anchor { - margin-left: 1.2375em; -} .oo-ui-inputWidget { margin-right: 0.5em; } @@ -977,13 +969,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { cursor: pointer; } .oo-ui-textInputWidget.oo-ui-widget-disabled input, -.oo-ui-textInputWidget.oo-ui-widget-disabled textarea { - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} +.oo-ui-textInputWidget.oo-ui-widget-disabled textarea, .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label { -webkit-touch-callout: none; -webkit-user-select: none; @@ -1032,27 +1018,27 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { } .oo-ui-textInputWidget input::-webkit-input-placeholder, .oo-ui-textInputWidget textarea::-webkit-input-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-textInputWidget input:-ms-input-placeholder, .oo-ui-textInputWidget textarea:-ms-input-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-textInputWidget input::-moz-placeholder, .oo-ui-textInputWidget textarea::-moz-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-textInputWidget input:-moz-placeholder, .oo-ui-textInputWidget textarea:-moz-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-textInputWidget input::placeholder, .oo-ui-textInputWidget textarea::placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-textInputWidget.oo-ui-widget-enabled input:focus, @@ -1063,7 +1049,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { } .oo-ui-textInputWidget.oo-ui-widget-enabled input[readonly], .oo-ui-textInputWidget.oo-ui-widget-enabled textarea[readonly] { - color: #777; + color: #767676; } .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid input, .oo-ui-textInputWidget.oo-ui-widget-enabled.oo-ui-flaggedElement-invalid textarea { @@ -1103,7 +1089,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { .oo-ui-textInputWidget > .oo-ui-labelElement-label { padding: 0.4em; line-height: 1.5em; - color: #888; + color: #767676; } .oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label { margin-right: 2.0875em; @@ -1148,9 +1134,11 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { background-color: #e1f3ff; } .oo-ui-menuSectionOptionWidget { - cursor: default; padding: 0.33em 0.75em; - color: #888; + color: #767676; +} +.oo-ui-menuSectionOptionWidget.oo-ui-widget-enabled { + cursor: default; } .oo-ui-dropdownWidget { display: inline-block; @@ -1161,6 +1149,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { margin-right: 0.5em; } .oo-ui-dropdownWidget-handle { + position: relative; width: 100%; display: block; white-space: nowrap; @@ -1263,7 +1252,11 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { height: 2.5em; padding: 0; } -.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator { +.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorWidget { + display: block; + position: absolute; + top: 0; + height: 100%; pointer-events: none; } .oo-ui-comboBoxInputWidget:last-child { @@ -1282,6 +1275,12 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { visibility: visible; margin: 0.775em; } +.oo-ui-comboBoxInputWidget-php .oo-ui-indicatorWidget { + right: 0; + max-height: 2.375em; + margin: 0; + margin-right: 0.775em; +} .oo-ui-comboBoxInputWidget.oo-ui-widget-disabled .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator { cursor: default; opacity: 0.2; diff --git a/resources/lib/oojs-ui/oojs-ui-core-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-core-mediawiki.css index bff0f50dfa..4cc49c8738 100644 --- a/resources/lib/oojs-ui/oojs-ui-core-mediawiki.css +++ b/resources/lib/oojs-ui/oojs-ui-core-mediawiki.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ .oo-ui-element-hidden { display: none !important; @@ -57,16 +57,41 @@ text-align: center; } .oo-ui-buttonElement > .oo-ui-buttonElement-button { + position: relative; font-weight: bold; text-decoration: none; } .oo-ui-buttonElement > .oo-ui-buttonElement-button:focus { - border-radius: 2px; outline: 0; } -.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { - margin-right: 0.25em; - margin-left: 0.46875em; +.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon, +.oo-ui-buttonElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { + position: absolute; + top: 0; + height: 100%; +} +.oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon { + display: block; +} +.oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button, +.oo-ui-buttonElement > input.oo-ui-buttonElement-button { + line-height: 1; +} +.oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { + line-height: 1.172em; +} +.oo-ui-buttonElement.oo-ui-labelElement.oo-ui-iconElement > .oo-ui-buttonElement-button { + padding-left: 2.03125em; +} +.oo-ui-buttonElement.oo-ui-labelElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button { + padding-right: 2.5em; +} +.oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { + display: block; +} +.oo-ui-buttonElement.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator, +.oo-ui-buttonElement.oo-ui-indicatorElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { + right: 0.9375em; } .oo-ui-buttonElement.oo-ui-widget-enabled > .oo-ui-buttonElement-button { -webkit-transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms; @@ -96,16 +121,24 @@ .oo-ui-buttonElement.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { opacity: 1; } -.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator { - margin-right: 0; +.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button { + border-radius: 1px; +} +.oo-ui-buttonElement-frameless.oo-ui-iconElement > .oo-ui-buttonElement-button { + min-width: 1.875em; + min-height: 1.875em; +} +.oo-ui-buttonElement-frameless.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon { + left: 0; } -.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { - margin-left: 0.25em; - margin-right: 0.25em; +.oo-ui-buttonElement-frameless.oo-ui-labelElement > .oo-ui-buttonElement-button { + margin-left: -1px; + border: 1px solid #fff; + padding: 0.3125em 0; } -.oo-ui-buttonElement-frameless > input.oo-ui-buttonElement-button { - padding-left: 0.25em; - padding-right: 0.25em; +.oo-ui-buttonElement-frameless.oo-ui-indicatorElement > .oo-ui-buttonElement-button { + min-width: 0.9375em; + min-height: 0.9375em; } .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button { color: #222; @@ -114,7 +147,7 @@ color: #444; } .oo-ui-buttonElement-frameless.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus { - box-shadow: inset 0 0 0 1px #36c, 0 0 0 1px #36c; + box-shadow: 0 0 0 2px #36c; } .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > input.oo-ui-buttonElement-button, .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button:active { @@ -172,48 +205,34 @@ .oo-ui-buttonElement-frameless.oo-ui-widget-disabled > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { opacity: 0.51; } -.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button, -.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button { - padding-left: 2.4em; -} .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button { - position: relative; min-height: 2.5em; border-radius: 2px; - padding: 0.625em 1em 0.546875em; + padding: 0.625em 0.9375em 0.546875em; } .oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button { - min-width: 3.125em; + padding-left: 2.03125em; } -.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button, -.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button { - min-width: 0; -} -.oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button, -.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button { - line-height: 1; +.oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon { + left: 0.546875em; } -.oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { - line-height: 1.172em; +.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button, +.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button { + padding-left: 2.5em; } -.oo-ui-buttonElement-framed.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon { - display: block; - position: absolute; - top: 0; - height: 100%; - left: 0.5625em; +.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon, +.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon { + left: 0.46875em; } -.oo-ui-buttonElement-framed.oo-ui-iconElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { - margin-left: 0.3em; +.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button { + padding-right: 2.03125em; } -.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator, -.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-iconElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { - margin-left: 0.46875em; - margin-right: -0.275em; +.oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { + right: 1.015625em; } -.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { - position: relative; - left: 0.2em; +.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-labelElement > .oo-ui-buttonElement-button, +.oo-ui-buttonElement-framed.oo-ui-indicatorElement.oo-ui-iconElement > .oo-ui-buttonElement-button { + padding-right: 2.5em; } .oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button { background-color: #c8ccd1; @@ -694,7 +713,8 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { color: #72777d; } .oo-ui-decoratedOptionWidget { - padding: 0.5em 2em 0.5em 3em; + padding: 0.703125em 0.9375em 0.625em; + line-height: 1; } .oo-ui-decoratedOptionWidget .oo-ui-iconElement-icon, .oo-ui-decoratedOptionWidget .oo-ui-indicatorElement-indicator { @@ -705,11 +725,17 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { top: 0; height: 100%; } +.oo-ui-decoratedOptionWidget.oo-ui-iconElement { + padding-left: 2.5em; +} .oo-ui-decoratedOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon { - left: 0.5em; + left: 0.46875em; +} +.oo-ui-decoratedOptionWidget .oo-ui-labelElement-label { + line-height: 1.172em; } .oo-ui-decoratedOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator { - right: 0.5em; + right: 0.9375em; } .oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-iconElement-icon, .oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator { @@ -989,14 +1015,6 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { .oo-ui-popupButtonWidget .oo-ui-popupWidget { cursor: auto; } -.oo-ui-popupButtonWidget-frameless-popup.oo-ui-popupWidget-anchored-top .oo-ui-popupWidget-anchor, -.oo-ui-popupButtonWidget-frameless-popup.oo-ui-popupWidget-anchored-bottom .oo-ui-popupWidget-anchor { - margin-left: 0.9375em; -} -.oo-ui-popupButtonWidget-framed-popup.oo-ui-popupWidget-anchored-top .oo-ui-popupWidget-anchor, -.oo-ui-popupButtonWidget-framed-popup.oo-ui-popupWidget-anchored-bottom .oo-ui-popupWidget-anchor { - margin-left: 1.5em; -} .oo-ui-inputWidget { margin-right: 0.5em; } @@ -1149,7 +1167,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { box-sizing: border-box; border: 1px solid #a2a9b1; border-radius: 2px; - padding: 0.5em 1em; + padding: 0.625em 0.9375em 0.546875em; font-size: inherit; font-family: inherit; vertical-align: middle; @@ -1161,13 +1179,13 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { background-position: right 1.75em center; width: calc( 100% + 1em ); height: 2.5em; - padding: 0 0 0 1em; + padding: 0 0 0 0.9375em; } .oo-ui-dropdownInputWidget option { font-size: inherit; font-family: inherit; height: 1.5em; - padding: 0.5em 1em; + padding: 0.625em 0.9375em; } .oo-ui-dropdownInputWidget.oo-ui-widget-enabled select { background-color: #f8f9fa; @@ -1367,13 +1385,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { cursor: pointer; } .oo-ui-textInputWidget.oo-ui-widget-disabled input, -.oo-ui-textInputWidget.oo-ui-widget-disabled textarea { - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} +.oo-ui-textInputWidget.oo-ui-widget-disabled textarea, .oo-ui-textInputWidget.oo-ui-widget-disabled .oo-ui-labelElement-label { -webkit-touch-callout: none; -webkit-user-select: none; @@ -1410,7 +1422,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { color: #000; border: 1px solid #a2a9b1; border-radius: 2px; - padding: 0.625em 0.546875em 0.546875em; + padding: 0.625em 0.625em 0.546875em; } .oo-ui-textInputWidget input { line-height: 1.172em; @@ -1513,34 +1525,31 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { } .oo-ui-textInputWidget.oo-ui-iconElement input, .oo-ui-textInputWidget.oo-ui-iconElement textarea { - padding-left: 2.875em; + padding-left: 2.5em; } .oo-ui-textInputWidget.oo-ui-iconElement .oo-ui-iconElement-icon { - left: 0; + left: 0.46875em; max-height: 2.5em; - margin-left: 0.5em; - background-position: right center; } .oo-ui-textInputWidget.oo-ui-indicatorElement input, .oo-ui-textInputWidget.oo-ui-indicatorElement textarea { - padding-right: 2.4875em; + padding-right: 2.1875em; } .oo-ui-textInputWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator { - height: 100%; max-height: 2.5em; - margin: 0 0.775em; + margin-right: 0.9375em; } .oo-ui-textInputWidget > .oo-ui-labelElement-label { color: #72777d; margin-top: 1px; - padding: 0.625em 0.546875em 0.546875em; + padding: 0.625em 0 0.546875em 0.625em; line-height: 1.172em; } .oo-ui-textInputWidget-labelPosition-after.oo-ui-indicatorElement > .oo-ui-labelElement-label { - margin-right: 2.0875em; + margin-right: 2.5em; } .oo-ui-textInputWidget-labelPosition-before.oo-ui-iconElement > .oo-ui-labelElement-label { - margin-left: 2.475em; + padding-left: 2.5em; } .oo-ui-menuSelectWidget { position: absolute; @@ -1566,7 +1575,6 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { display: none; } .oo-ui-menuOptionWidget { - padding: 0.5em 1em; -webkit-transition: background-color 100ms, color 100ms; -moz-transition: background-color 100ms, color 100ms; transition: background-color 100ms, color 100ms; @@ -1593,10 +1601,16 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { background-color: rgba(41, 98, 204, 0.1); color: #36c; } +.oo-ui-menuOptionWidget.oo-ui-iconElement { + padding-left: 0.9375em; +} .oo-ui-menuSectionOptionWidget { - cursor: default; color: #72777d; - padding: 0.33em 0.75em; + padding: 0.703125em 0.9375em 0.3125em; + font-weight: bold; +} +.oo-ui-menuSectionOptionWidget.oo-ui-widget-enabled { + cursor: default; } .oo-ui-dropdownWidget { display: inline-block; @@ -1606,6 +1620,7 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { margin-right: 0.5em; } .oo-ui-dropdownWidget-handle { + position: relative; width: 100%; display: block; white-space: nowrap; @@ -1634,22 +1649,26 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { margin-right: 0; } .oo-ui-dropdownWidget-handle { - padding: 0.546875em 0; - height: 2.5em; - line-height: 1.275; + min-height: 2.5em; border: 1px solid #a2a9b1; border-radius: 2px; -} -.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator { - right: 0; - margin: 0 0.775em; + padding: 0.625em 0.9375em 0.546875em; + line-height: 1; } .oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon { - left: 0.25em; - margin: 0 0.3em; + left: 0.46875em; +} +.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator { + right: 0.9375em; } .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label { - margin: 0 1em; + line-height: 1.172em; +} +.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle { + padding-left: 2.5em; +} +.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle { + padding-right: 1.875em; } .oo-ui-dropdownWidget.oo-ui-widget-enabled .oo-ui-dropdownWidget-handle { background-color: #f8f9fa; @@ -1702,12 +1721,6 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { .oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator { opacity: 0.15; } -.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label { - margin-left: 3em; -} -.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label { - margin-right: 2em; -} .oo-ui-comboBoxInputWidget { display: inline-block; position: relative; @@ -1742,7 +1755,11 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { height: 2.5em; padding: 0; } -.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorElement-indicator { +.oo-ui-comboBoxInputWidget-php > .oo-ui-indicatorWidget { + display: block; + position: absolute; + top: 0; + height: 100%; pointer-events: none; } .oo-ui-comboBoxInputWidget input { @@ -1757,20 +1774,26 @@ body:not( :-moz-handler-blocked ) .oo-ui-fieldsetLayout { border-bottom-right-radius: 2px; border-right-width: 1px; } -.oo-ui-comboBoxInputWidget-dropdownButton { +.oo-ui-comboBoxInputWidget-dropdownButton.oo-ui-indicatorElement { width: 2.5em; } -.oo-ui-comboBoxInputWidget-dropdownButton .oo-ui-buttonElement-button { +.oo-ui-comboBoxInputWidget-dropdownButton.oo-ui-indicatorElement .oo-ui-buttonElement-button { min-width: 2.5em; min-height: 2.5em; padding-left: 0; - padding-right: 0; } -.oo-ui-comboBoxInputWidget-dropdownButton .oo-ui-buttonElement-button, -.oo-ui-comboBoxInputWidget-dropdownButton .oo-ui-buttonElement-button:focus { +.oo-ui-comboBoxInputWidget-dropdownButton.oo-ui-indicatorElement .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { + right: 0.703125em; +} +.oo-ui-comboBoxInputWidget-dropdownButton.oo-ui-indicatorElement .oo-ui-buttonElement-button, +.oo-ui-comboBoxInputWidget-dropdownButton.oo-ui-indicatorElement .oo-ui-buttonElement-button:focus { border-top-left-radius: 0; border-bottom-left-radius: 0; } +.oo-ui-comboBoxInputWidget-php .oo-ui-indicatorWidget { + right: 0.9375em; + margin: 0; +} .oo-ui-comboBoxInputWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator { opacity: 1; } diff --git a/resources/lib/oojs-ui/oojs-ui-core.js b/resources/lib/oojs-ui/oojs-ui-core.js index 44b21ab081..e566d96cd2 100644 --- a/resources/lib/oojs-ui/oojs-ui-core.js +++ b/resources/lib/oojs-ui/oojs-ui-core.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:37Z + * Date: 2017-04-11T22:51:05Z */ ( function ( OO ) { @@ -73,10 +73,10 @@ OO.ui.generateElementId = function () { /** * Check if an element is focusable. - * Inspired from :focusable in jQueryUI v1.11.4 - 2015-04-14 + * Inspired by :focusable in jQueryUI v1.11.4 - 2015-04-14 * * @param {jQuery} $element Element to test - * @return {boolean} + * @return {boolean} Element is focusable */ OO.ui.isFocusableElement = function ( $element ) { var nodeName, @@ -131,7 +131,7 @@ OO.ui.isFocusableElement = function ( $element ) { * * @param {jQuery} $container Container to search in * @param {boolean} [backwards] Search backwards - * @return {jQuery} Focusable child, an empty jQuery object if none found + * @return {jQuery} Focusable child, or an empty jQuery object if none found */ OO.ui.findFocusable = function ( $container, backwards ) { var $focusable = $( [] ), @@ -235,10 +235,10 @@ OO.ui.contains = function ( containers, contained, matchContainers ) { * * Ported from: http://underscorejs.org/underscore.js * - * @param {Function} func - * @param {number} wait - * @param {boolean} immediate - * @return {Function} + * @param {Function} func Function to debounce + * @param {number} [wait=0] Wait period in milliseconds + * @param {boolean} [immediate] Trigger on leading edge + * @return {Function} Debounced function */ OO.ui.debounce = function ( func, wait, immediate ) { var timeout; @@ -264,7 +264,7 @@ OO.ui.debounce = function ( func, wait, immediate ) { /** * Puts a console warning with provided message. * - * @param {string} message + * @param {string} message Message */ OO.ui.warnDeprecation = function ( message ) { if ( OO.getProp( window, 'console', 'warn' ) !== undefined ) { @@ -282,9 +282,9 @@ OO.ui.warnDeprecation = function ( message ) { * when the wrapper is called, return values from the function are entirely * discarded. * - * @param {Function} func - * @param {number} wait - * @return {Function} + * @param {Function} func Function to throttle + * @param {number} wait Throttle window length, in milliseconds + * @return {Function} Throttled function */ OO.ui.throttle = function ( func, wait ) { var context, args, timeout, @@ -319,7 +319,7 @@ OO.ui.throttle = function ( func, wait ) { /** * A (possibly faster) way to get the current timestamp as an integer * - * @return {number} Current timestamp + * @return {number} Current timestamp, in milliseconds since the Unix epoch */ OO.ui.now = Date.now || function () { return new Date().getTime(); @@ -3583,9 +3583,11 @@ OO.ui.ButtonWidget.prototype.setNoFollow = function ( noFollow ) { // Override method visibility hints from ButtonElement /** * @method setActive + * @inheritdoc */ /** * @method isActive + * @inheritdoc */ /** @@ -4560,7 +4562,7 @@ OO.ui.mixin.ClippableElement.prototype.isClippedVertically = function () { }; /** - * Set the ideal size. These are the dimensions the element will have when it's not being clipped. + * Set the ideal size. These are the dimensions #$clippable will have when it's not being clipped. * * @param {number|string} [width] Width as a number of pixels or CSS string with unit suffix * @param {number|string} [height] Height as a number of pixels or CSS string with unit suffix @@ -4645,7 +4647,7 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () { } else { this.$clippable.css( { overflowX: '', - width: this.idealWidth ? this.idealWidth - extraWidth : '', + width: this.idealWidth || '', maxWidth: Math.max( 0, allotedWidth ) } ); } @@ -4661,7 +4663,7 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () { } else { this.$clippable.css( { overflowY: '', - height: this.idealHeight ? this.idealHeight - extraHeight : '', + height: this.idealHeight || '', maxHeight: Math.max( 0, allotedHeight ) } ); } @@ -4710,8 +4712,10 @@ OO.ui.mixin.ClippableElement.prototype.clip = function () { * @cfg {number} [height] Height of popup in pixels. Omit to use the automatic height. * @cfg {boolean} [anchor=true] Show anchor pointing to origin of popup * @cfg {string} [position='below'] Where to position the popup relative to $floatableContainer - * 'above': Put popup above $floatableContainer; anchor points down to the start edge of $floatableContainer - * 'below': Put popup below $floatableContainer; anchor points up to the start edge of $floatableContainer + * 'above': Put popup above $floatableContainer; anchor points down to the horizontal center + * of $floatableContainer + * 'below': Put popup below $floatableContainer; anchor points up to the horizontal center + * of $floatableContainer * 'before': Put popup to the left (LTR) / right (RTL) of $floatableContainer; anchor points * endwards (right/left) to the vertical center of $floatableContainer * 'after': Put popup to the right (LTR) / left (RTL) of $floatableContainer; anchor points @@ -5148,8 +5152,7 @@ OO.ui.PopupWidget.prototype.computePosition = function () { } // Position the anchor (which is positioned relative to the popup) to point to $floatableContainer - // For popups above/below, we point to the start edge; for popups before/after, we point to the center - anchorPos = vertical ? ( floatablePos[ start ] + floatablePos[ end ] ) / 2 : floatablePos[ start ]; + anchorPos = ( floatablePos[ start ] + floatablePos[ end ] ) / 2; anchorOffset = ( start === far ? -1 : 1 ) * ( anchorPos - popupPos[ start ] ); // If the anchor is less than 2*anchorSize from either edge, move the popup to make more space @@ -6734,8 +6737,10 @@ OO.ui.MenuSectionOptionWidget.static.highlightable = false; * that toggles the menu's visibility on click, the menu will be hidden then re-shown when the user clicks * that button, unless the button (or its parent widget) is passed in here. * @cfg {boolean} [autoHide=true] Hide the menu when the mouse is pressed outside the menu. + * @cfg {jQuery} [$autoCloseIgnore] If these elements are clicked, don't auto-hide the menu. * @cfg {boolean} [hideOnChoose=true] Hide the menu when the user chooses an option. * @cfg {boolean} [filterFromInput=false] Filter the displayed options from the input + * @cfg {boolean} [highlightOnFilter] Highlight the first result when filtering */ OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) { // Configuration initialization @@ -6753,8 +6758,10 @@ OO.ui.MenuSelectWidget = function OoUiMenuSelectWidget( config ) { this.filterFromInput = !!config.filterFromInput; this.$input = config.$input ? config.$input : config.input ? config.input.$input : null; this.$widget = config.widget ? config.widget.$element : null; + this.$autoCloseIgnore = config.$autoCloseIgnore || $( [] ); this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this ); this.onInputEditHandler = OO.ui.debounce( this.updateItemVisibility.bind( this ), 100 ); + this.highlightOnFilter = !!config.highlightOnFilter; // Initialization this.$element @@ -6783,8 +6790,12 @@ OO.mixinClass( OO.ui.MenuSelectWidget, OO.ui.mixin.ClippableElement ); */ OO.ui.MenuSelectWidget.prototype.onDocumentMouseDown = function ( e ) { if ( - !OO.ui.contains( this.$element[ 0 ], e.target, true ) && - ( !this.$widget || !OO.ui.contains( this.$widget[ 0 ], e.target, true ) ) + this.isVisible() && + !OO.ui.contains( + this.$element.add( this.$widget ).add( this.$autoCloseIgnore ).get(), + e.target, + true + ) ) { this.toggle( false ); } @@ -6831,6 +6842,7 @@ OO.ui.MenuSelectWidget.prototype.onKeyDown = function ( e ) { */ OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { var i, item, visible, section, sectionEmpty, + firstItemFound = false, anyVisible = false, len = this.items.length, showAll = !this.isVisible(), @@ -6852,6 +6864,11 @@ OO.ui.MenuSelectWidget.prototype.updateItemVisibility = function () { anyVisible = anyVisible || visible; sectionEmpty = sectionEmpty && !visible; item.toggle( visible ); + if ( this.highlightOnFilter && visible && !firstItemFound ) { + // Highlight the first item in the list + this.highlightItem( item ); + firstItemFound = true; + } } } // Process the final section @@ -7832,6 +7849,7 @@ OO.ui.CheckboxMultiselectWidget.prototype.onClick = function ( e ) { * Deprecated, omit this parameter and specify `$container` instead. * @param {Object} [config] Configuration options * @cfg {jQuery} [$container=inputWidget.$element] Element to render menu under + * @cfg {number} [width] Width of the menu */ OO.ui.FloatingMenuSelectWidget = function OoUiFloatingMenuSelectWidget( inputWidget, config ) { // Allow 'inputWidget' parameter and config for backwards compatibility @@ -7843,6 +7861,8 @@ OO.ui.FloatingMenuSelectWidget = function OoUiFloatingMenuSelectWidget( inputWid // Configuration initialization config = config || {}; + this.width = config.width; + // Parent constructor OO.ui.FloatingMenuSelectWidget.parent.call( this, config ); @@ -7876,7 +7896,7 @@ OO.ui.FloatingMenuSelectWidget.prototype.toggle = function ( visible ) { if ( change && visible ) { // Make sure the width is set before the parent method runs. - this.setIdealSize( this.$container.width() ); + this.setIdealSize( this.width || this.$container.width() ); } // Parent method @@ -8657,8 +8677,12 @@ OO.ui.DropdownInputWidget.prototype.onMenuSelect = function ( item ) { * @inheritdoc */ OO.ui.DropdownInputWidget.prototype.setValue = function ( value ) { + var selected; value = this.cleanUpValue( value ); this.dropdownWidget.getMenu().selectItemByData( value ); + // Only allow setting values that are actually present in the dropdown + selected = this.dropdownWidget.getMenu().getSelectedItem(); + value = selected ? selected.getData() : ''; OO.ui.DropdownInputWidget.parent.prototype.setValue.call( this, value ); return this; }; @@ -9560,14 +9584,14 @@ OO.ui.TextInputWidget.prototype.setRequired = function ( state ) { this.required = !!state; if ( this.required ) { this.$input - .attr( 'required', 'required' ) + .prop( 'required', true ) .attr( 'aria-required', 'true' ); if ( this.getIndicator() === null ) { this.setIndicator( 'required' ); } } else { this.$input - .removeAttr( 'required' ) + .prop( 'required', false ) .removeAttr( 'aria-required' ); if ( this.getIndicator() === 'required' ) { this.setIndicator( null ); @@ -10259,9 +10283,14 @@ OO.ui.ComboBoxInputWidget = function OoUiComboBoxInputWidget( config ) { autocomplete: false }, config ); - // ComboBoxInputWidget shouldn't support multiline + // ComboBoxInputWidget shouldn't support `multiline` config.multiline = false; + // See InputWidget#reusePreInfuseDOM about `config.$input` + if ( config.$input ) { + config.$input.removeAttr( 'list' ); + } + // Parent constructor OO.ui.ComboBoxInputWidget.parent.call( this, config ); diff --git a/resources/lib/oojs-ui/oojs-ui-mediawiki.js b/resources/lib/oojs-ui/oojs-ui-mediawiki.js index 8538b6ed78..ec6e45dc65 100644 --- a/resources/lib/oojs-ui/oojs-ui-mediawiki.js +++ b/resources/lib/oojs-ui/oojs-ui-mediawiki.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:37Z + * Date: 2017-04-11T22:51:05Z */ ( function ( OO ) { diff --git a/resources/lib/oojs-ui/oojs-ui-toolbars-apex.css b/resources/lib/oojs-ui/oojs-ui-toolbars-apex.css index 983bc69410..cb6ebc3881 100644 --- a/resources/lib/oojs-ui/oojs-ui-toolbars-apex.css +++ b/resources/lib/oojs-ui/oojs-ui-toolbars-apex.css @@ -1,21 +1,17 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ .oo-ui-popupTool .oo-ui-popupWidget-popup, .oo-ui-popupTool .oo-ui-popupWidget-anchor { z-index: 4; } -.oo-ui-popupTool .oo-ui-popupWidget-anchored-top .oo-ui-popupWidget-anchor, -.oo-ui-popupTool .oo-ui-popupWidget-anchored-bottom .oo-ui-popupWidget-anchor { - margin-left: 1.25em; -} .oo-ui-toolGroupTool > .oo-ui-popupToolGroup { border: 0; border-radius: 0; @@ -123,10 +119,10 @@ border-color: rgba(0, 0, 0, 0.2); box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07); background-color: #f8fbfd; - background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #F1F7FB), color-stop(100%, #fff)); - background-image: -webkit-linear-gradient(top, #F1F7FB 0, #fff 100%); - background-image: -moz-linear-gradient(top, #F1F7FB 0, #fff 100%); - background-image: linear-gradient(to bottom, #F1F7FB 0, #fff 100%); + background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #fff)); + background-image: -webkit-linear-gradient(top, #f1f7fb 0, #fff 100%); + background-image: -moz-linear-gradient(top, #f1f7fb 0, #fff 100%); + background-image: linear-gradient(to bottom, #f1f7fb 0, #fff 100%); -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )"; } .oo-ui-barToolGroup.oo-ui-widget-enabled > .oo-ui-toolGroup-tools > .oo-ui-tool.oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled { @@ -269,10 +265,10 @@ border-bottom-right-radius: 0; box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07); background-color: #f8fbfd; - background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #F1F7FB), color-stop(100%, #fff)); - background-image: -webkit-linear-gradient(top, #F1F7FB 0, #fff 100%); - background-image: -moz-linear-gradient(top, #F1F7FB 0, #fff 100%); - background-image: linear-gradient(to bottom, #F1F7FB 0, #fff 100%); + background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #fff)); + background-image: -webkit-linear-gradient(top, #f1f7fb 0, #fff 100%); + background-image: -moz-linear-gradient(top, #f1f7fb 0, #fff 100%); + background-image: linear-gradient(to bottom, #f1f7fb 0, #fff 100%); -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )"; } .oo-ui-popupToolGroup .oo-ui-toolGroup-tools { @@ -303,7 +299,7 @@ line-height: 2em; } .oo-ui-popupToolGroup .oo-ui-tool-link .oo-ui-tool-accel { - color: #888; + color: #767676; } .oo-ui-listToolGroup .oo-ui-tool { display: block; @@ -326,10 +322,10 @@ border-color: rgba(0, 0, 0, 0.1); box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07); background-color: #f8fbfd; - background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #F1F7FB), color-stop(100%, #fff)); - background-image: -webkit-linear-gradient(top, #F1F7FB 0, #fff 100%); - background-image: -moz-linear-gradient(top, #F1F7FB 0, #fff 100%); - background-image: linear-gradient(to bottom, #F1F7FB 0, #fff 100%); + background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #f1f7fb), color-stop(100%, #fff)); + background-image: -webkit-linear-gradient(top, #f1f7fb 0, #fff 100%); + background-image: -moz-linear-gradient(top, #f1f7fb 0, #fff 100%); + background-image: linear-gradient(to bottom, #f1f7fb 0, #fff 100%); -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff1f7fb', endColorstr='#ffffffff' )"; } .oo-ui-listToolGroup .oo-ui-tool-active.oo-ui-widget-enabled + .oo-ui-tool-active.oo-ui-widget-enabled { diff --git a/resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css index 502b7b1c41..85095d3751 100644 --- a/resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css +++ b/resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ .oo-ui-tool.oo-ui-widget-enabled { -webkit-transition: background-color 100ms; @@ -22,10 +22,6 @@ .oo-ui-popupTool .oo-ui-popupWidget-anchor { z-index: 4; } -.oo-ui-popupTool .oo-ui-popupWidget-anchored-top .oo-ui-popupWidget-anchor, -.oo-ui-popupTool .oo-ui-popupWidget-anchored-bottom .oo-ui-popupWidget-anchor { - margin-left: 1.25em; -} .oo-ui-toolGroupTool > .oo-ui-toolGroup { border-right: 0; } @@ -219,7 +215,7 @@ width: 0.9375em; height: 100%; margin: 0 0.5em; - opacity: 0.3; + opacity: 0.87; } .oo-ui-toolbar-narrow .oo-ui-popupToolGroup-handle .oo-ui-indicatorElement-indicator { right: -0.3125em; @@ -278,10 +274,15 @@ .oo-ui-popupToolGroup.oo-ui-widget-enabled.oo-ui-popupToolGroup-active .oo-ui-tool-active.oo-ui-widget-enabled .oo-ui-tool-link .oo-ui-tool-title { color: #36c; } -.oo-ui-popupToolGroup.oo-ui-widget-enabled-handle:hover { +.oo-ui-popupToolGroup.oo-ui-widget-enabled > .oo-ui-popupToolGroup-handle { + -webkit-transition: background-color 100ms, box-shadow 100ms; + -moz-transition: background-color 100ms, box-shadow 100ms; + transition: background-color 100ms, box-shadow 100ms; +} +.oo-ui-popupToolGroup.oo-ui-widget-enabled > .oo-ui-popupToolGroup-handle:hover { background-color: #eaecf0; } -.oo-ui-popupToolGroup.oo-ui-widget-enabled-handle:active { +.oo-ui-popupToolGroup.oo-ui-widget-enabled > .oo-ui-popupToolGroup-handle:active { background-color: #eaf3ff; } .oo-ui-listToolGroup .oo-ui-tool { @@ -408,18 +409,43 @@ background-color: transparent; box-shadow: none; } -.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement { +.oo-ui-toolbar-narrow .oo-ui-toolbar-bar:after { + content: ''; + display: block; + position: absolute; + top: 3.125em; + left: 0; + width: 100%; + height: 0; + border-bottom: 1px solid #c8ccd1; +} +.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement, +.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-indicatorElement, +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-labelElement, +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-indicatorElement { margin: 0; } -.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button { +.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button, +.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button, +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button, +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button { border: 0; border-radius: 0; padding: 0 0.3125em; } -.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { +.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label, +.oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label, +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label, +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { margin: 0 1em; line-height: 3.125em; } +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-indicatorElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-labelElement-label { + margin: 0; +} +.oo-ui-toolbar-actions > .oo-ui-buttonGroupWidget > .oo-ui-buttonElement.oo-ui-indicatorElement:not( .oo-ui-labelElement ) > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator { + margin: 0 0.3125em; +} .oo-ui-toolbar-actions > .oo-ui-toolbar:not( :last-child ) { border-right: 1px solid #c8ccd1; } diff --git a/resources/lib/oojs-ui/oojs-ui-toolbars.js b/resources/lib/oojs-ui/oojs-ui-toolbars.js index 4cc0db1337..665e9dc539 100644 --- a/resources/lib/oojs-ui/oojs-ui-toolbars.js +++ b/resources/lib/oojs-ui/oojs-ui-toolbars.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:37Z + * Date: 2017-04-11T22:51:05Z */ ( function ( OO ) { diff --git a/resources/lib/oojs-ui/oojs-ui-widgets-apex.css b/resources/lib/oojs-ui/oojs-ui-widgets-apex.css index a9b31da81b..0d517828d9 100644 --- a/resources/lib/oojs-ui/oojs-ui-widgets-apex.css +++ b/resources/lib/oojs-ui/oojs-ui-widgets-apex.css @@ -1,14 +1,13 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ -.oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ), .oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ).oo-ui-widget { cursor: move; cursor: url(images/grab.cur ); @@ -411,11 +410,6 @@ } .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-fileName { display: block; - padding-top: 0.5em; - padding-right: 2.375em; -} -.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-clearButton { - right: 0.5em; } .oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail, .oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info { @@ -460,9 +454,6 @@ .oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget { height: auto; } -.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label { - padding: 1em; -} .oo-ui-selectFileWidget:last-child { margin-right: 0; } @@ -530,6 +521,9 @@ .oo-ui-selectFileWidget-supported.oo-ui-widget-enabled.oo-ui-selectFileWidget-canDrop.oo-ui-selectFileWidget-dropTarget { background-color: #e1f3ff; } +.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-label { + padding: 1em; +} .oo-ui-selectFileWidget-dropTarget { background-color: #fff; border: 1px solid #aaa; @@ -544,6 +538,13 @@ overflow: inherit; white-space: normal; } +.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-fileName { + padding-top: 0.5em; + padding-right: 2.375em; +} +.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-clearButton { + right: 0.5em; +} .oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget { border-style: dashed; } @@ -693,6 +694,169 @@ background-color: #fff; border-color: #ddd; } +.oo-ui-tagMultiselectWidget { + display: inline-block; + position: relative; + width: 100%; + max-width: 50em; +} +.oo-ui-tagMultiselectWidget-handle { + width: 100%; + display: block; + position: relative; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-iconElement-icon, +.oo-ui-tagMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { + position: absolute; + top: 0; + height: 100%; +} +.oo-ui-tagMultiselectWidget-content { + position: relative; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-content > input { + display: none; +} +.oo-ui-tagMultiselectWidget-group { + display: inline; +} +.oo-ui-tagMultiselectWidget-inputPosition-outline { + width: 100%; +} +.oo-ui-tagMultiselectWidget-focusTrap { + display: inline-block; + height: 1px; + width: 1px; +} +.oo-ui-tagMultiselectWidget-handle { + background-color: #fff; + cursor: text; + min-height: 2.4em; + margin-right: 0.5em; + padding: 0.15em 0.25em; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 0.25em; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.oo-ui-tagMultiselectWidget-handle:last-child { + margin-right: 0; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input { + border: 0; + line-height: 1.675em; + margin: 0; + margin-left: 0.2em; + padding: 0; + font-size: inherit; + font-family: inherit; + background-color: transparent; + color: #000; + vertical-align: middle; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input::-webkit-input-placeholder { + color: #767676; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input:-ms-input-placeholder { + color: #767676; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input::-moz-placeholder { + color: #767676; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input:-moz-placeholder { + color: #767676; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input::placeholder { + color: #767676; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input:focus { + outline: 0; +} +.oo-ui-tagMultiselectWidget.oo-ui-iconElement .oo-ui-tagMultiselectWidget-handle { + padding-left: 2.475em; +} +.oo-ui-tagMultiselectWidget.oo-ui-iconElement .oo-ui-tagMultiselectWidget-handle > .oo-ui-iconElement-icon { + left: 0; + margin: 0 0.3em; +} +.oo-ui-tagMultiselectWidget.oo-ui-indicatorElement .oo-ui-tagMultiselectWidget-handle { + padding-right: 2.4875em; +} +.oo-ui-tagMultiselectWidget.oo-ui-indicatorElement .oo-ui-tagMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { + right: 0; + margin: 0 0.775em; +} +.oo-ui-tagMultiselectWidget:hover .oo-ui-tagMultiselectWidget-handle { + border-color: rgba(0, 0, 0, 0.2); +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-handle { + color: #ccc; + text-shadow: 0 1px 1px #fff; + border-color: #ddd; + background-color: #f3f3f3; + cursor: default; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-handle > .oo-ui-iconElement-icon, +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { + opacity: 0.2; +} +.oo-ui-tagMultiselectWidget-popup > .oo-ui-popupWidget-popup { + border: 0; +} +.oo-ui-tagItemWidget { + position: relative; + display: inline-block; + cursor: default; + white-space: nowrap; + width: auto; + max-width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + vertical-align: middle; + padding: 0 0.4em; + margin: 0.1em; + height: 1.7em; + line-height: 1.7em; + background-color: #eeeeee; + background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0, #fff), color-stop(100%, #ddd)); + background-image: -webkit-linear-gradient(top, #fff 0, #ddd 100%); + background-image: -moz-linear-gradient(top, #fff 0, #ddd 100%); + background-image: linear-gradient(to bottom, #fff 0, #ddd 100%); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )"; + border: 1px solid #ccc; + color: #333; + border-radius: 0.25em; +} +.oo-ui-tagItemWidget.oo-ui-labelElement .oo-ui-labelElement-label { + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + cursor: text; +} +.oo-ui-tagItemWidget:focus { + outline: 0; + border-color: #087ecc; +} +.oo-ui-tagItemWidget.oo-ui-widget-disabled { + opacity: 0.5; + -webkit-transform: translateZ(0); + transform: translateZ(0); + box-shadow: none; + color: #333; + background: #eee; + border-color: #ccc; +} +.oo-ui-tagItemWidget > .oo-ui-buttonElement { + margin-top: -1.25em; + padding-left: 0.3em; +} .oo-ui-capsuleMultiselectWidget { display: inline-block; position: relative; @@ -755,23 +919,23 @@ vertical-align: middle; } .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input::-webkit-input-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input:-ms-input-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input::-moz-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input:-moz-placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input::placeholder { - color: #72777d; + color: #767676; opacity: 1; } .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-capsuleMultiselectWidget-content > input:focus { @@ -830,7 +994,7 @@ background-image: linear-gradient(to bottom, #fff 0, #ddd 100%); -ms-filter: "progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffffff', endColorstr='#ffdddddd' )"; border: 1px solid #ccc; - color: #555; + color: #333; border-radius: 0.25em; } .oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label { diff --git a/resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css index 8438a3d113..25add3d624 100644 --- a/resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css +++ b/resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css @@ -1,14 +1,16 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ -.oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ), +.oo-ui-draggableElement { + padding: 1.0546875em 0.9375em 0.9375em; +} .oo-ui-draggableElement-handle:not( .oo-ui-draggableElement-undraggable ).oo-ui-widget { cursor: move; cursor: url(images/grab.cur ); @@ -32,7 +34,7 @@ .oo-ui-draggableGroupElement-horizontal .oo-ui-draggableElement { display: inline-block; } -.oo-ui-draggableElement-handle:focus { +.oo-ui-draggableElement-handle.oo-ui-widget-enabled:focus { border-radius: 2px; box-shadow: inset 0 0 0 1px #36c, 0 0 0 1px #36c; outline: 0; @@ -194,6 +196,10 @@ border-bottom-right-radius: 2px; border-top-right-radius: 2px; } +.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget.oo-ui-iconElement .oo-ui-iconElement-icon, +.oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator { + position: absolute; +} .oo-ui-buttonSelectWidget.oo-ui-widget-enabled:focus .oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected .oo-ui-buttonElement-button { border-color: #36c; box-shadow: inset 0 0 0 1px #36c, inset 0 0 0 2px #fff; @@ -476,11 +482,6 @@ } .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-fileName { display: block; - padding-top: 0.5em; - padding-right: 2.375em; -} -.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-clearButton { - right: 0.5em; } .oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-thumbnail, .oo-ui-selectFileWidget-empty.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-info { @@ -525,9 +526,6 @@ .oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget { height: auto; } -.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label { - padding: 1em; -} .oo-ui-selectFileWidget:last-child { margin-right: 0; } @@ -535,19 +533,19 @@ border-radius: 0 2px 2px 0; } .oo-ui-selectFileWidget-info { - height: 2.4em; background-color: #fff; border: 1px solid #a2a9b1; border-radius: 2px 0 0 2px; - border-width: 1px 0 1px 1px; + border-right-width: 0; } .oo-ui-selectFileWidget-info > .oo-ui-iconElement-icon { - left: 0; - margin-left: 0.5em; + left: 0.46875em; + min-height: 2.4em; + margin-left: -1px; } .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator { - right: 0; - margin-right: 0.775em; + right: 0.9375em; + min-height: 2.4em; } .oo-ui-selectFileWidget-label { -webkit-box-sizing: border-box; @@ -556,7 +554,7 @@ display: block; right: 2.375em; padding-top: 0.625em; - padding-left: 0.546875em; + padding-left: 0.625em; padding-bottom: 0.546875em; line-height: 1.172em; white-space: nowrap; @@ -568,7 +566,7 @@ } .oo-ui-selectFileWidget-clearButton { top: -1px; - right: 0; + right: 0.46875em; min-width: 24px; width: 1.875em; margin-right: 0; @@ -580,10 +578,10 @@ color: #72777d; } .oo-ui-selectFileWidget.oo-ui-iconElement .oo-ui-selectFileWidget-label { - left: 2.875em; + left: 2.5em; } .oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-label { - right: 4.4625em; + right: 3.75em; padding-left: 0; } .oo-ui-selectFileWidget.oo-ui-indicatorElement .oo-ui-selectFileWidget-clearButton { @@ -608,6 +606,12 @@ overflow: inherit; white-space: normal; } +.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-fileName { + padding-right: 2.5em; +} +.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-clearButton { + top: 0; +} .oo-ui-selectFileWidget-empty.oo-ui-widget-enabled.oo-ui-selectFileWidget-dropTarget { background-color: #fff; border-style: dashed; @@ -646,6 +650,9 @@ .oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator { opacity: 0.15; } +.oo-ui-selectFileWidget-notsupported.oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-label { + padding: 1em 0.9375em; +} .oo-ui-widget-disabled .oo-ui-selectFileWidget-dropLabel { display: none; } @@ -661,8 +668,7 @@ -moz-user-select: none; -ms-user-select: none; user-select: none; - font-size: 1.1em; - padding: 0.75em; + padding: 1.0546875em 0.9375em 0.9375em; -webkit-transition: background-color 100ms, color 100ms; -moz-transition: background-color 100ms, color 100ms; transition: background-color 100ms, color 100ms; @@ -679,17 +685,14 @@ background-color: rgba(41, 98, 204, 0.1); color: #36c; } -.oo-ui-outlineOptionWidget .oo-ui-iconElement-icon { - font-size: 90.90909%; -} .oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-labelElement-label { padding-right: 1.5em; } .oo-ui-outlineOptionWidget.oo-ui-indicatorElement .oo-ui-indicatorElement-indicator { opacity: 0.5; } -.oo-ui-outlineOptionWidget-level-0.oo-ui-iconElement { - padding-left: 2.571em; +.oo-ui-outlineOptionWidget .oo-ui-labelElement-label { + font-size: 1.1em; } .oo-ui-outlineOptionWidget-level-1 { padding-left: 2.571em; @@ -787,6 +790,211 @@ background-color: #fff; color: #000; } +.oo-ui-tagMultiselectWidget { + display: inline-block; + position: relative; + width: 100%; + max-width: 50em; +} +.oo-ui-tagMultiselectWidget-handle { + width: 100%; + display: block; + position: relative; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-iconElement-icon, +.oo-ui-tagMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { + position: absolute; + top: 0; + height: 100%; +} +.oo-ui-tagMultiselectWidget-content { + position: relative; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-content > input { + display: none; +} +.oo-ui-tagMultiselectWidget-group { + display: inline; +} +.oo-ui-tagMultiselectWidget-inputPosition-outline { + width: 100%; +} +.oo-ui-tagMultiselectWidget-focusTrap { + display: inline-block; + height: 1px; + width: 1px; +} +.oo-ui-tagMultiselectWidget-handle { + min-height: 2.4em; + margin-right: 0.5em; + padding: 0.15em 0.25em; + border: 1px solid #a2a9b1; + border-radius: 2px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.oo-ui-tagMultiselectWidget-handle:last-child { + margin-right: 0; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input { + border: 0; + line-height: 1.675; + margin: 0 0 0 0.2em; + padding: 0; + font-size: inherit; + font-family: inherit; + background-color: transparent; + color: #000; + vertical-align: middle; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input::-webkit-input-placeholder { + color: #72777d; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input:-ms-input-placeholder { + color: #72777d; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input::-moz-placeholder { + color: #72777d; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input:-moz-placeholder { + color: #72777d; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input::placeholder { + color: #72777d; + opacity: 1; +} +.oo-ui-tagMultiselectWidget-handle > .oo-ui-tagMultiselectWidget-content > input:focus { + outline: 0; +} +.oo-ui-tagMultiselectWidget.oo-ui-iconElement .oo-ui-tagMultiselectWidget-handle { + padding-left: 2.475em; +} +.oo-ui-tagMultiselectWidget.oo-ui-iconElement .oo-ui-tagMultiselectWidget-handle > .oo-ui-iconElement-icon { + left: 0; + margin: 0 0.3em; +} +.oo-ui-tagMultiselectWidget.oo-ui-indicatorElement .oo-ui-tagMultiselectWidget-handle { + padding-right: 2.4875em; +} +.oo-ui-tagMultiselectWidget.oo-ui-indicatorElement .oo-ui-tagMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { + right: 0; + margin: 0 0.775em; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-enabled .oo-ui-tagMultiselectWidget-handle { + background-color: #fff; + cursor: text; + -webkit-transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1); + -moz-transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1); + transition: border-color 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1); +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-enabled:hover .oo-ui-tagMultiselectWidget-handle { + border-color: #72777d; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-enabled.oo-ui-tagMultiselectWidget-open .oo-ui-tagMultiselectWidget-handle { + border-color: #36c; + outline: 0; + box-shadow: inset 0 0 0 1px #36c; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-handle { + color: #72777d; + text-shadow: 0 1px 1px #fff; + border-color: #c8ccd1; + background-color: #eaecf0; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-handle > .oo-ui-iconElement-icon { + opacity: 0.51; +} +.oo-ui-tagMultiselectWidget.oo-ui-widget-disabled .oo-ui-tagMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { + opacity: 0.15; +} +.oo-ui-tagItemWidget { + position: relative; + display: inline-block; + cursor: default; + white-space: nowrap; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: auto; + max-width: 100%; + height: 1.7em; + margin: 0.1em; + border: 1px solid #a2a9b1; + border-radius: 2px; + padding: 0 0.4em; + line-height: 1.7; + vertical-align: middle; +} +.oo-ui-tagItemWidget.oo-ui-labelElement .oo-ui-labelElement-label { + display: inline-block; + text-overflow: ellipsis; + overflow: hidden; + cursor: text; +} +.oo-ui-tagItemWidget.oo-ui-flaggedElement-invalid { + border-color: #d33; +} +.oo-ui-tagItemWidget.oo-ui-flaggedElement-invalid:hover { + border-color: #d33; +} +.oo-ui-tagItemWidget.oo-ui-flaggedElement-invalid:focus { + border-color: #d33; + box-shadow: inset 0 0 0 1px #d33; +} +.oo-ui-tagItemWidget.oo-ui-widget-enabled { + background-color: #f8f9fa; + color: #222; + padding-right: 1.5375em; + -webkit-transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms; + -moz-transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms; + transition: background-color 100ms, color 100ms, border-color 100ms, box-shadow 100ms; +} +.oo-ui-tagItemWidget.oo-ui-widget-enabled:hover { + background-color: #fff; + color: #444; + border-color: #a2a9b1; +} +.oo-ui-tagItemWidget.oo-ui-widget-enabled:focus { + border-color: #36c; + box-shadow: inset 0 0 0 1px #36c; + outline: 0; +} +.oo-ui-tagItemWidget.oo-ui-widget-enabled > .oo-ui-buttonElement { + display: block; + position: absolute; + top: 0; + right: 0; + bottom: 0; +} +.oo-ui-tagItemWidget.oo-ui-widget-enabled .oo-ui-buttonElement-button { + display: block; + width: 1.5375em; + height: 100%; +} +.oo-ui-tagItemWidget.oo-ui-widget-enabled .oo-ui-buttonElement-button .oo-ui-indicator-clear { + position: absolute; + top: 0; + right: 0.3em; + bottom: 0; + height: auto; +} +.oo-ui-tagItemWidget.oo-ui-widget-disabled { + background-color: #eaecf0; + color: #72777d; + border-color: #c8ccd1; + text-shadow: 0 1px 1px #fff; +} +.oo-ui-tagItemWidget.oo-ui-widget-disabled:focus { + outline: 0; +} +.oo-ui-tagItemWidget.oo-ui-widget-disabled > .oo-ui-buttonElement { + display: none; +} .oo-ui-capsuleMultiselectWidget { display: inline-block; position: relative; @@ -869,18 +1077,16 @@ outline: 0; } .oo-ui-capsuleMultiselectWidget.oo-ui-iconElement .oo-ui-capsuleMultiselectWidget-handle { - padding-left: 2.475em; + padding-left: 2.5em; } .oo-ui-capsuleMultiselectWidget.oo-ui-iconElement .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-iconElement-icon { - left: 0; - margin: 0 0.3em; + left: 0.46875em; } .oo-ui-capsuleMultiselectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiselectWidget-handle { - padding-right: 2.4875em; + padding-right: 2.8125em; } .oo-ui-capsuleMultiselectWidget.oo-ui-indicatorElement .oo-ui-capsuleMultiselectWidget-handle > .oo-ui-indicatorElement-indicator { - right: 0; - margin: 0 0.775em; + right: 0.9375em; } .oo-ui-capsuleMultiselectWidget-popup { margin-top: -1px; diff --git a/resources/lib/oojs-ui/oojs-ui-widgets.js b/resources/lib/oojs-ui/oojs-ui-widgets.js index 348225633d..2b2f2caa03 100644 --- a/resources/lib/oojs-ui/oojs-ui-widgets.js +++ b/resources/lib/oojs-ui/oojs-ui-widgets.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:37Z + * Date: 2017-04-11T22:51:05Z */ ( function ( OO ) { @@ -98,7 +98,7 @@ OO.ui.mixin.DraggableElement.prototype.toggleDraggable = function ( isDraggable if ( this.draggable !== isDraggable ) { this.draggable = isDraggable; - this.$element.toggleClass( 'oo-ui-draggableElement-undraggable', !this.draggable ); + this.$handle.toggleClass( 'oo-ui-draggableElement-undraggable', !this.draggable ); } }; @@ -4465,6 +4465,1425 @@ OO.ui.CapsuleMultiselectWidget.prototype.focus = function () { return this; }; +/** + * TagItemWidgets are used within a {@link OO.ui.TagMultiselectWidget + * TagMultiselectWidget} to display the selected items. + * + * @class + * @extends OO.ui.Widget + * @mixins OO.ui.mixin.ItemWidget + * @mixins OO.ui.mixin.LabelElement + * @mixins OO.ui.mixin.FlaggedElement + * @mixins OO.ui.mixin.TabIndexedElement + * @mixins OO.ui.mixin.DraggableElement + * + * @constructor + * @param {Object} [config] Configuration object + * @cfg {boolean} [valid=true] Item is valid + */ +OO.ui.TagItemWidget = function OoUiTagItemWidget( config ) { + config = config || {}; + + // Parent constructor + OO.ui.TagItemWidget.parent.call( this, config ); + + // Mixin constructors + OO.ui.mixin.ItemWidget.call( this ); + OO.ui.mixin.LabelElement.call( this, config ); + OO.ui.mixin.FlaggedElement.call( this, config ); + OO.ui.mixin.TabIndexedElement.call( this, config ); + OO.ui.mixin.DraggableElement.call( this, config ); + + this.valid = config.valid === undefined ? true : !!config.valid; + + this.closeButton = new OO.ui.ButtonWidget( { + framed: false, + indicator: 'clear', + tabIndex: -1 + } ); + this.closeButton.setDisabled( this.isDisabled() ); + + // Events + this.closeButton + .connect( this, { click: 'remove' } ); + this.$element + .on( 'click', this.select.bind( this ) ) + .on( 'keydown', this.onKeyDown.bind( this ) ) + // Prevent propagation of mousedown; the tag item "lives" in the + // clickable area of the TagMultiselectWidget, which listens to + // mousedown to open the menu or popup. We want to prevent that + // for clicks specifically on the tag itself, so the actions taken + // are more deliberate. When the tag is clicked, it will emit the + // selection event (similar to how #OO.ui.MultioptionWidget emits 'change') + // and can be handled separately. + .on( 'mousedown', function ( e ) { e.stopPropagation(); } ); + + // Initialization + this.$element + .addClass( 'oo-ui-tagItemWidget' ) + .append( this.$label, this.closeButton.$element ); +}; + +/* Initialization */ + +OO.inheritClass( OO.ui.TagItemWidget, OO.ui.Widget ); +OO.mixinClass( OO.ui.TagItemWidget, OO.ui.mixin.ItemWidget ); +OO.mixinClass( OO.ui.TagItemWidget, OO.ui.mixin.LabelElement ); +OO.mixinClass( OO.ui.TagItemWidget, OO.ui.mixin.FlaggedElement ); +OO.mixinClass( OO.ui.TagItemWidget, OO.ui.mixin.TabIndexedElement ); +OO.mixinClass( OO.ui.TagItemWidget, OO.ui.mixin.DraggableElement ); + +/* Events */ + +/** + * @event remove + * + * A remove action was performed on the item + */ + +/** + * @event navigate + * @param {string} direction Direction of the movement, forward or backwards + * + * A navigate action was performed on the item + */ + +/** + * @event select + * + * The tag widget was selected. This can occur when the widget + * is either clicked or enter was pressed on it. + */ + +/** + * @event valid + * @param {boolean} isValid Item is valid + * + * Item validity has changed + */ + +/* Methods */ + +/** + * @inheritdoc + */ +OO.ui.TagItemWidget.prototype.setDisabled = function ( state ) { + // Parent method + OO.ui.TagItemWidget.parent.prototype.setDisabled.call( this, state ); + + if ( this.closeButton ) { + this.closeButton.setDisabled( state ); + } + return this; +}; + +/** + * Handle removal of the item + * + * This is mainly for extensibility concerns, so other children + * of this class can change the behavior if they need to. This + * is called by both clicking the 'remove' button but also + * on keypress, which is harder to override if needed. + * + * @fires remove + */ +OO.ui.TagItemWidget.prototype.remove = function () { + if ( !this.isDisabled() ) { + this.emit( 'remove' ); + } +}; + +/** + * Handle a keydown event on the widget + * + * @fires navigate + * @fires remove + * @return {boolean|undefined} false to stop the operation + */ +OO.ui.TagItemWidget.prototype.onKeyDown = function ( e ) { + var movement; + + if ( e.keyCode === OO.ui.Keys.BACKSPACE || e.keyCode === OO.ui.Keys.DELETE ) { + this.remove(); + return false; + } else if ( e.keyCode === OO.ui.Keys.ENTER ) { + this.select(); + return false; + } else if ( + e.keyCode === OO.ui.Keys.LEFT || + e.keyCode === OO.ui.Keys.RIGHT + ) { + if ( OO.ui.Element.static.getDir( this.$element ) === 'rtl' ) { + movement = { + left: 'forwards', + right: 'backwards' + }; + } else { + movement = { + left: 'backwards', + right: 'forwards' + }; + } + + this.emit( + 'navigate', + e.keyCode === OO.ui.Keys.LEFT ? + movement.left : movement.right + ); + } +}; + +/** + * Focuses the capsule + */ +OO.ui.TagItemWidget.prototype.focus = function () { + if ( !this.isDisabled() ) { + this.$element.focus(); + } +}; + +/** + * Select this item + * + * @fires select + */ +OO.ui.TagItemWidget.prototype.select = function () { + if ( !this.isDisabled() ) { + this.emit( 'select' ); + } +}; + +/** + * Set the valid state of this item + * + * @param {boolean} [valid] Item is valid + * @fires valid + */ +OO.ui.TagItemWidget.prototype.toggleValid = function ( valid ) { + valid = valid === undefined ? !this.valid : !!valid; + + if ( this.valid !== valid ) { + this.valid = valid; + + this.setFlags( { invalid: !this.valid } ); + + this.emit( 'valid', this.valid ); + } +}; + +/** + * Check whether the item is valid + * + * @return {boolean} Item is valid + */ +OO.ui.TagItemWidget.prototype.isValid = function () { + return this.valid; +}; + +/** + * A basic tag multiselect widget, similar in concept to {@link OO.ui.ComboBoxInputWidget combo box widget} + * that allows the user to add multiple values that are displayed in a tag area. + * + * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1]. + * + * This widget is a base widget; see {@link OO.ui.MenuTagMultiselectWidget MenuTagMultiselectWidget} and + * {@link OO.ui.PopupTagMultiselectWidget PopupTagMultiselectWidget} for the implementations that use + * a menu and a popup respectively. + * + * @example + * // Example: A basic TagMultiselectWidget. + * var widget = new OO.ui.TagMultiselectWidget( { + * inputPosition: 'outline', + * allowedValues: [ 'Option 1', 'Option 2', 'Option 3' ], + * selected: [ 'Option 1' ] + * } ); + * $( 'body' ).append( widget.$element ); + * + * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options + * + * @class + * @extends OO.ui.Widget + * @mixins OO.ui.mixin.GroupWidget + * @mixins OO.ui.mixin.DraggableGroupElement + * @mixins OO.ui.mixin.IndicatorElement + * @mixins OO.ui.mixin.IconElement + * @mixins OO.ui.mixin.TabIndexedElement + * @mixins OO.ui.mixin.FlaggedElement + * + * @constructor + * @param {Object} config Configuration object + * @cfg {Object} [input] Configuration options for the input widget + * @cfg {boolean} [inputPosition='inline'] Position of the input. Options are: + * - inline: The input is invisible, but exists inside the tag list, so + * the user types into the tag groups to add tags. + * - outline: The input is underneath the tag area. + * - none: No input supplied + * @cfg {boolean} [allowEditTags=true] Allow editing of the tags by clicking them + * @cfg {boolean} [allowArbitrary=false] Allow data items to be added even if + * not present in the menu. + * @cfg {Object[]} [allowedValues] An array representing the allowed items + * by their datas. + * @cfg {boolean} [allowDuplicates=false] Allow duplicate items to be added + * @cfg {boolean} [allowDisplayInvalidTags=false] Allow the display of + * invalid tags. These tags will display with an invalid state, and + * the widget as a whole will have an invalid state if any invalid tags + * are present. + * @cfg {boolean} [allowReordering=true] Allow reordering of the items + * @cfg {Object[]|String[]} [selected] A set of selected tags. If given, + * these will appear in the tag list on initialization, as long as they + * pass the validity tests. + */ +OO.ui.TagMultiselectWidget = function OoUiTagMultiselectWidget( config ) { + var inputEvents, + rAF = window.requestAnimationFrame || setTimeout, + widget = this, + $tabFocus = $( '' ) + .addClass( 'oo-ui-tagMultiselectWidget-focusTrap' ); + + config = config || {}; + + // Parent constructor + OO.ui.TagMultiselectWidget.parent.call( this, config ); + + // Mixin constructors + OO.ui.mixin.GroupWidget.call( this, config ); + OO.ui.mixin.IndicatorElement.call( this, config ); + OO.ui.mixin.IconElement.call( this, config ); + OO.ui.mixin.TabIndexedElement.call( this, config ); + OO.ui.mixin.FlaggedElement.call( this, config ); + OO.ui.mixin.DraggableGroupElement.call( this, config ); + + this.toggleDraggable( + config.allowReordering === undefined ? + true : !!config.allowReordering + ); + + this.inputPosition = this.constructor.static.allowedInputPositions.indexOf( config.inputPosition ) > -1 ? + config.inputPosition : 'inline'; + this.allowEditTags = config.allowEditTags === undefined ? true : !!config.allowEditTags; + this.allowArbitrary = !!config.allowArbitrary; + this.allowDuplicates = !!config.allowDuplicates; + this.allowedValues = config.allowedValues || []; + this.allowDisplayInvalidTags = config.allowDisplayInvalidTags; + this.hasInput = this.inputPosition !== 'none'; + this.height = null; + this.valid = true; + + this.$content = $( '
' ) + .addClass( 'oo-ui-tagMultiselectWidget-content' ); + this.$handle = $( '
' ) + .addClass( 'oo-ui-tagMultiselectWidget-handle' ) + .append( + this.$indicator, + this.$icon, + this.$content + .append( + this.$group + .addClass( 'oo-ui-tagMultiselectWidget-group' ) + ) + ); + + // Events + this.aggregate( { + remove: 'itemRemove', + navigate: 'itemNavigate', + select: 'itemSelect' + } ); + this.connect( this, { + itemRemove: 'onTagRemove', + itemSelect: 'onTagSelect', + itemNavigate: 'onTagNavigate', + change: 'onChangeTags' + } ); + this.$handle.on( { + mousedown: this.onMouseDown.bind( this ) + } ); + + // Initialize + this.$element + .addClass( 'oo-ui-tagMultiselectWidget' ) + .addClass( 'oo-ui-tagMultiselectWidget-inputPosition-' + this.inputPosition ) + .append( this.$handle ); + + if ( this.hasInput ) { + this.input = new OO.ui.TextInputWidget( $.extend( { + placeholder: config.placeholder, + classes: [ 'oo-ui-tagMultiselectWidget-input' ] + }, config.input ) ); + this.input.setDisabled( this.isDisabled() ); + + inputEvents = { + focus: this.onInputFocus.bind( this ), + blur: this.onInputBlur.bind( this ), + 'propertychange change click mouseup keydown keyup input cut paste select focus': + OO.ui.debounce( this.updateInputSize.bind( this ) ), + keydown: this.onInputKeyDown.bind( this ), + keypress: this.onInputKeyPress.bind( this ) + }; + + this.input.$input.on( inputEvents ); + + if ( this.inputPosition === 'outline' ) { + // Override max-height for the input widget + // in the case the widget is outline so it can + // stretch all the way if the widet is wide + this.input.$element.css( 'max-width', 'inherit' ); + this.$element.append( this.input.$element ); + } else { + // HACK: When the widget is using 'inline' input, the + // behavior needs to only use the $input itself + // so we style and size it accordingly (otherwise + // the styling and sizing can get very convoluted + // when the wrapping divs and other elements) + // We are taking advantage of still being able to + // call the widget itself for operations like + // .getValue() and setDisabled() and .focus() but + // having only the $input attached to the DOM + this.$content.append( this.input.$input ); + } + } + + this.setTabIndexedElement( + this.hasInput ? + this.input.$input : + $tabFocus + ); + + if ( config.selected ) { + this.setValue( config.selected ); + } + + // HACK: Input size needs to be calculated after everything + // else is rendered + rAF( function () { + if ( widget.hasInput ) { + widget.updateInputSize(); + } + } ); +}; + +/* Initialization */ + +OO.inheritClass( OO.ui.TagMultiselectWidget, OO.ui.Widget ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.GroupWidget ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.DraggableGroupElement ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.IndicatorElement ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.IconElement ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.TabIndexedElement ); +OO.mixinClass( OO.ui.TagMultiselectWidget, OO.ui.mixin.FlaggedElement ); + +/* Static properties */ + +/** + * Allowed input positions. + * - inline: The input is inside the tag list + * - outline: The input is under the tag list + * - none: There is no input + * + * @property {Array} + */ +OO.ui.TagMultiselectWidget.static.allowedInputPositions = [ 'inline', 'outline', 'none' ]; + +/* Methods */ + +/** + * Handle mouse down events. + * + * @private + * @param {jQuery.Event} e Mouse down event + * @return {boolean} False to prevent defaults + */ +OO.ui.TagMultiselectWidget.prototype.onMouseDown = function ( e ) { + if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) { + this.focus(); + return false; + } +}; + +/** + * Handle key press events. + * + * @private + * @param {jQuery.Event} e Key press event + * @return {boolean} Whether to prevent defaults + */ +OO.ui.TagMultiselectWidget.prototype.onInputKeyPress = function ( e ) { + var stopOrContinue, + withMetaKey = e.metaKey || e.ctrlKey; + + if ( !this.isDisabled() ) { + if ( e.which === OO.ui.Keys.ENTER ) { + stopOrContinue = this.doInputEnter( e, withMetaKey ); + } + + // Make sure the input gets resized. + setTimeout( this.updateInputSize.bind( this ), 0 ); + return stopOrContinue; + } +}; + +/** + * Handle key down events. + * + * @private + * @param {jQuery.Event} e Key down event + * @return {boolean} + */ +OO.ui.TagMultiselectWidget.prototype.onInputKeyDown = function ( e ) { + var movement, direction, + withMetaKey = e.metaKey || e.ctrlKey; + + if ( !this.isDisabled() ) { + // 'keypress' event is not triggered for Backspace + if ( e.keyCode === OO.ui.Keys.BACKSPACE ) { + return this.doInputBackspace( e, withMetaKey ); + } else if ( e.keyCode === OO.ui.Keys.ESCAPE ) { + return this.doInputEscape( e ); + } else if ( + e.keyCode === OO.ui.Keys.LEFT || + e.keyCode === OO.ui.Keys.RIGHT + ) { + if ( OO.ui.Element.static.getDir( this.$element ) === 'rtl' ) { + movement = { + left: 'forwards', + right: 'backwards' + }; + } else { + movement = { + left: 'backwards', + right: 'forwards' + }; + } + direction = e.keyCode === OO.ui.Keys.LEFT ? + movement.left : movement.right; + + return this.doInputArrow( e, direction, withMetaKey ); + } + } +}; + +/** + * Respond to input focus event + */ +OO.ui.TagMultiselectWidget.prototype.onInputFocus = function () {}; + +/** + * Respond to input blur event + */ +OO.ui.TagMultiselectWidget.prototype.onInputBlur = function () {}; + +/** + * Perform an action after the enter key on the input + * + * @param {jQuery.Event} e Event data + * @param {boolean} [withMetaKey] Whether this key was pressed with + * a meta key like 'ctrl' + * @return {boolean} Whether to prevent defaults + */ +OO.ui.TagMultiselectWidget.prototype.doInputEnter = function () { + this.addTagFromInput(); + return false; +}; + +/** + * Perform an action responding to the enter key on the input + * + * @param {jQuery.Event} e Event data + * @param {boolean} [withMetaKey] Whether this key was pressed with + * a meta key like 'ctrl' + * @return {boolean} Whether to prevent defaults + */ +OO.ui.TagMultiselectWidget.prototype.doInputBackspace = function () { + var items, item; + + if ( + this.inputPosition === 'inline' && + this.input.getValue() === '' && + !this.isEmpty() + ) { + // Delete the last item + items = this.getItems(); + item = items[ items.length - 1 ]; + this.input.setValue( item.getData() ); + this.removeItems( [ item ] ); + + return false; + } +}; + +/** + * Perform an action after the escape key on the input + * + * @param {jQuery.Event} e Event data + * @return {boolean} Whether to prevent defaults + */ +OO.ui.TagMultiselectWidget.prototype.doInputEscape = function () { + this.clearInput(); +}; + +/** + * Perform an action after the arrow key on the input, select the previous + * or next item from the input. + * See #getPreviousItem and #getNextItem + * + * @param {jQuery.Event} e Event data + * @param {string} direction Direction of the movement; forwards or backwards + * @param {boolean} [withMetaKey] Whether this key was pressed with + * a meta key like 'ctrl' + * @return {boolean} Whether to prevent defaults + */ +OO.ui.TagMultiselectWidget.prototype.doInputArrow = function ( direction ) { + if ( + this.inputPosition === 'inline' && + !this.isEmpty() + ) { + if ( direction === 'backwards' ) { + // Get previous item + this.getPreviousItem().focus(); + } else { + // Get next item + this.getNextItem().focus(); + } + } +}; + +/** + * Respond to item select event + */ +OO.ui.TagMultiselectWidget.prototype.onTagSelect = function ( item ) { + if ( this.hasInput && this.allowEditTags ) { + if ( this.input.getValue() ) { + this.addTagFromInput(); + } + // 1. Get the label of the tag into the input + this.input.setValue( item.getData() ); + // 2. Remove the tag + this.removeItems( [ item ] ); + // 3. Focus the input + this.focus(); + } +}; + +/** + * Respond to change event, where items were added, removed, or cleared. + */ +OO.ui.TagMultiselectWidget.prototype.onChangeTags = function () { + this.toggleValid( this.checkValidity() ); + if ( this.hasInput ) { + this.updateInputSize(); + } + this.updateIfHeightChanged(); +}; + +/** + * @inheritdoc + */ +OO.ui.TagMultiselectWidget.prototype.setDisabled = function ( isDisabled ) { + // Parent method + OO.ui.TagMultiselectWidget.parent.prototype.setDisabled.call( this, isDisabled ); + + if ( this.hasInput && this.input ) { + this.input.setDisabled( !!isDisabled ); + } + + if ( this.items ) { + this.getItems().forEach( function ( item ) { + item.setDisabled( !!isDisabled ); + } ); + } +}; + +/** + * Respond to tag remove event + * @param {OO.ui.TagItemWidget} item Removed tag + */ +OO.ui.TagMultiselectWidget.prototype.onTagRemove = function ( item ) { + this.removeTagByData( item.getData() ); +}; + +/** + * Respond to navigate event on the tag + * + * @param {OO.ui.TagItemWidget} item Removed tag + * @param {string} direction Direction of movement; 'forwards' or 'backwards' + */ +OO.ui.TagMultiselectWidget.prototype.onTagNavigate = function ( item, direction ) { + if ( direction === 'forwards' ) { + this.getNextItem( item ).focus(); + } else { + this.getPreviousItem( item ).focus(); + } +}; + +/** + * Add tag from input value + */ +OO.ui.TagMultiselectWidget.prototype.addTagFromInput = function () { + var val = this.input.getValue(), + isValid = this.isAllowedData( val ); + + if ( !val ) { + return; + } + + if ( isValid || this.allowDisplayInvalidTags ) { + this.addTag( val ); + this.clearInput(); + this.focus(); + } +}; + +/** + * Clear the input + */ +OO.ui.TagMultiselectWidget.prototype.clearInput = function () { + this.input.setValue( '' ); +}; + +/** + * Check whether the given value is a duplicate of an existing + * tag already in the list. + * + * @param {string|Object} data Requested value + * @return {boolean} Value is duplicate + */ +OO.ui.TagMultiselectWidget.prototype.isDuplicateData = function ( data ) { + return !!this.getItemFromData( data ); +}; + +/** + * Check whether a given value is allowed to be added + * + * @param {string|Object} data Requested value + * @return {boolean} Value exists in the allowed values list + */ +OO.ui.TagMultiselectWidget.prototype.isAllowedData = function ( data ) { + var hash = OO.getHash( data ); + + if ( this.allowArbitrary ) { + return true; + } + + if ( + !this.allowDuplicates && + this.isDuplicateData( data ) + ) { + return false; + } + + // Check with allowed values + if ( + this.getAllowedValues().some( function ( value ) { + return hash === OO.getHash( value ); + } ) + ) { + return true; + } + + return false; +}; + +/** + * Get the allowed values list + * + * @return {string[]} Allowed data values + */ +OO.ui.TagMultiselectWidget.prototype.getAllowedValues = function () { + return this.allowedValues; +}; + +/** + * Add a value to the allowed values list + * + * @param {string} value Allowed data value + */ +OO.ui.TagMultiselectWidget.prototype.addAllowedValue = function ( value ) { + if ( this.allowedValues.indexOf( value ) === -1 ) { + this.allowedValues.push( value ); + } +}; + +/** + * Focus the widget + */ +OO.ui.TagMultiselectWidget.prototype.focus = function () { + if ( this.hasInput ) { + this.input.focus(); + } +}; + +/** + * Get the datas of the currently selected items + * + * @return {string[]|Object[]} Datas of currently selected items + */ +OO.ui.TagMultiselectWidget.prototype.getValue = function () { + return this.getItems() + .filter( function ( item ) { + return item.isValid(); + } ) + .map( function ( item ) { + return item.getData(); + } ); +}; + +/** + * Set the value of this widget by datas. + * + * @param {string|string[]|Object|Object[]} value An object representing the data + * and label of the value. If the widget allows arbitrary values, + * the items will be added as-is. Otherwise, the data value will + * be checked against allowedValues. + * This object must contain at least a data key. Example: + * { data: 'foo', label: 'Foo item' } + * For multiple items, use an array of objects. For example: + * [ + * { data: 'foo', label: 'Foo item' }, + * { data: 'bar', label: 'Bar item' } + * ] + * Value can also be added with plaintext array, for example: + * [ 'foo', 'bar', 'bla' ] or a single string, like 'foo' + */ +OO.ui.TagMultiselectWidget.prototype.setValue = function ( valueObject ) { + valueObject = Array.isArray( valueObject ) ? valueObject : [ valueObject ]; + + this.clearItems(); + valueObject.forEach( function ( obj ) { + if ( typeof obj === 'string' ) { + this.addTag( obj ); + } else { + this.addTag( obj.data, obj.label ); + } + }.bind( this ) ); +}; + +/** + * Add tag to the display area + * + * @param {string|Object} data Tag data + * @param {string} [label] Tag label. If no label is provided, the + * stringified version of the data will be used instead. + * @return {boolean} Item was added successfully + */ +OO.ui.TagMultiselectWidget.prototype.addTag = function ( data, label ) { + var newItemWidget, + isValid = this.isAllowedData( data ); + + if ( isValid || this.allowDisplayInvalidTags ) { + newItemWidget = this.createTagItemWidget( data, label ); + newItemWidget.toggleValid( isValid ); + this.addItems( [ newItemWidget ] ); + } +}; + +/** + * Remove tag by its data property. + * + * @param {string|Object} data Tag data + */ +OO.ui.TagMultiselectWidget.prototype.removeTagByData = function ( data ) { + var item = this.getItemFromData( data ); + + this.removeItems( [ item ] ); +}; + +/** + * Construct a OO.ui.TagItemWidget (or a subclass thereof) from given label and data. + * + * @protected + * @param {string} data Item data + * @param {string} label The label text. + * @return {OO.ui.TagItemWidget} + */ +OO.ui.TagMultiselectWidget.prototype.createTagItemWidget = function ( data, label ) { + label = label || data; + + return new OO.ui.TagItemWidget( { data: data, label: label } ); +}; + +/** + * Given an item, returns the item after it. If the item is already the + * last item, return `this.input`. If no item is passed, returns the + * very first item. + * + * @protected + * @param {OO.ui.TagItemWidget} [item] Tag item + * @return {OO.ui.Widget} The next widget available. + */ +OO.ui.TagMultiselectWidget.prototype.getNextItem = function ( item ) { + var itemIndex = this.items.indexOf( item ); + + if ( item === undefined || itemIndex === -1 ) { + return this.items[ 0 ]; + } + + if ( itemIndex === this.items.length - 1 ) { // Last item + if ( this.hasInput ) { + return this.input; + } else { + // Return first item + return this.items[ 0 ]; + } + } else { + return this.items[ itemIndex + 1 ]; + } +}; + +/** + * Given an item, returns the item before it. If the item is already the + * first item, return `this.input`. If no item is passed, returns the + * very last item. + * + * @protected + * @param {OO.ui.TagItemWidget} [item] Tag item + * @return {OO.ui.Widget} The previous widget available. + */ +OO.ui.TagMultiselectWidget.prototype.getPreviousItem = function ( item ) { + var itemIndex = this.items.indexOf( item ); + + if ( item === undefined || itemIndex === -1 ) { + return this.items[ this.items.length - 1 ]; + } + + if ( itemIndex === 0 ) { + if ( this.hasInput ) { + return this.input; + } else { + // Return the last item + return this.items[ this.items.length - 1 ]; + } + } else { + return this.items[ itemIndex - 1 ]; + } +}; + +/** + * Update the dimensions of the text input field to encompass all available area. + * This is especially relevant for when the input is at the edge of a line + * and should get smaller. The usual operation (as an inline-block with min-width) + * does not work in that case, pushing the input downwards to the next line. + * + * @private + */ +OO.ui.TagMultiselectWidget.prototype.updateInputSize = function () { + var $lastItem, direction, contentWidth, currentWidth, bestWidth; + if ( this.inputPosition === 'inline' && !this.isDisabled() ) { + this.input.$input.css( 'width', '1em' ); + $lastItem = this.$group.children().last(); + direction = OO.ui.Element.static.getDir( this.$handle ); + + // Get the width of the input with the placeholder text as + // the value and save it so that we don't keep recalculating + if ( + this.contentWidthWithPlaceholder === undefined && + this.input.getValue() === '' && + this.input.$input.attr( 'placeholder' ) !== undefined + ) { + this.input.setValue( this.input.$input.attr( 'placeholder' ) ); + this.contentWidthWithPlaceholder = this.input.$input[ 0 ].scrollWidth; + this.input.setValue( '' ); + + } + + // Always keep the input wide enough for the placeholder text + contentWidth = Math.max( + this.input.$input[ 0 ].scrollWidth, + // undefined arguments in Math.max lead to NaN + ( this.contentWidthWithPlaceholder === undefined ) ? + 0 : this.contentWidthWithPlaceholder + ); + currentWidth = this.input.$input.width(); + + if ( contentWidth < currentWidth ) { + this.updateIfHeightChanged(); + // All is fine, don't perform expensive calculations + return; + } + + if ( $lastItem.length === 0 ) { + bestWidth = this.$content.innerWidth(); + } else { + bestWidth = direction === 'ltr' ? + this.$content.innerWidth() - $lastItem.position().left - $lastItem.outerWidth() : + $lastItem.position().left; + } + + // Some safety margin for sanity, because I *really* don't feel like finding out where the few + // pixels this is off by are coming from. + bestWidth -= 10; + if ( contentWidth > bestWidth ) { + // This will result in the input getting shifted to the next line + bestWidth = this.$content.innerWidth() - 10; + } + this.input.$input.width( Math.floor( bestWidth ) ); + this.updateIfHeightChanged(); + } else { + this.updateIfHeightChanged(); + } +}; + +/** + * Determine if widget height changed, and if so, + * emit the resize event. This is useful for when there are either + * menus or popups attached to the bottom of the widget, to allow + * them to change their positioning in case the widget moved down + * or up. + * + * @private + */ +OO.ui.TagMultiselectWidget.prototype.updateIfHeightChanged = function () { + var height = this.$element.height(); + if ( height !== this.height ) { + this.height = height; + this.emit( 'resize' ); + } +}; + +/** + * Check whether all items in the widget are valid + * + * @return {boolean} Widget is valid + */ +OO.ui.TagMultiselectWidget.prototype.checkValidity = function () { + return this.getItems().every( function ( item ) { + return item.isValid(); + } ); +}; + +/** + * Set the valid state of this item + * + * @param {boolean} [valid] Item is valid + * @fires valid + */ +OO.ui.TagMultiselectWidget.prototype.toggleValid = function ( valid ) { + valid = valid === undefined ? !this.valid : !!valid; + + if ( this.valid !== valid ) { + this.valid = valid; + + this.setFlags( { invalid: !this.valid } ); + + this.emit( 'valid', this.valid ); + } +}; + +/** + * Get the current valid state of the widget + * + * @return {boolean} Widget is valid + */ +OO.ui.TagMultiselectWidget.prototype.isValid = function () { + return this.valid; +}; + +/** + * PopupTagMultiselectWidget is a {@link OO.ui.TagMultiselectWidget OO.ui.TagMultiselectWidget} intended + * to use a popup. The popup can be configured to have a default input to insert values into the widget. + * + * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1]. + * + * @example + * // Example: A basic PopupTagMultiselectWidget. + * var widget = new OO.ui.PopupTagMultiselectWidget(); + * $( 'body' ).append( widget.$element ); + * + * // Example: A PopupTagMultiselectWidget with an external popup. + * var popupInput = new OO.ui.TextInputWidget(), + * widget = new OO.ui.PopupTagMultiselectWidget( { + * popupInput: popupInput, + * popup: { + * $content: popupInput.$element + * } + * } ); + * $( 'body' ).append( widget.$element ); + * + * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options + * + * @class + * @extends OO.ui.TagMultiselectWidget + * @mixins OO.ui.mixin.PopupElement + * + * @param {Object} config Configuration object + * @cfg {jQuery} [$overlay] An overlay for the popup + * @cfg {Object} [popup] Configuration options for the popup + * @cfg {OO.ui.InputWidget} [popupInput] An input widget inside the popup that will be + * focused when the popup is opened and will be used as replacement for the + * general input in the widget. + */ +OO.ui.PopupTagMultiselectWidget = function OoUiPopupTagMultiselectWidget( config ) { + var defaultInput, + defaultConfig = { popup: {} }; + + config = config || {}; + + // Parent constructor + OO.ui.PopupTagMultiselectWidget.parent.call( this, $.extend( { inputPosition: 'none' }, config ) ); + + this.$overlay = config.$overlay || this.$element; + + if ( !config.popup ) { + // For the default base implementation, we give a popup + // with an input widget inside it. For any other use cases + // the popup needs to be populated externally and the + // event handled to add tags separately and manually + defaultInput = new OO.ui.TextInputWidget(); + + defaultConfig.popupInput = defaultInput; + defaultConfig.popup.$content = defaultInput.$element; + + this.$element.addClass( 'oo-ui-popupTagMultiselectWidget-defaultPopup' ); + } + + // Add overlay, and add that to the autoCloseIgnore + defaultConfig.popup.$overlay = this.$overlay; + defaultConfig.popup.$autoCloseIgnore = this.hasInput ? + this.input.$element.add( this.$overlay ) : this.$overlay; + + // Allow extending any of the above + config = $.extend( defaultConfig, config ); + + // Mixin constructors + OO.ui.mixin.PopupElement.call( this, config ); + + if ( this.hasInput ) { + this.input.$input.on( 'focus', this.popup.toggle.bind( this.popup, true ) ); + } + + // Configuration options + this.popupInput = config.popupInput; + if ( this.popupInput ) { + this.popupInput.connect( this, { + enter: 'onPopupInputEnter' + } ); + } + + // Events + this.popup.connect( this, { toggle: 'onPopupToggle' } ); + this.$tabIndexed + .on( 'focus', this.focus.bind( this ) ); + + // Initialize + this.$element + .append( this.popup.$element ) + .addClass( 'oo-ui-popupTagMultiselectWidget' ); +}; + +/* Initialization */ + +OO.inheritClass( OO.ui.PopupTagMultiselectWidget, OO.ui.TagMultiselectWidget ); +OO.mixinClass( OO.ui.PopupTagMultiselectWidget, OO.ui.mixin.PopupElement ); + +/* Methods */ + +/** + * @inheritdoc + */ +OO.ui.PopupTagMultiselectWidget.prototype.focus = function () { + // Since the parent deals with input focus, only + // call the parent method if our input isn't in the + // popup + if ( !this.popupInput ) { + // Parent method + OO.ui.PopupTagMultiselectWidget.parent.prototype.focus.call( this ); + } + + this.popup.toggle( true ); +}; + +/** + * Respond to popup toggle event + * + * @param {boolean} isVisible Popup is visible + */ +OO.ui.PopupTagMultiselectWidget.prototype.onPopupToggle = function ( isVisible ) { + if ( isVisible && this.popupInput ) { + this.popupInput.focus(); + } +}; + +/** + * Respond to popup input enter event + */ +OO.ui.PopupTagMultiselectWidget.prototype.onPopupInputEnter = function () { + if ( this.popupInput ) { + this.addTagByPopupValue( this.popupInput.getValue() ); + this.popupInput.setValue( '' ); + } +}; + +/** + * @inheritdoc + */ +OO.ui.PopupTagMultiselectWidget.prototype.onTagSelect = function ( item ) { + if ( this.popupInput && this.allowEditTags ) { + this.popupInput.setValue( item.getData() ); + this.removeItems( [ item ] ); + + this.popup.toggle( true ); + this.popupInput.focus(); + } else { + // Parent + OO.ui.PopupTagMultiselectWidget.parent.prototype.onTagSelect.call( this, item ); + } +}; + +/** + * Add a tag by the popup value. + * Whatever is responsible for setting the value in the popup should call + * this method to add a tag, or use the regular methods like #addTag or + * #setValue directly. + * + * @param {string} data The value of item + * @param {string} [label] The label of the tag. If not given, the data is used. + */ +OO.ui.PopupTagMultiselectWidget.prototype.addTagByPopupValue = function ( data, label ) { + this.addTag( data, label ); +}; + +/** + * MenuTagMultiselectWidget is a {@link OO.ui.TagMultiselectWidget OO.ui.TagMultiselectWidget} intended + * to use a menu of selectable options. + * + * For more information about menus and options, please see the [OOjs UI documentation on MediaWiki][1]. + * + * @example + * // Example: A basic MenuTagMultiselectWidget. + * var widget = new OO.ui.MenuTagMultiselectWidget( { + * inputPosition: 'outline', + * options: [ + * { data: 'option1', label: 'Option 1' }, + * { data: 'option2', label: 'Option 2' }, + * { data: 'option3', label: 'Option 3' }, + * ], + * selected: [ 'option1', 'option2' ] + * } ); + * $( 'body' ).append( widget.$element ); + * + * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Selects_and_Options#Menu_selects_and_options + * + * @class + * @extends OO.ui.TagMultiselectWidget + * + * @constructor + * @param {Object} [config] Configuration object + * @cfg {Object} [menu] Configuration object for the menu widget + * @cfg {jQuery} [$overlay] An overlay for the menu + * @cfg {Object[]} [options=[]] Array of menu options in the format `{ data: …, label: … }` + */ +OO.ui.MenuTagMultiselectWidget = function OoUiMenuTagMultiselectWidget( config ) { + config = config || {}; + + // Parent constructor + OO.ui.MenuTagMultiselectWidget.parent.call( this, config ); + + this.$overlay = config.$overlay || this.$element; + + this.menu = this.createMenuWidget( $.extend( { + widget: this, + input: this.hasInput ? this.input : null, + $input: this.hasInput ? this.input.$input : null, + filterFromInput: !!this.hasInput, + $autoCloseIgnore: this.hasInput ? + this.input.$element.add( this.$overlay ) : this.$overlay, + $container: this.hasInput && this.inputPosition === 'outline' ? + this.input.$element : this.$element, + $overlay: this.$overlay, + disabled: this.isDisabled() + }, config.menu ) ); + this.addOptions( config.options || [] ); + + // Events + this.menu.connect( this, { + choose: 'onMenuChoose', + toggle: 'onMenuToggle' + } ); + if ( this.hasInput ) { + this.input.connect( this, { change: 'onInputChange' } ); + } + this.connect( this, { resize: 'onResize' } ); + + // Initialization + this.$overlay + .append( this.menu.$element ); + this.$element + .addClass( 'oo-ui-menuTagMultiselectWidget' ); +}; + +/* Initialization */ + +OO.inheritClass( OO.ui.MenuTagMultiselectWidget, OO.ui.TagMultiselectWidget ); + +/* Methods */ + +/** + * Respond to resize event + */ +OO.ui.MenuTagMultiselectWidget.prototype.onResize = function () { + // Reposition the menu + this.menu.position(); +}; + +/** + * @inheritdoc + */ +OO.ui.MenuTagMultiselectWidget.prototype.onInputFocus = function () { + // Parent method + OO.ui.MenuTagMultiselectWidget.parent.prototype.onInputFocus.call( this ); + + this.menu.toggle( true ); +}; + +/** + * Respond to input change event + */ +OO.ui.MenuTagMultiselectWidget.prototype.onInputChange = function () { + this.menu.toggle( true ); +}; + +/** + * Respond to menu choose event + * + * @param {OO.ui.OptionWidget} menuItem Chosen menu item + */ +OO.ui.MenuTagMultiselectWidget.prototype.onMenuChoose = function ( menuItem ) { + // Add tag + this.addTag( menuItem.getData(), menuItem.getLabel() ); +}; + +/** + * Respond to menu toggle event. Reset item highlights on hide. + * + * @param {boolean} isVisible The menu is visible + */ +OO.ui.MenuTagMultiselectWidget.prototype.onMenuToggle = function ( isVisible ) { + if ( !isVisible ) { + this.menu.selectItem( null ); + this.menu.highlightItem( null ); + } +}; + +/** + * @inheritdoc + */ +OO.ui.MenuTagMultiselectWidget.prototype.onTagSelect = function ( tagItem ) { + var menuItem = this.menu.getItemFromData( tagItem.getData() ); + // Override the base behavior from TagMultiselectWidget; the base behavior + // in TagMultiselectWidget is to remove the tag to edit it in the input, + // but in our case, we want to utilize the menu selection behavior, and + // definitely not remove the item. + + // Select the menu item + this.menu.selectItem( menuItem ); + + this.focus(); +}; + +/** + * @inheritdoc + */ +OO.ui.MenuTagMultiselectWidget.prototype.addTagFromInput = function () { + var inputValue = this.input.getValue(), + highlightedItem = this.menu.getHighlightedItem(), + item = this.menu.getItemFromData( inputValue ); + + // Override the parent method so we add from the menu + // rather than directly from the input + + // Look for a highlighted item first + if ( highlightedItem ) { + this.addTag( highlightedItem.getData(), highlightedItem.getLabel() ); + } else if ( item ) { + // Look for the element that fits the data + this.addTag( item.getData(), item.getLabel() ); + } else { + // Otherwise, add the tag - the method will only add if the + // tag is valid or if invalid tags are allowed + this.addTag( inputValue ); + } +}; + +/** + * Return the visible items in the menu. This is mainly used for when + * the menu is filtering results. + * + * @return {OO.ui.MenuOptionWidget[]} Visible results + */ +OO.ui.MenuTagMultiselectWidget.prototype.getMenuVisibleItems = function () { + return this.menu.getItems().filter( function ( menuItem ) { + return menuItem.isVisible(); + } ); +}; + +/** + * Create the menu for this widget. This is in a separate method so that + * child classes can override this without polluting the constructor with + * unnecessary extra objects that will be overidden. + * + * @param {Object} menuConfig Configuration options + * @return {OO.ui.MenuSelectWidget} Menu widget + */ +OO.ui.MenuTagMultiselectWidget.prototype.createMenuWidget = function ( menuConfig ) { + return new OO.ui.FloatingMenuSelectWidget( menuConfig ); +}; + +/** + * Add options to the menu + * + * @param {Object[]} options Object defining options + */ +OO.ui.MenuTagMultiselectWidget.prototype.addOptions = function ( menuOptions ) { + var widget = this, + items = menuOptions.map( function ( obj ) { + return widget.createMenuOptionWidget( obj.data, obj.label ); + } ); + + this.menu.addItems( items ); +}; + +/** + * Create a menu option widget. + * + * @param {string} data Item data + * @param {string} [label] Item label + * @return {OO.ui.OptionWidget} Option widget + */ +OO.ui.MenuTagMultiselectWidget.prototype.createMenuOptionWidget = function ( data, label ) { + return new OO.ui.MenuOptionWidget( { + data: data, + label: label || data + } ); +}; + +/** + * Get the menu + * + * @return {OO.ui.MenuSelectWidget} Menu + */ +OO.ui.MenuTagMultiselectWidget.prototype.getMenu = function () { + return this.menu; +}; + +/** + * @inheritdoc + */ +OO.ui.MenuTagMultiselectWidget.prototype.isAllowedData = function ( data ) { + return OO.ui.MenuTagMultiselectWidget.parent.prototype.isAllowedData.call( this, data ) && + !!this.menu.getItemFromData( data ); +}; + +/** + * @inheritdoc + */ +OO.ui.MenuTagMultiselectWidget.prototype.focus = function () { + // Parent method + OO.ui.MenuTagMultiselectWidget.parent.prototype.focus.call( this ); + + if ( !this.isDisabled() ) { + this.menu.toggle( true ); + } +}; + /** * SelectFileWidgets allow for selecting files, using the HTML5 File API. These * widgets can be configured with {@link OO.ui.mixin.IconElement icons} and {@link diff --git a/resources/lib/oojs-ui/oojs-ui-windows-apex.css b/resources/lib/oojs-ui/oojs-ui-windows-apex.css index 4842a46586..013d52d3fb 100644 --- a/resources/lib/oojs-ui/oojs-ui-windows-apex.css +++ b/resources/lib/oojs-ui/oojs-ui-windows-apex.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ .oo-ui-actionWidget.oo-ui-pendingElement-pending { background-image: /* @embed */ url(themes/apex/images/textures/pending.gif); @@ -404,7 +404,7 @@ top: 1em; bottom: 1em; max-height: 100%; - max-height: calc(100% - 2em); + max-height: calc( 100% - 2em ); border: 1px solid #ccc; border-radius: 0.5em; box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.3); diff --git a/resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css b/resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css index beca51076c..1cb55d62f7 100644 --- a/resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css +++ b/resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:41Z + * Date: 2017-04-11T22:51:10Z */ .oo-ui-window { background: transparent; diff --git a/resources/lib/oojs-ui/oojs-ui-windows.js b/resources/lib/oojs-ui/oojs-ui-windows.js index 1cbec34976..b23949f123 100644 --- a/resources/lib/oojs-ui/oojs-ui-windows.js +++ b/resources/lib/oojs-ui/oojs-ui-windows.js @@ -1,12 +1,12 @@ /*! - * OOjs UI v0.20.2 + * OOjs UI v0.21.0 * https://www.mediawiki.org/wiki/OOjs_UI * * Copyright 2011–2017 OOjs UI Team and other contributors. * Released under the MIT license * http://oojs.mit-license.org * - * Date: 2017-03-30T20:34:37Z + * Date: 2017-04-11T22:51:05Z */ ( function ( OO ) { @@ -60,14 +60,6 @@ OO.ui.ActionWidget = function OoUiActionWidget( config ) { OO.inheritClass( OO.ui.ActionWidget, OO.ui.ButtonWidget ); OO.mixinClass( OO.ui.ActionWidget, OO.ui.mixin.PendingElement ); -/* Events */ - -/** - * A resize event is emitted when the size of the widget changes. - * - * @event resize - */ - /* Methods */ /** @@ -102,90 +94,6 @@ OO.ui.ActionWidget.prototype.getModes = function () { return this.modes.slice(); }; -/** - * Emit a resize event if the size has changed. - * - * @private - * @chainable - */ -OO.ui.ActionWidget.prototype.propagateResize = function () { - var width, height; - - if ( this.isElementAttached() ) { - width = this.$element.width(); - height = this.$element.height(); - - if ( width !== this.width || height !== this.height ) { - this.width = width; - this.height = height; - this.emit( 'resizePrivate' ); - if ( this.emit( 'resize' ) ) { - OO.ui.warnDeprecation( 'ActionWidget: resize event is deprecated. See T129162.' ); - } - } - } - - return this; -}; - -/** - * @inheritdoc - */ -OO.ui.ActionWidget.prototype.setIcon = function () { - // Mixin method - OO.ui.mixin.IconElement.prototype.setIcon.apply( this, arguments ); - this.propagateResize(); - - return this; -}; - -/** - * @inheritdoc - */ -OO.ui.ActionWidget.prototype.setLabel = function () { - // Mixin method - OO.ui.mixin.LabelElement.prototype.setLabel.apply( this, arguments ); - this.propagateResize(); - - return this; -}; - -/** - * @inheritdoc - */ -OO.ui.ActionWidget.prototype.setFlags = function () { - // Mixin method - OO.ui.mixin.FlaggedElement.prototype.setFlags.apply( this, arguments ); - this.propagateResize(); - - return this; -}; - -/** - * @inheritdoc - */ -OO.ui.ActionWidget.prototype.clearFlags = function () { - // Mixin method - OO.ui.mixin.FlaggedElement.prototype.clearFlags.apply( this, arguments ); - this.propagateResize(); - - return this; -}; - -/** - * Toggle the visibility of the action button. - * - * @param {boolean} [show] Show button, omit to toggle visibility - * @chainable - */ -OO.ui.ActionWidget.prototype.toggle = function () { - // Parent method - OO.ui.ActionWidget.parent.prototype.toggle.apply( this, arguments ); - this.propagateResize(); - - return this; -}; - /* eslint-disable no-unused-vars */ /** * ActionSets manage the behavior of the {@link OO.ui.ActionWidget action widgets} that comprise them. @@ -320,14 +228,6 @@ OO.ui.ActionSet.static.specialFlags = [ 'safe', 'primary' ]; * @param {OO.ui.ActionWidget} action Action that was clicked */ -/** - * @event resize - * - * A 'resize' event is emitted when an action widget is resized. - * - * @param {OO.ui.ActionWidget} action Action that was resized - */ - /** * @event add * @@ -566,11 +466,6 @@ OO.ui.ActionSet.prototype.add = function ( actions ) { click: [ 'emit', 'click', action ], toggle: [ 'onActionChange' ] } ); - action.on( 'resizePrivate', function ( action ) { - if ( this.emit( 'resize', action ) ) { - OO.ui.warnDeprecation( 'ActionSet: resize event is deprecated. See T129162.' ); - } - }, [ action ], this ); this.list.push( action ); } this.organized = false; @@ -2842,6 +2737,7 @@ OO.ui.MessageDialog.prototype.getActionProcess = function ( action ) { * @param {Object} [data] Dialog opening data * @param {jQuery|string|Function|null} [data.title] Description of the action being confirmed * @param {jQuery|string|Function|null} [data.message] Description of the action's consequence + * @param {string} [data.size] Symbolic name of the dialog size, see OO.ui.Window * @param {Object[]} [data.actions] List of OO.ui.ActionOptionWidget configuration options for each * action item */ @@ -2857,6 +2753,7 @@ OO.ui.MessageDialog.prototype.getSetupProcess = function ( data ) { this.message.setLabel( data.message !== undefined ? data.message : this.constructor.static.message ); + this.size = data.size !== undefined ? data.size : this.constructor.static.size; }, this ); }; @@ -3408,6 +3305,8 @@ OO.ui.getWindowManager = function () { * console.log( 'User closed the dialog.' ); * } ); * + * OO.ui.alert( 'Something larger happened!', { size: 'large' } ); + * * @param {jQuery|string} text Message text to display * @param {Object} [options] Additional options, see OO.ui.MessageDialog#getSetupProcess * @return {jQuery.Promise} Promise resolved when the user closes the dialog diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.svg index 9dd3404f4b..c1944975e0 100644 --- a/resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.svg +++ b/resources/lib/oojs-ui/themes/apex/images/icons/articles-ltr.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.svg index b57dae255f..40a9348f22 100644 --- a/resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.svg +++ b/resources/lib/oojs-ui/themes/apex/images/icons/articles-rtl.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.svg index 884e55c3ea..86b004f82f 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-invert.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg index 599aa6951a..838655bacb 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr-progressive.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.svg index 9dd3404f4b..c1944975e0 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-ltr.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.svg index 64b918157c..e2793b5aae 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-invert.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg index 18799af2be..bf3d169f3f 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl-progressive.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.svg index b57dae255f..40a9348f22 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articles-rtl.svg @@ -1,4 +1,4 @@ - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.png index 582adceb760aca3e81a6517bdaadb698a7a66077..ae66af4883c5f5cda4082d5b3ef0c5c83ec6634e 100644 GIT binary patch delta 123 zcmV->0EGW_t^trabzn(EK~yM_#g8ElfG`jQw*>ehKdHnaNwC#OWDPIF4}t^HP^f~D zmzhmY`V7F8c$LXs(gM_(u>lBIV00EWi9AW_C2i@7=O6+t;DBJaG55zAt-$;!-$d_n d5|zpSvjdX{VvT>6GPeK#002ovPDHLkV1k7HG@k$f delta 66 zcmZ3-SUSPdM%~lJF+^ixa)N}~ga7~k^Zw^-W(zVgoscA4cGSY;VR+B7PK%?={0#3o VizZLD6)s=^0#8>zmvv4FO#oJ)7*hZM diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg index e194d0daea..b5e6144062 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down-invert.svg @@ -1,6 +1,4 @@ - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-down.png index af67c7ad2afecc1c5ca4831a2ab0fe9ff425fba0..34f50419061e05d66755c5482fd539dc1d89303c 100644 GIT binary patch delta 130 zcmV-|0Db>Vhd}m2SK~yM_&CW3nfRZ9F+^ixa)N~2g2u+ihxUwj9J - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr-invert.png index 05578bed7aa359bf2cda935edc993357792d0ca7..68bf47509281830a363a7cb7e578617e8f01e651 100644 GIT binary patch delta 113 zcmV-%0FM8L0iywsIcil&L_t(2k)@C!4uC)m1h;puCOiPnB2X9%za)bIMF?$S^-Png z^k+a1jDSU817^;Jr2;Hpq%|v5V0)6vo9A9mcU+&EGVtKR_l8|klhm16tijs>%=v|h T7L-_y00000NkvXXu0mjftky1F delta 84 zcmZ3?*v2@)($~V%#W6%Qo6Nv oOMy`0Q4gt(V4kZEdyzopr0PB1qeE - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.png index d7a41110506c662e98f671a57e8d38b772d2860c..cadc38f2dd4f517d5da5d93b98b8b6f42a987fb9 100644 GIT binary patch delta 107 zcmV-x0F?iKo&k_KWl%{(K~yM_rOz=AKtLD;;cq#$H~?CON~3X0S(Qd)?a!-cUNZb# zj}fT`PFRp5QiJu2v_+*B`;*iZq+IiSom6)u%n^8agYOLoCUgP~#hq)?6ZrOqg#-Wq N002ovPDHLkV1he>E7Je~ delta 76 zcmbQoSU4?p4296c^+Be8)k81Wvk960O+T=>voj()^O4FTk;5he gYiBfu9A#l-_%L6j_|JUL0}Mdm>FVdQ&MBb@0C|TUwEzGB diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.svg index 059372d580..e99855b3d0 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-ltr.svg @@ -1,6 +1,4 @@ - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.png index 9731658bb914464ef2d9152e012e22eb58ec7a88..fae56f1a040d689430558c053e106ce29638998c 100644 GIT binary patch delta 110 zcmV-!0FnQM0iXepIcHKyL_t(2k*$s~4uBvK1osS|#-7;H*w9eR diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg index d032b05569..bc0ff6f562 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl-invert.svg @@ -1,6 +1,4 @@ - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.png index afceed6cee25917bf602d53cee4013c8f0d44b0b..5a4c0b69c657a49055b6b7901d8e34cdd5393c9b 100644 GIT binary patch delta 112 zcmb4?m4295-^+BS-OBo{#9eH1L8JZXfzIIz>$vInXQmzuG f$%Ivm%nS^x-is99=v?xK0SG)@{an^LB{Ts5&2ky5 diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.svg index c6498e82aa..16475014d9 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.svg +++ b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-rtl.svg @@ -1,6 +1,4 @@ - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up-invert.png index 218a7005c6402d84fcab9889b3d39cd636851997..56eeea451794de32ed768958d05762a3c633ff38 100644 GIT binary patch delta 145 zcmV;C0B-+z!vT;ve|1SjK~yM_#gMBG!cY)I&$N6l0?QY)7KNxnzz-5^59U&CN?QyG zldRZz>?Q+$ff_YS+6P&TU0IJw{C2tbjehOZ+ z?22{=&cHs&I5m=t>H&BF6JNKpS;&_DJ3IlaNr~~F%HB`_3jhEBNkvXXu0mjfowh;G delta 70 zcmX@iSUJJcLEF>CF+^ixa)N}^ga7~k^8#_cf~z6VgX=BJB=)PxbZyL5S}?=nIj - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up.png b/resources/lib/oojs-ui/themes/mediawiki/images/indicators/arrow-up.png index ee12d6e8f8efc09bda6625c4faa0798bbd674d54..672b7f37c26c199a03918c5516c7cffb20072fdd 100644 GIT binary patch delta 130 zcmV-|0Db>-wE>Vhd}m2SK~yM_#n3SdgHRAf(W8P}%AT^4w5hEubPqP!hl??$8DS9o z3MqW>nwfiH8170Hy$rvir_u(m(V~>$5kpDJaE~V19Z4J9p-%G}?<8qr@xY8HzDZay kp~ZsF{;X0izxluX0FU4o1`%iY0RR9107*qoM6N<$f;#9s1poj5 delta 65 zcmdnWSTe!VTFukNF+^ixa)N}>0>-Y+us`x$oLNp`J^{jJM=eYr2K#LJ+{VSg!2eO= Ut){ - - - + diff --git a/resources/lib/oojs-ui/themes/mediawiki/indicators.json b/resources/lib/oojs-ui/themes/mediawiki/indicators.json index df32417c8c..1b9936547a 100644 --- a/resources/lib/oojs-ui/themes/mediawiki/indicators.json +++ b/resources/lib/oojs-ui/themes/mediawiki/indicators.json @@ -6,18 +6,6 @@ "invert": { "color": "#fff", "global": true - }, - "progressive": { - "color": "#36c" - }, - "constructive": { - "color": "#36c" - }, - "destructive": { - "color": "#d33" - }, - "warning": { - "color": "#ff5d00" } }, "images": { -- 2.20.1