-
Notifications
You must be signed in to change notification settings - Fork 684
Expand file tree
/
Copy pathbutton.js
More file actions
113 lines (91 loc) · 3.49 KB
/
button.js
File metadata and controls
113 lines (91 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
const HOLD_DURATION = 2000; // 2 seconds
const COOLDOWN_DURATION = 1500; // cooldown after trigger
const CIRCUMFERENCE = 2 * Math.PI * 45; // circle circumference
export default function ({ parentElement, setTriggerValue, data }) {
const button = parentElement.querySelector("#danger-btn");
const progress = parentElement.querySelector("#ring-progress");
const icon = parentElement.querySelector("#icon");
const label = parentElement.querySelector("#label");
let startTime = null;
let animationFrame = null;
let isDisabled = false; // Prevent interaction during cooldown
function updateProgress() {
if (!startTime) return;
const elapsed = Date.now() - startTime;
const progressPercent = Math.min(elapsed / HOLD_DURATION, 1);
const offset = CIRCUMFERENCE * (1 - progressPercent);
progress.style.strokeDashoffset = offset;
if (progressPercent >= 1) {
// Triggered!
triggerAction();
} else {
animationFrame = requestAnimationFrame(updateProgress);
}
}
function startHold() {
if (isDisabled) return; // Ignore if in cooldown
startTime = Date.now();
button.classList.add("holding");
label.textContent = data?.continue ?? "Keep holding...";
animationFrame = requestAnimationFrame(updateProgress);
}
function cancelHold() {
if (isDisabled) return; // Ignore if in cooldown
startTime = null;
button.classList.remove("holding");
label.textContent = data?.start ?? "Hold to Delete";
progress.style.strokeDashoffset = CIRCUMFERENCE;
if (animationFrame) {
cancelAnimationFrame(animationFrame);
animationFrame = null;
}
}
function triggerAction() {
cancelAnimationFrame(animationFrame);
animationFrame = null;
startTime = null;
isDisabled = true; // Disable during cooldown
button.classList.remove("holding");
button.classList.add("triggered");
button.disabled = true;
icon.textContent = "✓";
label.textContent = data?.completed ?? "Deleted!";
progress.style.strokeDashoffset = 0;
// Send trigger to Python
setTriggerValue("confirmed", true);
// Reset after cooldown
setTimeout(() => {
button.classList.remove("triggered");
button.disabled = false;
isDisabled = false;
icon.textContent = data?.icon ?? "🗑️";
label.textContent = data?.start ?? "Hold to Delete";
progress.style.strokeDashoffset = CIRCUMFERENCE;
}, COOLDOWN_DURATION);
}
function handleTouchStart(e) {
e.preventDefault();
startHold();
}
// Mouse events
button.addEventListener("mousedown", startHold);
button.addEventListener("mouseup", cancelHold);
button.addEventListener("mouseleave", cancelHold);
button.addEventListener("contextmenu", cancelHold); // Ctrl+Click on Mac
// Touch events for mobile
button.addEventListener("touchstart", handleTouchStart);
button.addEventListener("touchend", cancelHold);
button.addEventListener("touchcancel", cancelHold);
return () => {
if (animationFrame) cancelAnimationFrame(animationFrame);
// Remove mouse event listeners
button.removeEventListener("mousedown", startHold);
button.removeEventListener("mouseup", cancelHold);
button.removeEventListener("mouseleave", cancelHold);
button.removeEventListener("contextmenu", cancelHold);
// Remove touch event listeners
button.removeEventListener("touchstart", handleTouchStart);
button.removeEventListener("touchend", cancelHold);
button.removeEventListener("touchcancel", cancelHold);
};
}