Merge "Show a "Return to reply/new topic" button when widget is scrolled off the screen"

This commit is contained in:
jenkins-bot 2022-07-18 16:09:54 +00:00 committed by Gerrit Code Review
commit 6e57e3ba78
5 changed files with 125 additions and 1 deletions

View file

@ -194,6 +194,8 @@
"discussiontools-replywidget-preferences",
"discussiontools-replywidget-preview",
"discussiontools-replywidget-reply",
"discussiontools-replywidget-return-to-newtopic",
"discussiontools-replywidget-return-to-reply",
"discussiontools-replywidget-summary",
"discussiontools-replywidget-transcluded",
"ooui-popup-widget-close-button-aria-label",

View file

@ -97,6 +97,8 @@
"discussiontools-replywidget-preview": "Preview",
"discussiontools-replywidget-reply": "Reply",
"discussiontools-replywidget-reply-link": "https://www.mediawiki.org/wiki/Special:MyLanguage/Talk_pages_project/Replying",
"discussiontools-replywidget-return-to-newtopic": "Return to new topic",
"discussiontools-replywidget-return-to-reply": "Return to reply",
"discussiontools-replywidget-signature-body": "$1 has been transformed into your signature. To undo this, type $2. Note that you don't need to sign comments with this tool. Your signature will be added automatically.",
"discussiontools-replywidget-signature-title": "Signature markup detected",
"discussiontools-replywidget-summary": "Summary",

View file

@ -108,6 +108,8 @@
"discussiontools-replywidget-preview": "Label for the preview area of the reply widget",
"discussiontools-replywidget-reply": "Label for the button to submit a reply in the reply widget",
"discussiontools-replywidget-reply-link": "{{notranslate}}\nUsed by {{msg-mw|Tag-discussiontools-reply}} as a link to a page where users learn about the feature. Defaults to a page on MediaWiki.org.",
"discussiontools-replywidget-return-to-newtopic": "Label for the button to scroll the new topic widget back into view.",
"discussiontools-replywidget-return-to-reply": "Label for the button to scroll the reply widget back into view.",
"discussiontools-replywidget-signature-body": "Body of the warning about typing in your signature.\n\nParameters:\n* $1 the signature markup, i.e. <code><nowiki>~~~~</nowiki></code>\n* $2 localised keyboard shortcut to undo, for example <kbd><kbd>Ctrl</kbd>+<kbd>Z</kbd></kbd>",
"discussiontools-replywidget-signature-title": "Title of the warning about typing in your signature",
"discussiontools-replywidget-summary": "Label for the summary field that describes your comment. This will be stored as an edit summary, but in the context of the reply tool we don't want to refer to 'edits'\n{{identical|Summary}}",

View file

@ -47,6 +47,14 @@ function ReplyWidget( commentController, commentDetails, config ) {
// eslint-disable-next-line no-jquery/no-global-selector
this.contentDir = $( '#mw-content-text' ).css( 'direction' );
this.hideNewCommentsWarning = false;
// Floating position for scroll back buttons: 'top', 'bottom', or null
this.floating = null;
// Copied from ve.init.Platform.static.isIos
this.isIos = /ipad|iphone|ipod/i.test( navigator.userAgent );
this.$window = $( this.getElementWindow() );
this.onWindowScrollThrottled = OO.ui.throttle( this.onWindowScroll.bind( this ), 100 );
this.onViewportChangeThrottled = OO.ui.throttle( this.onViewportChange.bind( this ), 100 );
var inputConfig = $.extend(
{
@ -200,6 +208,28 @@ function ReplyWidget( commentController, commentDetails, config ) {
);
this.$actionsWrapper.append( this.$footer, this.$actions );
this.viewportScrollContainer = OO.ui.Element.static.getClosestScrollableContainer( document.body );
this.scrollBackTopButton = new OO.ui.ButtonWidget( {
classes: [ 'ext-discussiontools-ui-replyWidget-scrollback-top' ],
icon: 'collapse',
label: mw.msg(
this.isNewTopic ?
'discussiontools-replywidget-return-to-newtopic' :
'discussiontools-replywidget-return-to-reply'
),
flags: [ 'progressive' ]
} );
this.scrollBackBottomButton = new OO.ui.ButtonWidget( {
classes: [ 'ext-discussiontools-ui-replyWidget-scrollback-bottom' ],
icon: 'expand',
label: mw.msg(
this.isNewTopic ?
'discussiontools-replywidget-return-to-newtopic' :
'discussiontools-replywidget-return-to-reply'
),
flags: [ 'progressive' ]
} );
// Events
this.replyButton.connect( this, { click: 'onReplyClick' } );
this.cancelButton.connect( this, { click: 'tryTeardown' } );
@ -213,6 +243,8 @@ function ReplyWidget( commentController, commentDetails, config ) {
if ( this.isNewTopic ) {
this.commentController.sectionTitle.$input.on( 'keydown', this.onKeyDown.bind( this, false ) );
}
this.scrollBackTopButton.connect( this, { click: 'onScrollBackButtonClick' } );
this.scrollBackBottomButton.connect( this, { click: 'onScrollBackButtonClick' } );
this.onInputChangeThrottled = OO.ui.throttle( this.onInputChange.bind( this ), 1000 );
@ -223,7 +255,9 @@ function ReplyWidget( commentController, commentDetails, config ) {
this.$preview,
this.advancedToggle.$element,
this.advanced.$element,
this.$actionsWrapper
this.$actionsWrapper,
this.scrollBackTopButton.$element,
this.scrollBackBottomButton.$element
);
// Set direction to interface direction
this.$element.attr( 'dir', $( document.body ).css( 'direction' ) );
@ -364,6 +398,41 @@ ReplyWidget.prototype.clearStorage = function () {
this.emit( 'clearStorage' );
};
/**
* Handle window scroll events
*/
ReplyWidget.prototype.onWindowScroll = function () {
var rect = this.$element[ 0 ].getBoundingClientRect();
var viewportHeight = window.visualViewport ? visualViewport.height : this.viewportScrollContainer.clientHeight;
var floating = rect.bottom < 0 ? 'top' :
( rect.top > viewportHeight ? 'bottom' : null );
if ( floating !== this.floating ) {
this.floating = floating;
// Always remove classes as we have switched directly from top to bottom with a fast scroll
this.$element
.removeClass( 'ext-discussiontools-ui-replyWidget-floating-top ext-discussiontools-ui-replyWidget-floating-bottom' );
if ( this.floating ) {
// The following classes are used here:
// * ext-discussiontools-ui-replyWidget-floating-top
// * ext-discussiontools-ui-replyWidget-floating-bottom
this.$element.addClass( 'ext-discussiontools-ui-replyWidget-floating-' + this.floating );
}
}
};
/**
* Handle events which change the visualViewport (scroll/resize)
*/
ReplyWidget.prototype.onViewportChange = function () {
if ( this.floating ) {
var isKeyboardOpen = visualViewport.height < this.viewportScrollContainer.clientHeight;
this.scrollBackBottomButton.toggle( !isKeyboardOpen );
this.scrollBackTopButton.toggle( !isKeyboardOpen );
}
};
ReplyWidget.prototype.setPending = function ( pending ) {
this.pending = pending;
if ( pending ) {
@ -554,6 +623,12 @@ ReplyWidget.prototype.setup = function ( data ) {
mw.hook( 'wikipage.watchlistChange' ).add( this.onWatchToggleHandler );
// TODO: Use ve.addPassiveEventListener
this.$window.on( 'scroll', this.onWindowScrollThrottled );
if ( this.isIos && window.visualViewport ) {
$( visualViewport ).on( 'scroll resize', this.onViewportChangeThrottled );
}
return this;
};
@ -639,6 +714,10 @@ ReplyWidget.prototype.teardown = function ( mode ) {
this.modeTabSelect.blur();
}
this.unbindBeforeUnloadHandler();
this.$window.off( 'scroll', this.onWindowScrollThrottled );
if ( this.isIos && window.visualViewport ) {
$( visualViewport ).off( 'scroll resize', this.onViewportChangeThrottled );
}
mw.hook( 'wikipage.watchlistChange' ).remove( this.onWatchToggleHandler );
this.clear( mode === 'refresh' );
@ -1008,4 +1087,11 @@ ReplyWidget.prototype.onReplyClick = function () {
} );
};
/**
* Handle click events on one of the scroll back buttons
*/
ReplyWidget.prototype.onScrollBackButtonClick = function () {
this.commentController.showAndFocus();
};
module.exports = ReplyWidget;

View file

@ -48,6 +48,38 @@
}
}
&-scrollback-top {
transform: translate( -50%, -150% );
top: 0;
left: 50%;
}
&-scrollback-bottom {
transform: translate( -50%, 150% );
bottom: 0;
left: 50%;
}
&-scrollback-top,
&-scrollback-bottom {
position: fixed;
opacity: 0;
transition: transform 250ms, opacity 250ms;
z-index: 1;
margin: 1em 0;
box-shadow: 0 2px 2px 0 rgba( 0, 0, 0, 0.25 );
.skin-monobook & {
box-shadow: 0 0.2em 1em rgba( 0, 0, 0, 0.3 );
}
}
&-floating-top &-scrollback-top,
&-floating-bottom &-scrollback-bottom {
opacity: 1;
transform: translate( -50%, 0 );
}
.ve-ui-targetToolbar > .oo-ui-toolbar-bar {
background: none;
box-shadow: none;