mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Widget & Cart: Add custom number spinners for item quantity
This commit is contained in:
committed by
GitHub
parent
f97effd0b7
commit
1d0eb81659
@@ -2608,6 +2608,15 @@ Your {organizer} team"""))
|
||||
label=_("Use round edges"),
|
||||
)
|
||||
},
|
||||
'widget_use_native_spinners': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Use native spinners in the widget instead of custom ones for numeric inputs such as quantity."),
|
||||
)
|
||||
},
|
||||
'primary_font': {
|
||||
'default': 'Open Sans',
|
||||
'type': str,
|
||||
|
||||
@@ -190,7 +190,9 @@
|
||||
<i class="fa fa-cart-plus fa-lg" aria-hidden="true"></i>
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if var.initial %}value="{{ var.initial }}"{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-variation-{{form.pos.pk}}-{{ item.pk }}-{{ var.pk }}"
|
||||
@@ -199,6 +201,8 @@
|
||||
id="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}"
|
||||
name="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-label="{% blocktrans with item=item.name var=var %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<button type="button" data-step="1" data-controls="cp_{{ form.pos.pk }}_variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -319,7 +323,9 @@
|
||||
<i class="fa fa-cart-plus fa-lg" aria-hidden="true"></i>
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="cp_{{ form.pos.pk }}_item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-item-{{ form.pos.pk }}-{{ item.pk }}"
|
||||
{% endif %}
|
||||
@@ -329,6 +335,8 @@
|
||||
id="cp_{{ form.pos.pk }}_item_{{ item.id }}"
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="cp-{{ form.pos.pk }}-item-{{ item.id }}-description"{% endif %}>
|
||||
<button type="button" data-step="1" data-controls="cp_{{ form.pos.pk }}_item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -197,15 +197,19 @@
|
||||
<i class="fa fa-cart-plus fa-lg" aria-hidden="true"></i>
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
{% endif %}
|
||||
max="{{ var.order_max }}"
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
name="variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-label="{% blocktrans with item=item.name var=var.name %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-variation-{{ item.pk }}-{{ var.pk }}"
|
||||
{% endif %}
|
||||
max="{{ var.order_max }}"
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
name="variation_{{ item.id }}_{{ var.id }}"
|
||||
aria-label="{% blocktrans with item=item.name var=var.name %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<button type="button" data-step="1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
@@ -334,17 +338,21 @@
|
||||
<i class="fa fa-cart-plus fa-lg" aria-hidden="true"></i>
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if itemnum == 1 %}value="1"{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-item-{{ item.pk }}"
|
||||
{% endif %}
|
||||
max="{{ item.order_max }}"
|
||||
name="item_{{ item.id }}"
|
||||
id="item_{{ item.id }}"
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="item-{{ item.id }}-description"{% endif %}>
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
{% if not ev.presale_is_running %}disabled{% endif %}
|
||||
{% if itemnum == 1 %}value="1"{% endif %}
|
||||
{% if item.free_price %}
|
||||
data-checked-onchange="price-item-{{ item.pk }}"
|
||||
{% endif %}
|
||||
max="{{ item.order_max }}"
|
||||
name="item_{{ item.id }}"
|
||||
id="item_{{ item.id }}"
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="item-{{ item.id }}-description"{% endif %}>
|
||||
<button type="button" data-step="1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -254,12 +254,16 @@
|
||||
<i class="fa fa-cart-plus fa-lg" aria-hidden="true"></i>
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count" placeholder="0" min="0"
|
||||
max="{{ item.order_max }}"
|
||||
id="variation_{{ item.id }}_{{ var.id }}"
|
||||
name="variation_{{ item.id }}_{{ var.id }}"
|
||||
{% if options == 1 %}value="1"{% endif %}
|
||||
aria-label="{% blocktrans with item=item.name var=var.name %}Quantity of {{ item }}, {{ var }} to order{% endblocktrans %}">
|
||||
<button type="button" data-step="1" data-controls="variation_{{ item.id }}_{{ var.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label>
|
||||
@@ -392,7 +396,9 @@
|
||||
<i class="fa fa-cart-plus fa-lg" aria-hidden="true"></i>
|
||||
</label>
|
||||
{% else %}
|
||||
<input type="number" class="form-control input-item-count"
|
||||
<div class="input-item-count-group">
|
||||
<button type="button" data-step="-1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-dec" aria-label="{% trans "Decrease quantity" %}">-</button>
|
||||
<input type="number" class="form-control input-item-count"
|
||||
placeholder="0" min="0"
|
||||
max="{{ item.order_max }}"
|
||||
id="item_{{ item.id }}"
|
||||
@@ -400,6 +406,8 @@
|
||||
{% if options == 1 %}value="1"{% endif %}
|
||||
aria-label="{% blocktrans with item=item.name %}Quantity of {{ item }} to order{% endblocktrans %}"
|
||||
{% if item.description %} aria-describedby="item-{{ item.id }}-description"{% endif %}>
|
||||
<button type="button" data-step="1" data-controls="item_{{ item.id }}" class="btn btn-default input-item-count-inc" aria-label="{% trans "Increase quantity" %}">+</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label>
|
||||
|
||||
@@ -668,6 +668,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
data = {
|
||||
'currency': request.event.currency,
|
||||
'display_net_prices': request.event.settings.display_net_prices,
|
||||
'use_native_spinners': request.event.settings.widget_use_native_spinners,
|
||||
'show_variations_expanded': request.event.settings.show_variations_expanded,
|
||||
'waiting_list_enabled': request.event.settings.waiting_list_enabled,
|
||||
'voucher_explanation_text': str(rich_text(request.event.settings.voucher_explanation_text, safelinks=False)),
|
||||
|
||||
@@ -117,6 +117,14 @@ var form_handlers = function (el) {
|
||||
$(this).datetimepicker(opts);
|
||||
});
|
||||
|
||||
el.find(".input-item-count-dec, .input-item-count-inc").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
var step = parseFloat(this.getAttribute("data-step"));
|
||||
var controls = document.getElementById(this.getAttribute("data-controls"));
|
||||
var currentValue = parseFloat(controls.value);
|
||||
controls.value = Math.max(controls.min, Math.min(controls.max, (currentValue || 0) + step));
|
||||
});
|
||||
|
||||
el.find("script[data-replace-with-qr]").each(function () {
|
||||
var $div = $("<div>");
|
||||
$div.insertBefore($(this));
|
||||
|
||||
@@ -15,6 +15,8 @@ Vue.component('resize-observer', VueResize.ResizeObserver)
|
||||
|
||||
var strings = {
|
||||
'quantity': django.pgettext('widget', 'Quantity'),
|
||||
'quantity_dec': django.pgettext('widget', 'Decrease quantity'),
|
||||
'quantity_inc': django.pgettext('widget', 'Increase quantity'),
|
||||
'price': django.pgettext('widget', 'Price'),
|
||||
'select_item': django.pgettext('widget', 'Select %s'),
|
||||
'select_variant': django.pgettext('widget', 'Select variant %s'),
|
||||
@@ -218,10 +220,14 @@ Vue.component('availbox', {
|
||||
+ ' v-bind:aria-label="label_select_item"'
|
||||
+ '>'
|
||||
+ '</label>'
|
||||
+ '<input type="number" class="pretix-widget-item-count-multiple" placeholder="0" min="0"'
|
||||
+ ' v-model="amount_selected" :max="order_max" :name="input_name"'
|
||||
+ '<div :class="count_group_classes" v-else>'
|
||||
+ '<button v-if="!$root.use_native_spinners" type="button" @click="on_step" data-step="-1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-dec" aria-label="' + strings.quantity_dec + '"><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"'
|
||||
+ ' aria-label="' + strings.quantity + '"'
|
||||
+ ' v-if="order_max !== 1">'
|
||||
+ ' >'
|
||||
+ '<button v-if="!$root.use_native_spinners" type="button" @click="on_step" data-step="1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-inc" aria-label="' + strings.quantity_inc + '"><span>+</span></button>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'),
|
||||
props: {
|
||||
@@ -238,6 +244,11 @@ Vue.component('availbox', {
|
||||
this.$root.$emit('amounts_changed')
|
||||
},
|
||||
computed: {
|
||||
count_group_classes: function () {
|
||||
return {
|
||||
'pretix-widget-item-count-group': !this.$root.use_native_spinners
|
||||
}
|
||||
},
|
||||
require_voucher: function () {
|
||||
return this.item.require_voucher && !this.$root.voucher_code
|
||||
},
|
||||
@@ -296,6 +307,13 @@ Vue.component('availbox', {
|
||||
methods: {
|
||||
focus_voucher_field: function () {
|
||||
this.$root.$emit('focus_voucher_field')
|
||||
},
|
||||
on_step: function (e) {
|
||||
e.preventDefault();
|
||||
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, (this.amount_selected || 0) + step));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1438,11 +1456,11 @@ Vue.component('pretix-widget', {
|
||||
},
|
||||
computed: {
|
||||
classObject: function () {
|
||||
var o = {'pretix-widget': true};
|
||||
if (this.mobile) {
|
||||
o['pretix-widget-mobile'] = true;
|
||||
}
|
||||
return o;
|
||||
return {
|
||||
'pretix-widget': true,
|
||||
'pretix-widget-mobile': this.mobile,
|
||||
'pretix-widget-use-custom-spinners': !this.$root.use_native_spinners
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1588,6 +1606,7 @@ 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;
|
||||
@@ -1833,6 +1852,7 @@ var create_widget = function (element) {
|
||||
variation_filter: variations,
|
||||
voucher_code: voucher,
|
||||
display_net_prices: false,
|
||||
use_native_spinners: false,
|
||||
voucher_explanation_text: null,
|
||||
show_variations_expanded: !!variations,
|
||||
skip_ssl: skip_ssl,
|
||||
|
||||
@@ -54,6 +54,43 @@ a.btn, button.btn {
|
||||
}
|
||||
}
|
||||
|
||||
.input-item-count-group {
|
||||
display: flex;
|
||||
}
|
||||
.input-item-count-group input {
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
padding-right: 12px;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
.input-item-count-group input::-webkit-outer-spin-button,
|
||||
.input-item-count-group input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
.input-item-count-dec {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
width: 2.5em;
|
||||
z-index: 2;
|
||||
}
|
||||
.input-item-count-inc {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
width: 2.5em;
|
||||
}
|
||||
.input-group-price {
|
||||
input {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.required-legend span {
|
||||
color: $brand-primary;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
@include opacity(.65);
|
||||
@include box-shadow(none);
|
||||
}
|
||||
&.pretix-widget-btn-default {
|
||||
@include button-variant($btn-default-color, $btn-default-bg, $btn-default-border);
|
||||
}
|
||||
}
|
||||
input[type="text"], input[type="number"] {
|
||||
line-height: normal;
|
||||
@@ -99,6 +102,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.pretix-widget-use-custom-spinners input[type=number] {
|
||||
padding-right: $padding-base-horizontal;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
.pretix-widget-use-custom-spinners input[type=number]::-webkit-outer-spin-button,
|
||||
.pretix-widget-use-custom-spinners input[type=number]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
.pretix-widget {
|
||||
margin: 10px 0;
|
||||
padding: 0 10px;
|
||||
@@ -254,6 +266,34 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pretix-widget-item-count-group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.pretix-widget-item-count-group input {
|
||||
border-radius: 0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.pretix-widget-item-count-group button span {
|
||||
vertical-align: 25%;
|
||||
line-height: 0.5;
|
||||
}
|
||||
|
||||
.pretix-widget-item-count-dec {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
width: 2.5em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.pretix-widget-item-count-inc {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
.pretix-widget-item-count-multiple {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -169,6 +169,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"currency": "EUR",
|
||||
"show_variations_expanded": False,
|
||||
"display_net_prices": False,
|
||||
"use_native_spinners": False,
|
||||
"vouchers_exist": False,
|
||||
"waiting_list_enabled": False,
|
||||
"error": None,
|
||||
@@ -354,6 +355,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"currency": "EUR",
|
||||
"show_variations_expanded": False,
|
||||
"display_net_prices": False,
|
||||
"use_native_spinners": False,
|
||||
"has_seating_plan": False,
|
||||
"has_seating_plan_waitinglist": False,
|
||||
"vouchers_exist": True,
|
||||
@@ -409,6 +411,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"currency": "EUR",
|
||||
"show_variations_expanded": False,
|
||||
"display_net_prices": False,
|
||||
"use_native_spinners": False,
|
||||
"vouchers_exist": True,
|
||||
"has_seating_plan": False,
|
||||
"has_seating_plan_waitinglist": False,
|
||||
@@ -481,6 +484,7 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'poweredby': '<a href="https://pretix.eu" target="_blank" rel="noopener">ticketing powered by pretix</a>',
|
||||
"show_variations_expanded": False,
|
||||
"display_net_prices": False,
|
||||
"use_native_spinners": False,
|
||||
"has_seating_plan": False,
|
||||
"has_seating_plan_waitinglist": False,
|
||||
"vouchers_exist": True,
|
||||
|
||||
Reference in New Issue
Block a user