Skip to content

Commit

Permalink
XWIKI-22581: FocusCatcher input has no label (#3577)
Browse files Browse the repository at this point in the history
* Replaced the FocusCatcher artificial input with a semantically better span.
* Updated the style of the gallery to use css grid instead of hackish solutions all around.
* Improved accessibility by changing the div interactive controllers with buttons.
* Fixed the full screen display vertical overflow issue.
  • Loading branch information
Sereza7 authored Dec 10, 2024
1 parent b1e5e4f commit 4241c3c
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,7 @@ core.widgets.gallery.previousImage=Show previous image
core.widgets.gallery.nextImage=Show next image
core.widgets.gallery.maximize=Maximize
core.widgets.gallery.minimize=Minimize
core.widgets.gallery.index.description=Press the right and left arrow keys to quickly navigate through the images.

core.widgets.suggest.noResults=No results!
core.widgets.suggest.showResults=Go to search page\u2026
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
background-color: black;
max-width: 100%;
padding: 10px;
border-radius: 7px; /* Same value as @border-radius-base from Flamingo. */
/* Position relative is required because some of the inner elements have position absolute and the gallery container
must be their offset parent. */
position: relative;
Expand All @@ -10,13 +11,9 @@
/* Those width/height values can be overridden by the inline styling added with the macro parameters */
width: 620px;
height: 349px;
}

.xGallery:before {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
display: grid;
grid-template-columns: 1fr 10fr 1fr;
grid-template-rows: 1fr 10fr 1fr;
}

.xGallery.maximized {
Expand All @@ -26,83 +23,82 @@
top: 0;
z-index: 1001;
width: 100% !important;
border-radius: 0;
}

.xGallery .currentImage {
max-height: 100%;
grid-area: 2 / 2 / 3 / 3;
align-self: center;
justify-self: center;
object-fit: scale-down;
max-width: 100%;
padding: 22px;
vertical-align: middle;
max-height: 100%;
}

/* Transparent buttons that should fill the space they've been given on the grid */
.xGallery .previous, .xGallery .next,
.xGallery .maximize, .xGallery .minimize {
background-color: transparent;
border-color: transparent;
width: 100%;
height: 100%;
}

.xGallery .previous, .xGallery .next {
color: #A0A0A0;
cursor: pointer;
font-family: courier,monospace;
font-size: 32px;
font-weight: 100;
height: 124px;
line-height: 124px;
margin-top: -64px;
position: absolute;
text-align: center;
top: 50%;
width: 32px;
}

.xGallery .previous:hover, .xGallery .next:hover {
color: white;
}

.xGallery .previous {
left: 0;
grid-area: 2 / 1 / 3 / 2;
}

.xGallery .next {
right: 0;
grid-area: 2 / 3 / 3 / 4;
}

.xGallery .index {
bottom: 10px;
color: #C0C0C0;
font-family: sans-serif;
font-size: smaller;
left: 10px;
line-height: 1;
position: absolute;
grid-area: 3 / 1 / 4 / 2;
align-self: end;
}

.xGallery .loading {
background-image: url('loading.gif') !important;
}

.xGallery .focusCatcher {
background-color: black;
border: 0 none;
color: black;
height: 1px;
left: 0;
overflow: hidden;
position: absolute;
top: 0;
width: 1px;
z-index: -1;
}

.xGallery .maximize, .xGallery .minimize {
cursor: pointer;
height: 16px;
opacity: .5;
position: absolute;
right: 10px;
top: 10px;
width: 16px;
grid-area: 1 / 3 / 2 / 4;
justify-self: end;
}

.xGallery .maximize:hover, .xGallery .minimize:hover {
opacity: 1;
}

/* Elements on the left of the grid are left aligned */
.xGallery .index, .xGallery .previous {
text-align: start;
}

/* Elements on the right of the grid are right aligned */
.xGallery .maximize, .xGallery .minimize, .xGallery .next {
text-align: end;
}

.xGallery .maximize {
background: transparent url('maximize.gif') no-repeat scroll center;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,29 @@ var XWiki = (function (XWiki) {
XWiki.Gallery = Class.create({
initialize : function(container) {
this.images = this._collectImages(container);

this.container = container.update(
'<input type="text" tabindex="-1" class="focusCatcher"/>' +
'<button class="maximize" title="${escapetool.xml($services.localization.render("core.widgets.gallery.maximize"))}"></button>' +
'<button class="previous" title="${escapetool.xml($services.localization.render("core.widgets.gallery.previousImage"))}">&lt;</button>' +
'<img class="currentImage" alt="${escapetool.xml($services.localization.render("core.widgets.gallery.currentImage"))}"/>' +
'<div class="previous" title="${escapetool.xml($services.localization.render("core.widgets.gallery.previousImage"))}">&lt;</div>' +
'<div class="next" title="${escapetool.xml($services.localization.render("core.widgets.gallery.nextImage"))}">&gt;</div>' +
'<div class="index">0 / 0</div>' +
'<div class="maximize" title="${escapetool.xml($services.localization.render("core.widgets.gallery.maximize"))}"></div>'
'<button class="next" title="${escapetool.xml($services.localization.render("core.widgets.gallery.nextImage"))}">&gt;</button>' +
'<div class="index" tabindex="0" title="${escapetool.xml($services.localization.render("core.widgets.gallery.index.description"))}" aria-description="${escapetool.xml($services.localization.render("core.widgets.gallery.index.description"))}">0 / 0</div>'
);
this.container.addClassName('xGallery');

this.focusCatcher = this.container.down('.focusCatcher');
this.container.addClassName('xGallery');

// Instead of an arbitrary element to catch focus, we use the index.
// This index already stores the current image state, might as well be responsible for providing quick controls and
// explanations about these quick controls.
// Note that wrapping the image in an interactive container to handle this would have been a good solution too.
// However, this wrapping caused the image to overflow the CSS grid vertically when in maximized mode.
// Technically I couldn't find a CSS solution to prevent this, so I decided to make do without wrapping.
this.focusCatcher = this.container.down('.index');
this.focusCatcher.observe('keydown', this._onKeyDown.bindAsEventListener(this));
this.container.observe('click', function() {
this.focusCatcher.focus();
}.bind(this));

this.container.down('.previous').observe('click', this._onPreviousImage.bind(this));
this.container.down('.next').observe('click', this._onNextImage.bind(this));
this.container.observe('click', function() {
this.focusCatcher.focus();
}.bind(this));

this.currentImage = this.container.down('.currentImage');
this.currentImage.observe('load', this._onLoadImage.bind(this));
Expand Down

0 comments on commit 4241c3c

Please sign in to comment.