Reticle@etched/reticle 0.0.0

File input

A button that opens the file picker, and the list of chosen files, each with a remove button. With label, it is wrapped in a Field.

Résumé
  • ana-ruiz-resume.pdf243 KB
import { useState } from "react";
import { FileInput } from "@etched/reticle/file-input";

export function Preview() {
  const [files, setFiles] = useState(() => [new File([new Uint8Array(248_000)], "ana-ruiz-resume.pdf", { type: "application/pdf" })]);

  return (
    <div className="grid">
      <FileInput label="Résumé" accept=".pdf,.docx,.txt" files={files} onFilesChange={setFiles}>
        Upload résumé
      </FileInput>
    </div>
  );
}

Installation

import { FileInput } from "@etched/reticle/file-input";

Examples

Résumé

One file: a new pick replaces it. The app holds the list in files and checks each pick; a problem shows as the field's error.

Résumé

PDF, DOCX or text, up to 10 MB.

import { useState } from "react";
import { FileInput } from "@etched/reticle/file-input";

// Orbit's résumé check (IntakeAttachments): the picker only suggests these types, so the app checks each pick.
const resumeTypes = /\.(pdf|docx|txt)$/i;

const maxBytes = 10 * 1024 * 1024;

export function Resume() {
  const [files, setFiles] = useState<File[]>([]);
  const [error, setError] = useState("");

  const check = (next: File[]) => {
    const bad = next.find((file) => !resumeTypes.test(file.name) || file.size > maxBytes);
    setError(bad ? `${bad.name}: choose a PDF, DOCX or text file up to 10 MB.` : "");

    if (!bad) setFiles(next);
  };

  return (
    <div className="grid">
      <FileInput
        label="Résumé"
        accept=".pdf,.docx,.txt"
        description="PDF, DOCX or text, up to 10 MB."
        error={error || undefined}
        files={files}
        onFilesChange={check}
      >
        Upload résumé
      </FileInput>
    </div>
  );
}

Several files

With multiple, each pick adds to the list. Without files, the input keeps its own list and reports it with onFilesChange.

Candidate files

Any file type, up to 10 MB each.

import { FileInput } from "@etched/reticle/file-input";

export function SeveralFiles() {
  return (
    <div className="grid">
      <FileInput label="Candidate files" multiple description="Any file type, up to 10 MB each.">
        Upload files
      </FileInput>
    </div>
  );
}

Optional and disabled

Cover letter (optional)
Offer letter
  • offer-letter-ana-ruiz.pdf178 KB
import { FileInput } from "@etched/reticle/file-input";

const offer = new File([new Uint8Array(182_000)], "offer-letter-ana-ruiz.pdf", { type: "application/pdf" });

export function OptionalAndDisabled() {
  return (
    <div className="grid">
      <FileInput label="Cover letter" required={false} accept=".pdf">
        Upload cover letter
      </FileInput>
      <FileInput label="Offer letter" files={[offer]} disabled>
        Upload offer letter
      </FileInput>
    </div>
  );
}

API reference

PropTypeDefaultDescription
labelReactNode–
descriptionReactNode–
errorFieldError–
filesFile[]–The chosen files, when the app controls them. Without it, the input keeps its own list.
onFilesChange(files: File[]) => void–Called with the new list after a pick or a remove.
acceptstring–The file types the picker offers, as on a native input: ".pdf,.docx" or "image/*". The user can still pick others, so check them.
multipleboolean–Each pick adds to the list (a file already in it is not added again). Without it, a pick replaces the file.
disabledboolean–
requiredboolean–false adds the optional marker after the label.
childrenReactNode"Choose file", or "Choose files" with `multiple`The button's text.
classNamestring–