Fix: CSS issues, keyboard shortcuts, group galleries

This commit is contained in:
Kujiu 2021-06-02 23:58:10 +02:00
parent ebfbd3b6fc
commit ac3d805175
Signed by: kujiu
GPG key ID: ABBB2CAC6855599F
11 changed files with 300 additions and 95 deletions

View file

@ -38,8 +38,8 @@ Just use the galleria directive like this:
:align: Align to 'right', 'left' or 'center' (default to center) - node level
:hide_title: Flag - hide title under image (not in dialog) - image level
:hide_alt: Flag - hide alternative text under image - image level
:transition: (optional, once) Transition for gallery - image level
:class: (optional, once) HTML class for gallery - node level
:no_transition: Flag - avoid transition effect - node level
:class: (optional) HTML class for gallery - node level
Image level options are same for all images defined by
the directive. If you need separated descriptions and

5
babel.cfg Normal file
View file

@ -0,0 +1,5 @@
[python: **.py]
encoding = utf-8
[javascript: **.js]
[javascript: **.mjs]

View file

@ -5,3 +5,24 @@ license-files =
LICENSE-de
LICENSE-fr
LICENSE-nl
[extract_messages]
mapping_file = babel.cfg
output_file = sphinx_galleria/locale/sphinx.pot
keywords = _ l_ lazy_gettext gettext ngettext
add_comments = Translators:
[init_catalog]
domain = sphinx
input_file = sphinx_galleria/locale/sphinx.pot
output_dir = sphinx_galleria/locale/
[update_catalog]
domain = sphinx
input_file = sphinx_galleria/locale/sphinx.pot
output_dir = sphinx_galleria/locale/
[compile_catalog]
domain = sphinx
directory = sphinx_galleria/locale/

View file

@ -1,4 +1,28 @@
from setuptools import setup
import distutils
from babel.messages import frontend as babel
class TranslateCommand(distutils.cmd.Command):
description = "Translation"
user_options = []
sub_commands = [
('extract_messages', None),
('update_catalog', None),
('compile_catalog', None),
]
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)
with open("README.rst", "r") as fh:
long_description = fh.read()
@ -14,6 +38,12 @@ setup(
long_description=long_description,
long_description_content_type="text/x-rst",
packages=["sphinx_galleria"],
cmdclass={
'compile_catalog': babel.compile_catalog,
'extract_messages': babel.extract_messages,
'init_catalog': babel.init_catalog,
'update_catalog': babel.update_catalog
},
package_data={
"sphinx_galleria": [
"*.py",

View file

@ -32,10 +32,14 @@ def copy_images_files(app: Sphinx, env: BuildEnvironment) -> None:
relpath = os.path.relpath(thumb, env.srcdir)
dest = os.path.join(app.outdir, relpath)
basename, ext = os.path.splitext(dest)
if ext.lower() in ('.svg', '.svgz'):
continue
thumbsize = basename.split('-')[-1].split('x')
thumbsize = [int(size) for size in thumbsize]
original = '.'.join(basename.split('.')[:-1]) + ext
dest = basename + '.jpg'
dest = basename + '.png'
ensuredir(os.path.dirname(dest))
with Image.open(original) as im:
@ -60,7 +64,7 @@ def copy_images_files(app: Sphinx, env: BuildEnvironment) -> None:
(im.size[1]+thumbsize[0]*im.size[1]/thumbsize[0])//2,
))
out.save(dest, "JPEG")
out.save(dest, "PNG")
def install_static_files(app: Sphinx, env: BuildEnvironment) -> None:
@ -93,12 +97,6 @@ def install_static_files(app: Sphinx, env: BuildEnvironment) -> None:
app.add_css_file(os.path.join('sphinxgalleria', static))
def init_directive(app: Sphinx, config) -> None:
if 'galleria_override_image' in config and \
config['galleria_override_image']:
app.add_directive('image', directive.GalleriaDirective)
def setup(app: Sphinx) -> Dict[str, Any]:
app.add_node(
directive.galleria,
@ -112,8 +110,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
)
app.add_directive('galleria', directive.GalleriaDirective)
app.add_env_collector(collector.GalleriaCollector)
app.add_config_value('galleria_override_image', False, 'boolean')
app.connect('env-updated', install_static_files)
app.connect('env-updated', copy_images_files)
app.connect('config-inited', init_directive)
return {'version': __version__}

View file

@ -8,6 +8,7 @@ Galleria image collector
import os
import glob
import logging
from copy import copy
from typing import Set
from sphinx.environment.collectors import EnvironmentCollector
from docutils import nodes
@ -63,9 +64,10 @@ class GalleriaCollector(EnvironmentCollector):
images = []
for imageglob in node['images']:
thumbsize = imageglob['thumbsize']
size_array = []
try:
for size in thumbsize.split('x'):
int(size)
size_array.append(int(size))
except ValueError:
logger.error(
__('thumbsize %s is invalid (use 100x120 format)'),
@ -73,25 +75,35 @@ class GalleriaCollector(EnvironmentCollector):
raise
del imageglob['thumbsize']
glob_path = os.path.join(app.env.srcdir, imageglob['image'])
glob_path = os.path.join(
os.path.dirname(node.source),
imageglob['path']
)
for image_path in glob.glob(glob_path):
app.env.galleria.add_file(docname, image_path)
basename, ext = os.path.splitext(image_path)
thumb_path = basename + ".thumb-" + thumbsize + ext
thumb_path_jpg = basename + ".thumb-" + thumbsize + '.jpg'
thumb_path_cropped = basename + ".thumb-" + thumbsize
thumb_path_cropped += '.png'
if ext.lower() in ('.svg', '.svgz'):
thumb_path_cropped = image_path
thumb_path = image_path
jsonimage = copy(imageglob)
jsonimage['thumbsize'] = size_array
jsonimage['thumb'] = os.path.relpath(
thumb_path_cropped,
app.env.srcdir)
jsonimage['path'] = os.path.relpath(
image_path,
app.env.srcdir
)
images.append(jsonimage)
images.append({
'title': imageglob['title'],
'alt': imageglob['alt'],
'thumb': os.path.relpath(
thumb_path_jpg,
app.env.srcdir),
'image': os.path.relpath(image_path, app.env.srcdir)
})
app.env.dependencies[docname].add(image_path)
app.env.dependencies[docname].add(thumb_path_jpg)
app.env.dependencies[docname].add(thumb_path_cropped)
if not os.access(
os.path.join(
app.srcdir,

View file

@ -18,7 +18,7 @@ from sphinx.writers.texinfo import TexinfoTranslator
from sphinx.writers.text import TextTranslator
from sphinx.writers.manpage import ManualPageTranslator
from sphinx.util.osutil import relative_uri
from sphinx.locale import __
from sphinx.locale import _
class galleria(nodes.General, nodes.Element):
@ -26,12 +26,11 @@ class galleria(nodes.General, nodes.Element):
def html_visit_galleria(self: HTMLTranslator, node: galleria) -> None:
galleria_id = 'galleria-%s' % uuid.uuid4()
self.body.append(
"<div id='%s' class='%s' width='%s' height='%s'>" % (
galleria_id,
node['class'] + ' align-%s' % node['options']['align'],
"<div id='%s' class='%s' style='width: %s; height: %s;'>" % (
node['options']['galleria'],
node['class'] + ' align-%s sphinxgalleria-core' %
node['options']['align'],
node['options']['width'],
node['options']['height'],
)
@ -40,12 +39,22 @@ def html_visit_galleria(self: HTMLTranslator, node: galleria) -> None:
if len(node['images']) > 0:
self.body.append("<figure><div class='row'>")
self.body.append("<img src='%s' title='%s' alt='%s'>" % (
node['images'][0]['image'],
node['images'][0]['path'],
node['images'][0]['title'],
node['images'][0]['alt']
))
self.body.append('</div></figure>')
for img in node['images']:
img['path'] = relative_uri(
self.builder.get_target_uri(self.builder.current_docname),
img['path']
)
img['thumb'] = relative_uri(
self.builder.get_target_uri(self.builder.current_docname),
img['thumb']
)
self.body.append(
"</div><script type='module'>" +
"import {SphinxGalleria} from './%s';\n" %
@ -53,7 +62,7 @@ def html_visit_galleria(self: HTMLTranslator, node: galleria) -> None:
self.builder.get_target_uri(self.builder.current_docname),
'_static/sphinxgalleria/sphinxgalleria.mjs') +
"new SphinxGalleria('%s', %s, %s).init();</script>" % (
galleria_id,
node['options']['galleria'],
json.dumps(node['options']),
json.dumps(node['images'])
)
@ -82,7 +91,7 @@ def text_visit_galleria(self: TextTranslator, node: galleria) -> None:
def gemini_visit_galleria(self, node: galleria) -> None:
for image in node['images']:
self.body.append('=> %s %s' % (image['path'], image['alt']))
self.body += '=> %s %s' % (image['path'], image['alt'])
raise nodes.SkipNode
@ -108,51 +117,54 @@ class GalleriaDirective(Directive):
"alt": directives.unchanged,
"title": directives.unchanged,
"thumbsize": directives.unchanged,
"transition": directives.unchanged,
'width': directives.length_or_percentage_or_unitless,
'height': directives.length_or_unitless,
'align': align_choices,
'hide_title': directives.flag,
'hide_alt': directives.flag,
'no_transition': directives.flag,
}
def run(self):
source = self.state.document.settings._source
document = self.state.document
if not document.hasattr('galleria_nodes'):
document.galleria_nodes = {}
if source not in document.galleria_nodes:
document.galleria_nodes[source] = {}
source = self.state_machine.document.settings._source
env = self.state_machine.document.settings.env
try:
if source not in env.galleria_nodes:
env.galleria_nodes[source] = {}
galleria_name = self.options.get('galleria') or uuid.uuid4()
except AttributeError:
env.galleria_nodes = {}
env.galleria_nodes[source] = {}
galleria_name = self.options.get('galleria')
galleria_name = galleria_name or str(uuid.uuid4()).replace('-', '')
created = False
if galleria_name in document.galleria_nodes[source]:
node = document.galleria_nodes[source][galleria_name]
if galleria_name in env.galleria_nodes[source]:
node = env.galleria_nodes[source][galleria_name]
else:
node = galleria()
node['class'] = 'galleria'
node['options'] = {
'transition': 'fade',
'label_prev': __('Previous'),
'label_next': __('Next'),
'label_close': __('Close'),
'label_thumbnail': __('Thumbnail, click to enlarge'),
'galleria': 'galleria-' + galleria_name,
'label_prev': _('Previous'),
'label_next': _('Next'),
'label_close': _('Close'),
'label_thumbnail': _('Thumbnail, click to enlarge'),
}
node['images'] = []
document.galleria_nodes[source][galleria_name] = node
env.galleria_nodes[source][galleria_name] = node
created = True
if self.options.get('class'):
node['class'] = self.options['class']
if self.options.get('transition'):
node['options']['transition'] = self.options['transition']
node['class'] = ' '.join(self.options['class'])
node['options']["width"] = self.options.get('width') or 'auto'
node['options']["height"] = self.options.get('height') or 'auto'
node['options']["align"] = self.options.get('align') or 'center'
node['options']["no_transition"] = 'no_transition' in self.options
images_path = self.arguments
for path in images_path:
@ -160,9 +172,9 @@ class GalleriaDirective(Directive):
image["alt"] = self.options.get('alt')
image["title"] = self.options.get('title')
image["thumbsize"] = self.options.get('thumbsize') or '100x100'
image["hide_alt"] = bool(self.options.get('hide_alt'))
image["hide_title"] = bool(self.options.get('hide_title'))
image["image"] = path
image["hide_alt"] = 'hide_alt' in self.options
image["hide_title"] = 'hide_title' in self.options
image["path"] = path
node['images'].append(image)
if created:

View file

@ -0,0 +1,37 @@
# French translations for sphinx_galleria.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the sphinx_galleria
# project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: sphinx_galleria 1.0.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2021-06-02 23:53+0200\n"
"PO-Revision-Date: 2021-06-02 23:55+0200\n"
"Language: fr\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
"Last-Translator: \n"
"X-Generator: Poedit 2.4.3\n"
#: sphinx_galleria/directive.py:151
msgid "Previous"
msgstr "Précédent"
#: sphinx_galleria/directive.py:152
msgid "Next"
msgstr "Suivant"
#: sphinx_galleria/directive.py:153
msgid "Close"
msgstr "Fermer"
#: sphinx_galleria/directive.py:154
msgid "Thumbnail, click to enlarge"
msgstr "Vignette, cliquer pour agrandir"

View file

@ -0,0 +1,37 @@
# Dutch translations for sphinx_galleria.
# Copyright (C) 2021 ORGANIZATION
# This file is distributed under the same license as the sphinx_galleria
# project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2021.
#
msgid ""
msgstr ""
"Project-Id-Version: sphinx_galleria 1.0.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2021-06-02 23:53+0200\n"
"PO-Revision-Date: 2021-06-02 23:57+0200\n"
"Language: nl\n"
"Language-Team: nl <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.9.1\n"
"Last-Translator: \n"
"X-Generator: Poedit 2.4.3\n"
#: sphinx_galleria/directive.py:151
msgid "Previous"
msgstr "Vorig"
#: sphinx_galleria/directive.py:152
msgid "Next"
msgstr "Volgende"
#: sphinx_galleria/directive.py:153
msgid "Close"
msgstr "Sluit"
#: sphinx_galleria/directive.py:154
msgid "Thumbnail, click to enlarge"
msgstr "Miniatuur, klik om te vergroten"

View file

@ -1,17 +1,27 @@
.sphinxgalleria-core {
text-align: center;
box-sizing: border-box;
margin-bottom: 1rem;
}
.sphinxgalleria-core.align-center {
margin: auto;
margin-left: auto;
margin-right: auto;
}
.sphinxgalleria-core.align-left {
float: left;
margin-right: 1rem;
}
.sphinxgalleria-core.align-right {
float: right;
margin-left: 1rem;
}
.sphinxgalleria-core figure {
margin: 0;
padding: 0;
}
.sphinxgalleria-core figure .row {
@ -31,10 +41,17 @@
margin: 0;
padding: 0;
border: none;
width: max-content;
border-radius: unset;
box-sizing: border-box;
width: 100%;
height: max-content;
display: flex;
flex-flow: column nowrap;
background-color: transparent;
}
.sphinxgalleria-core figure .row button.button-modal::hover {
background-color: transparent;
}
.sphinxgalleria-core figure .row button.button-modal .caption {
@ -46,6 +63,7 @@
border: none;
margin: 0;
padding: .5rem 1.5rem .5rem 1.5rem;
box-sizing: border-box;
width: 100%;
text-align: left;
}
@ -59,9 +77,13 @@
margin: 0;
padding: 0;
border: none;
width: auto;
width: 100%;
height: auto;
object-fit: contain;
object-fit: cover;
transition: width ease .4s, height ease .4s, opacity ease .4s;
}
.sphinxgalleria-core.no-transition figure .row button.button-modal img {
transition: unset;
}
.sphinxgalleria-core figure input[type=range] {
@ -163,6 +185,10 @@
flex-shrink: 1000;
margin: 0.5rem 0 0.5rem 0;
padding: 0;
transition: width ease 1s, height ease 1s, opacity ease .4s;
}
.sphinxgalleria-core.no-transition dialog img {
transition: unset;
}
.sphinxgalleria-core dialog menu {

View file

@ -12,7 +12,7 @@ export class SphinxGalleria {
self.node_figure = document.createElement('figure');
self.button_modal = document.createElement('button');
self.button_modal.setAttribute('aria-haspopup', 'dialog');
self.button_modal.setAttribute('class', 'button-modal');
self.button_modal.classList.add('button-modal');
self.node_image = document.createElement('img');
self.node_caption = document.createElement('figcaption');
@ -20,22 +20,22 @@ export class SphinxGalleria {
self.node_alt.setAttribute('id', self.target+'-'+'alt');
self.node_image.setAttribute('aria-describedby', self.target+'-'+'alt');
self.node_thumbnails = document.createElement('ul');
self.node_thumbnails.setAttribute('class', 'thumbnails');
self.node_thumbnails.classList.add('thumbnails');
self.node_dialog = document.createElement('dialog');
self.node_mask = document.createElement('div');
self.node_mask.setAttribute('class', 'mask');
self.node_mask.classList.add('mask');
self.node_mask.hidden = true;
var node_submask = document.createElement('div');
node_submask.setAttribute('class', 'submask');
node_submask.classList.add('submask');
self.node_div_caption = document.createElement('div');
self.node_div_caption.setAttribute('class', 'caption');
self.node_div_caption.classList.add('caption');
var figure_row = document.createElement('div');
figure_row.setAttribute('class', 'row');
figure_row.classList.add('row');
self.dialog_button_close = document.createElement('button');
self.dialog_button_close.setAttribute('class', 'close');
self.dialog_button_close.classList.add('close');
self.dialog_button_close_icon = document.createElement('button');
self.dialog_button_close_icon.setAttribute('aria-label', self.options.label_close);
@ -50,7 +50,7 @@ export class SphinxGalleria {
if(self.oneimage) {
self.node_thumbnails.hidden = true;
self.node_thumbnails.setAttribute('aria-hidden', true);
self.node_thumbnails.style.visibility = 'hidden';
self.node_thumbnails.style.display = 'none';
} else {
self.node_slider = document.createElement('input');
self.node_slider.setAttribute('type', 'range');
@ -59,17 +59,17 @@ export class SphinxGalleria {
self.node_slider.setAttribute('value', 1);
self.button_prev = document.createElement('button');
self.button_prev.setAttribute('class', 'prev');
self.button_prev.classList.add('prev');
self.button_prev.setAttribute('aria-label', self.options.label_prev);
self.button_next = document.createElement('button');
self.button_next.setAttribute('class', 'next');
self.button_next.classList.add('next');
self.button_next.setAttribute('aria-label', self.options.label_next);
self.dialog_button_prev = document.createElement('button');
self.dialog_button_prev.setAttribute('class', 'prev');
self.dialog_button_prev.classList.add('prev');
self.dialog_button_next = document.createElement('button');
self.dialog_button_next.setAttribute('class', 'next');
self.dialog_button_next.classList.add('next');
self.dialog_button_prev.appendChild(document.createTextNode(self.options.label_prev));
self.dialog_button_next.appendChild(document.createTextNode(self.options.label_next));
@ -107,18 +107,20 @@ export class SphinxGalleria {
init() {
var self = this;
self.node_target = document.getElementById(self.target);
if(self.options.no_transition || self.oneimage) {
if(!self.node_target.classList.contains('no-transition')) {
self.node_target.classList.add('no-transition');
}
}
self.node_target.innerHTML = '';
self.node_target.setAttribute(
'class',
self.node_target.getAttribute('class') + " sphinxgalleria-core"
);
self.node_target.classList.add("sphinxgalleria-core");
self.node_target.appendChild(self.node_figure);
self.node_target.appendChild(self.node_thumbnails);
self.node_target.appendChild(self.node_mask);
var onmodal = function(event) {
var key = event.keyCode || event.charCode || event.which;
if(event.type==='keypress' && key!==13 && key!==32) {
var key = event.key;
if(event.type==='keypress' && key!==" " && key!=="Enter") {
return;
}
event.preventDefault();
@ -128,8 +130,8 @@ export class SphinxGalleria {
self.button_modal.addEventListener('keypress', onmodal);
var onclose = function(event) {
var key = event.keyCode || event.charCode || event.which;
if(event.type==='keypress' && key!==13 && key!==32) {
var key = event.key;
if(event.type==='keypress' && key!==" " && key!=="Enter") {
return;
}
event.preventDefault();
@ -145,8 +147,8 @@ export class SphinxGalleria {
if(!self.oneimage) {
var onprev = function(event) {
var key = event.keyCode || event.charCode || event.which;
if(event.type==='keypress' && key!==13 && key!==32) {
var key = event.key;
if(event.type==='keypress' && key!==" " && key!=="Enter") {
return;
}
event.preventDefault();
@ -154,8 +156,8 @@ export class SphinxGalleria {
};
var onnext = function(event) {
var key = event.keyCode || event.charCode || event.which;
if(event.type==='keypress' && key!==13 && key!==32) {
var key = event.key;
if(event.type==='keypress' && key!==" " && key!=="Enter") {
return;
}
event.preventDefault();
@ -195,7 +197,7 @@ export class SphinxGalleria {
var image_element = document.createElement('li');
var image_button = document.createElement('input');
image_button.setAttribute('type', 'radio');
image_button.setAttribute('value', image_data.image);
image_button.setAttribute('value', image_data.path);
image_button.setAttribute('name', self.target);
image_button.setAttribute('data-index', idx);
image_button.setAttribute('id', self.target+'-'+idx);
@ -211,6 +213,8 @@ export class SphinxGalleria {
var image_thumb = document.createElement('img');
image_thumb.setAttribute('src', image_data.thumb);
image_thumb.setAttribute('width', image_data.thumbsize[0]);
image_thumb.setAttribute('heigh', image_data.thumbsize[1]);
if(image_data.alt) {
image_button.setAttribute('data-alt', image_data.alt);
@ -229,28 +233,37 @@ export class SphinxGalleria {
image_button.setAttribute('data-hide-title', true);
}
if(image_data.transition) {
image_thumb.setAttribute('data-transition', image_data.transition);
}
image_label.appendChild(image_thumb);
self.node_thumbnails.appendChild(image_element);
image_button.addEventListener('change', function() {self.changeImage();});
});
self.node_target.addEventListener('keypress', function(event) {
var key = event.keyCode || event.charCode || event.which;
document.addEventListener('keydown', function(event) {
var key = event.key;
if(!self.oneimage && key===37) {
if(!self.node_dialog.open && !self.node_target.contains(document.activeElement)) {
return;
}
if(!self.oneimage && key==="ArrowLeft") {
self.prev();
} else if(!self.oneimage && key===39) {
event.preventDefault();
} else if(!self.oneimage && key==="ArrowRight") {
self.next();
} else if(key===27) {
event.preventDefault();
} else if(key==="Escape") {
self.closeModal();
event.preventDefault();
}
});
if(self.node_dialog.showModal) {
self.node_dialog.addEventListener('close', function() {
self.closeModal();
});
}
self.changeImage();
}
@ -291,6 +304,11 @@ export class SphinxGalleria {
var hide_caption = true;
var idx = parseInt(current.getAttribute('data-index'));
if(!self.options.no_transition && !self.oneimage) {
self.node_image.style.opacity = 0.2;
self.dialog_image.style.opacity = 0.2;
}
if(!self.oneimage) {
if(self.node_slider.value !== idx+1) {
self.node_slider.value = idx + 1;
@ -338,8 +356,19 @@ export class SphinxGalleria {
self.node_div_caption.hidden = false;
}
if(self.options.no_transition || self.oneimage) {
self.node_image.setAttribute('src', url);
self.dialog_image.setAttribute('src', url);
} else {
setTimeout(function() {
self.node_image.setAttribute('src', url);
self.dialog_image.setAttribute('src', url);
setTimeout(function() {
self.node_image.style.opacity = 1;
self.dialog_image.style.opacity = 1;
}, 200);
}, 200);
}
}
showModal() {