AddLibraryButton

tcmts.studio.AddLibraryButton · #xhdjehrf · term · runs in the browser

uses useState

Add a library (tcm lib add): where it comes from (a release its author announced, @alice.dev/geo; a remote, a tcm serve URL, another codebase's directory, or tcm for tcm's own), and, from a remote, URL or directory, which package (or namespace hash); what to call it, if not its package's name. Given source and what (a registry's find), it's one button.

import { addLib, toldOf } from "#codebase/tcmts/studio";
import { useState } from "react";
import { Button, Dialog, DialogTrigger, Heading, Input, Label, Popover, TextField } from "react-aria-components";

export function AddLibraryButton({
  source = "",
  what = "",
  label = "Add a library",
}: {
  source?: string;
  what?: string;
  label?: string;
}) {
  const [form, setForm] = useState({ source, what, as: "" });
  const [busy, setBusy] = useState(false);
  const add = async (close?: () => void) => {
    setBusy(true);
    if (await toldOf(addLib(form.source, form.what, form.as), "Added it", "Couldn't add it"))
      close?.();
    setBusy(false);
  };
  if (source)
    return (
      <Button className="btn small" isPending={busy} onPress={() => void add()}>
        {label}
      </Button>
    );
  const field = (k: "source" | "what" | "as", name: string, hint: string) => (
    <TextField className="fieldrow" value={form[k]} onChange={(v) => setForm({ ...form, [k]: v })}>
      <Label>{name}</Label>
      <Input className="field mono" placeholder={hint} />
    </TextField>
  );
  return (
    <DialogTrigger onOpenChange={(open) => open && setForm({ source: "", what: "", as: "" })}>
      <Button className="btn">{label}</Button>
      <Popover className="menu-pop getit" placement="bottom end" offset={6}>
        <Dialog className="getit-body" aria-label="Add a library">
          {({ close }) => (
            <form
              className="prodform"
              onSubmit={(e) => {
                e.preventDefault();
                void add(close);
              }}
            >
              <Heading slot="title" className="getit-title">
                Add a library
              </Heading>
              {field("source", "From", "@alice.dev/geo, a remote, a URL, or tcm")}
              {field("what", "Which (from a remote, URL or tcm)", "geo@1.2.0, geo, or #hash")}
              {field("as", "Call it", "its package's name")}
              <p className="muted">
                It's fetched with what it uses, checked by hash, and mounted read-only under lib.
              </p>
              <div className="actions">
                <Button className="btn" onPress={close}>
                  Cancel
                </Button>
                <Button
                  className="btn primary"
                  type="submit"
                  isPending={busy}
                  isDisabled={!form.source.trim()}
                >
                  Add it
                </Button>
              </div>
            </form>
          )}
        </Dialog>
      </Popover>
    </DialogTrigger>
  );
}

uses

used by