Gergő Tisza has uploaded a new change for review.
https://gerrit.wikimedia.org/r/313623
Change subject: [TEST] Make PopupElement extend over containing blocks
......................................................................
[TEST] Make PopupElement extend over containing blocks
Bug: T146531
Change-Id: I204dca07378be25b6977be672263cc767bbf5fe7
---
M resources/lib/oojs-ui/oojs-ui-core.js
M resources/src/mediawiki.special/mediawiki.special.apisandbox.js
2 files changed, 220 insertions(+), 208 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/23/313623/1
diff --git a/resources/lib/oojs-ui/oojs-ui-core.js
b/resources/lib/oojs-ui/oojs-ui-core.js
index c982010..0df24db 100644
--- a/resources/lib/oojs-ui/oojs-ui-core.js
+++ b/resources/lib/oojs-ui/oojs-ui-core.js
@@ -4600,6 +4600,213 @@
};
/**
+ * Element that will stick under a specified container, even when it is
inserted elsewhere in the
+ * document (for example, in a OO.ui.Window's $overlay).
+ *
+ * The elements's position is automatically calculated and maintained when
window is resized or the
+ * page is scrolled. If you reposition the container manually, you have to
call #position to make
+ * sure the element is still placed correctly.
+ *
+ * As positioning is only possible when both the element and the container are
attached to the DOM
+ * and visible, it's only done after you call #togglePositioning. You might
want to do this inside
+ * the #toggle method to display a floating popup, for example.
+ *
+ * @abstract
+ * @class
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {jQuery} [$floatable] Node to position, assigned to #$floatable, omit
to use #$element
+ * @cfg {jQuery} [$floatableContainer] Node to position below
+ */
+OO.ui.mixin.FloatableElement = function OoUiMixinFloatableElement( config ) {
+ // Configuration initialization
+ config = config || {};
+
+ // Properties
+ this.$floatable = null;
+ this.$floatableContainer = null;
+ this.$floatableWindow = null;
+ this.$floatableClosestScrollable = null;
+ this.onFloatableScrollHandler = this.position.bind( this );
+ this.onFloatableWindowResizeHandler = this.position.bind( this );
+
+ // Initialization
+ this.setFloatableContainer( config.$floatableContainer );
+ this.setFloatableElement( config.$floatable || this.$element );
+};
+
+/* Methods */
+
+/**
+ * Set floatable element.
+ *
+ * If an element is already set, it will be cleaned up before setting up the
new element.
+ *
+ * @param {jQuery} $floatable Element to make floatable
+ */
+OO.ui.mixin.FloatableElement.prototype.setFloatableElement = function (
$floatable ) {
+ if ( this.$floatable ) {
+ this.$floatable.removeClass( 'oo-ui-floatableElement-floatable'
);
+ this.$floatable.css( { left: '', top: '' } );
+ }
+
+ this.$floatable = $floatable.addClass(
'oo-ui-floatableElement-floatable' );
+ this.position();
+};
+
+/**
+ * Set floatable container.
+ *
+ * The element will be always positioned under the specified container.
+ *
+ * @param {jQuery|null} $floatableContainer Container to keep visible, or null
to unset
+ */
+OO.ui.mixin.FloatableElement.prototype.setFloatableContainer = function (
$floatableContainer ) {
+ this.$floatableContainer = $floatableContainer;
+ if ( this.$floatable ) {
+ this.position();
+ }
+};
+
+/**
+ * Toggle positioning.
+ *
+ * Do not turn positioning on until after the element is attached to the DOM
and visible.
+ *
+ * @param {boolean} [positioning] Enable positioning, omit to toggle
+ * @chainable
+ */
+OO.ui.mixin.FloatableElement.prototype.togglePositioning = function (
positioning ) {
+ var closestScrollableOfContainer, closestScrollableOfFloatable;
+
+ positioning = positioning === undefined ? !this.positioning :
!!positioning;
+
+ if ( this.positioning !== positioning ) {
+ this.positioning = positioning;
+
+ closestScrollableOfContainer =
OO.ui.Element.static.getClosestScrollableContainer( this.$floatableContainer[ 0
] );
+ closestScrollableOfFloatable =
OO.ui.Element.static.getClosestScrollableContainer( this.$floatable[ 0 ] );
+ this.needsCustomPosition = closestScrollableOfContainer !==
closestScrollableOfFloatable;
+ // If the scrollable is the root, we have to listen to scroll
events
+ // on the window because of browser inconsistencies.
+ if ( $( closestScrollableOfContainer ).is( 'html, body' ) ) {
+ closestScrollableOfContainer =
OO.ui.Element.static.getWindow( closestScrollableOfContainer );
+ }
+
+ if ( positioning ) {
+ this.$floatableWindow = $( this.getElementWindow() );
+ this.$floatableWindow.on( 'resize',
this.onFloatableWindowResizeHandler );
+
+ this.$floatableClosestScrollable = $(
closestScrollableOfContainer );
+ this.$floatableClosestScrollable.on( 'scroll',
this.onFloatableScrollHandler );
+
+ // Initial position after visible
+ this.position();
+ } else {
+ if ( this.$floatableWindow ) {
+ this.$floatableWindow.off( 'resize',
this.onFloatableWindowResizeHandler );
+ this.$floatableWindow = null;
+ }
+
+ if ( this.$floatableClosestScrollable ) {
+ this.$floatableClosestScrollable.off( 'scroll',
this.onFloatableScrollHandler );
+ this.$floatableClosestScrollable = null;
+ }
+
+ this.$floatable.css( { left: '', top: '' } );
+ }
+ }
+
+ return this;
+};
+
+/**
+ * Check whether the bottom edge of the given element is within the viewport
of the given container.
+ *
+ * @private
+ * @param {jQuery} $element
+ * @param {jQuery} $container
+ * @return {boolean}
+ */
+OO.ui.mixin.FloatableElement.prototype.isElementInViewport = function (
$element, $container ) {
+ var elemRect, contRect,
+ leftEdgeInBounds = false,
+ bottomEdgeInBounds = false,
+ rightEdgeInBounds = false;
+
+ elemRect = $element[ 0 ].getBoundingClientRect();
+ if ( $container[ 0 ] === window ) {
+ contRect = {
+ top: 0,
+ left: 0,
+ right: document.documentElement.clientWidth,
+ bottom: document.documentElement.clientHeight
+ };
+ } else {
+ contRect = $container[ 0 ].getBoundingClientRect();
+ }
+
+ // For completeness, if we still cared about topEdgeInBounds, that'd be:
+ // elemRect.top >= contRect.top && elemRect.top <= contRect.bottom
+ if ( elemRect.left >= contRect.left && elemRect.left <= contRect.right
) {
+ leftEdgeInBounds = true;
+ }
+ if ( elemRect.bottom >= contRect.top && elemRect.bottom <=
contRect.bottom ) {
+ bottomEdgeInBounds = true;
+ }
+ if ( elemRect.right >= contRect.left && elemRect.right <=
contRect.right ) {
+ rightEdgeInBounds = true;
+ }
+
+ // We only care that any part of the bottom edge is visible
+ return bottomEdgeInBounds && ( leftEdgeInBounds || rightEdgeInBounds );
+};
+
+/**
+ * Position the floatable below its container.
+ *
+ * This should only be done when both of them are attached to the DOM and
visible.
+ *
+ * @chainable
+ */
+OO.ui.mixin.FloatableElement.prototype.position = function () {
+ var pos;
+
+ if ( !this.positioning ) {
+ return this;
+ }
+
+ if ( !this.isElementInViewport( this.$floatableContainer,
this.$floatableClosestScrollable ) ) {
+ this.$floatable.addClass( 'oo-ui-floatableElement-hidden' );
+ return;
+ } else {
+ this.$floatable.removeClass( 'oo-ui-floatableElement-hidden' );
+ }
+
+ if ( !this.needsCustomPosition ) {
+ return;
+ }
+
+ pos = OO.ui.Element.static.getRelativePosition(
this.$floatableContainer, this.$floatable.offsetParent() );
+
+ // Position under container
+ pos.top += this.$floatableContainer.height();
+ this.$floatable.css( pos );
+
+ // We updated the position, so re-evaluate the clipping state.
+ // (ClippableElement does not listen to 'scroll' events on
$floatableContainer's parent, and so
+ // will not notice the need to update itself.)
+ // TODO: This is terrible, we shouldn't need to know about
ClippableElement at all here. Why does
+ // it not listen to the right events in the right places?
+ if ( this.clip ) {
+ this.clip();
+ }
+
+ return this;
+};
+
+/**
* PopupElement is mixed into other classes to generate a {@link
OO.ui.PopupWidget popup widget}.
* A popup is a container for content. It is overlaid and positioned
absolutely. By default, each
* popup has an anchor, which is an arrow-like protrusion that points toward
the popup’s origin.
@@ -4607,6 +4814,7 @@
*
* @abstract
* @class
+ * @mixins OO.ui.mixin.FloatableElement
*
* @constructor
* @param {Object} [config] Configuration options
@@ -4623,7 +4831,16 @@
config.popup,
{ $autoCloseIgnore: this.$element }
) );
+
+ OO.ui.mixin.FloatableElement.call( this, $.extend( {}, config, {
+ $floatable: this.popup.$element,
+ $floatableContainer: this.$element
+ } ) );
};
+
+/* Setup */
+
+OO.mixinClass( OO.ui.mixin.PopupElement, OO.ui.mixin.FloatableElement );
/* Methods */
@@ -4692,6 +4909,7 @@
*/
OO.ui.PopupButtonWidget.prototype.onAction = function () {
this.popup.toggle();
+ this.togglePositioning( this.popup.isVisible() );
};
/**
@@ -7041,213 +7259,6 @@
if ( $nowClicked.length ) {
this.$lastClicked = $nowClicked;
}
-};
-
-/**
- * Element that will stick under a specified container, even when it is
inserted elsewhere in the
- * document (for example, in a OO.ui.Window's $overlay).
- *
- * The elements's position is automatically calculated and maintained when
window is resized or the
- * page is scrolled. If you reposition the container manually, you have to
call #position to make
- * sure the element is still placed correctly.
- *
- * As positioning is only possible when both the element and the container are
attached to the DOM
- * and visible, it's only done after you call #togglePositioning. You might
want to do this inside
- * the #toggle method to display a floating popup, for example.
- *
- * @abstract
- * @class
- *
- * @constructor
- * @param {Object} [config] Configuration options
- * @cfg {jQuery} [$floatable] Node to position, assigned to #$floatable, omit
to use #$element
- * @cfg {jQuery} [$floatableContainer] Node to position below
- */
-OO.ui.mixin.FloatableElement = function OoUiMixinFloatableElement( config ) {
- // Configuration initialization
- config = config || {};
-
- // Properties
- this.$floatable = null;
- this.$floatableContainer = null;
- this.$floatableWindow = null;
- this.$floatableClosestScrollable = null;
- this.onFloatableScrollHandler = this.position.bind( this );
- this.onFloatableWindowResizeHandler = this.position.bind( this );
-
- // Initialization
- this.setFloatableContainer( config.$floatableContainer );
- this.setFloatableElement( config.$floatable || this.$element );
-};
-
-/* Methods */
-
-/**
- * Set floatable element.
- *
- * If an element is already set, it will be cleaned up before setting up the
new element.
- *
- * @param {jQuery} $floatable Element to make floatable
- */
-OO.ui.mixin.FloatableElement.prototype.setFloatableElement = function (
$floatable ) {
- if ( this.$floatable ) {
- this.$floatable.removeClass( 'oo-ui-floatableElement-floatable'
);
- this.$floatable.css( { left: '', top: '' } );
- }
-
- this.$floatable = $floatable.addClass(
'oo-ui-floatableElement-floatable' );
- this.position();
-};
-
-/**
- * Set floatable container.
- *
- * The element will be always positioned under the specified container.
- *
- * @param {jQuery|null} $floatableContainer Container to keep visible, or null
to unset
- */
-OO.ui.mixin.FloatableElement.prototype.setFloatableContainer = function (
$floatableContainer ) {
- this.$floatableContainer = $floatableContainer;
- if ( this.$floatable ) {
- this.position();
- }
-};
-
-/**
- * Toggle positioning.
- *
- * Do not turn positioning on until after the element is attached to the DOM
and visible.
- *
- * @param {boolean} [positioning] Enable positioning, omit to toggle
- * @chainable
- */
-OO.ui.mixin.FloatableElement.prototype.togglePositioning = function (
positioning ) {
- var closestScrollableOfContainer, closestScrollableOfFloatable;
-
- positioning = positioning === undefined ? !this.positioning :
!!positioning;
-
- if ( this.positioning !== positioning ) {
- this.positioning = positioning;
-
- closestScrollableOfContainer =
OO.ui.Element.static.getClosestScrollableContainer( this.$floatableContainer[ 0
] );
- closestScrollableOfFloatable =
OO.ui.Element.static.getClosestScrollableContainer( this.$floatable[ 0 ] );
- this.needsCustomPosition = closestScrollableOfContainer !==
closestScrollableOfFloatable;
- // If the scrollable is the root, we have to listen to scroll
events
- // on the window because of browser inconsistencies.
- if ( $( closestScrollableOfContainer ).is( 'html, body' ) ) {
- closestScrollableOfContainer =
OO.ui.Element.static.getWindow( closestScrollableOfContainer );
- }
-
- if ( positioning ) {
- this.$floatableWindow = $( this.getElementWindow() );
- this.$floatableWindow.on( 'resize',
this.onFloatableWindowResizeHandler );
-
- this.$floatableClosestScrollable = $(
closestScrollableOfContainer );
- this.$floatableClosestScrollable.on( 'scroll',
this.onFloatableScrollHandler );
-
- // Initial position after visible
- this.position();
- } else {
- if ( this.$floatableWindow ) {
- this.$floatableWindow.off( 'resize',
this.onFloatableWindowResizeHandler );
- this.$floatableWindow = null;
- }
-
- if ( this.$floatableClosestScrollable ) {
- this.$floatableClosestScrollable.off( 'scroll',
this.onFloatableScrollHandler );
- this.$floatableClosestScrollable = null;
- }
-
- this.$floatable.css( { left: '', top: '' } );
- }
- }
-
- return this;
-};
-
-/**
- * Check whether the bottom edge of the given element is within the viewport
of the given container.
- *
- * @private
- * @param {jQuery} $element
- * @param {jQuery} $container
- * @return {boolean}
- */
-OO.ui.mixin.FloatableElement.prototype.isElementInViewport = function (
$element, $container ) {
- var elemRect, contRect,
- leftEdgeInBounds = false,
- bottomEdgeInBounds = false,
- rightEdgeInBounds = false;
-
- elemRect = $element[ 0 ].getBoundingClientRect();
- if ( $container[ 0 ] === window ) {
- contRect = {
- top: 0,
- left: 0,
- right: document.documentElement.clientWidth,
- bottom: document.documentElement.clientHeight
- };
- } else {
- contRect = $container[ 0 ].getBoundingClientRect();
- }
-
- // For completeness, if we still cared about topEdgeInBounds, that'd be:
- // elemRect.top >= contRect.top && elemRect.top <= contRect.bottom
- if ( elemRect.left >= contRect.left && elemRect.left <= contRect.right
) {
- leftEdgeInBounds = true;
- }
- if ( elemRect.bottom >= contRect.top && elemRect.bottom <=
contRect.bottom ) {
- bottomEdgeInBounds = true;
- }
- if ( elemRect.right >= contRect.left && elemRect.right <=
contRect.right ) {
- rightEdgeInBounds = true;
- }
-
- // We only care that any part of the bottom edge is visible
- return bottomEdgeInBounds && ( leftEdgeInBounds || rightEdgeInBounds );
-};
-
-/**
- * Position the floatable below its container.
- *
- * This should only be done when both of them are attached to the DOM and
visible.
- *
- * @chainable
- */
-OO.ui.mixin.FloatableElement.prototype.position = function () {
- var pos;
-
- if ( !this.positioning ) {
- return this;
- }
-
- if ( !this.isElementInViewport( this.$floatableContainer,
this.$floatableClosestScrollable ) ) {
- this.$floatable.addClass( 'oo-ui-floatableElement-hidden' );
- return;
- } else {
- this.$floatable.removeClass( 'oo-ui-floatableElement-hidden' );
- }
-
- if ( !this.needsCustomPosition ) {
- return;
- }
-
- pos = OO.ui.Element.static.getRelativePosition(
this.$floatableContainer, this.$floatable.offsetParent() );
-
- // Position under container
- pos.top += this.$floatableContainer.height();
- this.$floatable.css( pos );
-
- // We updated the position, so re-evaluate the clipping state.
- // (ClippableElement does not listen to 'scroll' events on
$floatableContainer's parent, and so
- // will not notice the need to update itself.)
- // TODO: This is terrible, we shouldn't need to know about
ClippableElement at all here. Why does
- // it not listen to the right events in the right places?
- if ( this.clip ) {
- this.clip();
- }
-
- return this;
};
/**
diff --git a/resources/src/mediawiki.special/mediawiki.special.apisandbox.js
b/resources/src/mediawiki.special/mediawiki.special.apisandbox.js
index 3085426..aeb81a6 100644
--- a/resources/src/mediawiki.special/mediawiki.special.apisandbox.js
+++ b/resources/src/mediawiki.special/mediawiki.special.apisandbox.js
@@ -1000,7 +1000,8 @@
icon: 'info',
popup: {
$content: $( '<div>' ).append( mw.message( 'apisandbox-continue-help'
).parse() ),
-
padded: true
+
padded: true,
+
$container: $( 'body' )
}
}
).$element
)
--
To view, visit https://gerrit.wikimedia.org/r/313623
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I204dca07378be25b6977be672263cc767bbf5fe7
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Gergő Tisza <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits