From 9e4e0599ed09b0f4a2d404952abb2e3bdfd15fa2 Mon Sep 17 00:00:00 2001 From: Dennis Reimann Date: Thu, 9 Nov 2023 18:29:50 +0100 Subject: [PATCH] Keypad updates --- BTCPayApp.UI/Components/Keypad.razor | 70 ++++++++++++++------ BTCPayApp.UI/Layout/SimpleLayout.razor | 8 +-- BTCPayApp.UI/Pages/PointOfSalePage.razor.css | 3 + BTCPayApp.UI/wwwroot/css/global.css | 54 ++++++++++++++- BTCPayApp.UI/wwwroot/js/global.js | 5 ++ 5 files changed, 110 insertions(+), 30 deletions(-) create mode 100644 BTCPayApp.UI/Pages/PointOfSalePage.razor.css diff --git a/BTCPayApp.UI/Components/Keypad.razor b/BTCPayApp.UI/Components/Keypad.razor index d467235..8ebc87e 100644 --- a/BTCPayApp.UI/Components/Keypad.razor +++ b/BTCPayApp.UI/Components/Keypad.razor @@ -1,9 +1,12 @@ @using Newtonsoft.Json.Linq +@using System.Text.RegularExpressions +@using System.Globalization +@inject IJSRuntime JS
-
+
@CurrencyCode
-
@FormatCurrency(GetTotal(), false)
+
@FormatCurrency(GetTotal(), false)
@Calculation(Model)
@if (IsDiscountEnabled || IsTipEnabled) @@ -13,7 +16,7 @@ {
- @Model.DiscountPercent% discount + @(Model.DiscountPercent ?? 0)% discount
} @@ -75,7 +78,7 @@ @if (IsSubmitting) {
- Loading... + Loading...
} else @@ -112,11 +115,14 @@ Tip } + private ElementReference _keypadTop; + private ElementReference _keypadAmount; + private InputMode Mode { get; set; } = InputMode.Amount; public class KeypadModel { - public List Amounts { get; set; } = new (); + public List Amounts { get; set; } = new () { 0 }; public int? DiscountPercent { get; set; } public int? TipPercent { get; set; } public decimal? Tip { get; set; } @@ -129,7 +135,7 @@ IsSubmitting = true; } - private void KeyPress(char key = '1') + private async Task KeyPress(char key) { if (Mode == InputMode.Amount) { var lastIndex = Model.Amounts.Count - 1; @@ -148,8 +154,9 @@ } else if (key == '+' && lastAmount != 0) { Model.Amounts.Add(0); } else { // Is a digit - Model.Amounts[lastIndex] = ApplyKeyToValue(key, lastAmount, CurrencyDivisibility); + Model.Amounts[lastIndex] = Math.Min(ApplyKeyToValue(key, lastAmount, CurrencyDivisibility), decimal.MaxValue / 10); } + await UpdateFontSize(); } else { if (key == 'C') { if (Mode == InputMode.Tip) @@ -165,37 +172,43 @@ var divisibility = Mode == InputMode.Tip ? CurrencyDivisibility : 0; if (Mode == InputMode.Tip) { - Model.Tip = ApplyKeyToValue(key, Model.Tip ?? 0, divisibility); + Model.Tip = Math.Min(ApplyKeyToValue(key, Model.Tip ?? 0, divisibility), decimal.MaxValue / 10); Model.TipPercent = null; } else { - Model.DiscountPercent = (int)ApplyKeyToValue(key, Model.DiscountPercent ?? 0, divisibility); + var num = (int)ApplyKeyToValue(key, Model.DiscountPercent ?? 0, divisibility); + Model.DiscountPercent = Math.Min(num, 100); } } } } - public decimal ApplyKeyToValue(char key, decimal value, int divisibility) + private decimal ApplyKeyToValue(char key, decimal value, int divisibility) { - var val = value is 0 ? "" : Formatted(value, divisibility); - val = (val + key) - .Replace(".", "") - .PadLeft(divisibility, '0'); - //.Replace(new Regex("(\\d*)(\\d{{divisibility}})", "\\1.\\2"); - return decimal.Parse(val); + var str = value is 0 ? "" : Formatted(value, divisibility); + //var minDiv = str.Length < divisibility ? str.Length + 1 : divisibility; + str = (str + key).Replace(".", ""); + if (divisibility > 0) + { + str = str.PadLeft(divisibility + 1, '0'); + str = Regex.Replace(str, $"(\\d*)(\\d{{{divisibility}}})", "$1.$2"); + } + + return decimal.Parse(str, CultureInfo.InvariantCulture); } - public void DoublePress(char key) + private void DoublePress(char key) { if (key == 'C') { Clear(); } } - public void Clear() + private void Clear() { Model.Amounts.Clear(); + Model.Amounts.Add(0); Model.DiscountPercent = null; Model.TipPercent = null; Model.Tip = null; @@ -272,10 +285,10 @@ if (model.Amounts.Count < 2 && model.DiscountPercent is not > 0 && !model.Tip.HasValue) return null; var calc = string.Join(" + ", model.Amounts.Select(amt => FormatCurrency(amt, true))); var discount = GetDiscount(); - if (discount > 0) calc += $" - {FormatCurrency(discount, true)} (${model.DiscountPercent}%)"; + if (discount > 0) calc += $" - {FormatCurrency(discount, true)} ({model.DiscountPercent}%)"; var tip = GetTip(); - if (model.Tip > 0) calc += $" + ${FormatCurrency(tip, true)}"; - if (model.TipPercent > 0) calc += $" (${model.TipPercent}%)"; + if (tip > 0) calc += $" + {FormatCurrency(tip, true)}"; + if (model.TipPercent > 0) calc += $" ({model.TipPercent}%)"; return calc; } @@ -298,6 +311,19 @@ } private string Formatted(decimal value, int divisibility) { - return string.Format($"{{0:0.{new string('0', divisibility)}}}", value); + return string.Format(CultureInfo.InvariantCulture, $"{{0:0.{new string('0', divisibility)}}}", value); + } + + private async Task UpdateFontSize() + { + var top = await JS.InvokeAsync("Interop.getWidth", new object[] { _keypadTop }); + var amt = await JS.InvokeAsync("Interop.getWidth", new object[] { _keypadAmount }); + var gamma = top / amt; + + FontSize = (int)(top < amt + ? Math.Floor(FontSize * gamma) + : Math.Min(FontSize * gamma, DefaultFontSize)); + + StateHasChanged(); } } diff --git a/BTCPayApp.UI/Layout/SimpleLayout.razor b/BTCPayApp.UI/Layout/SimpleLayout.razor index d7cf3d5..20564d9 100644 --- a/BTCPayApp.UI/Layout/SimpleLayout.razor +++ b/BTCPayApp.UI/Layout/SimpleLayout.razor @@ -1,7 +1,5 @@ @inherits Fluxor.Blazor.Web.Components.FluxorLayout -
-
- @Body -
-
+
+ @Body +
diff --git a/BTCPayApp.UI/Pages/PointOfSalePage.razor.css b/BTCPayApp.UI/Pages/PointOfSalePage.razor.css new file mode 100644 index 0000000..be63032 --- /dev/null +++ b/BTCPayApp.UI/Pages/PointOfSalePage.razor.css @@ -0,0 +1,3 @@ +.public-page-wrap { + --wrap-max-width: 560px; +} diff --git a/BTCPayApp.UI/wwwroot/css/global.css b/BTCPayApp.UI/wwwroot/css/global.css index 13ce8fe..b9693b4 100644 --- a/BTCPayApp.UI/wwwroot/css/global.css +++ b/BTCPayApp.UI/wwwroot/css/global.css @@ -1,6 +1,4 @@ - - -::-moz-focus-inner { +::-moz-focus-inner { padding: 0; border-style: none; } @@ -17,6 +15,56 @@ transform: rotate(-180deg); } +/* Layout */ +.public-page-wrap { + --wrap-max-width: none; + --wrap-padding-vertical: var(--btcpay-space-l); + --wrap-padding-horizontal: var(--btcpay-space-m); + + display: flex; + flex-direction: column; + gap: 1.5rem; + max-width: var(--wrap-max-width); + margin: 0 auto; + padding: var(--wrap-padding-vertical) var(--wrap-padding-horizontal); +} + +.min-vh-100, +.public-page-wrap { + min-height: -webkit-fill-available !important; + min-height: 100dvh !important; +} + +.tile { + --section-padding: 1.5rem; + --section-border-radius: var(--btcpay-border-radius-l); + + padding: var(--section-padding); + background: var(--btcpay-bg-tile); + border-radius: var(--section-border-radius); + box-shadow: var(--btcpay-box-shadow-lg); +} +.tile .buttons { + display: flex; + flex-direction: column; + gap: var(--btcpay-space-m); +} +.tile > :last-child { + margin-bottom: 0; +} + +@media (max-width: 400px) { + .public-page-wrap { + padding-left: 0; + padding-right: 0; + --wrap-padding-horizontal: 0; + } + .tile { + --section-padding: 1rem; + --section-border-radius: none; + } +} + /* Badges */ .badge-new, .badge-pending { diff --git a/BTCPayApp.UI/wwwroot/js/global.js b/BTCPayApp.UI/wwwroot/js/global.js index e69de29..0c6301c 100644 --- a/BTCPayApp.UI/wwwroot/js/global.js +++ b/BTCPayApp.UI/wwwroot/js/global.js @@ -0,0 +1,5 @@ +Interop = { + getWidth(el) { + return el.clientWidth; + } +}