fix: revamp reset popup

This commit is contained in:
2025-07-29 18:59:11 +02:00
parent 9ca30f412e
commit 977bdf3043
6 changed files with 110 additions and 99 deletions

View File

@@ -0,0 +1,51 @@
$animation-duration: 150ms;
$translate: translateY(8px);
[popover] {
position: absolute;
inset: unset;
transform: $translate;
margin: 0;
padding: 8px;
border: 1px solid var(--md-sys-color-outline);
border-radius: 8px;
color: var(--md-sys-color-on-surface);
opacity: 0;
background: var(--md-sys-color-surface);
transition:
transform $animation-duration ease,
opacity $animation-duration ease,
overlay $animation-duration allow-discrete,
display $animation-duration allow-discrete;
position-area: bottom span-right;
position-try-fallbacks:
top span-right,
bottom span-left,
top span-left;
position-visibility: no-overflow;
&:popover-open {
transform: translateY(0);
opacity: 1;
}
> h1:first-child,
h2:first-child,
h3:first-child {
margin-top: 0;
text-align: center;
}
}
@starting-style {
[popover]:popover-open {
transform: $translate;
opacity: 0;
}
}

View File

@@ -8,6 +8,7 @@
@use "print";
@use "elements/h1";
@use "elements/popover";
body {
overflow: hidden;

View File

@@ -161,7 +161,10 @@
><span class="icon">reset_settings</span>Reset Settings</button
>
{/if}
<button use:popup={ResetPopup}>Recovery...</button>
<button popovertarget="reset-device" popovertargetaction="toggle"
>Recovery...</button
>
<div id="reset-device" popover="auto"><ResetPopup /></div>
{/if}
</div>
</div>

View File

@@ -1,43 +0,0 @@
<script lang="ts">
import { serialPort } from "$lib/serial/connection";
let { challenge, onconfirm }: { challenge: string; onconfirm: () => void } =
$props();
let challengeInput = $state("");
let challengeString = $derived(`${challenge} ${$serialPort!.device}`);
let isValid = $derived(challengeInput === challengeString);
</script>
<h3>Type the following to confirm the action</h3>
<p>{challengeString}</p>
<!-- svelte-ignore a11y_autofocus -->
<input
autofocus
type="text"
bind:value={challengeInput}
placeholder={challengeString}
/>
<button disabled={!isValid} onclick={onconfirm}>Confirm {challenge}</button>
<style lang="scss">
input[type="text"] {
color: inherit;
font-family: inherit;
background: none;
border: none;
border-bottom: 1px solid currentcolor;
width: 100%;
&:focus {
outline: none;
border-color: var(--md-sys-color-secondary);
}
}
button {
color: var(--md-sys-color-error);
}
</style>

View File

@@ -1,5 +1,4 @@
<script lang="ts">
import { confirmChallenge } from "./confirm-challenge";
import { serialPort } from "$lib/serial/connection";
const options = [
@@ -17,21 +16,27 @@
</script>
<h3>Reset Device</h3>
<p>Resetting might take <b>up to 2 Minutes</b>.</p>
{#each options as category, i}
{#if i > 0}
<hr />
{/if}
{#each category as [command, description]}
<button
class="error"
use:confirmChallenge={{
onConfirm() {
$serialPort?.reset(command);
$serialPort = undefined;
},
challenge: description,
}}>{description}...</button
<form
onsubmit={(event) => {
event.preventDefault();
$serialPort?.reset(command);
$serialPort = undefined;
}}
>
<input
type="text"
placeholder={description}
required
pattern="^{description}$"
/>
<button class="icon" type="submit">send</button>
</form>
{/each}
{/each}
@@ -39,4 +44,43 @@
hr {
opacity: 0.25;
}
p {
width: 22ch;
}
button.icon {
font-size: 20px;
opacity: 0.5;
}
form {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
input[type="text"] {
color: inherit;
font-family: inherit;
background: none;
border: none;
border-bottom: 1px solid transparent;
width: fit-content;
&:focus {
outline: none;
border-color: var(--md-sys-color-outline);
}
}
input[type="text"]:valid {
color: var(--md-sys-color-error);
& + button {
color: var(--md-sys-color-error);
opacity: 1;
}
}
</style>

View File

@@ -1,45 +0,0 @@
import type { Action } from "svelte/action";
import ConfirmChallenge from "./ConfirmChallenge.svelte";
import tippy from "tippy.js";
import { mount, unmount } from "svelte";
export const confirmChallenge: Action<
HTMLElement,
{ onConfirm: () => void; challenge: string }
> = (node, { onConfirm, challenge }) => {
let component: {} | undefined;
let target: HTMLElement | undefined;
const edit = tippy(node, {
interactive: true,
trigger: "click",
onShow(instance) {
target = instance.popper.querySelector(".tippy-content") as HTMLElement;
target.classList.add("active");
if (component === undefined) {
component = mount(ConfirmChallenge, {
target,
props: {
challenge,
onconfirm() {
edit.hide();
onConfirm();
},
},
});
}
},
onHidden() {
if (component) {
unmount(component);
}
target?.classList.remove("active");
component = undefined;
},
});
return {
destroy() {
edit.destroy();
},
};
};