ui: Remove photo cropper element

It turns out the cropper is _way_ too hard to use on mobile.  The
cropper handles are much too small for a touch screen.  Maybe I can
figure out how to improve the UX eventually, but for now, Tabitha
requested that we remove that functionality.
bugfix/ci-buildah
Dustin 2025-03-08 20:20:29 -06:00
parent 9cef220f90
commit 3b586413ff
4 changed files with 6 additions and 171 deletions

133
js/package-lock.json generated
View File

@ -6,133 +6,12 @@
"": { "": {
"name": "receipts", "name": "receipts",
"dependencies": { "dependencies": {
"@shoelace-style/shoelace": "^2.20.0", "@shoelace-style/shoelace": "^2.20.0"
"cropperjs": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.25.0" "esbuild": "^0.25.0"
} }
}, },
"node_modules/@cropper/element": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element/-/element-2.0.0.tgz",
"integrity": "sha512-lsthn0nQq73GExUE7Mg/ss6Q3RXADGDv055hxoLFwvl/wGHgy6ZkYlfLZ/VmgBHC6jDK5IgPBFnqrPqlXWSGBA==",
"license": "MIT",
"dependencies": {
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-canvas": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-canvas/-/element-canvas-2.0.0.tgz",
"integrity": "sha512-GPtGJgSm92crJhhhwUsaMw3rz2KfJWWSz7kRAlufFEV/EHTP5+6r6/Z1BCGRna830i+Avqbm435XLOtA7PVJwA==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-crosshair": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-crosshair/-/element-crosshair-2.0.0.tgz",
"integrity": "sha512-KfPfyrdeFvUC31Ws7ATtcalWWSaMtrC6bMoCipZhqbUOE7wZoL4ecDSL6BUOZxPa74awZUqfzirCDjHvheBfyw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-grid": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-grid/-/element-grid-2.0.0.tgz",
"integrity": "sha512-i78SQ0IJTLFveKX6P7svkfMYVdgHrQ8ZmmEw8keFy9n1ZVbK+SK0UHK5FNMRNI/gtVhKJOGEnK/zeyjUdj4Iyw==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-handle": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-handle/-/element-handle-2.0.0.tgz",
"integrity": "sha512-ZJvW+0MkK9E8xYymGdoruaQn2kwjSHFpNSWinjyq6csuVQiCPxlX5ovAEDldmZ9MWePPtWEi3vLKQOo2Yb0T8g==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-image": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-image/-/element-image-2.0.0.tgz",
"integrity": "sha512-9BxiTS/aHRmrjopaFQb9mQQXmx4ruhYHGkDZMVz24AXpMFjUY6OpqrWse/WjzD9tfhMFvEdu17b3VAekcAgpeg==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-selection": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-selection/-/element-selection-2.0.0.tgz",
"integrity": "sha512-ensNnbIfJsJ8bhbJTH/RXtk2URFvTOO4TvfRk461n2FPEC588D7rwBmUJxQg74IiTi4y1JbCI+6j+4LyzYBLCQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-shade": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-shade/-/element-shade-2.0.0.tgz",
"integrity": "sha512-jv/2bbNZnhU4W+T4G0c8ADocLIZvQFTXgCf2RFDNhI5UVxurzWBnDdb8Mx8LnVplnkTqO+xUmHZYve0CwgWo+Q==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/element-viewer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/element-viewer/-/element-viewer-2.0.0.tgz",
"integrity": "sha512-zY+3VRN5TvpM8twlphYtXw0tzJL2VgzeK7ufhL1BixVqOdRxwP13TprYIhqwGt9EW/SyJZUiaIu396T89kRX8A==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/@cropper/elements": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/elements/-/elements-2.0.0.tgz",
"integrity": "sha512-PQkPo1nUjxLFUQuHYu+6atfHxpX9B41Xribao6wpvmvmNIFML6LQdNqqWYb6LyM7ujsu71CZdBiMT5oetjJVoQ==",
"license": "MIT",
"dependencies": {
"@cropper/element": "^2.0.0",
"@cropper/element-canvas": "^2.0.0",
"@cropper/element-crosshair": "^2.0.0",
"@cropper/element-grid": "^2.0.0",
"@cropper/element-handle": "^2.0.0",
"@cropper/element-image": "^2.0.0",
"@cropper/element-selection": "^2.0.0",
"@cropper/element-shade": "^2.0.0",
"@cropper/element-viewer": "^2.0.0"
}
},
"node_modules/@cropper/utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@cropper/utils/-/utils-2.0.0.tgz",
"integrity": "sha512-cprLYr+7kK3faGgoOsTW9gIn5sefDr2KwOmgyjzIXk+8PLpW8FgFKEg5FoWfRD5zMAmkCBuX6rGKDK3VdUEGrg==",
"license": "MIT"
},
"node_modules/@ctrl/tinycolor": { "node_modules/@ctrl/tinycolor": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz", "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz",
@ -680,16 +559,6 @@
"@floating-ui/utils": "^0.2.5" "@floating-ui/utils": "^0.2.5"
} }
}, },
"node_modules/cropperjs": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-2.0.0.tgz",
"integrity": "sha512-TO2j0Qre01kPHbow4FuTrbdEB4jTmGRySxW49jyEIqlJZuEBfrvCTT0vC3eRB2WBXudDfKi1Onako6DKWKxeAQ==",
"license": "MIT",
"dependencies": {
"@cropper/elements": "^2.0.0",
"@cropper/utils": "^2.0.0"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",

View File

@ -2,8 +2,7 @@
"name": "receipts", "name": "receipts",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@shoelace-style/shoelace": "^2.20.0", "@shoelace-style/shoelace": "^2.20.0"
"cropperjs": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.25.0" "esbuild": "^0.25.0"

View File

@ -12,7 +12,6 @@ import SlButton from "@shoelace-style/shoelace/dist/components/button/button.js"
import SlDetails from "@shoelace-style/shoelace/dist/components/details/details.js"; import SlDetails from "@shoelace-style/shoelace/dist/components/details/details.js";
import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js"; import SlIconButton from "@shoelace-style/shoelace/dist/components/icon-button/icon-button.js";
import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js"; import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js";
import Cropper from "cropperjs";
import { notify } from "./alert"; import { notify } from "./alert";
@ -25,30 +24,19 @@ const video = photoview.querySelector("video")!;
const btnshutter = photoview.querySelector( const btnshutter = photoview.querySelector(
" sl-icon-button[label='Take Photo']", " sl-icon-button[label='Take Photo']",
) as SlIconButton; ) as SlIconButton;
const btncrop = photoview.querySelector(
" sl-icon-button[label='Crop']",
) as SlIconButton;
const btnreset = photoview.querySelector( const btnreset = photoview.querySelector(
" sl-icon-button[label='Start Over']", " sl-icon-button[label='Start Over']",
) as SlIconButton; ) as SlIconButton;
const btnsubmit = form.querySelector("sl-button[type='submit']") as SlButton;
let cropper: Cropper | null = null;
let initialized = false; let initialized = false;
async function clearCamera() { async function clearCamera() {
if (cropper) {
cropper.getCropperCanvas()?.remove();
cropper = null;
}
video.pause(); video.pause();
video.srcObject = null; video.srcObject = null;
video.classList.add("invisible"); video.classList.add("invisible");
video.parentNode?.querySelectorAll("canvas").forEach((e) => e.remove()); video.parentNode?.querySelectorAll("canvas").forEach((e) => e.remove());
btnshutter.disabled = true; btnshutter.disabled = true;
btnshutter.classList.add("invisible"); btnshutter.classList.add("invisible");
btncrop.disabled = true;
btncrop.classList.add("invisible");
btnreset.disabled = true; btnreset.disabled = true;
btnreset.classList.add("invisible"); btnreset.classList.add("invisible");
photoview.classList.remove("invisible"); photoview.classList.remove("invisible");
@ -78,7 +66,8 @@ async function startCamera() {
} }
async function submitForm(data: FormData) { async function submitForm(data: FormData) {
btnsubmit.loading = true; const btn = form.querySelector("sl-button[type='submit']") as SlButton;
btn.loading = true;
let r: Response | null = null; let r: Response | null = null;
try { try {
r = await fetch("", { r = await fetch("", {
@ -93,7 +82,7 @@ async function submitForm(data: FormData) {
null, null,
); );
} }
btnsubmit.loading = false; btn.loading = false;
if (r) { if (r) {
if (r.ok) { if (r.ok) {
notify( notify(
@ -121,11 +110,11 @@ async function submitForm(data: FormData) {
} }
function takePhoto() { function takePhoto() {
btnsubmit.disabled = true;
btnshutter.disabled = true; btnshutter.disabled = true;
btnshutter.classList.add("invisible"); btnshutter.classList.add("invisible");
video.pause(); video.pause();
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.setAttribute("id", "camera-photo");
const context = canvas.getContext("2d"); const context = canvas.getContext("2d");
if (!context) { if (!context) {
notify( notify(
@ -144,10 +133,6 @@ function takePhoto() {
video.srcObject = null; video.srcObject = null;
video.classList.add("invisible"); video.classList.add("invisible");
video.parentNode!.appendChild(canvas); video.parentNode!.appendChild(canvas);
cropper = new Cropper(canvas);
cropper.getCropperCanvas()!.style.height = `${height}px`;
btncrop.disabled = false;
btncrop.classList.remove("invisible");
btnreset.disabled = false; btnreset.disabled = false;
btnreset.classList.remove("invisible"); btnreset.classList.remove("invisible");
} }
@ -168,21 +153,6 @@ btnshutter.addEventListener("click", async () => {
takePhoto(); takePhoto();
}); });
btncrop.addEventListener("click", async () => {
btnsubmit.disabled = false;
if (cropper) {
const canvas = await cropper.getCropperSelection()?.$toCanvas();
if (canvas) {
canvas.setAttribute("id", "camera-photo");
video.parentNode!.appendChild(canvas);
cropper.getCropperCanvas()?.remove();
btncrop.disabled = true;
btncrop.classList.add("invisible");
cropper = null;
}
}
});
btnreset.addEventListener("click", () => { btnreset.addEventListener("click", () => {
clearCamera(); clearCamera();
startCamera(); startCamera();

View File

@ -39,9 +39,6 @@
<sl-tooltip content="Take Photo"> <sl-tooltip content="Take Photo">
<sl-icon-button name="camera" label="Take Photo"></sl-icon-button> <sl-icon-button name="camera" label="Take Photo"></sl-icon-button>
</sl-tooltip> </sl-tooltip>
<sl-tooltip content="Crop">
<sl-icon-button name="crop" label="Crop"></sl-icon-button>
</sl-tooltip>
<sl-tooltip content="Start Over"> <sl-tooltip content="Start Over">
<sl-icon-button <sl-icon-button
name="trash" name="trash"