mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-30 20:28:11 -07:00
website: redesign part 33
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
import { createElement } from "../../dom.js";
|
||||
|
||||
const FILL_MS = 2_000;
|
||||
const DRAIN_MS = 600;
|
||||
const LABEL = "Reset vault";
|
||||
|
||||
/**
|
||||
* @param {number} value
|
||||
*/
|
||||
function clampProgress(value) {
|
||||
return Math.max(0, Math.min(1, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {HTMLButtonElement} button
|
||||
* @param {() => void} onReset
|
||||
*/
|
||||
function bindHold(button, onReset) {
|
||||
/** @type {number | undefined} */
|
||||
let frame;
|
||||
let holding = false;
|
||||
let progress = 0;
|
||||
let previous = 0;
|
||||
|
||||
function render() {
|
||||
button.style.setProperty("--reset-progress", String(progress));
|
||||
button.style.setProperty("--reset-progress-width", `${progress * 100}%`);
|
||||
button.classList.toggle("active", progress > 0);
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (frame === undefined) return;
|
||||
|
||||
cancelAnimationFrame(frame);
|
||||
frame = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} now
|
||||
*/
|
||||
function tick(now) {
|
||||
const elapsed = now - previous;
|
||||
const rate = elapsed / (holding ? FILL_MS : DRAIN_MS);
|
||||
|
||||
previous = now;
|
||||
progress = clampProgress(progress + (holding ? rate : -rate));
|
||||
render();
|
||||
|
||||
if (holding && progress === 1) {
|
||||
stop();
|
||||
holding = false;
|
||||
progress = 0;
|
||||
button.classList.remove("holding");
|
||||
render();
|
||||
onReset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!holding && progress === 0) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
frame = requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (frame !== undefined) return;
|
||||
|
||||
previous = performance.now();
|
||||
frame = requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function release() {
|
||||
if (!holding) return;
|
||||
|
||||
holding = false;
|
||||
button.classList.remove("holding");
|
||||
run();
|
||||
}
|
||||
|
||||
function hold() {
|
||||
stop();
|
||||
|
||||
holding = true;
|
||||
button.classList.add("holding");
|
||||
run();
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
button.addEventListener("pointerdown", (event) => {
|
||||
if (event.button !== 0) return;
|
||||
|
||||
button.setPointerCapture(event.pointerId);
|
||||
hold();
|
||||
});
|
||||
button.addEventListener("pointerup", release);
|
||||
button.addEventListener("pointercancel", release);
|
||||
button.addEventListener("lostpointercapture", release);
|
||||
button.addEventListener("keydown", (event) => {
|
||||
if (event.repeat || (event.key !== " " && event.key !== "Enter")) return;
|
||||
|
||||
event.preventDefault();
|
||||
hold();
|
||||
});
|
||||
button.addEventListener("keyup", (event) => {
|
||||
if (event.key === " " || event.key === "Enter") {
|
||||
release();
|
||||
}
|
||||
});
|
||||
button.addEventListener("blur", release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void} onReset
|
||||
*/
|
||||
export function createResetButton(onReset) {
|
||||
const button = createElement("button", "reset");
|
||||
const label = document.createElement("span");
|
||||
|
||||
button.type = "button";
|
||||
button.dataset.label = LABEL;
|
||||
button.title = "Hold to reset";
|
||||
label.append(LABEL);
|
||||
button.append(label);
|
||||
bindHold(button, onReset);
|
||||
|
||||
return button;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
main.wallets {
|
||||
.start {
|
||||
.reset {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
justify-self: start;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
border-color: transparent;
|
||||
color: color-mix(in oklch, var(--gray) 76%, transparent);
|
||||
background: transparent;
|
||||
font-size: var(--font-size-sm);
|
||||
--reset-progress: 0;
|
||||
--reset-progress-width: 0%;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
z-index: -1;
|
||||
background: var(--red);
|
||||
transform: scaleX(var(--reset-progress));
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: attr(data-label);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: inherit;
|
||||
color: var(--black);
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
clip-path: inset(0 calc(100% - var(--reset-progress-width)) 0 0);
|
||||
}
|
||||
|
||||
span {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&:is(:hover, :focus-visible, :active):not(.holding) {
|
||||
color: var(--red);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
&.active::before,
|
||||
&.active::after {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user