PasskeyToast.tsx

2.1 KB · raw

1import { createSignal, onMount, onCleanup } from 'solid-js';23interface PasskeyToastProps {4  site: string;5  displayName: string;6  username: string;7  requestId: string;8  onSignIn: () => void;9  onDeny: () => void;10}1112const TIMEOUT_MS = 30_000;1314export function PasskeyToast(props: PasskeyToastProps) {15  const [visible, setVisible] = createSignal(false);16  const [exiting, setExiting] = createSignal(false);17  let timeoutId: number;1819  onMount(() => {20    requestAnimationFrame(() => setVisible(true));2122    timeoutId = window.setTimeout(() => {23      handleDeny();24    }, TIMEOUT_MS);25  });2627  onCleanup(() => {28    clearTimeout(timeoutId);29  });3031  function handleSignIn() {32    clearTimeout(timeoutId);33    exit(props.onSignIn);34  }3536  function handleDeny() {37    clearTimeout(timeoutId);38    exit(props.onDeny);39  }4041  function exit(callback: () => void) {42    setExiting(true);43    setTimeout(callback, 220);44  }4546  // on:click bypasses Solid's document-level event delegation, which breaks47  // inside shadow DOM because event.target is retargeted to the shadow host.48  return (49    <div50      class={[51        'peach-passkey-toast',52        visible() ? 'is-visible' : '',53        exiting() ? 'is-exiting' : '',54      ].join(' ')}55    >56      <div class="peach-passkey-toast-body">57        <h2 class="peach-passkey-toast-headline">58          sign in to {props.site}?59        </h2>6061        <div class="peach-passkey-toast-user">62          {props.displayName} · {props.username}63        </div>6465        <div class="peach-passkey-toast-countdown">66          <div67            class="peach-passkey-toast-countdown-bar"68            style={{ 'animation-duration': `${TIMEOUT_MS}ms` }}69          />70        </div>7172        <div class="peach-passkey-toast-actions">73          <button74            class="peach-passkey-toast-btn peach-passkey-toast-btn--deny"75            on:click={handleDeny}76          >77            deny78          </button>79          <button80            class="peach-passkey-toast-btn peach-passkey-toast-btn--signin"81            on:click={handleSignIn}82          >83            sign in84          </button>85        </div>86      </div>87    </div>88  );89}90