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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
73 lines · 2.7 KB Raw History
import { useState } from 'react';
import { Skeleton } from '#/components/atoms/skeleton.jsx';
import { RefSelect } from '#/components/molecules/ref-select.jsx';
import { RepoTree } from '#/components/molecules/repo-tree.jsx';
import { SearchField } from '#/components/molecules/search-field.jsx';
import { useFileList } from '#/lib/source.js';

/**
 * The source view: one directory down the left, whatever the reader opened
 * down the right. Opening a file keeps its neighbours in view, which is what
 * makes reading a repository feel like reading a checkout.
 *
 * The rail opens on the ref picker and a filter over the tree, the way a
 * forge's file rail does. The gauges are the home's, not the rail's. The tree is
 * the whole repository, opened along the way to `dirPath`, with `selected`
 * marked: the file in view, or the directory itself. The pane keeps its own
 * scroll, so a long tree and a long file each move on their own.
 * @param {{repo: string, record: Record<string, unknown>, refName: string, onSelectRef: (ref: string) => void, dirPath: string, selected?: string, space?: string, children: import('react').ReactNode}} props
 */
export function SourcePanes({
  repo,
  record,
  refName,
  onSelectRef,
  dirPath,
  selected = '',
  space,
  children,
}) {
  const { data: files } = useFileList(repo, refName, space);
  const [filter, setFilter] = useState('');

  return (
    // The panes fill what the window has left and each takes its own scroll,
    // so a long listing and a long file move past each other.
    <div className="flex min-h-0 flex-1 items-stretch">
      <aside className="hidden w-72 shrink-0 flex-col border-border border-r md:flex">
        <div className="flex shrink-0 flex-col gap-2 border-border border-b px-3 py-3">
          <RefSelect
            repo={record}
            value={refName}
            onSelect={onSelectRef}
            className="w-full justify-start [&>span]:max-w-none"
          />
          <SearchField
            value={filter}
            onChange={setFilter}
            placeholder="Filter files"
          />
        </div>
        <div className="min-h-0 flex-1 overflow-y-auto">
          {files ? (
            <RepoTree
              repo={repo}
              refName={refName}
              files={files}
              path={dirPath}
              selected={selected || dirPath}
              filter={filter}
            />
          ) : (
            <div className="flex flex-col gap-2 p-4">
              <Skeleton className="h-4 w-32" />
              <Skeleton className="h-4 w-24" />
              <Skeleton className="h-4 w-36" />
            </div>
          )}
        </div>
      </aside>
      <div className="min-w-0 flex-1 overflow-y-auto">{children}</div>
    </div>
  );
}