mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
synced 2024-09-23 10:22:14 +00:00
MMV square buttons
- Use square cdx icon only buttons - gray transparent brackground - opaque background on hover, active, focus - Some fixes to the positioning and offsets of buttons and dialogs - Move the dialogs to be in the same container as the buttons This is better for accessibility but also fixes the z-index issue Bug: T365192 Change-Id: Idbc2a309fbca15bd528aaed7ca9bed584487c4f3
This commit is contained in:
parent
bdb40a8e04
commit
2a8b140ed3
|
@ -2,7 +2,7 @@
|
|||
"modules": [
|
||||
{
|
||||
"resourceModule": "mmv",
|
||||
"maxSize": "26.6 kB"
|
||||
"maxSize": "26.4 kB"
|
||||
},
|
||||
{
|
||||
"resourceModule": "mmv.ui.restriction",
|
||||
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
{
|
||||
"resourceModule": "mmv.codex",
|
||||
"maxSize": "5.1 kB"
|
||||
"maxSize": "5.2 kB"
|
||||
},
|
||||
{
|
||||
"resourceModule": "mmv.ui.reuse",
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
"mediawiki.user",
|
||||
"mediawiki.util",
|
||||
"mmv.bootstrap",
|
||||
"mmv.codex",
|
||||
"mmv.head"
|
||||
],
|
||||
"messages": [
|
||||
|
|
|
@ -55,12 +55,3 @@ body.mw-mmv-lightbox-open {
|
|||
@param-size-icon: @size-icon-small );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-button {
|
||||
background-color: transparent;
|
||||
min-width: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
text-indent: -9999em;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
height: initial;
|
||||
// positioned relative to the download button
|
||||
position: fixed;
|
||||
bottom: @metadatabar-above-fold-height + @progress-bar-height + 35px;
|
||||
@bottom-offset: @metadatabar-above-fold-height + @buttons-offset-edge + @buttons-offset-each;
|
||||
bottom: @bottom-offset - 5px;
|
||||
|
||||
.mw-mmv-dialog-down-arrow {
|
||||
bottom: @metadatabar-above-fold-height + @progress-bar-height + 45px;
|
||||
bottom: @bottom-offset + ( @buttons-size / 2 ) - ( @arrow-size / 2 );
|
||||
background-color: @background-color-base;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
// doesn't change when switching tabs
|
||||
min-height: 300px;
|
||||
// positioned relative to the reuse button
|
||||
bottom: @metadatabar-above-fold-height + @progress-bar-height - 5px;
|
||||
@bottom-offset: @metadatabar-above-fold-height + @buttons-offset-edge;
|
||||
bottom: @bottom-offset - 5px;
|
||||
|
||||
.mw-mmv-dialog-down-arrow {
|
||||
bottom: @metadatabar-above-fold-height + @progress-bar-height + 5px;
|
||||
bottom: @bottom-offset + ( @buttons-size / 2 ) - ( @arrow-size / 2 );
|
||||
}
|
||||
|
||||
.mw-mmv-dialog-warning {
|
||||
|
|
|
@ -121,16 +121,16 @@ class LightboxInterface extends UiElement {
|
|||
/** @property {DialogProxy|ReuseDialog} */
|
||||
this.fileReuse = new DialogProxy( 'mmv-reuse-open', ( req ) => {
|
||||
const { ReuseDialog } = req( 'mmv.ui.reuse' );
|
||||
this.fileReuse = new ReuseDialog( this.$innerWrapper, this.buttons.$download, this.config );
|
||||
this.fileReuse = new ReuseDialog( this.$preDiv, this.buttons.$download, this.config );
|
||||
return this.fileReuse;
|
||||
} );
|
||||
/** @property {DialogProxy|DownloadDialog} */
|
||||
this.downloadDialog = new DialogProxy( 'mmv-download-open', ( req ) => {
|
||||
const { DownloadDialog } = req( 'mmv.ui.reuse' );
|
||||
this.downloadDialog = new DownloadDialog( this.$innerWrapper, this.buttons.$download, this.config );
|
||||
this.downloadDialog = new DownloadDialog( this.$preDiv, this.buttons.$download, this.config );
|
||||
return this.downloadDialog;
|
||||
} );
|
||||
this.optionsDialog = new OptionsDialog( this.$innerWrapper, this.buttons.$options, this.config );
|
||||
this.optionsDialog = new OptionsDialog( this.$preDiv, this.buttons.$options, this.config );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,9 +151,7 @@ class LightboxInterface extends UiElement {
|
|||
*/
|
||||
empty() {
|
||||
this.panel.empty();
|
||||
|
||||
this.canvas.empty();
|
||||
|
||||
this.buttons.empty();
|
||||
|
||||
this.$main.addClass( 'metadata-panel-is-closed' )
|
||||
|
@ -216,14 +214,7 @@ class LightboxInterface extends UiElement {
|
|||
// mousemove generates a ton of events, which is why we throttle it
|
||||
this.handleEvent( 'mousemove.lip', mw.util.throttle( ( e ) => {
|
||||
this.mousemove( e );
|
||||
}, 250 ) );
|
||||
|
||||
this.handleEvent( 'mmv-faded-out', ( e ) => {
|
||||
this.fadedOut( e );
|
||||
} );
|
||||
this.handleEvent( 'mmv-fade-stopped', ( e ) => {
|
||||
this.fadeStopped( e );
|
||||
} );
|
||||
}, 100, true ) );
|
||||
|
||||
this.buttons.connect( this, {
|
||||
next: [ 'emit', 'next' ],
|
||||
|
@ -233,7 +224,7 @@ class LightboxInterface extends UiElement {
|
|||
const $parent = $( parentId || document.body );
|
||||
|
||||
// Clean up fullscreen data left attached to the DOM
|
||||
this.$main.removeClass( 'jq-fullscreened' );
|
||||
this.$main.removeClass( 'jq-fullscreened' ).removeClass( 'user-inactive' );
|
||||
this.isFullscreen = false;
|
||||
|
||||
$parent
|
||||
|
@ -258,19 +249,12 @@ class LightboxInterface extends UiElement {
|
|||
this.panel.scroller.toggle( 'down' );
|
||||
} );
|
||||
|
||||
// Buttons fading might not had been reset properly after a hard fullscreen exit
|
||||
// This needs to happen after the parent attach() because the buttons need to be attached
|
||||
// to the DOM for $.fn.stop() to work
|
||||
this.buttons.stopFade();
|
||||
this.buttons.attach();
|
||||
|
||||
this.fileReuse.attach();
|
||||
this.downloadDialog.attach();
|
||||
this.optionsDialog.attach();
|
||||
|
||||
// Reset the cursor fading
|
||||
this.fadeStopped();
|
||||
|
||||
this.attached = true;
|
||||
}
|
||||
|
||||
|
@ -334,6 +318,8 @@ class LightboxInterface extends UiElement {
|
|||
}
|
||||
this.isFullscreen = false;
|
||||
this.$main.removeClass( 'jq-fullscreened' );
|
||||
clearTimeout( this.interactionTimer );
|
||||
this.userActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -346,6 +332,34 @@ class LightboxInterface extends UiElement {
|
|||
}
|
||||
this.isFullscreen = true;
|
||||
this.$main.addClass( 'jq-fullscreened' );
|
||||
this.resetInteractionTimer();
|
||||
this.userInactive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt and reset the 3sec delay to hide the controls
|
||||
*/
|
||||
resetInteractionTimer() {
|
||||
clearTimeout( this.interactionTimer );
|
||||
this.interactionTimer = setTimeout( () => {
|
||||
this.userInactive();
|
||||
}, 3000 );
|
||||
}
|
||||
|
||||
/**
|
||||
* In fullscreen, hide the mouse cursor and the controls
|
||||
* Called from resetInteractionTimer()
|
||||
*/
|
||||
userInactive() {
|
||||
this.$main.addClass( 'user-inactive' );
|
||||
}
|
||||
|
||||
/**
|
||||
* In fullscreen, show the mouse cursor and the controls
|
||||
* Call this after any interactivity
|
||||
*/
|
||||
userActivity() {
|
||||
this.$main.removeClass( 'user-inactive' );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -353,29 +367,18 @@ class LightboxInterface extends UiElement {
|
|||
*/
|
||||
setupCanvasButtons() {
|
||||
this.$closeButton = $( '<button>' )
|
||||
.text( ' ' )
|
||||
.addClass( 'mw-mmv-close' )
|
||||
.addClass( 'cdx-button cdx-button--icon-only mw-mmv-button mw-mmv-close' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-close-popup-text' ) )
|
||||
.on( 'click', () => {
|
||||
this.unattach();
|
||||
} );
|
||||
|
||||
this.$fullscreenButton = $( '<button>' )
|
||||
.text( ' ' )
|
||||
.addClass( 'mw-mmv-fullscreen' )
|
||||
.addClass( 'cdx-button cdx-button--icon-only mw-mmv-button mw-mmv-fullscreen' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-fullscreen-popup-text' ) )
|
||||
.on( 'click', ( e ) => {
|
||||
.on( 'click', () => {
|
||||
if ( this.isFullscreen ) {
|
||||
this.exitFullscreen();
|
||||
|
||||
// mousemove is throttled and the mouse coordinates only
|
||||
// register every 250ms, so there is a chance that we moved
|
||||
// our mouse over one of the buttons but it didn't register,
|
||||
// and a fadeOut is triggered; when we're coming back from
|
||||
// fullscreen, we'll want to make sure the mouse data is
|
||||
// current so that the fadeOut behavior will not trigger
|
||||
this.mousePosition = { x: e.pageX, y: e.pageY };
|
||||
this.buttons.revealAndFade( this.mousePosition );
|
||||
} else {
|
||||
this.enterFullscreen();
|
||||
}
|
||||
|
@ -412,14 +415,7 @@ class LightboxInterface extends UiElement {
|
|||
}
|
||||
|
||||
if ( this.isFullscreen ) {
|
||||
// When entering fullscreen without a mousemove, the browser
|
||||
// still thinks that the cursor is where it was prior to entering
|
||||
// fullscreen. I.e. on top of the fullscreen button
|
||||
// Thus, we purposefully reset the saved position, so that
|
||||
// the fade out really takes place (otherwise it's cancelled
|
||||
// by updateControls which is called a few times when fullscreen opens)
|
||||
this.mousePosition = { x: 0, y: 0 };
|
||||
this.buttons.fadeOut();
|
||||
this.userInactive();
|
||||
}
|
||||
|
||||
// Some browsers only send resize events before toggling fullscreen, but not once the toggling is done
|
||||
|
@ -450,6 +446,10 @@ class LightboxInterface extends UiElement {
|
|||
} else if ( e.key === 'End' ) {
|
||||
this.emit( 'last' );
|
||||
e.preventDefault();
|
||||
} else if ( this.isFullscreen ) {
|
||||
// Any other key in fullscreen reveals the controls
|
||||
this.resetInteractionTimer();
|
||||
this.userActivity();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,35 +469,16 @@ class LightboxInterface extends UiElement {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( e ) {
|
||||
// Saving the mouse position is useful whenever we need to
|
||||
// run LIP.mousemove manually, such as when going to the next/prev
|
||||
// element
|
||||
this.mousePosition = { x: e.pageX, y: e.pageY };
|
||||
}
|
||||
|
||||
if ( this.isFullscreen ) {
|
||||
this.buttons.revealAndFade( this.mousePosition );
|
||||
this.resetInteractionTimer();
|
||||
this.userActivity();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the buttons have completely faded out and disappeared
|
||||
*/
|
||||
fadedOut() {
|
||||
this.$main.addClass( 'cursor-hidden' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the buttons have stopped fading and are back into view
|
||||
*/
|
||||
fadeStopped() {
|
||||
this.$main.removeClass( 'cursor-hidden' );
|
||||
}
|
||||
|
||||
touchTap() {
|
||||
if ( this.isFullscreen ) {
|
||||
this.buttons.revealAndFade( this.mousePosition );
|
||||
this.resetInteractionTimer();
|
||||
this.userActivity();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,7 +489,7 @@ class LightboxInterface extends UiElement {
|
|||
* @param {boolean} showNextButton Whether the next button should be revealed or not
|
||||
*/
|
||||
updateControls( showPrevButton, showNextButton ) {
|
||||
const prevNextTop = `${ ( this.$imageWrapper.height() / 2 ) - 60 }px`;
|
||||
const prevNextTop = `${ ( this.$imageWrapper.height() - 60 ) / 2 }px`;
|
||||
|
||||
if ( this.isFullscreen ) {
|
||||
this.$postDiv.css( 'top', '' );
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
|
||||
// Fullscreen styles
|
||||
|
||||
.cursor-hidden {
|
||||
.user-inactive {
|
||||
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
|
||||
cursor: none;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,16 @@
|
|||
@arrow-border-size: 2px;
|
||||
|
||||
// Some button things that get included all over
|
||||
@navbutton-width: 18px;
|
||||
@buttons-offset-right: 5px;
|
||||
@buttons-offset-each-top: 37px;
|
||||
@buttons-size: 32px;
|
||||
@buttons-size-large: 44px;
|
||||
@buttons-gap: 10px;
|
||||
@buttons-offset-edge: 14px;
|
||||
@buttons-offset-each: @buttons-size + @buttons-gap;
|
||||
|
||||
@buttons-icon-size: 20px; //icon size medium
|
||||
@buttons-icon-size-large: 30px;
|
||||
|
||||
// dark mode version of --background-color-interactive-subtle
|
||||
@buttons-background-color: #202122;
|
||||
// dark mode version of --background-color-interactive-subtle: #202122;
|
||||
@buttons-background-color-faded: ~'#20212274';
|
||||
|
|
|
@ -36,30 +36,26 @@ class CanvasButtons extends UiElement {
|
|||
|
||||
this.$reuse = $( '<a>' )
|
||||
.attr( 'role', 'button' )
|
||||
.addClass( 'mw-mmv-reuse-button' )
|
||||
.text( '\u00A0' )
|
||||
.addClass( 'cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--icon-only mw-mmv-button mw-mmv-reuse-button' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-reuse-link' ) );
|
||||
|
||||
this.$options = $( '<button>' )
|
||||
.text( ' ' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-options-tooltip' ) )
|
||||
.addClass( 'mw-mmv-options-button' );
|
||||
.addClass( 'cdx-button cdx-button--icon-only mw-mmv-button mw-mmv-options-button' );
|
||||
|
||||
this.$download = $( '<a>' )
|
||||
.attr( 'role', 'button' )
|
||||
.addClass( 'mw-mmv-download-button' )
|
||||
.text( '\u00A0' )
|
||||
.addClass( 'cdx-button cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--icon-only mw-mmv-button mw-mmv-download-button' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-download-link' ) );
|
||||
|
||||
this.$next = $( '<button>' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-next-image-alt-text' ) )
|
||||
.addClass( 'mw-mmv-next-image disabled' )
|
||||
.text( '\u00A0' );
|
||||
.addClass( 'cdx-button cdx-button--icon-only cdx-button--size-large mw-mmv-button mw-mmv-next-image disabled' );
|
||||
|
||||
this.$prev = $( '<button>' )
|
||||
.prop( 'title', mw.msg( 'multimediaviewer-prev-image-alt-text' ) )
|
||||
.addClass( 'mw-mmv-prev-image disabled' )
|
||||
.text( '\u00A0' );
|
||||
.addClass( 'cdx-button cdx-button--icon-only cdx-button--size-large mw-mmv-button mw-mmv-prev-image disabled' );
|
||||
|
||||
this.$nav = this.$next
|
||||
.add( this.$prev );
|
||||
|
@ -102,18 +98,6 @@ class CanvasButtons extends UiElement {
|
|||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the fading animation of the buttons and cancel any opacity value
|
||||
*/
|
||||
stopFade() {
|
||||
this.$buttons
|
||||
.stop( true )
|
||||
.removeClass( 'hidden' )
|
||||
.css( 'opacity', '' );
|
||||
|
||||
this.$container.trigger( $.Event( 'mmv-fade-stopped' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles buttons being disabled or not
|
||||
*
|
||||
|
@ -125,74 +109,6 @@ class CanvasButtons extends UiElement {
|
|||
this.$prev.toggleClass( 'disabled', !showNextButton );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades out the active buttons
|
||||
*/
|
||||
fadeOut() {
|
||||
// We don't use animation chaining because delay() can't be stop()ed
|
||||
this.buttonsFadeTimeout = setTimeout( () => {
|
||||
// FIXME: Use CSS transition
|
||||
// eslint-disable-next-line no-jquery/no-animate
|
||||
this.$buttons.not( '.disabled' ).animate( { opacity: 0 }, 1000, 'swing',
|
||||
() => {
|
||||
this.$buttons.addClass( 'hidden' );
|
||||
this.$container.trigger( $.Event( 'mmv-faded-out' ) );
|
||||
} );
|
||||
}, 1500 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any active buttons are currently hovered, given a position
|
||||
*
|
||||
* @param {number} x The horizontal coordinate of the position
|
||||
* @param {number} y The vertical coordinate of the position
|
||||
* @return {boolean}
|
||||
*/
|
||||
isAnyActiveButtonHovered( x, y ) {
|
||||
// We don't use mouseenter/mouseleave events because content is subject
|
||||
// to change underneath the cursor, eg. when entering fullscreen or
|
||||
// when going prev/next (the button can disappear when reaching ends)
|
||||
let hovered = false;
|
||||
|
||||
this.$buttons.not( '.disabled' ).each( ( idx, e ) => {
|
||||
const $e = $( e );
|
||||
const offset = $e.offset();
|
||||
|
||||
if ( y >= offset.top &&
|
||||
// using css( 'height' ) & css( 'width' ) instead of .height()
|
||||
// and .width() since those don't include padding, and as a
|
||||
// result can return a smaller size than is actually the button
|
||||
y <= offset.top + parseInt( $e.css( 'height' ) ) &&
|
||||
x >= offset.left &&
|
||||
x <= offset.left + parseInt( $e.css( 'width' ) ) ) {
|
||||
hovered = true;
|
||||
}
|
||||
} );
|
||||
|
||||
return hovered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reveals all active buttons and schedule a fade out if needed
|
||||
*
|
||||
* @param {Object} [mousePosition] Mouse position containing 'x' and 'y' properties
|
||||
*/
|
||||
revealAndFade( mousePosition ) {
|
||||
if ( this.buttonsFadeTimeout ) {
|
||||
clearTimeout( this.buttonsFadeTimeout );
|
||||
}
|
||||
|
||||
// Stop ongoing animations and make sure the buttons that need to be displayed are displayed
|
||||
this.stopFade();
|
||||
|
||||
// mousePosition can be empty, for instance when we enter fullscreen and haven't
|
||||
// recorded a real mousemove event yet
|
||||
if ( !mousePosition ||
|
||||
!this.isAnyActiveButtonHovered( mousePosition.x, mousePosition.y ) ) {
|
||||
this.fadeOut();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers listeners.
|
||||
*
|
||||
|
|
|
@ -3,129 +3,142 @@
|
|||
@import 'mediawiki.mixins.less';
|
||||
@import '../mmv.mixins.less';
|
||||
|
||||
.mw-mmv-download-button,
|
||||
.mw-mmv-reuse-button,
|
||||
.mw-mmv-options-button,
|
||||
.mw-mmv-close,
|
||||
.mw-mmv-fullscreen,
|
||||
.mw-mmv-next-image,
|
||||
.mw-mmv-prev-image {
|
||||
cursor: pointer;
|
||||
// Common to all buttons
|
||||
// We override the codex background and borders to be their forced darkmode variant
|
||||
// We use transparency on the background and on the icon color
|
||||
.cdx-button.mw-mmv-button {
|
||||
background-color: @buttons-background-color-faded;
|
||||
position: fixed;
|
||||
background-color: transparent;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0.8;
|
||||
border: 0;
|
||||
z-index: 1003;
|
||||
// Cancel out default outline
|
||||
border-width: 0;
|
||||
outline: 0;
|
||||
transition-property: background-color, opacity, border-width, border-color, box-shadow, transform;
|
||||
|
||||
&.mw-mmv-dialog-open,
|
||||
&.mw-mmv-dialog-open, /* A button that has opened a dialog */
|
||||
&:active,
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
background-color: @buttons-background-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: @buttons-background-color;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
// Default transparency of the icon
|
||||
content: '';
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&.mw-mmv-dialog-open::after,
|
||||
&:active::after,
|
||||
&:focus::after,
|
||||
&:hover::after {
|
||||
transition: opacity 0.1s ease-in;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.user-select( none );
|
||||
}
|
||||
|
||||
.mw-mmv-download-button.hidden,
|
||||
.mw-mmv-reuse-button.hidden,
|
||||
.mw-mmv-options-button.hidden,
|
||||
.mw-mmv-close.hidden,
|
||||
.mw-mmv-fullscreen.hidden,
|
||||
.mw-mmv-next-image.hidden,
|
||||
.mw-mmv-prev-image.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cursor-hidden {
|
||||
.mw-mmv-download-button,
|
||||
.mw-mmv-reuse-button,
|
||||
.mw-mmv-close,
|
||||
.mw-mmv-fullscreen,
|
||||
.mw-mmv-next-image,
|
||||
.mw-mmv-prev-image {
|
||||
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
|
||||
cursor: none;
|
||||
// when the cursor is hidden, the buttons are hidden as well
|
||||
.user-inactive & {
|
||||
opacity: 0;
|
||||
transform: scale( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
// Positioning of buttons
|
||||
.mw-mmv-download-button,
|
||||
.mw-mmv-reuse-button,
|
||||
.mw-mmv-options-button,
|
||||
.mw-mmv-close,
|
||||
.mw-mmv-fullscreen {
|
||||
right: @buttons-offset-right;
|
||||
right: @buttons-offset-edge;
|
||||
left: auto;
|
||||
transition: opacity 0.25s;
|
||||
background-position: center;
|
||||
margin-top: 14px;
|
||||
width: 24px + 2 * 14px;
|
||||
min-width: @buttons-size;
|
||||
min-height: @buttons-size;
|
||||
}
|
||||
|
||||
// Vertical positioning of left/right button
|
||||
.mw-mmv-next-image,
|
||||
.mw-mmv-prev-image {
|
||||
top: -999px;
|
||||
width: 80px;
|
||||
height: 120px;
|
||||
transition: opacity 0.25s, margin 0.25s;
|
||||
|
||||
&.disabled {
|
||||
display: none;
|
||||
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
&&::after {
|
||||
mask-size: @buttons-icon-size-large;
|
||||
}
|
||||
}
|
||||
|
||||
// Per button positioning and providing icons
|
||||
.mw-mmv-close {
|
||||
padding-right: @spacing-150;
|
||||
top: @buttons-offset-right;
|
||||
.cdx-mixin-css-icon( @cdx-icon-close, @param-fill-color: @color-inverted-fixed );
|
||||
top: @buttons-offset-edge;
|
||||
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-close, @param-fill-color: @color-inverted-fixed );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-fullscreen {
|
||||
padding-right: @spacing-150;
|
||||
top: ( @buttons-offset-right + ( @buttons-offset-each-top ) );
|
||||
.cdx-mixin-css-icon( @cdx-icon-full-screen, @param-fill-color: @color-inverted-fixed );
|
||||
top: ( @buttons-offset-edge + ( @buttons-offset-each ) );
|
||||
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-full-screen, @param-fill-color: @color-inverted-fixed );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-options-button {
|
||||
padding-right: @spacing-150;
|
||||
top: ( @buttons-offset-right + ( 2 * @buttons-offset-each-top ) );
|
||||
.cdx-mixin-css-icon( @cdx-icon-settings, @param-fill-color: @color-inverted-fixed );
|
||||
top: ( @buttons-offset-edge + ( 2 * @buttons-offset-each ) );
|
||||
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-settings, @param-fill-color: @color-inverted-fixed );
|
||||
}
|
||||
}
|
||||
|
||||
.jq-fullscreened {
|
||||
.mw-mmv-fullscreen {
|
||||
.mw-mmv-fullscreen::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-exit-fullscreen, @param-fill-color: @color-inverted-fixed );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-next-image {
|
||||
.cdx-mixin-css-icon( @cdx-icon-next, @param-fill-color: @color-inverted-fixed, @param-size-icon: @size-icon-medium );
|
||||
right: @size-icon-medium;
|
||||
|
||||
&:hover {
|
||||
margin-right: -4px;
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-next, @param-fill-color: @color-inverted-fixed, @param-size-icon: @size-icon-medium );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-prev-image {
|
||||
.cdx-mixin-css-icon( @cdx-icon-previous, @param-fill-color: @color-inverted-fixed, @param-size-icon: @size-icon-medium );
|
||||
left: @size-icon-medium;
|
||||
|
||||
&:hover {
|
||||
margin-left: -4px;
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-previous, @param-fill-color: @color-inverted-fixed, @param-size-icon: @size-icon-medium );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-reuse-button {
|
||||
padding-right: @spacing-150;
|
||||
bottom: @buttons-offset-right + @metadatabar-above-fold-height + @progress-bar-height;
|
||||
.cdx-mixin-css-icon( @cdx-icon-share, @param-fill-color: @color-inverted-fixed );
|
||||
bottom: @buttons-offset-edge + @metadatabar-above-fold-height;
|
||||
|
||||
.jq-fullscreened & {
|
||||
bottom: @buttons-offset-edge + @progress-bar-height;
|
||||
}
|
||||
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-share, @param-fill-color: @color-inverted-fixed );
|
||||
}
|
||||
}
|
||||
|
||||
.mw-mmv-download-button {
|
||||
padding-right: @spacing-150;
|
||||
bottom: @buttons-offset-right + @metadatabar-above-fold-height + @progress-bar-height + 37px;
|
||||
.cdx-mixin-css-icon( @cdx-icon-download, @param-fill-color: @color-inverted-fixed );
|
||||
bottom: @buttons-offset-edge + @metadatabar-above-fold-height + @buttons-offset-each;
|
||||
|
||||
.jq-fullscreened & {
|
||||
bottom: @buttons-offset-edge + @progress-bar-height + @buttons-offset-each;
|
||||
}
|
||||
|
||||
&::after {
|
||||
.cdx-mixin-css-icon( @cdx-icon-download, @param-fill-color: @color-inverted-fixed );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
/* stylelint-disable-next-line plugin/no-unsupported-browser-features */
|
||||
.mw-mmv-dialog {
|
||||
position: fixed;
|
||||
right: 58px;
|
||||
right: @buttons-offset-edge + @buttons-offset-each + ( @arrow-size / 2 );
|
||||
display: none;
|
||||
width: @dialog-width;
|
||||
height: @dialog-height;
|
||||
|
@ -18,7 +18,7 @@
|
|||
z-index: 1004;
|
||||
|
||||
.mw-mmv-dialog-down-arrow {
|
||||
right: 48px;
|
||||
right: @buttons-offset-edge + @buttons-offset-each;
|
||||
background-color: @background-color;
|
||||
width: @arrow-size;
|
||||
height: @arrow-size;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@import '../mmv.mixins.less';
|
||||
|
||||
.mw-mmv-options-dialog {
|
||||
@offset-top: ( @buttons-offset-right + ( 2 * @buttons-offset-each-top ) + 6px );
|
||||
@offset-top: @buttons-offset-edge + ( 2 * @buttons-offset-each );
|
||||
top: @offset-top;
|
||||
height: auto;
|
||||
z-index: 1004;
|
||||
|
|
|
@ -213,47 +213,6 @@ const { getMultimediaViewer } = require( './mmv.testhelpers.js' );
|
|||
restoreScrollTo();
|
||||
} );
|
||||
|
||||
QUnit.test( 'isAnyActiveButtonHovered', ( assert ) => {
|
||||
const lightbox = new LightboxInterface();
|
||||
|
||||
stubScrollTo();
|
||||
|
||||
// Attach lightbox to testing fixture to avoid interference with other tests.
|
||||
lightbox.attach( '#qunit-fixture' );
|
||||
|
||||
lightbox.buttons.$buttons.each( ( i, button ) => {
|
||||
const $button = $( button );
|
||||
const offset = $button.show().offset();
|
||||
const width = $button.width();
|
||||
const height = $button.height();
|
||||
const disabled = $button.hasClass( 'disabled' );
|
||||
|
||||
assert.strictEqual( lightbox.buttons.isAnyActiveButtonHovered( offset.left, offset.top ),
|
||||
!disabled,
|
||||
'Hover detection works for top-left corner of element' );
|
||||
assert.strictEqual( lightbox.buttons.isAnyActiveButtonHovered( offset.left + width, offset.top ),
|
||||
!disabled,
|
||||
'Hover detection works for top-right corner of element' );
|
||||
assert.strictEqual( lightbox.buttons.isAnyActiveButtonHovered( offset.left, offset.top + height ),
|
||||
!disabled,
|
||||
'Hover detection works for bottom-left corner of element' );
|
||||
assert.strictEqual( lightbox.buttons.isAnyActiveButtonHovered( offset.left + width, offset.top + height ),
|
||||
!disabled,
|
||||
'Hover detection works for bottom-right corner of element' );
|
||||
assert.strictEqual(
|
||||
lightbox.buttons.isAnyActiveButtonHovered(
|
||||
offset.left + ( width / 2 ), offset.top + ( height / 2 )
|
||||
),
|
||||
!disabled,
|
||||
'Hover detection works for center of element'
|
||||
);
|
||||
} );
|
||||
|
||||
// Unattach lightbox from document
|
||||
lightbox.unattach();
|
||||
restoreScrollTo();
|
||||
} );
|
||||
|
||||
QUnit.test( 'Keyboard prev/next', ( assert ) => {
|
||||
const viewer = getMultimediaViewer();
|
||||
const lightbox = new LightboxInterface();
|
||||
|
|
Loading…
Reference in a new issue