/* global React */
// Scroll-as-depth — classic Explorer-style tree, web look.
// A normal indented tree (chevrons, folder icons, indent guide lines).
// Folders are NOT pre-expanded: each chosen folder opens automatically as you
// scroll down to it, and collapses as you scroll back up. One level per scroll
// step, with a clean expand/collapse animation (grid-rows).

const { useRef, useState } = React;

const T = {
  paper: "#f3eee2", paper2: "#e6dfc9", surface: "#fbf8ef",
  ink: "#1c1814", ink2: "#3a342c", muted: "#6c655a",
  hairline: "#d8cfba", hairline2: "#c5b99c",
  accent: "#6a1f23", accentInk: "#fbf8ef", accentSoft: "#f0d8d4", sel: "#e9dcc6",
  serif: '"Newsreader", Georgia, serif',
  sans: '"Geist", ui-sans-serif, system-ui, sans-serif',
  mono: '"Geist Mono", ui-monospace, Menlo, monospace',
};

const LEVELS = [
  { level: "Fonds", node: "Mira K. Adesanya Coastal Adaptation Photographic Archive",
    children: ["Field Studies and On-Site Documentation", "Press & Media Coverage", "Maps, Plans & Surveys", "Oral Histories", "Site Inspection Reports"], chosen: 0 },
  { level: "Series", node: "Field Studies and On-Site Documentation",
    children: ["Europe — Low Countries", "North America — Gulf & Eastern Seaboard", "West Africa", "Asia-Pacific Deltas"], chosen: 0 },
  { level: "Subseries", node: "Europe — Low Countries",
    children: ["Netherlands", "Belgium", "Luxembourg"], chosen: 0 },
  { level: "Group", node: "Netherlands",
    children: ["Rotterdam Metropolitan Region", "Amsterdam", "Nijmegen", "Dordrecht"], chosen: 0 },
  { level: "Subgroup", node: "Rotterdam Metropolitan Region",
    children: ["Maeslantkering Storm Surge Barrier", "Boompjes Embankment", "Katendrecht Walkway", "Noordereiland Flood Line"], chosen: 0 },
  { level: "File", node: "Maeslantkering Storm Surge Barrier",
    children: ["1998 Commissioning and Acceptance Testing", "2014 Decadal Stress Test", "2021 Structural Inspection"], chosen: 0 },
  { level: "Subfile", node: "1998 Commissioning and Acceptance Testing",
    children: ["Roll 14 — Second Full-Closure Stress Test", "Roll 15 — Public Observation Crowd", "Contact Sheet A"], chosen: 0 },
  { level: "Item group", node: "Roll 14 — Second Full-Closure Stress Test",
    children: ["Frame 21 — Northern Gate Approach", "Frame 22 — Northern Gate Sealing Sequence", "Frame 23 — Barrier Fully Sealed"], chosen: 1 },
  { level: "Item", node: "Frame 22 — Northern Gate Sealing Sequence",
    children: ["Recto — Silver gelatin print, full frame", "Verso — Engineer's annotation in pencil", "Conservation note", "Rights & reproduction statement"], chosen: 1 },
  { level: "Component", node: "Verso — Engineer's annotation in pencil",
    children: [], chosen: -1 },
];

const N = LEVELS.length;
const STEP = 96;     // scroll distance that opens one more level
const INDENT = 17;

// triangle chevron (Explorer-style)
const Tri = ({ open }) => (
  <svg viewBox="0 0 12 12" width="10" height="10" style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .18s" }}>
    <path d="M4 2l5 4-5 4z" fill="currentColor" />
  </svg>
);
const FolderClosed = ({ size = 15 }) => (
  <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="1.4">
    <path d="M3 7h6l2 2h10v10H3z" /></svg>
);
const FolderOpen = ({ size = 15 }) => (
  <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="1.4">
    <path d="M3 7h6l2 2h10v3H3z" /><path d="M3 12h17l-2 7H3z" /></svg>
);
const FileIco = ({ size = 15 }) => (
  <svg viewBox="0 0 24 24" width={size} height={size} fill="none" stroke="currentColor" strokeWidth="1.4">
    <path d="M6 3h9l4 4v14H6z" /><path d="M14 3v5h5" /></svg>
);
const SmallCaps = ({ children, style }) => (
  <span style={{ fontFamily: T.sans, fontSize: 9.5, letterSpacing: "0.13em",
    textTransform: "uppercase", fontWeight: 600, color: T.muted, ...style }}>{children}</span>
);
// children render only when open (nothing is there until you scroll to it)
const clamp = (v, a, b) => Math.max(a, Math.min(b, v));

if (!document.getElementById("ld-tree-kf")) {
  const s = document.createElement("style");
  s.id = "ld-tree-kf";
  s.textContent = "@keyframes ldOpen{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}} .ldOpen{animation:ldOpen .22s ease both}";
  document.head.appendChild(s);
}

// One tree row
function Row({ label, isFolder, open, active, deep, onClick }) {
  const Icon = !isFolder ? FileIco : (open ? FolderOpen : FolderClosed);
  return (
    <div onClick={onClick} title={label} style={{
      display: "flex", alignItems: "center", gap: 6, height: 28, padding: "0 8px 0 2px",
      background: active ? T.accentSoft : "transparent",
      borderLeft: active ? `2px solid ${T.accent}` : "2px solid transparent",
      color: active ? T.accent : T.ink, cursor: "default", whiteSpace: "nowrap",
    }}>
      <span style={{ width: 12, display: "inline-flex", justifyContent: "center",
        color: active ? T.accent : T.muted }}>
        {isFolder ? <Tri open={open} /> : null}
      </span>
      <span style={{ display: "inline-flex", color: active ? T.accent : (isFolder ? "#9a6b35" : T.muted) }}>
        <Icon />
      </span>
      <span style={{ fontSize: 13, fontWeight: active ? 600 : 400 }}>{label}</span>
    </div>
  );
}

// animated expand/collapse via grid-template-rows
function Collapse({ open, children }) {
  return (
    <div style={{ display: "grid", gridTemplateRows: open ? "1fr" : "0fr",
      transition: "grid-template-rows .26s ease", opacity: open ? 1 : 0.6 }}>
      <div style={{ overflow: "hidden" }}>{children}</div>
    </div>
  );
}

function Tree({ expanded, activeDepth, activeRef, onPick }) {
  function renderNode(d) {
    const L = LEVELS[d];
    const leaf = d === N - 1;
    const isOpen = d < expanded;
    const isActive = d === activeDepth;
    return (
      <div ref={isActive ? activeRef : undefined}>
        <Row label={L.node} isFolder={!leaf} open={isOpen} active={isActive}
          onClick={() => onPick(d)} />
        {!leaf && isOpen && (
          <div className="ldOpen" style={{ marginLeft: INDENT, borderLeft: `1px solid ${T.hairline2}` }}>
            {L.children.map((c, i) => {
              if (i === L.chosen) return <div key={i}>{renderNode(d + 1)}</div>;
              const childLeaf = d + 1 === N - 1;
              return (
                <Row key={i} label={c} isFolder={!childLeaf} open={false} active={false}
                  onClick={() => {}} />
              );
            })}
          </div>
        )}
      </div>
    );
  }
  return <div style={{ padding: "8px 0 8px 6px", minWidth: "max-content" }}>{renderNode(0)}</div>;
}

function Sidebar({ onActive }) {
  const wrapRef = useRef(null);   // outer clipping panel
  const treeRef = useRef(null);   // moves vertically
  const activeRef = useRef(null); // current frontier row
  const accum = useRef(0);
  const [depthF, setDepthF] = useState(0);
  const [scrollY, setScrollY] = useState(0);

  const expanded = clamp(Math.round(depthF), 0, N - 1); // chosen folders open
  const activeDepth = expanded;
  // subtle horizontal easing: deeper levels slide the tree over so long labels
  // at depth aren't clipped. Grows slower than the indent, so rows above never
  // slide off the left.
  const panX = depthF * 8;
  React.useEffect(() => { onActive && onActive(activeDepth); }, [activeDepth]);

  // after each depth change, glide the tree so the active row sits ~130px down
  React.useLayoutEffect(() => {
    const wrap = wrapRef.current, tree = treeRef.current, act = activeRef.current;
    if (!wrap || !tree || !act) return;
    const target = act.offsetTop - 130;
    const maxY = Math.max(0, tree.scrollHeight - wrap.clientHeight + 24);
    setScrollY(clamp(target, 0, maxY));
  }, [depthF]);

  function bump(dy) {
    accum.current = clamp(accum.current + dy, 0, (N - 1) * STEP);
    setDepthF(accum.current / STEP);
  }
  function onWheel(e) { e.preventDefault(); bump(e.deltaY); }
  const touchY = useRef(0);
  function onTouchStart(e) { touchY.current = e.touches[0].clientY; }
  function onTouchMove(e) {
    const y = e.touches[0].clientY; bump((touchY.current - y) * 1.4); touchY.current = y;
  }
  function pick(d) { accum.current = d * STEP; setDepthF(d); }

  return (
    <div ref={wrapRef} onWheel={onWheel} onTouchStart={onTouchStart} onTouchMove={onTouchMove}
      style={{ position: "relative", height: "100%", overflow: "hidden", background: T.surface }}>
      {/* depth pill */}
      <div style={{ position: "absolute", right: 10, top: 9, zIndex: 30, fontFamily: T.mono, fontSize: 10.5,
        color: T.muted, background: T.paper, border: `1px solid ${T.hairline}`, padding: "2px 7px" }}>
        L{activeDepth + 1}<span style={{ opacity: 0.5 }}>/{N}</span>
      </div>
      {/* scroll progress rail */}
      <div style={{ position: "absolute", right: 3, top: 8, bottom: 8, width: 3, background: T.hairline, zIndex: 20 }}>
        <div style={{ position: "absolute", left: 0, width: 3, height: 28, background: T.accent,
          top: `calc(${(depthF / (N - 1)) * 100}% - 14px)`, transition: "top .12s" }} />
      </div>

      <div ref={treeRef} style={{ transform: `translate(${-panX}px, ${-scrollY}px)`, transition: "transform .24s cubic-bezier(.4,0,.2,1)" }}>
        <Tree expanded={expanded} activeDepth={activeDepth} activeRef={activeRef} onPick={pick} />
      </div>
    </div>
  );
}

function App() {
  const [active, setActive] = useState(0);
  const cur = LEVELS[active];
  return (
    <div style={{ height: "100vh", display: "flex", flexDirection: "column", background: T.paper2,
      fontFamily: T.sans, color: T.ink }}>
      <div style={{ height: 52, background: T.paper, borderBottom: `1px solid ${T.hairline}`,
        display: "flex", alignItems: "center", padding: "0 22px", gap: 10, flexShrink: 0 }}>
        <span style={{ width: 26, height: 26, background: T.ink, color: T.paper,
          display: "grid", placeItems: "center", fontFamily: T.serif, fontWeight: 600, fontSize: 13 }}>E</span>
        <span style={{ fontFamily: T.serif, fontWeight: 600, fontSize: 15 }}>Eastbrook Libraries</span>
        <span style={{ color: T.muted, fontSize: 12.5 }}>· Finding aid</span>
        <div style={{ marginLeft: "auto", fontFamily: T.mono, fontSize: 11, color: T.muted }}>
          scroll ↓ to open each folder · scroll ↑ to close
        </div>
      </div>

      <div style={{ flex: 1, display: "grid", gridTemplateColumns: "364px 1fr", minHeight: 0 }}>
        <aside style={{ borderRight: `1px solid ${T.hairline}`, minHeight: 0 }}>
          <Sidebar onActive={setActive} />
        </aside>
        <main style={{ background: T.paper2, display: "flex", flexDirection: "column",
          alignItems: "center", justifyContent: "center", gap: 14, padding: 40, textAlign: "center" }}>
          <div style={{ width: 240, height: 176, background: "linear-gradient(135deg,#1f3a2e,#406b51 60%,#d6e4d8)" }} />
          <div style={{ maxWidth: 440 }}>
            <SmallCaps style={{ color: T.accent }}>{cur.level} · level {active + 1} of {N}</SmallCaps>
            <div style={{ fontFamily: T.serif, fontSize: 24, fontWeight: 500, lineHeight: 1.2,
              marginTop: 6, textWrap: "balance" }}>{cur.node}</div>
            <div style={{ fontFamily: T.mono, fontSize: 11, color: T.muted, marginTop: 8 }}>
              MS-2078 · the reading pane follows the open folder
            </div>
          </div>
        </main>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
