forked from CGM_Public/pretix_original
Widget: add versioning support and add v2 with improved a11y-support (#5136)
* Add support for versioning widget.js * add versionable css * add version deprecation + redirect * use dynamic template_path instead of dynamic css_path * remove dummy code from widget.v1.scss * fix typo * [A11y] fix input border & focus style (#5149) * [A11y] fix input border & focus style * Fix double semi-colon * [A11y] make collapse-indicator a button (#5150) * Fix source order for cart-exists-message (#5152) * [A11y] underline links (#5151) * [A11y] Move modal-dialogs to HTMLDialogElement (#5147) * [A11y] move widget/iframe to html-dialog * make lightbox a dialog * move error-alert to dialog * re-add crossorigin * fix esc-handling and move animation to icon to enable focusing the button * fix code-style issues * block canceling loading iframe * Escape/cancel blocking fix for Chrome * add round focus-outline when dialog is loading * Widget v2: change voucher-link to hash-based link (#5161) * Fix variants toggle-button being submit-button * Widget v2: make single-item-select button and always show custom-spinners (#5165) * Widget v2: make single-item-select=button default * remove native-spinners and single_item_select * Stop suggesting old parameter --------- Co-authored-by: Raphael Michel <michel@rami.io> * Widget v2: add filter button to events metadata-filter (#5162) * Widget v2: do not underline events in list and calendar (#5163) * Fix checkbox button missing border radius (#5158) * Widget v2: turn add-to-cart-button into resume-button if cart-exists and no items selected (#5160) * Widget v2: make cart-alert live=polite * Add resume-button if cart-exists and no items selected * fix error handling with new-tab and later returning to old window * Fix cart-message button being full height * fix amount_selected recalc * Fix broken v-model * fix merge * Widget v2: Remove link from variation-product title (#5159) * Remove link from variation-product, focus associated input * open variations onclick on product-title * clickable elements should be focussable and interactive, so better remove click-handler on product-title * Widget v2: Fix calendar events color contrast (#5164) * Widget v2: Fix calendar events color contrast * fix status-bubbles in list-view * fix color in mobile * add striped-background to calendar and week * improve display of calendar for super small screens * Fix meta-filter legend not being screen-reader accessible * update version_default to 2 Co-authored-by: Raphael Michel <michel@rami.io> --------- Co-authored-by: Raphael Michel <michel@rami.io>
This commit is contained in:
committed by
GitHub
parent
e46e689f01
commit
92f7456eca
@@ -19,8 +19,8 @@
|
||||
section of your website:
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<pre><link rel="stylesheet" type="text/css" href="{% abseventurl request.event "presale:event.widget.css" %}" crossorigin>
|
||||
<script type="text/javascript" src="{{ urlprefix }}{% url "presale:widget.js" lang=form.cleaned_data.language %}" async crossorigin></script></pre>
|
||||
<pre><link rel="stylesheet" type="text/css" href="{% abseventurl request.event "presale:event.widget.css" version=widget_version_default %}" crossorigin>
|
||||
<script type="text/javascript" src="{{ urlprefix }}{% url "presale:widget.js" lang=form.cleaned_data.language version=widget_version_default %}" async crossorigin></script></pre>
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Then, copy the following code to the place of your website where you want the widget to show up:
|
||||
@@ -32,7 +32,7 @@
|
||||
{% abseventurl request.event "presale:event.index" as indexurl %}
|
||||
{% endif %}
|
||||
{% if form.cleaned_data.compatibility_mode %}
|
||||
<pre><div class="pretix-widget-compat" event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %} single-item-select="button"></div>
|
||||
<pre><div class="pretix-widget-compat" event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %}></div>
|
||||
<noscript>
|
||||
<div class="pretix-widget">
|
||||
<div class="pretix-widget-info-message">
|
||||
@@ -45,7 +45,7 @@
|
||||
</noscript>
|
||||
</pre>
|
||||
{% else %}
|
||||
<pre><pretix-widget event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %} single-item-select="button"></pretix-widget>
|
||||
<pre><pretix-widget event="{% abseventurl request.event "presale:event.index" %}"{% if form.cleaned_data.subevent %} subevent="{{ form.cleaned_data.subevent.pk }}"{% endif %}{% if form.cleaned_data.voucher %} voucher="{{ form.cleaned_data.voucher }}"{% endif %}></pretix-widget>
|
||||
<noscript>
|
||||
<div class="pretix-widget">
|
||||
<div class="pretix-widget-info-message">
|
||||
|
||||
@@ -96,6 +96,9 @@ from pretix.control.views.user import RecentAuthenticationRequiredMixin
|
||||
from pretix.helpers.database import rolledback_transaction
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain
|
||||
from pretix.plugins.stripe.payment import StripeSettingsHolder
|
||||
from pretix.presale.views.widget import (
|
||||
version_default as widget_version_default,
|
||||
)
|
||||
|
||||
from ...base.i18n import language
|
||||
from ...base.models.items import (
|
||||
@@ -1408,6 +1411,7 @@ class WidgetSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['urlprefix'] = settings.SITE_URL
|
||||
ctx['widget_version_default'] = widget_version_default
|
||||
domain = get_event_domain(self.request.event, fallback=True)
|
||||
if domain:
|
||||
siteurlsplit = urlsplit(settings.SITE_URL)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{% load compress %}
|
||||
{% load static %}
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/x-scss" href="{% static "pretixpresale/scss/widget.v1.scss" %}"/>
|
||||
{% endcompress %}
|
||||
@@ -32,7 +32,7 @@
|
||||
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under the License.
|
||||
|
||||
from django.urls import include, re_path
|
||||
from django.urls import include, path, re_path
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
import pretix.presale.views.cart
|
||||
@@ -173,7 +173,7 @@ event_patterns = [
|
||||
|
||||
re_path(r'^widget/product_list$', pretix.presale.views.widget.WidgetAPIProductList.as_view(),
|
||||
name='event.widget.productlist'),
|
||||
re_path(r'^widget/v1.css$', pretix.presale.views.widget.widget_css, name='event.widget.css'),
|
||||
path('widget/v<int:version>.css', pretix.presale.views.widget.widget_css, name='event.widget.css'),
|
||||
re_path(r'^(?P<subevent>\d+)/widget/product_list$', pretix.presale.views.widget.WidgetAPIProductList.as_view(),
|
||||
name='event.widget.productlist'),
|
||||
|
||||
@@ -196,7 +196,7 @@ organizer_patterns = [
|
||||
|
||||
re_path(r'^widget/product_list$', pretix.presale.views.widget.WidgetAPIProductList.as_view(),
|
||||
name='organizer.widget.productlist'),
|
||||
re_path(r'^widget/v1.css$', pretix.presale.views.widget.widget_css, name='organizer.widget.css'),
|
||||
path('widget/v<int:version>.css', pretix.presale.views.widget.widget_css, name='organizer.widget.css'),
|
||||
|
||||
re_path(r'^theme.css$', pretix.presale.views.theme.theme_css, name='organizer.theme.css'),
|
||||
re_path(r'^accessibility$', pretix.presale.views.organizer.AccessibilityView.as_view(), name='organizer.accessibility'),
|
||||
@@ -237,5 +237,5 @@ locale_patterns = [
|
||||
re_path(r'^robots.txt$', pretix.presale.views.robots.robots_txt, name='robots.txt'),
|
||||
re_path(r'^browserconfig.xml$', pretix.presale.views.theme.browserconfig_xml, name='browserconfig.xml'),
|
||||
re_path(r'^site.webmanifest$', pretix.presale.views.theme.webmanifest, name='site.webmanifest'),
|
||||
re_path(r'^widget/v1\.(?P<lang>[a-zA-Z0-9_\-]+)\.js$', pretix.presale.views.widget.widget_js, name='widget.js'),
|
||||
path('widget/v<int:version>.<slug:lang>.js', pretix.presale.views.widget.widget_js, name='widget.js'),
|
||||
]
|
||||
|
||||
@@ -38,8 +38,10 @@ from django.core.files.base import ContentFile, File
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Q
|
||||
from django.http import FileResponse, Http404, HttpResponse, JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.template import Context, Engine
|
||||
from django.template.loader import get_template
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import get_language, gettext, pgettext
|
||||
@@ -81,15 +83,22 @@ logger = logging.getLogger(__name__)
|
||||
# we never change static source without restart, so we can cache this thread-wise
|
||||
_source_cache_key = None
|
||||
|
||||
version_min = 1
|
||||
version_max = 2
|
||||
version_default = 2 # used for output in widget-embed-code
|
||||
|
||||
def _get_source_cache_key():
|
||||
|
||||
def _get_source_cache_key(version):
|
||||
global _source_cache_key
|
||||
checksum = hashlib.sha256()
|
||||
if not _source_cache_key:
|
||||
with open(finders.find("pretixbase/scss/_theme_variables.scss"), "r") as f:
|
||||
checksum.update(f.read().encode())
|
||||
tpl = get_template('pretixpresale/widget_dummy.html')
|
||||
et = html.fromstring(tpl.render({})).xpath('/html/head/link')[0].attrib['href'].replace(settings.STATIC_URL, '')
|
||||
|
||||
template_path = 'pretixpresale/widget_dummy.html' if version == version_max else 'pretixpresale/widget_dummy.v{}.html'.format(version)
|
||||
|
||||
tpl = get_template(template_path)
|
||||
et = html.fromstring(tpl.render()).xpath('/html/head/link')[0].attrib['href'].replace(settings.STATIC_URL, '')
|
||||
checksum.update(et.encode())
|
||||
_source_cache_key = checksum.hexdigest()[:12]
|
||||
return _source_cache_key
|
||||
@@ -99,29 +108,39 @@ def indent(s):
|
||||
return s.replace('\n', '\n ')
|
||||
|
||||
|
||||
def widget_css_etag(request, **kwargs):
|
||||
def widget_css_etag(request, version, **kwargs):
|
||||
# This makes sure a new version of the theme is loaded whenever settings or the source files have changed
|
||||
if hasattr(request, 'event'):
|
||||
return (f'{_get_source_cache_key()}-'
|
||||
return (f'{_get_source_cache_key(version)}-'
|
||||
f'{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}-'
|
||||
f'{request.event.cache.get_or_set("css_version", default=lambda: int(time.time()))}')
|
||||
else:
|
||||
return f'{_get_source_cache_key()}-{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}'
|
||||
return f'{_get_source_cache_key(version)}-{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}'
|
||||
|
||||
|
||||
def widget_js_etag(request, lang, **kwargs):
|
||||
def widget_js_etag(request, version, lang, **kwargs):
|
||||
gs = GlobalSettingsObject()
|
||||
return gs.settings.get('widget_checksum_{}'.format(lang))
|
||||
return gs.settings.get('widget_checksum_{}_{}'.format(version, lang))
|
||||
|
||||
|
||||
@gzip_page
|
||||
@condition(etag_func=widget_css_etag)
|
||||
@cache_page(60)
|
||||
def widget_css(request, **kwargs):
|
||||
def widget_css(request, version, **kwargs):
|
||||
if version > version_max:
|
||||
raise Http404()
|
||||
if version < version_min:
|
||||
return redirect(reverse('presale:event.widget.css' if hasattr(request, 'event') else 'organizer.widget.css', kwargs={
|
||||
'version': version_min,
|
||||
'organizer': request.organizer.slug,
|
||||
'event': request.event.slug if hasattr(request, 'event') else None,
|
||||
}))
|
||||
o = getattr(request, 'event', request.organizer)
|
||||
|
||||
tpl = get_template('pretixpresale/widget_dummy.html')
|
||||
et = html.fromstring(tpl.render({})).xpath('/html/head/link')[0].attrib['href'].replace(settings.STATIC_URL, '')
|
||||
template_path = 'pretixpresale/widget_dummy.html' if version == version_max else 'pretixpresale/widget_dummy.v{}.html'.format(version)
|
||||
|
||||
tpl = get_template(template_path)
|
||||
et = html.fromstring(tpl.render()).xpath('/html/head/link')[0].attrib['href'].replace(settings.STATIC_URL, '')
|
||||
with open(finders.find(et), 'r') as f:
|
||||
widget_css = f.read()
|
||||
|
||||
@@ -134,7 +153,7 @@ def widget_css(request, **kwargs):
|
||||
return resp
|
||||
|
||||
|
||||
def generate_widget_js(lang):
|
||||
def generate_widget_js(version, lang):
|
||||
code = []
|
||||
with language(lang):
|
||||
# Provide isolation
|
||||
@@ -169,7 +188,7 @@ def generate_widget_js(lang):
|
||||
'vuejs/vue.js' if settings.DEBUG else 'vuejs/vue.min.js',
|
||||
'pretixpresale/js/widget/docready.js',
|
||||
'pretixpresale/js/widget/floatformat.js',
|
||||
'pretixpresale/js/widget/widget.js',
|
||||
'pretixpresale/js/widget/widget.js' if version == version_max else 'pretixpresale/js/widget/widget.v{}.js'.format(version),
|
||||
]
|
||||
for fname in files:
|
||||
f = finders.find(fname)
|
||||
@@ -188,11 +207,17 @@ def generate_widget_js(lang):
|
||||
|
||||
@gzip_page
|
||||
@condition(etag_func=widget_js_etag)
|
||||
def widget_js(request, lang, **kwargs):
|
||||
if lang not in [lc for lc, ll in settings.LANGUAGES]:
|
||||
def widget_js(request, version, lang, **kwargs):
|
||||
if version > version_max or lang not in [lc for lc, ll in settings.LANGUAGES]:
|
||||
raise Http404()
|
||||
|
||||
cached_js = cache.get('widget_js_data_{}'.format(lang))
|
||||
if version < version_min:
|
||||
return redirect(reverse('presale:widget.js', kwargs={
|
||||
'version': version_min,
|
||||
'lang': lang,
|
||||
}))
|
||||
|
||||
cached_js = cache.get('widget_js_data_{}_{}'.format(version, lang))
|
||||
if cached_js and not settings.DEBUG:
|
||||
resp = HttpResponse(cached_js, content_type='text/javascript')
|
||||
resp._csp_ignore = True
|
||||
@@ -200,7 +225,7 @@ def widget_js(request, lang, **kwargs):
|
||||
return resp
|
||||
|
||||
gs = GlobalSettingsObject()
|
||||
fname = gs.settings.get('widget_file_{}'.format(lang))
|
||||
fname = gs.settings.get('widget_file_{}_{}'.format(version, lang))
|
||||
resp = None
|
||||
if fname and not settings.DEBUG:
|
||||
if isinstance(fname, File):
|
||||
@@ -208,21 +233,21 @@ def widget_js(request, lang, **kwargs):
|
||||
try:
|
||||
data = default_storage.open(fname).read()
|
||||
resp = HttpResponse(data, content_type='text/javascript')
|
||||
cache.set('widget_js_data_{}'.format(lang), data, 3600 * 4)
|
||||
cache.set('widget_js_data_{}_{}'.format(version, lang), data, 3600 * 4)
|
||||
except:
|
||||
logger.exception('Failed to open widget.js')
|
||||
|
||||
if not resp:
|
||||
data = generate_widget_js(lang).encode()
|
||||
data = generate_widget_js(version, lang).encode()
|
||||
checksum = hashlib.sha1(data).hexdigest()
|
||||
if not settings.DEBUG:
|
||||
newname = default_storage.save(
|
||||
'widget/widget.{}.{}.js'.format(lang, checksum),
|
||||
'widget/widget.{}.{}.{}.js'.format(version, lang, checksum),
|
||||
ContentFile(data)
|
||||
)
|
||||
gs.settings.set('widget_file_{}'.format(lang), 'file://' + newname)
|
||||
gs.settings.set('widget_checksum_{}'.format(lang), checksum)
|
||||
cache.set('widget_js_data_{}'.format(lang), data, 3600 * 4)
|
||||
gs.settings.set('widget_file_{}_{}'.format(version, lang), 'file://' + newname)
|
||||
gs.settings.set('widget_checksum_{}_{}'.format(version, lang), checksum)
|
||||
cache.set('widget_js_data_{}_{}'.format(version, lang), data, 3600 * 4)
|
||||
resp = HttpResponse(data, content_type='text/javascript')
|
||||
resp._csp_ignore = True
|
||||
resp['Access-Control-Allow-Origin'] = '*'
|
||||
|
||||
@@ -14,6 +14,7 @@ $input-color-placeholder: lighten(#000, 70%) !default;
|
||||
$border-radius-base: var(--pretix-border-radius-base);
|
||||
$border-radius-large: var(--pretix-border-radius-large);
|
||||
$border-radius-small: var(--pretix-border-radius-small);
|
||||
$input-border: #949494 !default;
|
||||
|
||||
$navbar-inverse-bg: #3b1c4a !default;
|
||||
$navbar-inverse-link-color: white;
|
||||
@@ -72,7 +73,7 @@ $panel-default-heading-bg: #e5e5e5 !default;
|
||||
|
||||
$link-hover-color: var(--pretix-brand-primary-darken-15);
|
||||
|
||||
$btn-default-border: #CCCCCC;
|
||||
$btn-default-border: #949494;
|
||||
|
||||
$btn-primary-border: var(--pretix-brand-primary-darken-5);
|
||||
$btn-primary-border-active: var(--pretix-brand-primary-darken-30);
|
||||
@@ -122,6 +123,12 @@ $label-warning-bg-hover: var(--pretix-brand-warning-darken-10);
|
||||
$label-danger-bg: var(--pretix-brand-danger);
|
||||
$label-danger-bg-hover: var(--pretix-brand-danger-darken-10);
|
||||
|
||||
$alert-primary-bg: var(--pretix-brand-primary-tint-90);
|
||||
$alert-primary-text: var(--pretix-brand-primary-shade-42);
|
||||
$alert-primary-border: var(--pretix-brand-primary);
|
||||
$alert-primary-hr: var(--pretix-brand-primary-darken-5);
|
||||
$alert-primary-link: var(--pretix-brand-primary-shade-42);
|
||||
|
||||
$alert-success-bg: var(--pretix-brand-success-tint-85);
|
||||
$alert-success-text: var(--pretix-brand-success-shade-25);
|
||||
$alert-success-border: var(--pretix-brand-success);
|
||||
|
||||
@@ -16,6 +16,8 @@ var strings = {
|
||||
'quantity': django.pgettext('widget', 'Quantity'),
|
||||
'quantity_dec': django.pgettext('widget', 'Decrease quantity'),
|
||||
'quantity_inc': django.pgettext('widget', 'Increase quantity'),
|
||||
'filter_events_by': django.pgettext('widget', 'Filter events by'),
|
||||
'filter': django.pgettext('widget', 'Filter'),
|
||||
'price': django.pgettext('widget', 'Price'),
|
||||
'original_price': django.pgettext('widget', 'Original price: %s'),
|
||||
'new_price': django.pgettext('widget', 'New price: %s'),
|
||||
@@ -57,6 +59,8 @@ var strings = {
|
||||
'redeem': django.pgettext('widget', 'Redeem'),
|
||||
'voucher_code': django.pgettext('widget', 'Voucher code'),
|
||||
'close': django.pgettext('widget', 'Close'),
|
||||
'close_checkout': django.pgettext('widget', 'Close checkout'),
|
||||
'cancel_blocked': django.pgettext('widget', 'You cannot cancel this operation. Please wait for loading to finish.'),
|
||||
'continue': django.pgettext('widget', 'Continue'),
|
||||
'variations': django.pgettext('widget', 'Show variants'),
|
||||
'hide_variations': django.pgettext('widget', 'Hide variants'),
|
||||
@@ -208,7 +212,7 @@ Vue.component('availbox', {
|
||||
template: ('<div class="pretix-widget-availability-box">'
|
||||
+ '<div class="pretix-widget-availability-unavailable"'
|
||||
+ ' v-if="item.current_unavailability_reason === \'require_voucher\'">'
|
||||
+ '<small><a @click.prevent.stop="focus_voucher_field" role="button" tabindex="0">{{unavailability_reason_message}}</a></small>'
|
||||
+ '<small><a :href="voucher_jump_link" v-bind:aria-describedby="aria_labelledby">{{unavailability_reason_message}}</a></small>'
|
||||
+ '</div>'
|
||||
+ '<div class="pretix-widget-availability-unavailable"'
|
||||
+ ' v-else-if="unavailability_reason_message">'
|
||||
@@ -227,24 +231,19 @@ Vue.component('availbox', {
|
||||
+ '<a :href="waiting_list_url" target="_blank" @click="$root.open_link_in_frame">' + strings.waiting_list + '</a>'
|
||||
+ '</div>'
|
||||
+ '<div class="pretix-widget-availability-available" v-if="!unavailability_reason_message && avail[0] === 100">'
|
||||
+ '<label class="pretix-widget-item-count-single-label pretix-widget-btn-checkbox" v-if="order_max === 1 && $root.single_item_select == \'button\'">'
|
||||
+ '<input type="checkbox" value="1" :checked="!!amount_selected" @change="amount_selected = $event.target.checked" :name="input_name"'
|
||||
+ '<label class="pretix-widget-item-count-single-label pretix-widget-btn-checkbox" v-if="order_max === 1">'
|
||||
+ '<input ref="quantity" type="checkbox" value="1" :name="input_name"'
|
||||
+ ' v-bind:aria-label="label_select_item"'
|
||||
+ '>'
|
||||
+ '<span class="pretix-widget-icon-cart" aria-hidden="true"></span> ' + strings.select
|
||||
+ '</label>'
|
||||
+ '<label class="pretix-widget-item-count-single-label" v-else-if="order_max === 1">'
|
||||
+ '<input type="checkbox" value="1" :checked="!!amount_selected" @change="amount_selected = $event.target.checked" :name="input_name"'
|
||||
+ ' v-bind:aria-label="label_select_item"'
|
||||
+ '>'
|
||||
+ '</label>'
|
||||
+ '<div :class="count_group_classes" v-else role="group" v-bind:aria-label="item.name">'
|
||||
+ '<button v-if="!$root.use_native_spinners" type="button" @click.prevent.stop="on_step" data-step="-1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-dec" v-bind:aria-label="dec_label"><span>-</span></button>'
|
||||
+ '<input type="number" inputmode="numeric" pattern="\d*" class="pretix-widget-item-count-multiple" placeholder="0" min="0"'
|
||||
+ ' v-model="amount_selected" :max="order_max" :name="input_name" :id="\'input_\' + input_name"'
|
||||
+ ' v-bind:aria-labelledby="aria_labelledby" ref="quantity"'
|
||||
+ '<div class="pretix-widget-item-count-group" v-else role="group" v-bind:aria-label="item.name">'
|
||||
+ '<button type="button" @click.prevent.stop="on_step" data-step="-1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-dec" v-bind:aria-label="dec_label"><span>-</span></button>'
|
||||
+ '<input ref="quantity" type="number" inputmode="numeric" pattern="\d*" class="pretix-widget-item-count-multiple" placeholder="0" min="0"'
|
||||
+ ' :max="order_max" :name="input_name" :id="\'input_\' + input_name"'
|
||||
+ ' v-bind:aria-labelledby="aria_labelledby"'
|
||||
+ ' >'
|
||||
+ '<button v-if="!$root.use_native_spinners" type="button" @click.prevent.stop="on_step" data-step="1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-inc" v-bind:aria-label="inc_label"><span>+</span></button>'
|
||||
+ '<button type="button" @click.prevent.stop="on_step" data-step="1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-inc" v-bind:aria-label="inc_label"><span>+</span></button>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'),
|
||||
@@ -253,15 +252,17 @@ Vue.component('availbox', {
|
||||
variation: Object
|
||||
},
|
||||
mounted: function() {
|
||||
if (this.item.has_variations) {
|
||||
this.$set(this.variation, 'amount_selected', 0);
|
||||
} else {
|
||||
// Automatically set the only available item to be selected.
|
||||
this.$set(this.item, 'amount_selected', this.$root.itemnum === 1 && !this.$root.has_seating_plan ? 1 : 0);
|
||||
if (this.$root.itemnum === 1 && !this.$root.has_seating_plan ? 1 : 0) {
|
||||
this.$refs.quantity.value = 1;
|
||||
if (this.order_max === 1) {
|
||||
this.$refs.quantity.checked = true;
|
||||
}
|
||||
}
|
||||
this.$root.$emit('amounts_changed')
|
||||
},
|
||||
computed: {
|
||||
voucher_jump_link: function () {
|
||||
return '#' + this.$root.html_id + '-voucher-input';
|
||||
},
|
||||
aria_labelledby: function () {
|
||||
return this.$root.html_id + '-item-label-' + this.item.id;
|
||||
},
|
||||
@@ -271,11 +272,6 @@ Vue.component('availbox', {
|
||||
inc_label: function () {
|
||||
return '+ ' + (this.item.has_variations ? this.variation.value : this.item.name) + ': ' + strings.quantity_inc;
|
||||
},
|
||||
count_group_classes: function () {
|
||||
return {
|
||||
'pretix-widget-item-count-group': !this.$root.use_native_spinners
|
||||
}
|
||||
},
|
||||
unavailability_reason_message: function () {
|
||||
var reason = this.item.current_unavailability_reason || this.variation?.current_unavailability_reason;
|
||||
if (reason) {
|
||||
@@ -283,29 +279,6 @@ Vue.component('availbox', {
|
||||
}
|
||||
return "";
|
||||
},
|
||||
amount_selected: {
|
||||
cache: false,
|
||||
get: function () {
|
||||
var selected = this.item.has_variations ? this.variation.amount_selected : this.item.amount_selected
|
||||
if (selected === 0) return undefined;
|
||||
return selected
|
||||
},
|
||||
set: function (value) {
|
||||
// Unary operator to force boolean to integer conversion, as the HTML form submission
|
||||
// needs the value to be integer for all products.
|
||||
value = (+value);
|
||||
if (this.item.has_variations) {
|
||||
this.variation.amount_selected = value;
|
||||
} else {
|
||||
this.item.amount_selected = value;
|
||||
}
|
||||
if (this.$refs.quantity) {
|
||||
// manually set value on quantity as on reload somehow v-model binding breaks
|
||||
this.$refs.quantity.value = value;
|
||||
}
|
||||
this.$root.$emit("amounts_changed")
|
||||
}
|
||||
},
|
||||
label_select_item: function () {
|
||||
return this.item.has_variations
|
||||
? strings.select_variant.replace("%s", this.variation.value)
|
||||
@@ -341,14 +314,14 @@ Vue.component('availbox', {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
focus_voucher_field: function () {
|
||||
this.$root.$emit('focus_voucher_field')
|
||||
},
|
||||
on_step: function (e) {
|
||||
var t = e.target.tagName == 'BUTTON' ? e.target : e.target.closest('button');
|
||||
var step = parseFloat(t.getAttribute("data-step"));
|
||||
var controls = document.getElementById(t.getAttribute("data-controls"));
|
||||
this.amount_selected = Math.max(controls.min, Math.min(controls.max || Number.MAX_SAFE_INTEGER, (this.amount_selected || 0) + step));
|
||||
this.$refs.quantity.value = Math.max(controls.min, Math.min(controls.max || Number.MAX_SAFE_INTEGER, (parseInt(this.$refs.quantity.value || "0")) + step));
|
||||
this.$refs.quantity.dispatchEvent(new CustomEvent("change", {
|
||||
bubbles: true,
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -537,12 +510,7 @@ Vue.component('item', {
|
||||
+ '<div class="pretix-widget-item-info-col">'
|
||||
+ '<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" :alt="picture_alt_text"></a>'
|
||||
+ '<div class="pretix-widget-item-title-and-description">'
|
||||
+ '<a v-if="item.has_variations && show_toggle" :id="item_label_id" role="heading" v-bind:aria-level="headingLevel" class="pretix-widget-item-title" :href="\'#\' + item.id + \'-variants\'"'
|
||||
+ ' @click.prevent.stop="expand"'
|
||||
+ '>'
|
||||
+ '{{ item.name }}'
|
||||
+ '</a>'
|
||||
+ '<strong v-else class="pretix-widget-item-title" :id="item_label_id" role="heading" v-bind:aria-level="headingLevel">{{ item.name }}</strong>'
|
||||
+ '<strong class="pretix-widget-item-title" :id="item_label_id" role="heading" v-bind:aria-level="headingLevel">{{ item.name }}</strong>'
|
||||
+ '<div class="pretix-widget-item-description" :id="item_desc_id" v-if="item.description" v-html="item.description"></div>'
|
||||
+ '<p class="pretix-widget-item-meta" v-if="item.order_min && item.order_min > 1">'
|
||||
+ '<small>{{ min_order_str }}</small>'
|
||||
@@ -566,8 +534,8 @@ Vue.component('item', {
|
||||
|
||||
// Availability
|
||||
+ '<div class="pretix-widget-item-availability-col">'
|
||||
+ '<a class="pretix-widget-collapse-indicator" v-if="show_toggle" :href="\'#\' + item.id + \'-variants\'" @click.prevent.stop="expand" role="button" tabindex="0"'
|
||||
+ ' v-bind:aria-expanded="expanded ? \'true\': \'false\'" v-bind:aria-controls="item.id + \'-variants\'">{{ variationsToggleLabel }}</a>'
|
||||
+ '<button type="button" class="pretix-widget-collapse-indicator" v-if="show_toggle" @click.prevent.stop="expand"'
|
||||
+ ' v-bind:aria-expanded="expanded ? \'true\': \'false\'" v-bind:aria-controls="item.id + \'-variants\'" v-bind:aria-describedby="item_desc_id">{{ variationsToggleLabel }}</button>'
|
||||
+ '<availbox v-if="!item.has_variations" :item="item"></availbox>'
|
||||
+ '</div>'
|
||||
|
||||
@@ -622,7 +590,7 @@ Vue.component('item', {
|
||||
image: this.item.picture_fullsize,
|
||||
description: this.item.name,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
classObject: function () {
|
||||
@@ -855,53 +823,54 @@ var shared_loading_fragment = (
|
||||
);
|
||||
|
||||
var shared_iframe_fragment = (
|
||||
'<div :class="frameClasses" role="dialog" aria-modal="true" aria-label="'+strings.checkout+'">'
|
||||
'<dialog :class="frameClasses" role="alertdialog" aria-label="'+strings.checkout+'" @close="close" @cancel="cancel">'
|
||||
+ '<div class="pretix-widget-frame-loading" v-show="$root.frame_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>'
|
||||
+ '<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>'
|
||||
+ '<p :class="cancelBlockedClasses"><strong>'+strings.cancel_blocked+'</strong></p>'
|
||||
+ '</div>'
|
||||
+ '<div class="pretix-widget-frame-inner" ref="frame-container" v-show="$root.frame_shown">'
|
||||
+ '<div class="pretix-widget-frame-close"><a href="#" @click.prevent.stop="close" role="button" aria-label="'+strings.close+'">'
|
||||
+ '<svg alt="'+strings.close+'" 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>'
|
||||
+ '</a></div>'
|
||||
+ '<iframe frameborder="0" width="650" height="650" @load="iframeLoaded" '
|
||||
+ ' :name="$root.parent.widget_id" src="about:blank" v-once'
|
||||
+ ' allow="autoplay *; camera *; fullscreen *; payment *"'
|
||||
+ ' title="'+strings.checkout+'"'
|
||||
+ ' referrerpolicy="origin">'
|
||||
+ 'Please enable frames in your browser!'
|
||||
+ '</iframe>'
|
||||
+ '</div>'
|
||||
+ '<form class="pretix-widget-frame-close" method="dialog"><button aria-label="'+strings.close_checkout+'" autofocus="autofocus">'
|
||||
+ '<svg alt="'+strings.close+'" 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></form>'
|
||||
+ '<iframe frameborder="0" width="650" height="650" @load="iframeLoaded" '
|
||||
+ ' :name="$root.parent.widget_id" src="about:blank" v-once'
|
||||
+ ' allow="autoplay *; camera *; fullscreen *; payment *"'
|
||||
+ ' title="'+strings.checkout+'"'
|
||||
+ ' referrerpolicy="origin">'
|
||||
+ 'Please enable frames in your browser!'
|
||||
+ '</iframe>'
|
||||
+ '</div>'
|
||||
+ '</dialog>'
|
||||
);
|
||||
|
||||
var shared_alert_fragment = (
|
||||
'<div :class="alertClasses" role="alertdialog" v-bind:aria-labelledby="$root.parent.html_id + \'-error-message\'">'
|
||||
+ '<transition name="bounce" @after-enter="focusButton">'
|
||||
+ '<div class="pretix-widget-alert-box" v-if="$root.error_message">'
|
||||
'<dialog :class="alertClasses" role="alertdialog" v-bind:aria-labelledby="$root.parent.html_id + \'-error-message\'" @close="errorClose">'
|
||||
+ '<form class="pretix-widget-alert-box" method="dialog">'
|
||||
+ '<p :id="$root.parent.html_id + \'-error-message\'">{{ $root.error_message }}</p>'
|
||||
+ '<p><button v-if="$root.error_url_after" @click.prevent.stop="errorContinue">' + strings.continue + '</button>'
|
||||
+ '<button v-else @click.prevent.stop="errorClose">' + strings.close + '</button></p>'
|
||||
+ '</div>'
|
||||
+ '<p><button v-if="$root.error_url_after" value="continue" autofocus v-bind:aria-describedby="$root.parent.html_id + \'-error-message\'">' + strings.continue + '</button>'
|
||||
+ '<button v-else autofocus v-bind:aria-describedby="$root.parent.html_id + \'-error-message\'">' + strings.close + '</button></p>'
|
||||
+ '</form>'
|
||||
+ '<transition name="bounce">'
|
||||
+ '<svg v-if="$root.error_message" width="64" height="64" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" class="pretix-widget-alert-icon"><path style="fill:#ffffff;" d="M 599.86438,303.72882 H 1203.5254 V 1503.4576 H 599.86438 Z" /><path class="pretix-widget-primary-color" d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5-103 385.5-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103zm128 1247v-190q0-14-9-23.5t-22-9.5h-192q-13 0-23 10t-10 23v190q0 13 10 23t23 10h192q13 0 22-9.5t9-23.5zm-2-344l18-621q0-12-10-18-10-8-24-8h-220q-14 0-24 8-10 6-10 18l17 621q0 10 10 17.5t24 7.5h185q14 0 23.5-7.5t10.5-17.5z"/></svg>'
|
||||
+ '</transition>'
|
||||
+ '<svg width="64" height="64" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg" class="pretix-widget-alert-icon"><path style="fill:#ffffff;" d="M 599.86438,303.72882 H 1203.5254 V 1503.4576 H 599.86438 Z" /><path class="pretix-widget-primary-color" d="M896 128q209 0 385.5 103t279.5 279.5 103 385.5-103 385.5-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103zm128 1247v-190q0-14-9-23.5t-22-9.5h-192q-13 0-23 10t-10 23v190q0 13 10 23t23 10h192q13 0 22-9.5t9-23.5zm-2-344l18-621q0-12-10-18-10-8-24-8h-220q-14 0-24 8-10 6-10 18l17 621q0 10 10 17.5t24 7.5h185q14 0 23.5-7.5t10.5-17.5z"/></svg>'
|
||||
+ '</div>'
|
||||
+ '</dialog>'
|
||||
);
|
||||
|
||||
var shared_lightbox_fragment = (
|
||||
'<div :class="lightboxClasses" role="dialog" aria-modal="true" v-if="$root.lightbox" @click="lightboxClose">'
|
||||
'<dialog :class="lightboxClasses" role="alertdialog" @close="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="">'
|
||||
+ '<div class="pretix-widget-lightbox-inner" v-if="$root.lightbox">'
|
||||
+ '<form class="pretix-widget-lightbox-close" method="dialog"><button aria-label="'+strings.close+'" autofocus="autofocus">'
|
||||
+ '<svg alt="'+strings.close+'" 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></form>'
|
||||
+ '<figure class="pretix-widget-lightbox-image">'
|
||||
+ '<img :src="$root.lightbox.image" :alt="$root.lightbox.description" @load="lightboxLoaded" ref="lightboxImage" crossorigin>'
|
||||
+ '<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 alt="'+strings.close+'" 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>'
|
||||
+ '</dialog>'
|
||||
);
|
||||
|
||||
Vue.component('pretix-overlay', {
|
||||
@@ -911,6 +880,11 @@ Vue.component('pretix-overlay', {
|
||||
+ shared_lightbox_fragment
|
||||
+ '</div>'
|
||||
),
|
||||
data: function () {
|
||||
return {
|
||||
cancelBlocked: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$root.lightbox': function (newValue, oldValue) {
|
||||
if (newValue) {
|
||||
@@ -918,18 +892,24 @@ Vue.component('pretix-overlay', {
|
||||
this.$set(newValue, "loading", true);
|
||||
}
|
||||
if (!oldValue) {
|
||||
window.addEventListener('keyup', this.lightboxCloseOnKeyup);
|
||||
this.$el?.querySelector(".pretix-widget-lightbox-holder").showModal();
|
||||
}
|
||||
} else {
|
||||
window.removeEventListener('keyup', this.lightboxCloseOnKeyup);
|
||||
}
|
||||
}
|
||||
},
|
||||
'$root.error_message': function (newValue, oldValue) {
|
||||
if (newValue) {
|
||||
if (!oldValue) {
|
||||
this.$el?.querySelector(".pretix-widget-alert-holder").showModal();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
frameClasses: function () {
|
||||
return {
|
||||
'pretix-widget-frame-holder': true,
|
||||
'pretix-widget-frame-shown': this.$root.frame_shown || this.$root.frame_loading,
|
||||
'pretix-widget-frame-isloading': this.$root.frame_loading,
|
||||
};
|
||||
},
|
||||
alertClasses: function () {
|
||||
@@ -945,54 +925,67 @@ Vue.component('pretix-overlay', {
|
||||
'pretix-widget-lightbox-isloading': this.$root.lightbox?.loading,
|
||||
};
|
||||
},
|
||||
cancelBlockedClasses: function () {
|
||||
return {
|
||||
'pretix-widget-visibility-hidden': !this.cancelBlocked,
|
||||
}
|
||||
},
|
||||
},
|
||||
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 (e) {
|
||||
var dialog = e.target;
|
||||
if (dialog.returnValue == "continue" && this.$root.error_url_after) {
|
||||
if (this.$root.error_url_after_new_tab) {
|
||||
window.open(this.$root.error_url_after);
|
||||
} else if (this.$root.overlay) {
|
||||
this.$root.overlay.frame_src = this.$root.error_url_after;
|
||||
this.$root.frame_loading = true;
|
||||
}
|
||||
}
|
||||
this.$root.error_message = null;
|
||||
this.$root.error_url_after = null;
|
||||
this.$root.error_url_after_new_tab = false;
|
||||
},
|
||||
errorContinue: function () {
|
||||
if (this.$root.error_url_after_new_tab) {
|
||||
window.open(this.$root.error_url_after);
|
||||
close: function (e) {
|
||||
if (this.$root.frame_loading) {
|
||||
// Chrome does not allow blocking dialog.cancel event more than once
|
||||
// => wiggle the loading-element and re-open the modal
|
||||
this.cancel(e);
|
||||
e.target.showModal();
|
||||
return;
|
||||
}
|
||||
this.$root.overlay.frame_src = this.$root.error_url_after;
|
||||
this.$root.frame_loading = true;
|
||||
this.$root.error_message = null;
|
||||
this.$root.error_url_after = null;
|
||||
},
|
||||
close: function () {
|
||||
this.$root.frame_shown = false;
|
||||
this.$root.parent.frame_dismissed = true;
|
||||
this.$root.frame_src = "";
|
||||
this.$root.parent.reload();
|
||||
this.$root.parent.trigger_close_callback();
|
||||
},
|
||||
cancel: function (e) {
|
||||
// do not allow to cancel while frame is loading as we cannot abort the operation
|
||||
if (this.$root.frame_loading) {
|
||||
e.preventDefault();
|
||||
e.target.addEventListener("animationend", function () {
|
||||
e.target.classList.remove("pretix-widget-shake-once");
|
||||
}, {once: true});
|
||||
e.target.classList.add("pretix-widget-shake-once");
|
||||
this.cancelBlocked = true;
|
||||
}
|
||||
},
|
||||
iframeLoaded: function () {
|
||||
if (this.$root.frame_loading) {
|
||||
this.$root.frame_loading = false;
|
||||
this.cancelBlocked = false;
|
||||
if (this.$root.frame_src) {
|
||||
this.$root.frame_shown = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
focusButton: function () {
|
||||
this.$el.querySelector(".pretix-widget-alert-box button").focus();
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1033,13 +1026,11 @@ Vue.component('pretix-widget-event-form', {
|
||||
+ '<div class="pretix-widget-error-message" v-if="$root.error">{{ $root.error }}</div>'
|
||||
|
||||
// Resume cart
|
||||
+ '<div class="pretix-widget-info-message pretix-widget-clickable"'
|
||||
+ ' v-if="$root.cart_exists">'
|
||||
+ '<div class="pretix-widget-info-message pretix-widget-clickable" v-if="$root.cart_exists">'
|
||||
+ '<span :id="id_cart_exists_msg">' + strings['cart_exists'] + '</span>'
|
||||
+ '<button @click.prevent.stop="$parent.resume" class="pretix-widget-resume-button" type="button" v-bind:aria-describedby="id_cart_exists_msg">'
|
||||
+ strings['resume_checkout']
|
||||
+ '</button>'
|
||||
+ '<span :id="id_cart_exists_msg">' + strings['cart_exists'] + '</span>'
|
||||
+ '<div class="pretix-widget-clear"></div>'
|
||||
+ '</div>'
|
||||
|
||||
// Seating plan
|
||||
@@ -1067,7 +1058,10 @@ Vue.component('pretix-widget-event-form', {
|
||||
|
||||
// Buy button
|
||||
+ '<div class="pretix-widget-action" v-if="$root.display_add_to_cart">'
|
||||
+ '<button type="submit">{{ this.buy_label }}</button>'
|
||||
+ '<button v-if="!this.$root.cart_exists || this.is_items_selected" type="submit" v-bind:aria-describedby="id_cart_exists_msg">{{ buy_label }}</button>'
|
||||
+ '<button v-else @click.prevent.stop="$parent.resume" type="button" v-bind:aria-describedby="id_cart_exists_msg">'
|
||||
+ strings['resume_checkout']
|
||||
+ '</button>'
|
||||
+ '</div>'
|
||||
|
||||
+ '</form>'
|
||||
@@ -1079,7 +1073,7 @@ Vue.component('pretix-widget-event-form', {
|
||||
+ '<h3 class="pretix-widget-voucher-headline" :id="aria_labelledby">'+ strings['redeem_voucher'] +'</h3>'
|
||||
+ '<div v-if="$root.voucher_explanation_text" class="pretix-widget-voucher-text" v-html="$root.voucher_explanation_text"></div>'
|
||||
+ '<div class="pretix-widget-voucher-input-wrap">'
|
||||
+ '<input class="pretix-widget-voucher-input" ref="voucherinput" type="text" v-model="$parent.voucher" name="voucher" placeholder="'+strings.voucher_code+'" v-bind:aria-labelledby="aria_labelledby">'
|
||||
+ '<input :id="id_voucher_input" class="pretix-widget-voucher-input" ref="voucherinput" type="text" v-model="$parent.voucher" name="voucher" placeholder="'+strings.voucher_code+'" v-bind:aria-labelledby="aria_labelledby">'
|
||||
+ '</div>'
|
||||
+ '<input type="hidden" v-for="p in hiddenParams" :name="p[0]" :value="p[1]" />'
|
||||
+ '<div class="pretix-widget-voucher-button-wrap">'
|
||||
@@ -1091,14 +1085,32 @@ Vue.component('pretix-widget-event-form', {
|
||||
|
||||
+ '</div>'
|
||||
),
|
||||
data: function () {
|
||||
return {
|
||||
is_items_selected: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$root.overlay.frame_shown': function (newValue) {
|
||||
if (!newValue) {
|
||||
this.$refs.form.reset();
|
||||
this.calc_items_selected();
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
this.$root.$on('focus_voucher_field', this.focus_voucher_field)
|
||||
this.$root.$on('focus_voucher_field', this.focus_voucher_field);
|
||||
this.$refs.form.addEventListener("change", this.calc_items_selected);
|
||||
},
|
||||
beforeDestroy: function() {
|
||||
this.$root.$off('focus_voucher_field', this.focus_voucher_field)
|
||||
this.$root.$off('focus_voucher_field', this.focus_voucher_field);
|
||||
this.$refs.form.removeEventListener("change", this.calc_items_selected);
|
||||
},
|
||||
computed: {
|
||||
aria_labelledby: function() {
|
||||
id_voucher_input: function () {
|
||||
return this.$root.html_id + '-voucher-input';
|
||||
},
|
||||
aria_labelledby: function () {
|
||||
return this.$root.html_id + '-voucher-headline';
|
||||
},
|
||||
display_event_info: function () {
|
||||
@@ -1143,10 +1155,6 @@ Vue.component('pretix-widget-event-form', {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
focus_voucher_field: function() {
|
||||
this.$refs.voucherinput.scrollIntoView(false)
|
||||
this.$refs.voucherinput.focus()
|
||||
},
|
||||
back_to_list: function() {
|
||||
this.$root.target_url = this.$root.parent_stack.pop();
|
||||
this.$root.error = null;
|
||||
@@ -1173,32 +1181,26 @@ Vue.component('pretix-widget-event-form', {
|
||||
$el.focus();
|
||||
});
|
||||
},
|
||||
calc_items_selected: function () {
|
||||
this.is_items_selected = [...this.$refs.form.querySelectorAll("input[type=checkbox], input[type=radio]")].some(function(element) {
|
||||
return element.checked;
|
||||
}) || [...this.$refs.form.querySelectorAll(".pretix-widget-item-count-group input")].some(function(element) {
|
||||
return parseInt(element.value || "0") > 0;
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Vue.component('pretix-widget-event-list-filter-field', {
|
||||
template: ('<div class="pretix-widget-event-list-filter-field">'
|
||||
+ '<label :for="id">{{ field.label }}</label>'
|
||||
+ '<select :id="id" :name="field.key" @change="onChange($event)" :value="currentValue">'
|
||||
+ '<select :id="id" :name="field.key" :value="currentValue">'
|
||||
+ '<option v-for="choice in field.choices" :value="choice[0]">{{ choice[1] }}</option>'
|
||||
+ '</select>'
|
||||
+ '</div>'),
|
||||
props: {
|
||||
field: Object
|
||||
},
|
||||
methods: {
|
||||
onChange: function(event) {
|
||||
var filterParams = new URLSearchParams(this.$root.filter);
|
||||
if (event.target.value) {
|
||||
filterParams.set(this.field.key, event.target.value);
|
||||
} else {
|
||||
filterParams.delete(this.field.key);
|
||||
}
|
||||
this.$root.filter = filterParams.toString();
|
||||
this.$root.loading++;
|
||||
this.$root.reload();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
id: function () {
|
||||
return widget_id + "_" + this.field.key;
|
||||
@@ -1211,9 +1213,29 @@ Vue.component('pretix-widget-event-list-filter-field', {
|
||||
});
|
||||
|
||||
Vue.component('pretix-widget-event-list-filter-form', {
|
||||
template: ('<div class="pretix-widget-event-list-filter-form">'
|
||||
+ '<pretix-widget-event-list-filter-field v-for="field in $root.meta_filter_fields" :field="field" :key="field.key"></pretix-widget-event-list-filter-field>'
|
||||
+ '</div>'),
|
||||
template: ('<form ref="filterform" class="pretix-widget-event-list-filter-form" @submit="onSubmit">'
|
||||
+ '<fieldset class="pretix-widget-event-list-filter-fieldset">'
|
||||
+ '<legend>' + strings.filter_events_by + '</legend>'
|
||||
+ '<pretix-widget-event-list-filter-field v-for="field in $root.meta_filter_fields" :field="field" :key="field.key"></pretix-widget-event-list-filter-field>'
|
||||
+ '<button>' + strings.filter + '</button>'
|
||||
+ '</fieldset>'
|
||||
+ '</form>'),
|
||||
methods: {
|
||||
onSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
var formData = new FormData(this.$refs.filterform);
|
||||
var filterParams = new URLSearchParams(formData);
|
||||
formData.forEach(function (value, key) {
|
||||
if (value == "") {
|
||||
filterParams.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
this.$root.filter = filterParams.toString();
|
||||
this.$root.loading++;
|
||||
this.$root.reload();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Vue.component('pretix-widget-event-list-entry', {
|
||||
@@ -1740,7 +1762,7 @@ Vue.component('pretix-widget', {
|
||||
return {
|
||||
'pretix-widget': true,
|
||||
'pretix-widget-mobile': this.mobile,
|
||||
'pretix-widget-use-custom-spinners': !this.$root.use_native_spinners
|
||||
'pretix-widget-use-custom-spinners': true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1902,7 +1924,6 @@ var shared_root_methods = {
|
||||
root.categories = data.items_by_category;
|
||||
root.currency = data.currency;
|
||||
root.display_net_prices = data.display_net_prices;
|
||||
root.use_native_spinners = data.use_native_spinners;
|
||||
root.voucher_explanation_text = data.voucher_explanation_text;
|
||||
root.error = data.error;
|
||||
root.display_add_to_cart = data.display_add_to_cart;
|
||||
@@ -2161,28 +2182,11 @@ var create_overlay = function (app) {
|
||||
// show loading spinner only when previously no frame_src was set
|
||||
if (newValue && !oldValue) {
|
||||
this.frame_loading = true;
|
||||
this.$el?.querySelector('dialog.pretix-widget-frame-holder').showModal();
|
||||
}
|
||||
// to close and unload the iframe, frame_src can be empty -> make it valid HTML with about:blank
|
||||
this.$el.querySelector("iframe").src = newValue || "about:blank";
|
||||
},
|
||||
frame_shown: function (newValue) {
|
||||
if (newValue) {
|
||||
this.prevActiveElement = document.activeElement;
|
||||
var btn = this.$el?.querySelector(".pretix-widget-frame-close a");
|
||||
this.$nextTick(function () {
|
||||
btn?.focus();
|
||||
});
|
||||
} else {
|
||||
this.prevActiveElement?.focus();
|
||||
}
|
||||
},
|
||||
error_message: function (newValue) {
|
||||
if (newValue) {
|
||||
this.prevActiveElement = document.activeElement;
|
||||
} else {
|
||||
this.prevActiveElement?.focus();
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
app.$root.overlay = framechild;
|
||||
@@ -2228,7 +2232,6 @@ var create_widget = function (element, html_id=null) {
|
||||
var items = element.attributes.items ? element.attributes.items.value : null;
|
||||
var variations = element.attributes.variations ? element.attributes.variations.value : null;
|
||||
var categories = element.attributes.categories ? element.attributes.categories.value : null;
|
||||
var single_item_select = element.getAttribute("single-item-select") || "checkbox";
|
||||
for (var i = 0; i < element.attributes.length; i++) {
|
||||
var attrib = element.attributes[i];
|
||||
if (attrib.name.match(/^data-.*$/)) {
|
||||
@@ -2275,8 +2278,6 @@ var create_widget = function (element, html_id=null) {
|
||||
variation_filter: variations,
|
||||
voucher_code: voucher,
|
||||
display_net_prices: false,
|
||||
use_native_spinners: false,
|
||||
single_item_select: single_item_select,
|
||||
voucher_explanation_text: null,
|
||||
show_variations_expanded: !!variations,
|
||||
skip_ssl: skip_ssl,
|
||||
|
||||
2504
src/pretix/static/pretixpresale/js/widget/widget.v1.js
Normal file
2504
src/pretix/static/pretixpresale/js/widget/widget.v1.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
// before variables.scss because it only affects presale, not control
|
||||
$body-bg: #f5f5f5 !default;
|
||||
$input-border: #949494;
|
||||
|
||||
$font-size-base: 0.875rem !default;/* 14px/16px = 0.875rem */
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
.pretix-widget, .pretix-widget-alert-box {
|
||||
a {
|
||||
color: $link-color;
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@@ -17,9 +17,9 @@
|
||||
text-decoration: $link-hover-decoration;
|
||||
}
|
||||
&:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
outline: 2px solid $brand-primary;
|
||||
outline-offset: 2px;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
img {
|
||||
@@ -57,6 +57,9 @@
|
||||
&.focus {
|
||||
text-decoration: none;
|
||||
@include tab-focus;
|
||||
outline: 2px solid $brand-primary;
|
||||
outline-offset: 2px;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
&.disabled,
|
||||
@@ -72,6 +75,7 @@
|
||||
}
|
||||
label.pretix-widget-btn-checkbox {
|
||||
@include button-variant($btn-default-color, $btn-default-bg, $btn-default-border, darken($btn-default-bg, 10%), darken($btn-default-border, 25%), darken($btn-default-border, 12%));
|
||||
border-radius: $input-border-radius;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
position: relative;
|
||||
@@ -88,6 +92,11 @@
|
||||
background-color: #e6e6e6;
|
||||
border-color: #adadad;
|
||||
}
|
||||
&:focus-within {
|
||||
outline: 2px solid $brand-primary;
|
||||
outline-offset: 2px;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
.pretix-widget-icon-cart {
|
||||
display: inline-block;
|
||||
@@ -114,9 +123,9 @@
|
||||
$color-rgba: rgba(red($input-border-focus), green($input-border-focus), blue($input-border-focus), .6);
|
||||
|
||||
&:focus {
|
||||
border-color: $input-border-focus;
|
||||
outline: 0;
|
||||
@include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px $color-rgba);
|
||||
outline: 2px solid $brand-primary;
|
||||
outline-offset: 2px;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
input[type=number] {
|
||||
@@ -151,7 +160,6 @@
|
||||
border-radius: $input-border-radius;
|
||||
|
||||
.pretix-widget-resume-button {
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -160,6 +168,9 @@
|
||||
}
|
||||
|
||||
.pretix-widget-info-message {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
margin: 10px 0;
|
||||
@@ -168,6 +179,15 @@
|
||||
color: $state-info-text;
|
||||
border-radius: $alert-border-radius;
|
||||
}
|
||||
&.pretix-widget-mobile {
|
||||
.pretix-widget-info-message {
|
||||
flex-direction: column;
|
||||
}
|
||||
.pretix-widget-resume-button {
|
||||
margin-top: 10px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pretix-widget-error-message {
|
||||
padding: 10px;
|
||||
@@ -352,12 +372,15 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pretix-widget-item-picture {
|
||||
.pretix-widget-item-picture-link {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
}
|
||||
.pretix-widget-item-picture {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.pretix-widget-action {
|
||||
margin-left: 75%;
|
||||
@@ -431,6 +454,17 @@
|
||||
.pretix-widget-item-availability-col {
|
||||
text-align: center;
|
||||
|
||||
.pretix-widget-collapse-indicator {
|
||||
width: 100%;
|
||||
|
||||
border: 1px solid $input-border;
|
||||
border-radius: $input-border-radius;
|
||||
height: $input-height-base;
|
||||
padding: $padding-base-vertical $padding-base-horizontal;
|
||||
color: $input-color;
|
||||
background-color: $input-bg;
|
||||
}
|
||||
|
||||
.pretix-widget-collapse-indicator::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
@@ -514,6 +548,10 @@
|
||||
flex-wrap: wrap;
|
||||
color: $text-color;
|
||||
|
||||
&:has(.pretix-widget-event-list-entry-availability) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
background: $gray-lighter;
|
||||
text-decoration: none;
|
||||
@@ -540,41 +578,59 @@
|
||||
padding: 7px 5px 3px;
|
||||
box-sizing: border-box;
|
||||
span {
|
||||
position: relative;
|
||||
display: inline;
|
||||
padding: 2px 6px 3px;
|
||||
padding: 6px 6px 4px 17px;
|
||||
font-size: 75%;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
color: var(--status-text-color, #000);
|
||||
background-color: var(--status-bg-color, #fff);
|
||||
border: 1px solid var(--status-border-color, #000);
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
border-radius: 4px;
|
||||
}
|
||||
span:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 11px;
|
||||
height: 100%;
|
||||
background: var(--status-border-color, #000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pretix-widget-event-availability-orange .pretix-widget-event-list-entry-availability span,
|
||||
.pretix-widget-event-availability-orange.pretix-widget-event-calendar-event {
|
||||
background-color: $brand-warning;
|
||||
.pretix-widget-event-availability-orange,
|
||||
.pretix-widget-day-availability-orange {
|
||||
--status-bg-color: #{$alert-warning-bg};
|
||||
--status-text-color: #{$alert-warning-text};
|
||||
--status-border-color: #{$alert-warning-border};
|
||||
}
|
||||
.pretix-widget-event-availability-none .pretix-widget-event-list-entry-availability span,
|
||||
.pretix-widget-event-availability-none.pretix-widget-event-calendar-event {
|
||||
background-color: $brand-primary;
|
||||
.pretix-widget-event-availability-none,
|
||||
.pretix-widget-day-availability-none {
|
||||
--status-bg-color: #{$alert-primary-bg};
|
||||
--status-text-color: #{$alert-primary-text};
|
||||
--status-border-color: #{$alert-primary-border};
|
||||
}
|
||||
.pretix-widget-event-availability-green .pretix-widget-event-list-entry-availability span,
|
||||
.pretix-widget-event-availability-green.pretix-widget-event-calendar-event {
|
||||
background-color: $brand-success;
|
||||
.pretix-widget-event-availability-green,
|
||||
.pretix-widget-day-availability-green {
|
||||
--status-bg-color: #{$alert-success-bg};
|
||||
--status-text-color: #{$alert-success-text};
|
||||
--status-border-color: #{$alert-success-border};
|
||||
}
|
||||
.pretix-widget-event-availability-red .pretix-widget-event-list-entry-availability span,
|
||||
.pretix-widget-event-availability-red.pretix-widget-event-calendar-event {
|
||||
background-color: $brand-danger;
|
||||
.pretix-widget-event-availability-red,
|
||||
.pretix-widget-day-availability-red {
|
||||
--status-bg-color: #{$alert-danger-bg};
|
||||
--status-text-color: #{$alert-danger-text};
|
||||
--status-border-color: #{$alert-danger-border};
|
||||
}
|
||||
.pretix-widget-event-availability-low .pretix-widget-event-list-entry-availability span {
|
||||
border-left: 10px solid $brand-warning;
|
||||
}
|
||||
.pretix-widget-event-availability-low.pretix-widget-event-calendar-event {
|
||||
border-right: 10px solid $brand-warning;
|
||||
.pretix-widget-event-list .pretix-widget-event-availability-low .pretix-widget-event-list-entry-availability span:before,
|
||||
.pretix-widget-event-calendar .pretix-widget-event-availability-low.pretix-widget-event-calendar-event:before {
|
||||
background: linear-gradient(to bottom, var(--pretix-brand-warning) 1em, var(--status-border-color) 2.5em);
|
||||
}
|
||||
|
||||
.pretix-widget-event-calendar {
|
||||
@@ -587,7 +643,7 @@
|
||||
|
||||
.pretix-widget-event-week-col {
|
||||
flex: 1;
|
||||
margin: 0 5px;
|
||||
margin: 0;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
@@ -595,6 +651,12 @@
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&:nth-child(even) {
|
||||
background-color: $table-bg-accent;
|
||||
}
|
||||
.pretix-widget-event-calendar-events {
|
||||
margin: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,31 +678,62 @@
|
||||
}
|
||||
}
|
||||
.pretix-widget-event-calendar-event {
|
||||
position: relative;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--status-border-color, #000);
|
||||
background-color: var(--status-bg-color, #fff);
|
||||
color: var(--status-text-color, #000);
|
||||
padding: 5px;
|
||||
color: white;
|
||||
padding-left: 17px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 5px;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 11px;
|
||||
height: 100%;
|
||||
background: var(--status-border-color, #000);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.pretix-widget-event-calendar-table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
|
||||
th, td {
|
||||
width: 14.285714285714286%;
|
||||
vertical-align: top;
|
||||
padding: 10px 5px;
|
||||
padding: 4px;
|
||||
border-bottom: 1px solid $table-border-color;
|
||||
}
|
||||
th {
|
||||
border-bottom-width: 2px;
|
||||
color: $text-muted;
|
||||
}
|
||||
td:has(.pretix-widget-event-calendar-day):nth-child(even) {
|
||||
background: $table-bg-accent;
|
||||
}
|
||||
}
|
||||
.pretix-widget-event-calendar-day {
|
||||
font-weight: bold;
|
||||
font-size: 86%;
|
||||
padding: 5px 5px 1em;
|
||||
}
|
||||
.pretix-widget-event-week-table .pretix-widget-event-calendar-day {
|
||||
padding-bottom: 5px;
|
||||
background-color: #fff;
|
||||
border-bottom: 2px solid $table-border-color;
|
||||
color: $text-muted;
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,9 +749,24 @@
|
||||
|
||||
|
||||
.pretix-widget-event-list-filter-form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: end;
|
||||
.pretix-widget-event-list-filter-fieldset {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: end;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
> legend {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
margin-bottom: 15px;
|
||||
|
||||
.pretix-widget-event-list-filter-field {
|
||||
@@ -667,7 +775,7 @@
|
||||
margin: 0 15px 0 0;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@@ -682,7 +790,9 @@
|
||||
}
|
||||
}
|
||||
.pretix-widget.pretix-widget-mobile .pretix-widget-event-list-filter-form {
|
||||
display: block;
|
||||
.pretix-widget-event-list-filter-fieldset {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pretix-widget-event-list-filter-field {
|
||||
display: block;
|
||||
@@ -701,24 +811,120 @@
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.pretix-widget-alert-holder {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
z-index: 16777271;
|
||||
|
||||
.pretix-widget-visibility-hidden {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s; /* do not animate visibility or we'll have a flashing thing on load */
|
||||
}
|
||||
.pretix-widget-shake-once {
|
||||
animation: pretix-widget-shake .2s;
|
||||
transform: translate3d(0, 0, 0);
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
&.pretix-widget-alert-shown {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s, visibility 0.5s;
|
||||
@keyframes pretix-widget-shake {
|
||||
0% { transform: skewX(0deg); }
|
||||
20% { transform: skewX(-5deg); }
|
||||
40% { transform: skewX(5deg); }
|
||||
60% { transform: skewX(-5deg); }
|
||||
80% { transform: skewX(5deg); }
|
||||
100% { transform: skewX(0deg); }
|
||||
}
|
||||
|
||||
|
||||
.pretix-widget-alert-holder,
|
||||
.pretix-widget-frame-holder,
|
||||
.pretix-widget-lightbox-holder {
|
||||
border: none;
|
||||
background: transparent;
|
||||
overflow: visible;
|
||||
&::backdrop {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
&:focus {
|
||||
outline: 2px solid $brand-primary;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
.pretix-widget-frame-isloading:focus {
|
||||
outline: none;
|
||||
svg {
|
||||
outline: 2px solid $brand-primary;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
.pretix-widget-frame-loading p,
|
||||
.pretix-widget-lightbox-loading p {
|
||||
text-align: center;
|
||||
width: 256px;
|
||||
margin: 0 auto;
|
||||
color: $brand-danger;
|
||||
}
|
||||
.pretix-widget-frame-loading svg,
|
||||
.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-frame-close,
|
||||
.pretix-widget-lightbox-close {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: -12px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-close button,
|
||||
.pretix-widget-lightbox-close button {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
text-decoration: none;
|
||||
padding: 4px 0;
|
||||
display: inline-block;
|
||||
line-height: 16px;
|
||||
border: none;
|
||||
background: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: $brand-primary;
|
||||
border-radius: 12px;
|
||||
-moz-border-radius: 12px;
|
||||
-webkit-border-radius: 12px;
|
||||
text-align: center;
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid $brand-primary;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.pretix-widget-frame-close svg,
|
||||
.pretix-widget-lightbox-close svg {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-inner,
|
||||
.pretix-widget-lightbox-inner,
|
||||
.pretix-widget-alert-box {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.pretix-widget-alert-holder {
|
||||
.bounce-enter-active {
|
||||
animation: pretix-widget-bounce-in .5s;
|
||||
}
|
||||
@@ -727,19 +933,6 @@
|
||||
}
|
||||
|
||||
.pretix-widget-alert-box {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
width: 600px;
|
||||
margin-left: -300px;
|
||||
top: 100px;
|
||||
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: 42px 20px 20px 20px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
@@ -752,197 +945,47 @@
|
||||
}
|
||||
}
|
||||
.pretix-widget-alert-icon {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 64px;
|
||||
margin-left: -32px;
|
||||
top: 68px;
|
||||
top: -20px;
|
||||
}
|
||||
}
|
||||
.pretix-widget-frame-holder {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
z-index: 16777271;
|
||||
|
||||
|
||||
|
||||
.pretix-widget-frame-inner {
|
||||
width: 80vw;
|
||||
height: 80vh;
|
||||
}
|
||||
.pretix-widget-frame-inner iframe {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
.pretix-widget-lightbox-inner {
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
}
|
||||
.pretix-widget-lightbox-isloading .pretix-widget-lightbox-inner {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s, visibility 0.5s;
|
||||
|
||||
.pretix-widget-frame-loading {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-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-frame-shown {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s, visibility 0.5s;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-inner {
|
||||
position: fixed;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
top: 10%;
|
||||
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;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-close {
|
||||
position: fixed;
|
||||
right: 10%;
|
||||
top: 10%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: $brand-primary;
|
||||
margin: -12px -12px 0 0;
|
||||
border-radius: 12px;
|
||||
-moz-border-radius: 12px;
|
||||
-webkit-border-radius: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-close a {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
text-decoration: none;
|
||||
padding: 4px 0;
|
||||
display: inline-block;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-close svg {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.pretix-widget-frame-inner iframe {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
.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-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 {
|
||||
/* in SVG */
|
||||
@@ -1015,22 +1058,14 @@
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
td.pretix-widget-has-events {
|
||||
background: $brand-primary;
|
||||
color: white;
|
||||
td.pretix-widget-has-events .pretix-widget-event-calendar-day {
|
||||
background: var(--status-bg-color, #fff);
|
||||
color: var(--status-text-color, #000);
|
||||
border: 1px solid var(--status-border-color, inherit);
|
||||
border-top-width: 11px;
|
||||
padding-bottom: 5px;
|
||||
border-radius: $input-border-radius;
|
||||
cursor: pointer;
|
||||
&.pretix-widget-day-availability-red {
|
||||
background: $brand-danger;
|
||||
}
|
||||
&.pretix-widget-day-availability-green {
|
||||
background: $brand-success;
|
||||
}
|
||||
&.pretix-widget-day-availability-low {
|
||||
border-right: 5px solid $brand-warning;
|
||||
}
|
||||
&.pretix-widget-day-availability-orange {
|
||||
background: $brand-warning;
|
||||
}
|
||||
}
|
||||
|
||||
.pretix-widget-event-calendar-head {
|
||||
@@ -1048,41 +1083,3 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.pretix-widget-frame-holder {
|
||||
.pretix-widget-frame-inner {
|
||||
left: 50%;
|
||||
margin-left: -540px;
|
||||
width: 1080px;
|
||||
}
|
||||
.pretix-widget-frame-close {
|
||||
left: 50%;
|
||||
margin-left: 528px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.pretix-widget-frame-holder .pretix-widget-frame-inner {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
background: $brand-primary;
|
||||
border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
padding: 40px 0 0 0;
|
||||
}
|
||||
.pretix-widget-frame-holder .pretix-widget-frame-close {
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
background: white;
|
||||
svg path {
|
||||
fill: $brand-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1088
src/pretix/static/pretixpresale/scss/widget.v1.scss
Normal file
1088
src/pretix/static/pretixpresale/scss/widget.v1.scss
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user