mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-04-20 07:41:28 +02:00
feat: add contained, persistent calculator
This commit is contained in:
17
app/apps/common/templatetags/formats.py
Normal file
17
app/apps/common/templatetags/formats.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django import template
|
||||
from django.utils.formats import get_format
|
||||
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_thousand_separator():
|
||||
print(get_format("THOUSAND_SEPARATOR"))
|
||||
return get_format("THOUSAND_SEPARATOR")
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_decimal_separator():
|
||||
print(get_format("DECIMAL_SEPARATOR"))
|
||||
return get_format("DECIMAL_SEPARATOR")
|
||||
@@ -126,7 +126,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav mt-3 mb-2 mb-lg-0 mt-lg-0">
|
||||
<li class="nav-item text-center w-100">
|
||||
<a class="nav-item tw-text-2xl tw-cursor-pointer me-lg-4"
|
||||
data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="{% trans "Calculator" %}"
|
||||
_="on click toggle .tw-hidden on #calculator then trigger show on #calculator">
|
||||
<i class="fa-solid fa-calculator"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="text-center w-100">{% include 'includes/navbar/user_menu.html' %}</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{% load formats %}
|
||||
{% load i18n %}
|
||||
{% load title %}
|
||||
{% load webpack_loader %}
|
||||
@@ -19,6 +20,8 @@
|
||||
{% block extra_styles %}{% endblock %}
|
||||
|
||||
{% include 'includes/scripts.html' %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
|
||||
|
||||
{% block extra_js_head %}{% endblock %}
|
||||
</head>
|
||||
<body class="font-monospace">
|
||||
@@ -34,7 +37,93 @@
|
||||
{% include 'includes/offcanvas.html' %}
|
||||
{% include 'includes/toasts.html' %}
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="tw-hidden tw-w-80 position-fixed shadow rounded-3 bg-body tw-border-gray-700 tw-border tw-border-solid tw-text-center tw-align-middle tw-z-[2000] tw-touch-none user-select-none"
|
||||
id="calculator"
|
||||
hx-preserve
|
||||
_="on load
|
||||
-- Get localized separators from Django
|
||||
set window.decimalSeparator to '{% get_decimal_separator %}'
|
||||
set window.thousandSeparator to '{% get_thousand_separator %}'
|
||||
set window.argSeparator to ';'
|
||||
end
|
||||
|
||||
on focusin halt the event -- this prevents bootstrap's static offcanvas from hijacking the focus from the input when open end
|
||||
|
||||
on show if not I.classList.contains('tw-hidden') add { left: 50vw; top: 50vh; transform: translate(-50%, -50%); } then call #calculator-input.focus() end
|
||||
|
||||
on pointerdown(screenX, screenY)
|
||||
if event.target.closest('#calculator-handle')
|
||||
log event
|
||||
halt the event
|
||||
measure my x, y
|
||||
set xoff to screenX - x
|
||||
set yoff to screenY - y
|
||||
repeat until event pointerup from document
|
||||
wait for pointermove(screenX, screenY) or
|
||||
pointerup(screenX, screenY) from document
|
||||
add { left: ${screenX - xoff}px; top: ${screenY - yoff}px; transform: none }
|
||||
end
|
||||
end
|
||||
end">
|
||||
|
||||
<div id="calculator-handle"
|
||||
class="position-absolute bg-secondary rounded-start-2 tw-cursor-move d-flex align-items-center justify-content-center tw-left-[-20px] tw-top-[3px] tw-h-[2em] tw-w-[20px]">
|
||||
<i class="fa-solid fa-grip-vertical"></i>
|
||||
</div>
|
||||
|
||||
<input type="search"
|
||||
class="form-control"
|
||||
id="calculator-input"
|
||||
_="on click me.focus()
|
||||
on input or search
|
||||
js(my)
|
||||
return my.value.replace(new RegExp('\\' + window.decimalSeparator + '|\\' + window.argSeparator + '|\\' + window.thousandSeparator, 'g'),
|
||||
match => {
|
||||
if (match === window.decimalSeparator) return '.';
|
||||
if (match === window.argSeparator) return ',';
|
||||
return ''; // This will remove the thousandSeparator
|
||||
})
|
||||
end
|
||||
then set expr to it
|
||||
then call math.evaluate(expr)
|
||||
then log result
|
||||
if result exists and result is a Number
|
||||
js(result)
|
||||
return result.toString().replace(new RegExp(',|\\.', 'g'),
|
||||
match => match === '.' ? window.decimalSeparator : window.argSeparator)
|
||||
end
|
||||
then set localizedResult to it
|
||||
set #calculator-result.innerText to localizedResult
|
||||
then remove .tw-hidden from #calculator-result-container
|
||||
then add .swing-in-top-fwd to #calculator-result-container
|
||||
then settle
|
||||
then remove .swing-in-top-fwd from #calculator-result-container
|
||||
else
|
||||
add .swing-out-top-bck to #calculator-result-container
|
||||
then settle
|
||||
then add .tw-hidden to #calculator-result-container
|
||||
then remove .swing-out-top-bck from #calculator-result-container
|
||||
end
|
||||
catch e
|
||||
add .swing-out-top-bck to #calculator-result-container
|
||||
then settle
|
||||
then add .tw-hidden to #calculator-result-container
|
||||
then remove .swing-out-top-bck from #calculator-result-container
|
||||
end"
|
||||
placeholder="2 + 2">
|
||||
<div class="tw-hidden" id="calculator-result-container">
|
||||
<div class="d-flex flex-row p-2 justify-content-between">
|
||||
<div class="tw-text-gray-400">=</div>
|
||||
<div id="calculator-result" class="user-select-all"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="position-absolute tw-cursor-pointer top-0 start-100 translate-middle tw-p-0 text-bg-primary border border-light rounded-circle tw-flex tw-items-center tw-justify-center tw-w-5 tw-h-5"
|
||||
_="on click toggle .tw-hidden on #calculator">
|
||||
<i class="fa-solid fa-xmark tw-flex tw-items-center tw-justify-center tw-w-full tw-h-full"></i>
|
||||
</div>
|
||||
</div>
|
||||
{% block extra_js_body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
96
frontend/package-lock.json
generated
96
frontend/package-lock.json
generated
@@ -34,6 +34,7 @@
|
||||
"htmx.org": "^2.0.1",
|
||||
"hyperscript.org": "^0.9.12",
|
||||
"jquery": "^3.7.1",
|
||||
"mathjs": "^14.0.1",
|
||||
"mini-css-extract-plugin": "^2.5.1",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"postcss-preset-env": "^7.2.3",
|
||||
@@ -1615,9 +1616,10 @@
|
||||
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
|
||||
"integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
@@ -3342,6 +3344,19 @@
|
||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
|
||||
},
|
||||
"node_modules/complex.js": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.2.tgz",
|
||||
"integrity": "sha512-qtx7HRhPGSCBtGiST4/WGHuW+zeaND/6Ld+db6PbrulIB1i2Ev/2UPiqcmpQNPSyfBKraC0EOvOKCB5dGZKt3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/compressible": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
|
||||
@@ -3792,6 +3807,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
|
||||
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deep-is": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||
@@ -4050,6 +4071,12 @@
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/escape-latex": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
|
||||
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
@@ -5561,6 +5588,12 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jest-worker": {
|
||||
"version": "28.1.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz",
|
||||
@@ -5837,6 +5870,42 @@
|
||||
"resolved": "https://registry.npmjs.org/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz",
|
||||
"integrity": "sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg=="
|
||||
},
|
||||
"node_modules/mathjs": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.0.1.tgz",
|
||||
"integrity": "sha512-yyJgLwC6UXuve724np8tHRMYaTtb5UqiOGQkjwbSXgH8y1C/LcJ0pvdNDZLI2LT7r+iExh2Y5HwfAY+oZFtGIQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.7",
|
||||
"complex.js": "^2.2.5",
|
||||
"decimal.js": "^10.4.3",
|
||||
"escape-latex": "^1.2.0",
|
||||
"fraction.js": "^5.2.1",
|
||||
"javascript-natural-sort": "^0.7.1",
|
||||
"seedrandom": "^3.0.5",
|
||||
"tiny-emitter": "^2.1.0",
|
||||
"typed-function": "^4.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"mathjs": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mathjs/node_modules/fraction.js": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.2.1.tgz",
|
||||
"integrity": "sha512-Ah6t/7YCYjrPUFUFsOsRLMXAdnYM+aQwmojD2Ayb/Ezr82SwES0vuyQ8qZ3QO8n9j7W14VJuVZZet8U3bhSdQQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/mathml-tag-names": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
|
||||
@@ -7951,6 +8020,12 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/seedrandom": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
|
||||
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@@ -9091,6 +9166,12 @@
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="
|
||||
},
|
||||
"node_modules/tiny-emitter": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
@@ -9181,6 +9262,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typed-function": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
|
||||
"integrity": "sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/typer-dot-js": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/typer-dot-js/-/typer-dot-js-0.1.0.tgz",
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"htmx.org": "^2.0.1",
|
||||
"hyperscript.org": "^0.9.12",
|
||||
"jquery": "^3.7.1",
|
||||
"mathjs": "^14.0.1",
|
||||
"mini-css-extract-plugin": "^2.5.1",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"postcss-preset-env": "^7.2.3",
|
||||
|
||||
@@ -2,14 +2,18 @@ import _hyperscript from 'hyperscript.org/dist/_hyperscript.min';
|
||||
import './_htmx.js';
|
||||
import Alpine from "alpinejs";
|
||||
import mask from '@alpinejs/mask';
|
||||
import { create, all } from 'mathjs';
|
||||
|
||||
window.Alpine = Alpine;
|
||||
window._hyperscript = _hyperscript;
|
||||
window.math = create(all, { });
|
||||
|
||||
Alpine.plugin(mask);
|
||||
Alpine.start();
|
||||
_hyperscript.browserInit();
|
||||
|
||||
|
||||
|
||||
const successAudio = new Audio("/static/sounds/success.mp3");
|
||||
const popAudio = new Audio("/static/sounds/pop.mp3");
|
||||
window.paidSound = successAudio;
|
||||
|
||||
@@ -101,3 +101,32 @@
|
||||
z-index: 101;
|
||||
}
|
||||
}
|
||||
|
||||
.swing-out-top-bck {
|
||||
animation: swing-out-top-bck 0.45s cubic-bezier(0.600, -0.280, 0.735, 0.045) both;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------
|
||||
* Generated by Animista on 2024-12-14 15:59:19
|
||||
* Licensed under FreeBSD License.
|
||||
* See http://animista.net/license for more info.
|
||||
* w: http://animista.net, t: @cssanimista
|
||||
* ---------------------------------------------- */
|
||||
|
||||
/**
|
||||
* ----------------------------------------
|
||||
* animation swing-out-top-bck
|
||||
* ----------------------------------------
|
||||
*/
|
||||
@keyframes swing-out-top-bck {
|
||||
0% {
|
||||
transform: rotateX(0deg);
|
||||
transform-origin: top;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(-100deg);
|
||||
transform-origin: top;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user