Compare commits

...

1 Commits

Author SHA1 Message Date
Raphael Michel
3b62737d4c Widget: Support for inline seating plan 2022-12-22 13:24:42 +01:00
4 changed files with 83 additions and 21 deletions

View File

@@ -87,6 +87,18 @@ website. If you confident to have a good reason for not using SSL, you can overr
<pretix-widget event="https://pretix.eu/demo/democon/" skip-ssl-check></pretix-widget> <pretix-widget event="https://pretix.eu/demo/democon/" skip-ssl-check></pretix-widget>
Seating plans
-------------
By default, events with seating plans just show a button that opens the seating plan. You can also have the seating
plan embedded into the widget directly by using::
<pretix-widget event="https://pretix.eu/demo/democon/" seating-embedded></pretix-widget>
Note that the seating plan will only be embedded if the widget has enough space (currently min. 992 pixels width, may change
in the future) and that the seating plan part of the widget can unfortunately *not* be styled with CSS like the rest of
the widget.
Always open a new tab Always open a new tab
--------------------- ---------------------

View File

@@ -663,11 +663,15 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
return context return context
@method_decorator(allow_frame_if_namespaced, 'dispatch')
@method_decorator(iframe_entry_view_wrapper, 'dispatch') @method_decorator(iframe_entry_view_wrapper, 'dispatch')
class SeatingPlanView(EventViewMixin, TemplateView): class SeatingPlanView(EventViewMixin, TemplateView):
template_name = "pretixpresale/event/seatingplan.html" template_name = "pretixpresale/event/seatingplan.html"
def dispatch(self, request, *args, **kwargs):
r = super().dispatch(request, *args, **kwargs)
r.xframe_options_exempt = True
return r
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
from pretix.presale.views.cart import get_or_create_cart_id from pretix.presale.views.cart import get_or_create_cart_id

View File

@@ -141,18 +141,21 @@ var api = {
}, },
'_postFormJSON': function (endpoint, form, callback, err_callback) { '_postFormJSON': function (endpoint, form, callback, err_callback) {
var params = [].filter.call(form.elements, function (el) { var params;
return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked; if (Array.isArray(form)) {
}) params = form
.filter(function (el) { } else {
return !!el.name && !!el.value; params = [].filter.call(form.elements, function (el) {
}) return (el.type !== 'checkbox' && el.type !== 'radio') || el.checked;
.filter(function (el) { }).filter(function (el) {
return !!el.name && !!el.value;
}).filter(function (el) {
return !el.disabled; return !el.disabled;
}) })
.map(function (el) { }
return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value); params = params.map(function (el) {
}).join('&'); return encodeURIComponent(el.name) + '=' + encodeURIComponent(el.value);
}).join('&');
var xhr = api._getXHR(); var xhr = api._getXHR();
xhr.open("POST", endpoint, true); xhr.open("POST", endpoint, true);
@@ -522,7 +525,7 @@ Vue.component('category', {
}); });
var shared_methods = { var shared_methods = {
buy: function (event) { buy: function (event, data) {
if (this.$root.useIframe) { if (this.$root.useIframe) {
if (event) { if (event) {
event.preventDefault(); event.preventDefault();
@@ -541,7 +544,7 @@ var shared_methods = {
this.$root.overlay.frame_loading = true; this.$root.overlay.frame_loading = true;
this.async_task_interval = 100; this.async_task_interval = 100;
var form = this.$refs.form; var form = data === undefined ? this.$refs.form : data;
if (form === undefined) { if (form === undefined) {
form = this.$refs.formcomp.$refs.form; form = this.$refs.formcomp.$refs.form;
} }
@@ -663,7 +666,7 @@ var shared_methods = {
} }
}, },
handleResize: function () { handleResize: function () {
this.mobile = this.$refs.wrapper.clientWidth <= 800; this.clientWidth = this.$refs.wrapper.clientWidth;
} }
}; };
@@ -674,7 +677,7 @@ var shared_widget_data = function () {
async_task_timeout: null, async_task_timeout: null,
async_task_interval: 100, async_task_interval: 100,
voucher: null, voucher: null,
mobile: false, clientWidth: 1000,
} }
}; };
@@ -811,11 +814,16 @@ Vue.component('pretix-widget-event-form', {
+ '</div>' + '</div>'
// Seating plan // Seating plan
+ '<div class="pretix-widget-seating-link-wrapper" v-if="this.$root.has_seating_plan">' + '<div class="pretix-widget-seating-link-wrapper" v-if="$root.has_seating_plan && !show_seating_plan_inline">'
+ '<button class="pretix-widget-seating-link" @click.prevent.stop="$root.startseating">' + '<button class="pretix-widget-seating-link" @click.prevent.stop="$root.startseating">'
+ strings['show_seating'] + strings['show_seating']
+ '</button>' + '</button>'
+ '</div>' + '</div>'
+ '<div class="pretix-widget-seating-embed" v-else-if="$root.has_seating_plan && show_seating_plan_inline">'
+ '<iframe :key="\'seatingframe\' + $root.loadid" class="pretix-widget-seating-embed-iframe" ref="seatingframe"'
+ ' :src="seatingframe" frameborder="0" referrerpolicy="origin" allowtransparency="true">'
+ '</iframe>'
+ '</div>'
// Waiting list for seating plan // Waiting list for seating plan
+ '<div class="pretix-widget-seating-waitinglist" v-if="this.$root.has_seating_plan && this.$root.has_seating_plan_waitinglist">' + '<div class="pretix-widget-seating-waitinglist" v-if="this.$root.has_seating_plan && this.$root.has_seating_plan_waitinglist">'
@@ -870,10 +878,14 @@ Vue.component('pretix-widget-event-form', {
this.$root.$on('amounts_changed', this.calculate_buy_disabled) this.$root.$on('amounts_changed', this.calculate_buy_disabled)
this.$root.$on('focus_voucher_field', this.focus_voucher_field) this.$root.$on('focus_voucher_field', this.focus_voucher_field)
this.calculate_buy_disabled() this.calculate_buy_disabled()
window.addEventListener('message', this.on_seat_select);
}, },
beforeDestroy: function() { beforeDestroy: function() {
this.$root.$off('amounts_changed', this.calculate_buy_disabled) this.$root.$off('amounts_changed', this.calculate_buy_disabled)
this.$root.$off('focus_voucher_field', this.focus_voucher_field) this.$root.$off('focus_voucher_field', this.focus_voucher_field)
window.addEventListener('message', this.on_seat_select);
}, },
computed: { computed: {
buy_label: function () { buy_label: function () {
@@ -903,9 +915,26 @@ Vue.component('pretix-widget-event-form', {
} else { } else {
return strings.buy; return strings.buy;
} }
} },
show_seating_plan_inline: function () {
return this.$root.seating_embedded && this.$parent.clientWidth > 992;
},
seatingframe: function () {
var seatingframe_url = this.$root.target_url;
if (this.$root.subevent){
seatingframe_url += '/' + this.$root.subevent;
}
seatingframe_url += '/seatingframe/?inline=1&locale=' + lang + '&widget_id=' + this.$root.widgetindex;
return seatingframe_url;
},
}, },
methods: { methods: {
on_seat_select: function (ev) {
if (ev.data.source !== "pretix_widget_seating") return;
if (parseInt(ev.data.widget_id) !== this.$root.widgetindex) return; // In case multiple widgets are on this page
if (ev.data.action !== "buy") return;
this.$parent.buy(null, ev.data.data);
},
focus_voucher_field: function() { focus_voucher_field: function() {
this.$refs.voucherinput.scrollIntoView(false) this.$refs.voucherinput.scrollIntoView(false)
this.$refs.voucherinput.focus() this.$refs.voucherinput.focus()
@@ -1371,9 +1400,12 @@ Vue.component('pretix-widget', {
data: shared_widget_data, data: shared_widget_data,
methods: shared_methods, methods: shared_methods,
mounted: function () { mounted: function () {
this.mobile = this.$refs.wrapper.clientWidth <= 600; this.clientWidth = this.$refs.wrapper.clientWidth;
}, },
computed: { computed: {
mobile: function () {
return this.clientWidth <= 600;
},
classObject: function () { classObject: function () {
o = {'pretix-widget': true}; o = {'pretix-widget': true};
if (this.mobile) { if (this.mobile) {
@@ -1536,6 +1568,7 @@ var shared_root_methods = {
root.has_seating_plan_waitinglist = data.has_seating_plan_waitinglist; root.has_seating_plan_waitinglist = data.has_seating_plan_waitinglist;
root.itemnum = data.itemnum; root.itemnum = data.itemnum;
} }
root.loadid++; // force-reload iframes
root.poweredby = data.poweredby; root.poweredby = data.poweredby;
if (root.loading > 0) { if (root.loading > 0) {
root.loading--; root.loading--;
@@ -1674,7 +1707,7 @@ var shared_root_computed = {
}, },
widget_data_json: function () { widget_data_json: function () {
return JSON.stringify(this.widget_data); return JSON.stringify(this.widget_data);
} },
}; };
var create_overlay = function (app) { var create_overlay = function (app) {
@@ -1716,7 +1749,7 @@ function get_ga_client_id(tracking_id) {
return null; return null;
} }
var create_widget = function (element) { var create_widget = function (element, widgetindex) {
var target_url = element.attributes.event.value; var target_url = element.attributes.event.value;
if (!target_url.match(/\/$/)) { if (!target_url.match(/\/$/)) {
target_url += "/"; target_url += "/";
@@ -1725,6 +1758,7 @@ var create_widget = function (element) {
var subevent = element.attributes.subevent ? element.attributes.subevent.value : null; var subevent = element.attributes.subevent ? element.attributes.subevent.value : null;
var style = element.attributes.style ? element.attributes.style.value : null; var style = element.attributes.style ? element.attributes.style.value : null;
var skip_ssl = element.attributes["skip-ssl-check"] ? true : false; var skip_ssl = element.attributes["skip-ssl-check"] ? true : false;
var seating_embedded = element.attributes["seating-embedded"] ? true : false;
var disable_iframe = element.attributes["disable-iframe"] ? true : false; var disable_iframe = element.attributes["disable-iframe"] ? true : false;
var disable_vouchers = element.attributes["disable-vouchers"] ? true : false; var disable_vouchers = element.attributes["disable-vouchers"] ? true : false;
var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data)); var widget_data = JSON.parse(JSON.stringify(window.PretixWidget.widget_data));
@@ -1747,6 +1781,7 @@ var create_widget = function (element) {
el: element, el: element,
data: function () { data: function () {
return { return {
widgetindex: widgetindex,
target_url: target_url, target_url: target_url,
parent_stack: [], parent_stack: [],
subevent: subevent, subevent: subevent,
@@ -1768,6 +1803,7 @@ var create_widget = function (element) {
voucher_explanation_text: null, voucher_explanation_text: null,
show_variations_expanded: !!variations, show_variations_expanded: !!variations,
skip_ssl: skip_ssl, skip_ssl: skip_ssl,
seating_embedded: seating_embedded,
disable_iframe: disable_iframe, disable_iframe: disable_iframe,
style: style, style: style,
connection_error: false, connection_error: false,
@@ -1782,6 +1818,7 @@ var create_widget = function (element) {
display_add_to_cart: false, display_add_to_cart: false,
widget_data: widget_data, widget_data: widget_data,
loading: 1, loading: 1,
loadid: 1,
widget_id: 'pretix-widget-' + widget_id, widget_id: 'pretix-widget-' + widget_id,
vouchers_exist: false, vouchers_exist: false,
disable_vouchers: disable_vouchers, disable_vouchers: disable_vouchers,
@@ -1882,7 +1919,7 @@ window.PretixWidget.buildWidgets = function () {
var wlength = widgets.length; var wlength = widgets.length;
for (var i = 0; i < wlength; i++) { for (var i = 0; i < wlength; i++) {
var widget = widgets[i]; var widget = widgets[i];
widgetlist.push(create_widget(widget)); widgetlist.push(create_widget(widget, i + 1));
} }
var buttons = document.querySelectorAll("pretix-button, div.pretix-button-compat"); var buttons = document.querySelectorAll("pretix-button, div.pretix-button-compat");

View File

@@ -331,6 +331,15 @@
width: 100%; width: 100%;
} }
.pretix-widget-seating-embed {
margin: 0 -10px;
}
.pretix-widget-seating-embed-iframe {
width: 100%;
aspect-ratio: 2/1;
max-height: 60vh;
}
.pretix-widget-seating-waitinglist { .pretix-widget-seating-waitinglist {
margin: 15px 0; margin: 15px 0;
} }