mirror of
https://github.com/pretix/pretix.git
synced 2026-08-25 13:12:00 +00:00
Widget: add lightbox for product images (Z#23123811) (#3439)
This commit is contained in:
@@ -212,11 +212,14 @@ def price_dict(item, price):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_picture(event, picture):
|
def get_picture(event, picture, size=None):
|
||||||
try:
|
thumb = None
|
||||||
thumb = get_thumbnail(picture.name, '60x60^').thumb.url
|
if size:
|
||||||
except:
|
try:
|
||||||
logger.exception(f'Failed to create thumbnail of {picture.name}')
|
thumb = get_thumbnail(picture.name, size).thumb.url
|
||||||
|
except:
|
||||||
|
logger.exception(f'Failed to create thumbnail of {picture.name}')
|
||||||
|
if not thumb:
|
||||||
thumb = default_storage.url(picture.name)
|
thumb = default_storage.url(picture.name)
|
||||||
return urljoin(build_absolute_uri(event, 'presale:event.index'), thumb)
|
return urljoin(build_absolute_uri(event, 'presale:event.index'), thumb)
|
||||||
|
|
||||||
@@ -264,7 +267,8 @@ class WidgetAPIProductList(EventListMixin, View):
|
|||||||
{
|
{
|
||||||
'id': item.pk,
|
'id': item.pk,
|
||||||
'name': str(item.name),
|
'name': str(item.name),
|
||||||
'picture': get_picture(self.request.event, item.picture) if item.picture else None,
|
'picture': get_picture(self.request.event, item.picture, '60x60^') if item.picture else None,
|
||||||
|
'picture_fullsize': get_picture(self.request.event, item.picture) if item.picture else None,
|
||||||
'description': str(rich_text(item.description, safelinks=False)) if item.description else None,
|
'description': str(rich_text(item.description, safelinks=False)) if item.description else None,
|
||||||
'has_variations': item.has_variations,
|
'has_variations': item.has_variations,
|
||||||
'require_voucher': item.require_voucher,
|
'require_voucher': item.require_voucher,
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ Vue.component('item', {
|
|||||||
|
|
||||||
// Product description
|
// Product description
|
||||||
+ '<div class="pretix-widget-item-info-col">'
|
+ '<div class="pretix-widget-item-info-col">'
|
||||||
+ '<img :src="item.picture" v-if="item.picture" class="pretix-widget-item-picture">'
|
+ '<a :href="item.picture_fullsize" v-if="item.picture" class="pretix-widget-item-picture-link" @click.prevent.stop="lightbox"><img :src="item.picture" class="pretix-widget-item-picture"></a>'
|
||||||
+ '<div class="pretix-widget-item-title-and-description">'
|
+ '<div class="pretix-widget-item-title-and-description">'
|
||||||
+ '<a v-if="item.has_variations && show_toggle" class="pretix-widget-item-title" :href="\'#\' + item.id + \'-variants\'"'
|
+ '<a v-if="item.has_variations && show_toggle" class="pretix-widget-item-title" :href="\'#\' + item.id + \'-variants\'"'
|
||||||
+ ' @click.prevent.stop="expand" role="button" tabindex="0"'
|
+ ' @click.prevent.stop="expand" role="button" tabindex="0"'
|
||||||
@@ -530,6 +530,12 @@ Vue.component('item', {
|
|||||||
methods: {
|
methods: {
|
||||||
expand: function () {
|
expand: function () {
|
||||||
this.expanded = !this.expanded;
|
this.expanded = !this.expanded;
|
||||||
|
},
|
||||||
|
lightbox: function () {
|
||||||
|
this.$root.overlay.lightbox = {
|
||||||
|
image: this.item.picture_fullsize,
|
||||||
|
description: this.item.name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -784,12 +790,44 @@ var shared_alert_fragment = (
|
|||||||
+ '</div>'
|
+ '</div>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var shared_lightbox_fragment = (
|
||||||
|
'<div :class="lightboxClasses" role="dialog" aria-modal="true" v-if="$root.lightbox" @click="lightboxClose">'
|
||||||
|
+ '<div class="pretix-widget-lightbox-loading" v-if="$root.lightbox?.loading">'
|
||||||
|
+ '<svg width="256" height="256" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path class="pretix-widget-primary-color" d="M1152 896q0-106-75-181t-181-75-181 75-75 181 75 181 181 75 181-75 75-181zm512-109v222q0 12-8 23t-20 13l-185 28q-19 54-39 91 35 50 107 138 10 12 10 25t-9 23q-27 37-99 108t-94 71q-12 0-26-9l-138-108q-44 23-91 38-16 136-29 186-7 28-36 28h-222q-14 0-24.5-8.5t-11.5-21.5l-28-184q-49-16-90-37l-141 107q-10 9-25 9-14 0-25-11-126-114-165-168-7-10-7-23 0-12 8-23 15-21 51-66.5t54-70.5q-27-50-41-99l-183-27q-13-2-21-12.5t-8-23.5v-222q0-12 8-23t19-13l186-28q14-46 39-92-40-57-107-138-10-12-10-24 0-10 9-23 26-36 98.5-107.5t94.5-71.5q13 0 26 10l138 107q44-23 91-38 16-136 29-186 7-28 36-28h222q14 0 24.5 8.5t11.5 21.5l28 184q49 16 90 37l142-107q9-9 24-9 13 0 25 10 129 119 165 170 7 8 7 22 0 12-8 23-15 21-51 66.5t-54 70.5q26 50 41 98l183 28q13 2 21 12.5t8 23.5z"/></svg>'
|
||||||
|
+ '</div>'
|
||||||
|
+ '<div class="pretix-widget-lightbox-inner" @click.stop="">'
|
||||||
|
+ '<figure class="pretix-widget-lightbox-image">'
|
||||||
|
+ '<img :src="$root.lightbox.image" :alt="$root.lightbox.description" @load="lightboxLoaded" ref="lightboxImage">'
|
||||||
|
+ '<figcaption v-if="$root.lightbox.description">{{$root.lightbox.description}}</figcaption>'
|
||||||
|
+ '</figure>'
|
||||||
|
+ '<button type="button" class="pretix-widget-lightbox-close" @click="lightboxClose" aria-label="'+strings.close+'">'
|
||||||
|
+ '<svg height="16" viewBox="0 0 512 512" width="16" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M437.5,386.6L306.9,256l130.6-130.6c14.1-14.1,14.1-36.8,0-50.9c-14.1-14.1-36.8-14.1-50.9,0L256,205.1L125.4,74.5 c-14.1-14.1-36.8-14.1-50.9,0c-14.1,14.1-14.1,36.8,0,50.9L205.1,256L74.5,386.6c-14.1,14.1-14.1,36.8,0,50.9 c14.1,14.1,36.8,14.1,50.9,0L256,306.9l130.6,130.6c14.1,14.1,36.8,14.1,50.9,0C451.5,423.4,451.5,400.6,437.5,386.6z"/></svg>'
|
||||||
|
+ '</button>'
|
||||||
|
+ '</div>'
|
||||||
|
+ '</div>'
|
||||||
|
);
|
||||||
|
|
||||||
Vue.component('pretix-overlay', {
|
Vue.component('pretix-overlay', {
|
||||||
template: ('<div class="pretix-widget-overlay">'
|
template: ('<div class="pretix-widget-overlay">'
|
||||||
+ shared_iframe_fragment
|
+ shared_iframe_fragment
|
||||||
+ shared_alert_fragment
|
+ shared_alert_fragment
|
||||||
|
+ shared_lightbox_fragment
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
),
|
),
|
||||||
|
watch: {
|
||||||
|
'$root.lightbox': function (newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
if (newValue.image != oldValue?.image) {
|
||||||
|
this.$set(newValue, "loading", true);
|
||||||
|
}
|
||||||
|
if (!oldValue) {
|
||||||
|
window.addEventListener('keyup', this.lightboxCloseOnKeyup);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.removeEventListener('keyup', this.lightboxCloseOnKeyup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
frameClasses: function () {
|
frameClasses: function () {
|
||||||
return {
|
return {
|
||||||
@@ -803,8 +841,27 @@ Vue.component('pretix-overlay', {
|
|||||||
'pretix-widget-alert-shown': this.$root.error_message,
|
'pretix-widget-alert-shown': this.$root.error_message,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
lightboxClasses: function () {
|
||||||
|
return {
|
||||||
|
'pretix-widget-lightbox-holder': true,
|
||||||
|
'pretix-widget-lightbox-shown': this.$root.lightbox,
|
||||||
|
'pretix-widget-lightbox-isloading': this.$root.lightbox?.loading,
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
lightboxCloseOnKeyup: function (event) {
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
// abort on ESC-key
|
||||||
|
this.lightboxClose();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lightboxClose: function () {
|
||||||
|
this.$root.lightbox = null;
|
||||||
|
},
|
||||||
|
lightboxLoaded: function () {
|
||||||
|
this.$root.lightbox.loading = false;
|
||||||
|
},
|
||||||
errorClose: function () {
|
errorClose: function () {
|
||||||
this.$root.error_message = null;
|
this.$root.error_message = null;
|
||||||
this.$root.error_url_after = null;
|
this.$root.error_url_after = null;
|
||||||
@@ -1795,6 +1852,7 @@ var create_overlay = function (app) {
|
|||||||
error_url_after: null,
|
error_url_after: null,
|
||||||
error_url_after_new_tab: true,
|
error_url_after_new_tab: true,
|
||||||
error_message: null,
|
error_message: null,
|
||||||
|
lightbox: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -768,6 +768,102 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pretix-widget-lightbox-holder {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
z-index: 16777271;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s, visibility 0.5s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.pretix-widget-lightbox-loading svg {
|
||||||
|
margin: 40px;
|
||||||
|
-webkit-animation: pretix-widget-spin 6s linear infinite;
|
||||||
|
-moz-animation: pretix-widget-spin 6s linear infinite;
|
||||||
|
animation: pretix-widget-spin 6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pretix-widget-lightbox-shown {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.5s, visibility 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pretix-widget-lightbox-inner {
|
||||||
|
position: relative;
|
||||||
|
background: white;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
-moz-border-radius: 5px 5px 5px 5px;
|
||||||
|
-webkit-border-radius: 5px 5px 5px 5px;
|
||||||
|
box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.09);
|
||||||
|
-webkit-box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.09);
|
||||||
|
-moz-box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.09);
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px;
|
||||||
|
max-width: 90%;
|
||||||
|
max-height: 90%;
|
||||||
|
}
|
||||||
|
&.pretix-widget-lightbox-isloading .pretix-widget-lightbox-inner {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0,0,0,0);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pretix-widget-lightbox-image {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.pretix-widget-lightbox-image img {
|
||||||
|
max-width: 80vw;
|
||||||
|
max-height: 80vh;
|
||||||
|
object-fit: scale-down;
|
||||||
|
}
|
||||||
|
.pretix-widget-lightbox-image figcaption {
|
||||||
|
margin: 0.5em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pretix-widget-lightbox-close {
|
||||||
|
position: absolute;
|
||||||
|
right: -12px;
|
||||||
|
top: -12px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: $brand-primary;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
-moz-border-radius: 12px;
|
||||||
|
-webkit-border-radius: 12px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 4px 0;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pretix-widget-lightbox-close svg {
|
||||||
|
display: inline-block;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.pretix-widget-primary-color {
|
.pretix-widget-primary-color {
|
||||||
/* in SVG */
|
/* in SVG */
|
||||||
fill: $brand-primary;
|
fill: $brand-primary;
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
|||||||
"max_price": None,
|
"max_price": None,
|
||||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||||
"picture": None,
|
"picture": None,
|
||||||
|
"picture_fullsize": None,
|
||||||
"has_variations": 0,
|
"has_variations": 0,
|
||||||
"allow_waitinglist": True,
|
"allow_waitinglist": True,
|
||||||
"mandatory_priced_addons": False,
|
"mandatory_priced_addons": False,
|
||||||
@@ -204,6 +205,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
|||||||
"max_price": "14.00",
|
"max_price": "14.00",
|
||||||
"price": None,
|
"price": None,
|
||||||
"picture": None,
|
"picture": None,
|
||||||
|
"picture_fullsize": None,
|
||||||
"has_variations": 4,
|
"has_variations": 4,
|
||||||
"allow_waitinglist": True,
|
"allow_waitinglist": True,
|
||||||
"mandatory_priced_addons": False,
|
"mandatory_priced_addons": False,
|
||||||
@@ -265,6 +267,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
|||||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
|
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
|
||||||
"includes_mixed_tax_rate": False},
|
"includes_mixed_tax_rate": False},
|
||||||
"picture": None,
|
"picture": None,
|
||||||
|
"picture_fullsize": None,
|
||||||
"has_variations": 0,
|
"has_variations": 0,
|
||||||
"allow_waitinglist": True,
|
"allow_waitinglist": True,
|
||||||
"mandatory_priced_addons": False,
|
"mandatory_priced_addons": False,
|
||||||
@@ -310,6 +313,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
|||||||
"max_price": "14.00",
|
"max_price": "14.00",
|
||||||
"price": None,
|
"price": None,
|
||||||
"picture": None,
|
"picture": None,
|
||||||
|
"picture_fullsize": None,
|
||||||
"has_variations": 4,
|
"has_variations": 4,
|
||||||
"allow_waitinglist": True,
|
"allow_waitinglist": True,
|
||||||
"mandatory_priced_addons": False,
|
"mandatory_priced_addons": False,
|
||||||
@@ -371,6 +375,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
|||||||
"max_price": None,
|
"max_price": None,
|
||||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||||
"picture": None,
|
"picture": None,
|
||||||
|
"picture_fullsize": None,
|
||||||
"has_variations": 0,
|
"has_variations": 0,
|
||||||
"allow_waitinglist": True,
|
"allow_waitinglist": True,
|
||||||
"mandatory_priced_addons": False,
|
"mandatory_priced_addons": False,
|
||||||
@@ -425,6 +430,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
|||||||
'id': self.shirt.pk,
|
'id': self.shirt.pk,
|
||||||
'name': 'T-Shirt',
|
'name': 'T-Shirt',
|
||||||
'picture': None,
|
'picture': None,
|
||||||
|
"picture_fullsize": None,
|
||||||
'description': None,
|
'description': None,
|
||||||
'has_variations': 2,
|
'has_variations': 2,
|
||||||
"allow_waitinglist": True,
|
"allow_waitinglist": True,
|
||||||
|
|||||||
Reference in New Issue
Block a user