~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
54 lines · 1.5 KB Raw History
import { Form } from '@base-ui/react/form';
import { useState } from 'react';
import { Button } from '#/components/atoms/button.jsx';
import { Notice } from '#/components/atoms/notice.jsx';
import { cn } from '#/lib/utils.js';

/**
 * The form frame. Fields validate through native constraint attributes, so
 * an invalid submit is blocked by the browser in the browser's own words;
 * values are read off the element with FormData, never mirrored into state.
 * The submit handler's rejection becomes the root error.
 */
export function FormShell({
  className,
  submitLabel,
  onSubmit,
  onSucceed,
  children,
  ...props
}) {
  const [error, setError] = useState('');
  const [busy, setBusy] = useState(false);

  return (
    <Form
      // Base UI defaults to noValidate; the browser's gate goes back on
      noValidate={false}
      className={cn('flex flex-col gap-4', className)}
      onSubmit={async (event) => {
        event.preventDefault();
        const form = event.currentTarget;
        const values = Object.fromEntries(new FormData(form));
        setError('');
        setBusy(true);
        try {
          await onSubmit(values);
          form.reset();
          onSucceed?.();
        } catch (err) {
          setError(err instanceof Error ? err.message : String(err));
        } finally {
          setBusy(false);
        }
      }}
      {...props}
    >
      {children}
      {error && <Notice variant="error">{error}</Notice>}
      <Button type="submit" size="block" disabled={busy}>
        {submitLabel}
      </Button>
    </Form>
  );
}